aboutsummaryrefslogtreecommitdiff
path: root/contrib/bmake
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/bmake')
-rw-r--r--contrib/bmake/ChangeLog1101
-rw-r--r--contrib/bmake/FILES89
-rw-r--r--contrib/bmake/Makefile57
-rw-r--r--contrib/bmake/Makefile.config.in4
-rw-r--r--contrib/bmake/README17
-rw-r--r--contrib/bmake/VERSION2
-rw-r--r--contrib/bmake/_strtol.h213
-rw-r--r--contrib/bmake/arch.c461
-rw-r--r--contrib/bmake/bmake.12084
-rw-r--r--contrib/bmake/bmake.cat12443
-rwxr-xr-xcontrib/bmake/boot-strap46
-rw-r--r--contrib/bmake/bsd.after-import.mk18
-rw-r--r--contrib/bmake/buf.c53
-rw-r--r--contrib/bmake/buf.h23
-rw-r--r--contrib/bmake/compat.c267
-rw-r--r--contrib/bmake/cond.c983
-rw-r--r--contrib/bmake/config.h.in158
-rwxr-xr-xcontrib/bmake/configure5449
-rw-r--r--contrib/bmake/configure.in280
-rw-r--r--contrib/bmake/dir.c401
-rw-r--r--contrib/bmake/dir.h15
-rwxr-xr-xcontrib/bmake/enum.c80
-rwxr-xr-xcontrib/bmake/enum.h179
-rw-r--r--contrib/bmake/filemon/filemon.h4
-rw-r--r--contrib/bmake/filemon/filemon_dev.c12
-rw-r--r--contrib/bmake/filemon/filemon_ktrace.c3
-rwxr-xr-xcontrib/bmake/find_lib.sh7
-rw-r--r--contrib/bmake/for.c413
-rw-r--r--contrib/bmake/hash.c109
-rw-r--r--contrib/bmake/hash.h48
-rwxr-xr-xcontrib/bmake/import.sh43
-rwxr-xr-xcontrib/bmake/install-sh77
-rw-r--r--contrib/bmake/job.c328
-rw-r--r--contrib/bmake/job.h38
-rw-r--r--contrib/bmake/lst.c28
-rw-r--r--contrib/bmake/lst.h37
-rwxr-xr-xcontrib/bmake/machine.sh81
-rw-r--r--contrib/bmake/main.c843
-rwxr-xr-xcontrib/bmake/make-bootstrap.sh.in4
-rw-r--r--contrib/bmake/make-conf.h50
-rw-r--r--contrib/bmake/make.12084
-rw-r--r--contrib/bmake/make.c260
-rw-r--r--contrib/bmake/make.h819
-rw-r--r--contrib/bmake/make_malloc.c12
-rw-r--r--contrib/bmake/make_malloc.h26
-rw-r--r--contrib/bmake/meta.c368
-rw-r--r--contrib/bmake/meta.h8
-rw-r--r--contrib/bmake/metachar.c6
-rw-r--r--contrib/bmake/metachar.h16
-rw-r--r--contrib/bmake/mk/ChangeLog606
-rw-r--r--contrib/bmake/mk/FILES10
-rw-r--r--contrib/bmake/mk/auto.dep.mk56
-rw-r--r--contrib/bmake/mk/auto.obj.mk4
-rw-r--r--contrib/bmake/mk/autoconf.mk40
-rw-r--r--contrib/bmake/mk/autodep.mk94
-rw-r--r--contrib/bmake/mk/cc-wrap.mk68
-rw-r--r--contrib/bmake/mk/ccm.dep.mk60
-rw-r--r--contrib/bmake/mk/compiler.mk14
-rw-r--r--contrib/bmake/mk/cython.mk4
-rw-r--r--contrib/bmake/mk/dep.mk19
-rw-r--r--contrib/bmake/mk/dirdeps-cache-update.mk4
-rw-r--r--contrib/bmake/mk/dirdeps-options.mk18
-rw-r--r--contrib/bmake/mk/dirdeps-targets.mk16
-rw-r--r--contrib/bmake/mk/dirdeps.mk260
-rw-r--r--contrib/bmake/mk/doc.mk9
-rw-r--r--contrib/bmake/mk/dpadd.mk20
-rw-r--r--contrib/bmake/mk/files.mk4
-rw-r--r--contrib/bmake/mk/final.mk4
-rw-r--r--contrib/bmake/mk/gendirdeps.mk44
-rw-r--r--contrib/bmake/mk/host-target.mk53
-rw-r--r--contrib/bmake/mk/inc.mk4
-rw-r--r--contrib/bmake/mk/init.mk43
-rw-r--r--[-rwxr-xr-x]contrib/bmake/mk/install-mk23
-rw-r--r--contrib/bmake/mk/install-new.mk4
-rwxr-xr-xcontrib/bmake/mk/install-sh228
-rw-r--r--contrib/bmake/mk/java.mk5
-rw-r--r--contrib/bmake/mk/jobs.mk108
-rw-r--r--contrib/bmake/mk/ldorder.mk6
-rw-r--r--contrib/bmake/mk/lib.mk66
-rw-r--r--contrib/bmake/mk/libnames.mk4
-rw-r--r--contrib/bmake/mk/libs.mk4
-rw-r--r--contrib/bmake/mk/links.mk4
-rw-r--r--contrib/bmake/mk/man.mk151
-rw-r--r--contrib/bmake/mk/manifest.mk4
-rw-r--r--contrib/bmake/mk/meta.autodep.mk33
-rw-r--r--contrib/bmake/mk/meta.stage.mk21
-rw-r--r--contrib/bmake/mk/meta.subdir.mk5
-rw-r--r--contrib/bmake/mk/meta.sys.mk100
-rwxr-xr-xcontrib/bmake/mk/meta2deps.py89
-rwxr-xr-xcontrib/bmake/mk/meta2deps.sh51
-rw-r--r--contrib/bmake/mk/mk-files.txt183
-rw-r--r--contrib/bmake/mk/mkopt.sh33
-rwxr-xr-xcontrib/bmake/mk/newlog.sh414
-rw-r--r--contrib/bmake/mk/obj.mk11
-rw-r--r--contrib/bmake/mk/options.mk40
-rw-r--r--contrib/bmake/mk/own.mk36
-rw-r--r--contrib/bmake/mk/posix.mk106
-rw-r--r--contrib/bmake/mk/prlist.mk6
-rw-r--r--contrib/bmake/mk/prog.mk59
-rw-r--r--contrib/bmake/mk/progs.mk24
-rw-r--r--contrib/bmake/mk/rst2htm.mk11
-rw-r--r--contrib/bmake/mk/scripts.mk4
-rw-r--r--contrib/bmake/mk/srctop.mk4
-rwxr-xr-xcontrib/bmake/mk/stage-install.sh12
-rw-r--r--contrib/bmake/mk/subdir.mk131
-rw-r--r--contrib/bmake/mk/suffixes.mk195
-rw-r--r--contrib/bmake/mk/sys.clean-env.mk12
-rw-r--r--contrib/bmake/mk/sys.debug.mk4
-rw-r--r--contrib/bmake/mk/sys.dependfile.mk13
-rw-r--r--contrib/bmake/mk/sys.dirdeps.mk205
-rw-r--r--contrib/bmake/mk/sys.mk48
-rw-r--r--contrib/bmake/mk/sys.vars.mk91
-rw-r--r--contrib/bmake/mk/sys/AIX.mk3
-rw-r--r--contrib/bmake/mk/sys/Cygwin.mk21
-rw-r--r--contrib/bmake/mk/sys/Darwin.mk3
-rw-r--r--contrib/bmake/mk/sys/Generic.mk182
-rw-r--r--contrib/bmake/mk/sys/HP-UX.mk5
-rw-r--r--contrib/bmake/mk/sys/IRIX.mk16
-rw-r--r--contrib/bmake/mk/sys/Linux.mk9
-rw-r--r--contrib/bmake/mk/sys/NetBSD.mk3
-rw-r--r--contrib/bmake/mk/sys/OSF1.mk5
-rw-r--r--contrib/bmake/mk/sys/OpenBSD.mk3
-rw-r--r--contrib/bmake/mk/sys/SCO_SV.mk13
-rw-r--r--contrib/bmake/mk/sys/SunOS.mk5
-rw-r--r--contrib/bmake/mk/sys/UnixWare.mk27
-rw-r--r--contrib/bmake/mk/target-flags.mk4
-rw-r--r--contrib/bmake/mk/warnings.mk72
-rw-r--r--contrib/bmake/mk/whats.mk6
-rw-r--r--contrib/bmake/mk/yacc.mk28
-rwxr-xr-xcontrib/bmake/mkdeps.sh9
-rw-r--r--contrib/bmake/nonints.h347
-rwxr-xr-x[-rw-r--r--]contrib/bmake/os.sh40
-rw-r--r--contrib/bmake/parse.c2429
-rw-r--r--contrib/bmake/sigact.h104
-rw-r--r--contrib/bmake/sigaction.c397
-rw-r--r--contrib/bmake/sigcompat.c4
-rw-r--r--contrib/bmake/str.c206
-rw-r--r--contrib/bmake/str.h111
-rw-r--r--contrib/bmake/strlcpy.c5
-rw-r--r--contrib/bmake/suff.c343
-rw-r--r--contrib/bmake/targ.c158
-rw-r--r--contrib/bmake/trace.c21
-rw-r--r--contrib/bmake/unit-tests/Makefile260
-rw-r--r--contrib/bmake/unit-tests/Makefile.config.in5
-rw-r--r--contrib/bmake/unit-tests/archive.exp8
-rw-r--r--contrib/bmake/unit-tests/archive.mk33
-rw-r--r--contrib/bmake/unit-tests/cmd-errors-jobs.exp16
-rw-r--r--contrib/bmake/unit-tests/cmd-errors-jobs.mk41
-rw-r--r--contrib/bmake/unit-tests/cmd-errors-lint.exp8
-rw-r--r--contrib/bmake/unit-tests/cmd-errors-lint.mk11
-rw-r--r--contrib/bmake/unit-tests/cmd-errors.exp16
-rw-r--r--contrib/bmake/unit-tests/cmd-errors.mk21
-rwxr-xr-xcontrib/bmake/unit-tests/cmd-interrupt.exp1
-rwxr-xr-xcontrib/bmake/unit-tests/cmd-interrupt.mk4
-rw-r--r--contrib/bmake/unit-tests/cmdline-undefined.exp24
-rw-r--r--contrib/bmake/unit-tests/cmdline-undefined.mk16
-rw-r--r--contrib/bmake/unit-tests/cmdline.exp7
-rw-r--r--contrib/bmake/unit-tests/cmdline.mk26
-rw-r--r--contrib/bmake/unit-tests/comment.mk14
-rw-r--r--contrib/bmake/unit-tests/compat-error.mk27
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-numeric-eq.exp4
-rwxr-xr-xcontrib/bmake/unit-tests/cond-cmp-numeric-eq.mk9
-rwxr-xr-xcontrib/bmake/unit-tests/cond-cmp-numeric-ge.mk4
-rwxr-xr-xcontrib/bmake/unit-tests/cond-cmp-numeric-gt.mk6
-rwxr-xr-xcontrib/bmake/unit-tests/cond-cmp-numeric-le.mk4
-rwxr-xr-xcontrib/bmake/unit-tests/cond-cmp-numeric-lt.mk4
-rwxr-xr-xcontrib/bmake/unit-tests/cond-cmp-numeric-ne.mk4
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-numeric.exp12
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-numeric.mk27
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-string.exp16
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-string.mk40
-rwxr-xr-xcontrib/bmake/unit-tests/cond-cmp-unary.exp2
-rwxr-xr-xcontrib/bmake/unit-tests/cond-cmp-unary.mk37
-rw-r--r--contrib/bmake/unit-tests/cond-eof.exp9
-rw-r--r--contrib/bmake/unit-tests/cond-eof.mk20
-rw-r--r--contrib/bmake/unit-tests/cond-func-defined.exp7
-rw-r--r--contrib/bmake/unit-tests/cond-func-defined.mk22
-rw-r--r--contrib/bmake/unit-tests/cond-func-empty.exp4
-rw-r--r--contrib/bmake/unit-tests/cond-func-empty.mk150
-rw-r--r--contrib/bmake/unit-tests/cond-func-exists.mk6
-rw-r--r--contrib/bmake/unit-tests/cond-func-make.exp1
-rw-r--r--contrib/bmake/unit-tests/cond-func-make.mk7
-rw-r--r--contrib/bmake/unit-tests/cond-func.exp18
-rw-r--r--contrib/bmake/unit-tests/cond-func.mk42
-rw-r--r--contrib/bmake/unit-tests/cond-late.exp2
-rw-r--r--contrib/bmake/unit-tests/cond-late.mk17
-rw-r--r--contrib/bmake/unit-tests/cond-op-and-lint.exp2
-rw-r--r--contrib/bmake/unit-tests/cond-op-and-lint.mk3
-rw-r--r--contrib/bmake/unit-tests/cond-op-and.exp5
-rw-r--r--contrib/bmake/unit-tests/cond-op-and.mk42
-rw-r--r--contrib/bmake/unit-tests/cond-op-not.exp12
-rw-r--r--contrib/bmake/unit-tests/cond-op-not.mk8
-rw-r--r--contrib/bmake/unit-tests/cond-op-or-lint.exp2
-rw-r--r--contrib/bmake/unit-tests/cond-op-or-lint.mk3
-rw-r--r--contrib/bmake/unit-tests/cond-op-or.exp5
-rw-r--r--contrib/bmake/unit-tests/cond-op-or.mk42
-rw-r--r--contrib/bmake/unit-tests/cond-op-parentheses.exp7
-rw-r--r--contrib/bmake/unit-tests/cond-op-parentheses.mk41
-rw-r--r--contrib/bmake/unit-tests/cond-op.exp35
-rw-r--r--contrib/bmake/unit-tests/cond-op.mk55
-rw-r--r--contrib/bmake/unit-tests/cond-short.exp11
-rw-r--r--contrib/bmake/unit-tests/cond-short.mk228
-rw-r--r--contrib/bmake/unit-tests/cond-token-number.exp9
-rw-r--r--contrib/bmake/unit-tests/cond-token-number.mk43
-rw-r--r--contrib/bmake/unit-tests/cond-token-plain.exp63
-rw-r--r--contrib/bmake/unit-tests/cond-token-plain.mk109
-rw-r--r--contrib/bmake/unit-tests/cond-token-string.exp23
-rw-r--r--contrib/bmake/unit-tests/cond-token-string.mk39
-rw-r--r--contrib/bmake/unit-tests/cond-token-var.exp8
-rw-r--r--contrib/bmake/unit-tests/cond-token-var.mk22
-rwxr-xr-xcontrib/bmake/unit-tests/cond-undef-lint.exp11
-rwxr-xr-xcontrib/bmake/unit-tests/cond-undef-lint.mk9
-rw-r--r--contrib/bmake/unit-tests/cond1.exp23
-rw-r--r--contrib/bmake/unit-tests/cond1.mk114
-rw-r--r--contrib/bmake/unit-tests/dep-colon-bug-cross-file.exp4
-rw-r--r--contrib/bmake/unit-tests/dep-colon-bug-cross-file.mk4
-rw-r--r--contrib/bmake/unit-tests/dep-duplicate.exp4
-rw-r--r--contrib/bmake/unit-tests/dep-duplicate.mk27
-rw-r--r--contrib/bmake/unit-tests/dep-op-missing.exp4
-rw-r--r--contrib/bmake/unit-tests/dep-op-missing.mk13
-rwxr-xr-xcontrib/bmake/unit-tests/dep-var.exp26
-rwxr-xr-xcontrib/bmake/unit-tests/dep-var.mk32
-rw-r--r--contrib/bmake/unit-tests/dep-wildcards.exp2
-rw-r--r--contrib/bmake/unit-tests/dep-wildcards.mk8
-rw-r--r--contrib/bmake/unit-tests/dep.exp6
-rw-r--r--contrib/bmake/unit-tests/dep.mk28
-rw-r--r--contrib/bmake/unit-tests/depsrc-end.mk4
-rw-r--r--contrib/bmake/unit-tests/depsrc-ignore.exp2
-rw-r--r--contrib/bmake/unit-tests/depsrc-meta.exp2
-rw-r--r--contrib/bmake/unit-tests/depsrc-meta.mk21
-rw-r--r--contrib/bmake/unit-tests/depsrc-nopath.exp2
-rw-r--r--contrib/bmake/unit-tests/depsrc-nopath.mk25
-rw-r--r--contrib/bmake/unit-tests/depsrc-phony.mk3
-rw-r--r--contrib/bmake/unit-tests/depsrc-use.exp4
-rw-r--r--contrib/bmake/unit-tests/depsrc-use.mk21
-rw-r--r--contrib/bmake/unit-tests/depsrc-usebefore.exp46
-rw-r--r--contrib/bmake/unit-tests/depsrc-usebefore.mk109
-rw-r--r--contrib/bmake/unit-tests/depsrc-wait.exp13
-rw-r--r--contrib/bmake/unit-tests/depsrc-wait.mk22
-rw-r--r--contrib/bmake/unit-tests/depsrc.exp1
-rw-r--r--contrib/bmake/unit-tests/depsrc.mk14
-rw-r--r--contrib/bmake/unit-tests/deptgt-begin.exp4
-rw-r--r--contrib/bmake/unit-tests/deptgt-begin.mk12
-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-delete_on_error.exp4
-rw-r--r--contrib/bmake/unit-tests/deptgt-delete_on_error.mk2
-rw-r--r--contrib/bmake/unit-tests/deptgt-end-fail-indirect.mk4
-rw-r--r--contrib/bmake/unit-tests/deptgt-end-fail.mk4
-rw-r--r--contrib/bmake/unit-tests/deptgt-error.exp10
-rw-r--r--contrib/bmake/unit-tests/deptgt-error.mk22
-rw-r--r--contrib/bmake/unit-tests/deptgt-ignore.exp12
-rw-r--r--contrib/bmake/unit-tests/deptgt-ignore.mk30
-rw-r--r--contrib/bmake/unit-tests/deptgt-interrupt.exp3
-rw-r--r--contrib/bmake/unit-tests/deptgt-interrupt.mk9
-rw-r--r--contrib/bmake/unit-tests/deptgt-main.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-main.mk27
-rw-r--r--contrib/bmake/unit-tests/deptgt-makeflags.exp4
-rw-r--r--contrib/bmake/unit-tests/deptgt-makeflags.mk31
-rw-r--r--contrib/bmake/unit-tests/deptgt-notparallel.exp8
-rw-r--r--contrib/bmake/unit-tests/deptgt-notparallel.mk18
-rw-r--r--contrib/bmake/unit-tests/deptgt-order.exp7
-rw-r--r--contrib/bmake/unit-tests/deptgt-order.mk4
-rw-r--r--contrib/bmake/unit-tests/deptgt-path-suffix.exp5
-rw-r--r--contrib/bmake/unit-tests/deptgt-path-suffix.mk10
-rw-r--r--contrib/bmake/unit-tests/deptgt-phony.exp41
-rw-r--r--contrib/bmake/unit-tests/deptgt-phony.mk27
-rw-r--r--contrib/bmake/unit-tests/deptgt-posix.exp (renamed from contrib/bmake/unit-tests/envfirst.exp)0
-rw-r--r--contrib/bmake/unit-tests/deptgt-posix.mk123
-rw-r--r--contrib/bmake/unit-tests/deptgt-silent-jobs.exp7
-rw-r--r--contrib/bmake/unit-tests/deptgt-silent-jobs.mk34
-rw-r--r--contrib/bmake/unit-tests/deptgt.exp19
-rw-r--r--contrib/bmake/unit-tests/deptgt.mk16
-rw-r--r--contrib/bmake/unit-tests/dir.mk8
-rwxr-xr-xcontrib/bmake/unit-tests/directive-dinclude.exp5
-rwxr-xr-xcontrib/bmake/unit-tests/directive-dinclude.mk25
-rw-r--r--contrib/bmake/unit-tests/directive-elif.exp36
-rw-r--r--contrib/bmake/unit-tests/directive-elif.mk25
-rw-r--r--contrib/bmake/unit-tests/directive-elifdef.mk21
-rw-r--r--contrib/bmake/unit-tests/directive-elifndef.mk23
-rw-r--r--contrib/bmake/unit-tests/directive-else.exp16
-rw-r--r--contrib/bmake/unit-tests/directive-else.mk14
-rw-r--r--contrib/bmake/unit-tests/directive-endfor.exp2
-rw-r--r--contrib/bmake/unit-tests/directive-endfor.mk3
-rw-r--r--contrib/bmake/unit-tests/directive-endif.exp10
-rw-r--r--contrib/bmake/unit-tests/directive-endif.mk28
-rw-r--r--contrib/bmake/unit-tests/directive-error.exp2
-rw-r--r--contrib/bmake/unit-tests/directive-error.mk3
-rw-r--r--contrib/bmake/unit-tests/directive-export-gmake.exp7
-rw-r--r--contrib/bmake/unit-tests/directive-export-gmake.mk48
-rw-r--r--contrib/bmake/unit-tests/directive-export-impl.exp26
-rw-r--r--contrib/bmake/unit-tests/directive-export.exp3
-rw-r--r--contrib/bmake/unit-tests/directive-export.mk27
-rw-r--r--contrib/bmake/unit-tests/directive-for-break.exp5
-rw-r--r--contrib/bmake/unit-tests/directive-for-break.mk66
-rw-r--r--contrib/bmake/unit-tests/directive-for-empty.exp27
-rw-r--r--contrib/bmake/unit-tests/directive-for-empty.mk124
-rw-r--r--contrib/bmake/unit-tests/directive-for-errors.exp33
-rw-r--r--contrib/bmake/unit-tests/directive-for-errors.mk57
-rw-r--r--contrib/bmake/unit-tests/directive-for-escape.exp181
-rw-r--r--contrib/bmake/unit-tests/directive-for-escape.mk230
-rwxr-xr-xcontrib/bmake/unit-tests/directive-for-generating-endif.exp8
-rwxr-xr-xcontrib/bmake/unit-tests/directive-for-generating-endif.mk6
-rw-r--r--contrib/bmake/unit-tests/directive-for-if.exp8
-rw-r--r--contrib/bmake/unit-tests/directive-for-if.mk89
-rw-r--r--contrib/bmake/unit-tests/directive-for-lines.exp12
-rw-r--r--contrib/bmake/unit-tests/directive-for-lines.mk12
-rw-r--r--contrib/bmake/unit-tests/directive-for-null.exp6
-rw-r--r--contrib/bmake/unit-tests/directive-for-null.mk20
-rwxr-xr-xcontrib/bmake/unit-tests/directive-for.exp60
-rwxr-xr-xcontrib/bmake/unit-tests/directive-for.mk245
-rwxr-xr-xcontrib/bmake/unit-tests/directive-hyphen-include.exp5
-rwxr-xr-xcontrib/bmake/unit-tests/directive-hyphen-include.mk22
-rw-r--r--contrib/bmake/unit-tests/directive-if.exp29
-rw-r--r--contrib/bmake/unit-tests/directive-if.mk23
-rw-r--r--contrib/bmake/unit-tests/directive-ifdef.exp3
-rw-r--r--contrib/bmake/unit-tests/directive-ifdef.mk44
-rw-r--r--contrib/bmake/unit-tests/directive-ifmake.exp20
-rw-r--r--contrib/bmake/unit-tests/directive-ifmake.mk48
-rw-r--r--contrib/bmake/unit-tests/directive-ifndef.exp2
-rw-r--r--contrib/bmake/unit-tests/directive-ifndef.mk67
-rwxr-xr-xcontrib/bmake/unit-tests/directive-include-fatal.exp2
-rwxr-xr-xcontrib/bmake/unit-tests/directive-include-fatal.mk3
-rw-r--r--contrib/bmake/unit-tests/directive-include-guard.exp104
-rw-r--r--contrib/bmake/unit-tests/directive-include-guard.mk638
-rwxr-xr-xcontrib/bmake/unit-tests/directive-include.exp11
-rwxr-xr-xcontrib/bmake/unit-tests/directive-include.mk59
-rw-r--r--contrib/bmake/unit-tests/directive-info.exp24
-rw-r--r--contrib/bmake/unit-tests/directive-info.mk32
-rw-r--r--contrib/bmake/unit-tests/directive-misspellings.exp84
-rw-r--r--contrib/bmake/unit-tests/directive-misspellings.mk46
-rwxr-xr-xcontrib/bmake/unit-tests/directive-sinclude.exp5
-rwxr-xr-xcontrib/bmake/unit-tests/directive-sinclude.mk18
-rw-r--r--contrib/bmake/unit-tests/directive-undef.exp6
-rw-r--r--contrib/bmake/unit-tests/directive-undef.mk49
-rw-r--r--contrib/bmake/unit-tests/directive-unexport-env.exp8
-rw-r--r--contrib/bmake/unit-tests/directive-unexport-env.mk8
-rw-r--r--contrib/bmake/unit-tests/directive-unexport.exp8
-rw-r--r--contrib/bmake/unit-tests/directive-unexport.mk5
-rw-r--r--contrib/bmake/unit-tests/directive-warning.exp12
-rw-r--r--contrib/bmake/unit-tests/directive-warning.mk16
-rw-r--r--contrib/bmake/unit-tests/directive.exp12
-rw-r--r--contrib/bmake/unit-tests/directive.mk22
-rw-r--r--contrib/bmake/unit-tests/doterror.mk3
-rw-r--r--contrib/bmake/unit-tests/envfirst.mk44
-rw-r--r--contrib/bmake/unit-tests/error.exp6
-rw-r--r--contrib/bmake/unit-tests/error.mk5
-rw-r--r--contrib/bmake/unit-tests/escape.exp36
-rw-r--r--contrib/bmake/unit-tests/escape.mk7
-rw-r--r--contrib/bmake/unit-tests/export-env.mk6
-rw-r--r--contrib/bmake/unit-tests/export.exp2
-rw-r--r--contrib/bmake/unit-tests/export.mk4
-rw-r--r--contrib/bmake/unit-tests/forloop.exp20
-rw-r--r--contrib/bmake/unit-tests/forloop.mk53
-rw-r--r--contrib/bmake/unit-tests/forsubst.exp2
-rw-r--r--contrib/bmake/unit-tests/forsubst.mk22
-rw-r--r--contrib/bmake/unit-tests/hanoi-include.mk40
-rw-r--r--contrib/bmake/unit-tests/include-main.exp30
-rw-r--r--contrib/bmake/unit-tests/include-main.mk10
-rw-r--r--contrib/bmake/unit-tests/include-sub.inc (renamed from contrib/bmake/unit-tests/include-sub.mk)12
-rw-r--r--contrib/bmake/unit-tests/include-subsub.inc9
-rw-r--r--contrib/bmake/unit-tests/include-subsub.mk9
-rw-r--r--contrib/bmake/unit-tests/job-output-null.exp8
-rw-r--r--contrib/bmake/unit-tests/job-output-null.mk49
-rw-r--r--contrib/bmake/unit-tests/jobs-error-indirect.exp2
-rw-r--r--contrib/bmake/unit-tests/jobs-error-nested-make.exp2
-rw-r--r--contrib/bmake/unit-tests/jobs-error-nested.exp4
-rwxr-xr-xcontrib/bmake/unit-tests/lint.exp2
-rwxr-xr-xcontrib/bmake/unit-tests/lint.mk4
-rwxr-xr-xcontrib/bmake/unit-tests/make-exported.mk4
-rw-r--r--contrib/bmake/unit-tests/meta-cmd-cmp.exp16
-rw-r--r--contrib/bmake/unit-tests/meta-cmd-cmp.mk40
-rw-r--r--contrib/bmake/unit-tests/meta-ignore.inc63
-rw-r--r--contrib/bmake/unit-tests/moderrs.exp32
-rw-r--r--contrib/bmake/unit-tests/moderrs.mk6
-rw-r--r--contrib/bmake/unit-tests/modmatch.exp17
-rw-r--r--contrib/bmake/unit-tests/modmatch.mk30
-rw-r--r--contrib/bmake/unit-tests/modmisc.exp1
-rw-r--r--contrib/bmake/unit-tests/modmisc.mk10
-rw-r--r--contrib/bmake/unit-tests/modts.exp14
-rw-r--r--contrib/bmake/unit-tests/modts.mk47
-rw-r--r--contrib/bmake/unit-tests/modword.exp126
-rw-r--r--contrib/bmake/unit-tests/modword.mk161
-rw-r--r--contrib/bmake/unit-tests/objdir-writable.exp8
-rw-r--r--contrib/bmake/unit-tests/objdir-writable.mk15
-rw-r--r--contrib/bmake/unit-tests/opt-chdir.exp2
-rw-r--r--contrib/bmake/unit-tests/opt-chdir.mk8
-rw-r--r--contrib/bmake/unit-tests/opt-debug-cond.exp5
-rw-r--r--contrib/bmake/unit-tests/opt-debug-cond.mk22
-rw-r--r--contrib/bmake/unit-tests/opt-debug-curdir.mk10
-rw-r--r--contrib/bmake/unit-tests/opt-debug-errors-jobs.exp16
-rw-r--r--contrib/bmake/unit-tests/opt-debug-errors-jobs.mk14
-rw-r--r--contrib/bmake/unit-tests/opt-debug-file.exp13
-rw-r--r--contrib/bmake/unit-tests/opt-debug-file.mk53
-rw-r--r--contrib/bmake/unit-tests/opt-debug-for.exp12
-rw-r--r--contrib/bmake/unit-tests/opt-debug-graph1.exp14
-rw-r--r--contrib/bmake/unit-tests/opt-debug-graph2.exp12
-rw-r--r--contrib/bmake/unit-tests/opt-debug-graph3.exp12
-rw-r--r--contrib/bmake/unit-tests/opt-debug-hash.exp7
-rw-r--r--contrib/bmake/unit-tests/opt-debug-hash.mk10
-rw-r--r--contrib/bmake/unit-tests/opt-debug-jobs.mk4
-rw-r--r--contrib/bmake/unit-tests/opt-debug-lint.exp12
-rw-r--r--contrib/bmake/unit-tests/opt-debug-lint.mk13
-rw-r--r--contrib/bmake/unit-tests/opt-debug-loud.mk6
-rw-r--r--contrib/bmake/unit-tests/opt-debug-parse.exp27
-rw-r--r--contrib/bmake/unit-tests/opt-debug-parse.mk37
-rw-r--r--contrib/bmake/unit-tests/opt-debug-var.exp6
-rw-r--r--contrib/bmake/unit-tests/opt-debug-var.mk30
-rw-r--r--contrib/bmake/unit-tests/opt-debug-x-trace.exp2
-rw-r--r--contrib/bmake/unit-tests/opt-debug-x-trace.mk10
-rw-r--r--contrib/bmake/unit-tests/opt-define.mk42
-rw-r--r--contrib/bmake/unit-tests/opt-env.mk51
-rw-r--r--contrib/bmake/unit-tests/opt-file.exp4
-rw-r--r--contrib/bmake/unit-tests/opt-file.mk26
-rw-r--r--contrib/bmake/unit-tests/opt-jobs-internal.exp7
-rw-r--r--contrib/bmake/unit-tests/opt-jobs-internal.mk11
-rw-r--r--contrib/bmake/unit-tests/opt-jobs-no-action.mk4
-rw-r--r--contrib/bmake/unit-tests/opt-jobs.mk56
-rw-r--r--contrib/bmake/unit-tests/opt-keep-going-indirect.exp32
-rw-r--r--contrib/bmake/unit-tests/opt-keep-going-indirect.mk90
-rw-r--r--contrib/bmake/unit-tests/opt-m-include-dir.mk8
-rw-r--r--contrib/bmake/unit-tests/opt-query.exp24
-rw-r--r--contrib/bmake/unit-tests/opt-query.mk69
-rw-r--r--contrib/bmake/unit-tests/opt-raw.mk16
-rw-r--r--contrib/bmake/unit-tests/opt-silent.exp2
-rw-r--r--contrib/bmake/unit-tests/opt-silent.mk8
-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/opt-version.exp2
-rw-r--r--contrib/bmake/unit-tests/opt-version.mk12
-rw-r--r--contrib/bmake/unit-tests/opt-warnings-as-errors.exp4
-rw-r--r--contrib/bmake/unit-tests/opt-warnings-as-errors.mk4
-rw-r--r--contrib/bmake/unit-tests/opt-where-am-i.exp3
-rw-r--r--contrib/bmake/unit-tests/opt-where-am-i.mk14
-rw-r--r--contrib/bmake/unit-tests/opt-x-reduce-exported.exp4
-rw-r--r--contrib/bmake/unit-tests/opt-x-reduce-exported.mk22
-rw-r--r--contrib/bmake/unit-tests/opt.mk4
-rw-r--r--contrib/bmake/unit-tests/parse-var.mk126
-rw-r--r--contrib/bmake/unit-tests/parse.exp6
-rw-r--r--contrib/bmake/unit-tests/parse.mk55
-rw-r--r--contrib/bmake/unit-tests/posix.mk7
-rw-r--r--contrib/bmake/unit-tests/recursive.exp4
-rw-r--r--contrib/bmake/unit-tests/recursive.mk17
-rwxr-xr-xcontrib/bmake/unit-tests/sh-dots.mk6
-rw-r--r--contrib/bmake/unit-tests/sh-flags.exp72
-rw-r--r--contrib/bmake/unit-tests/sh-leading-at.exp1
-rw-r--r--contrib/bmake/unit-tests/sh-leading-at.mk6
-rw-r--r--contrib/bmake/unit-tests/sh-leading-hyphen.exp10
-rw-r--r--contrib/bmake/unit-tests/sh-leading-hyphen.mk19
-rw-r--r--contrib/bmake/unit-tests/sh-leading-plus.exp2
-rw-r--r--contrib/bmake/unit-tests/sh-leading-plus.mk6
-rw-r--r--contrib/bmake/unit-tests/sh.mk5
-rw-r--r--contrib/bmake/unit-tests/shell-sh.mk4
-rw-r--r--contrib/bmake/unit-tests/suff-incomplete.exp12
-rw-r--r--contrib/bmake/unit-tests/suff-main-several.exp44
-rw-r--r--contrib/bmake/unit-tests/suff-rebuild.exp22
-rw-r--r--contrib/bmake/unit-tests/suff-transform-debug.exp14
-rw-r--r--contrib/bmake/unit-tests/suff-use.exp7
-rw-r--r--contrib/bmake/unit-tests/suff-use.mk50
-rw-r--r--contrib/bmake/unit-tests/unexport.mk4
-rw-r--r--contrib/bmake/unit-tests/var-class-cmdline.exp4
-rw-r--r--contrib/bmake/unit-tests/var-class-global.mk8
-rw-r--r--contrib/bmake/unit-tests/var-class-local-legacy.mk8
-rw-r--r--contrib/bmake/unit-tests/var-class-local.exp5
-rw-r--r--contrib/bmake/unit-tests/var-class-local.mk45
-rw-r--r--contrib/bmake/unit-tests/var-class.mk9
-rw-r--r--contrib/bmake/unit-tests/var-eval-short.exp18
-rw-r--r--contrib/bmake/unit-tests/var-eval-short.mk39
-rw-r--r--contrib/bmake/unit-tests/var-op-append.mk50
-rw-r--r--contrib/bmake/unit-tests/var-op-assign.exp4
-rw-r--r--contrib/bmake/unit-tests/var-op-assign.mk6
-rw-r--r--contrib/bmake/unit-tests/var-op-default.mk8
-rw-r--r--contrib/bmake/unit-tests/var-op-expand.exp8
-rw-r--r--contrib/bmake/unit-tests/var-op-expand.mk124
-rw-r--r--contrib/bmake/unit-tests/var-op-shell.exp12
-rw-r--r--contrib/bmake/unit-tests/var-op-shell.mk37
-rw-r--r--contrib/bmake/unit-tests/var-op-sunsh.mk47
-rw-r--r--contrib/bmake/unit-tests/var-readonly.exp4
-rw-r--r--contrib/bmake/unit-tests/var-readonly.mk27
-rw-r--r--contrib/bmake/unit-tests/var-recursive.exp17
-rw-r--r--contrib/bmake/unit-tests/var-recursive.mk20
-rw-r--r--contrib/bmake/unit-tests/var-scope-cmdline.exp4
-rw-r--r--contrib/bmake/unit-tests/var-scope-cmdline.mk (renamed from contrib/bmake/unit-tests/var-class-cmdline.mk)10
-rw-r--r--contrib/bmake/unit-tests/var-scope-env.exp (renamed from contrib/bmake/unit-tests/var-class-env.exp)0
-rw-r--r--contrib/bmake/unit-tests/var-scope-env.mk (renamed from contrib/bmake/unit-tests/var-class-env.mk)2
-rw-r--r--contrib/bmake/unit-tests/var-scope-global.exp (renamed from contrib/bmake/unit-tests/var-class-global.exp)0
-rw-r--r--contrib/bmake/unit-tests/var-scope-global.mk18
-rw-r--r--contrib/bmake/unit-tests/var-scope-local-legacy.exp6
-rw-r--r--contrib/bmake/unit-tests/var-scope-local-legacy.mk35
-rw-r--r--contrib/bmake/unit-tests/var-scope-local.exp71
-rw-r--r--contrib/bmake/unit-tests/var-scope-local.mk270
-rw-r--r--contrib/bmake/unit-tests/var-scope.exp (renamed from contrib/bmake/unit-tests/var-class-local-legacy.exp)0
-rw-r--r--contrib/bmake/unit-tests/var-scope.mk9
-rw-r--r--contrib/bmake/unit-tests/varcmd.mk6
-rw-r--r--contrib/bmake/unit-tests/vardebug.exp30
-rw-r--r--contrib/bmake/unit-tests/vardebug.mk32
-rw-r--r--contrib/bmake/unit-tests/varmisc.exp25
-rw-r--r--contrib/bmake/unit-tests/varmisc.mk34
-rw-r--r--contrib/bmake/unit-tests/varmod-assign-shell.exp14
-rw-r--r--contrib/bmake/unit-tests/varmod-assign-shell.mk37
-rw-r--r--contrib/bmake/unit-tests/varmod-assign.exp46
-rw-r--r--contrib/bmake/unit-tests/varmod-assign.mk169
-rw-r--r--contrib/bmake/unit-tests/varmod-defined.exp8
-rw-r--r--contrib/bmake/unit-tests/varmod-defined.mk23
-rw-r--r--contrib/bmake/unit-tests/varmod-edge.exp44
-rw-r--r--contrib/bmake/unit-tests/varmod-edge.mk29
-rw-r--r--contrib/bmake/unit-tests/varmod-gmtime.exp20
-rw-r--r--contrib/bmake/unit-tests/varmod-gmtime.mk83
-rw-r--r--contrib/bmake/unit-tests/varmod-hash.exp6
-rw-r--r--contrib/bmake/unit-tests/varmod-head.exp10
-rw-r--r--contrib/bmake/unit-tests/varmod-head.mk65
-rw-r--r--contrib/bmake/unit-tests/varmod-ifelse.exp73
-rw-r--r--contrib/bmake/unit-tests/varmod-ifelse.mk166
-rw-r--r--contrib/bmake/unit-tests/varmod-indirect.exp40
-rw-r--r--contrib/bmake/unit-tests/varmod-indirect.mk65
-rw-r--r--contrib/bmake/unit-tests/varmod-l-name-to-value.mk4
-rw-r--r--contrib/bmake/unit-tests/varmod-localtime.exp20
-rw-r--r--contrib/bmake/unit-tests/varmod-localtime.mk45
-rw-r--r--contrib/bmake/unit-tests/varmod-loop-delete.exp4
-rw-r--r--contrib/bmake/unit-tests/varmod-loop-delete.mk34
-rw-r--r--contrib/bmake/unit-tests/varmod-loop-varname.exp16
-rw-r--r--contrib/bmake/unit-tests/varmod-loop-varname.mk17
-rw-r--r--contrib/bmake/unit-tests/varmod-loop.exp18
-rw-r--r--contrib/bmake/unit-tests/varmod-loop.mk73
-rwxr-xr-xcontrib/bmake/unit-tests/varmod-match-escape.exp10
-rwxr-xr-xcontrib/bmake/unit-tests/varmod-match-escape.mk55
-rw-r--r--contrib/bmake/unit-tests/varmod-match.exp26
-rw-r--r--contrib/bmake/unit-tests/varmod-match.mk364
-rw-r--r--contrib/bmake/unit-tests/varmod-mtime.exp14
-rw-r--r--contrib/bmake/unit-tests/varmod-mtime.mk125
-rw-r--r--contrib/bmake/unit-tests/varmod-no-match.mk98
-rw-r--r--contrib/bmake/unit-tests/varmod-order-numeric.exp (renamed from contrib/bmake/unit-tests/var-class.exp)0
-rw-r--r--contrib/bmake/unit-tests/varmod-order-numeric.mk59
-rw-r--r--contrib/bmake/unit-tests/varmod-order-reverse.mk9
-rw-r--r--contrib/bmake/unit-tests/varmod-order-shuffle.mk24
-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.exp27
-rw-r--r--contrib/bmake/unit-tests/varmod-order.mk116
-rw-r--r--contrib/bmake/unit-tests/varmod-path.mk14
-rw-r--r--contrib/bmake/unit-tests/varmod-quote-dollar.exp3
-rw-r--r--contrib/bmake/unit-tests/varmod-quote-dollar.mk13
-rw-r--r--contrib/bmake/unit-tests/varmod-range.exp21
-rw-r--r--contrib/bmake/unit-tests/varmod-range.mk36
-rw-r--r--contrib/bmake/unit-tests/varmod-remember.mk69
-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.exp125
-rw-r--r--contrib/bmake/unit-tests/varmod-select-words.mk165
-rw-r--r--contrib/bmake/unit-tests/varmod-shell.exp10
-rw-r--r--contrib/bmake/unit-tests/varmod-shell.mk10
-rw-r--r--contrib/bmake/unit-tests/varmod-subst-regex.exp2
-rw-r--r--contrib/bmake/unit-tests/varmod-subst-regex.mk57
-rw-r--r--contrib/bmake/unit-tests/varmod-subst.exp2
-rw-r--r--contrib/bmake/unit-tests/varmod-subst.mk139
-rw-r--r--contrib/bmake/unit-tests/varmod-sun-shell.exp11
-rw-r--r--contrib/bmake/unit-tests/varmod-sun-shell.mk7
-rw-r--r--contrib/bmake/unit-tests/varmod-sysv.exp2
-rw-r--r--contrib/bmake/unit-tests/varmod-sysv.mk11
-rw-r--r--contrib/bmake/unit-tests/varmod-to-abs.exp4
-rw-r--r--contrib/bmake/unit-tests/varmod-to-abs.mk5
-rw-r--r--contrib/bmake/unit-tests/varmod-to-lower.mk15
-rw-r--r--contrib/bmake/unit-tests/varmod-to-separator.exp26
-rw-r--r--contrib/bmake/unit-tests/varmod-to-separator.mk107
-rw-r--r--contrib/bmake/unit-tests/varmod-undefined.mk8
-rw-r--r--contrib/bmake/unit-tests/varmod-unique.mk35
-rw-r--r--contrib/bmake/unit-tests/varmod.exp10
-rw-r--r--contrib/bmake/unit-tests/varmod.mk68
-rw-r--r--contrib/bmake/unit-tests/varname-dollar.exp8
-rw-r--r--contrib/bmake/unit-tests/varname-dollar.mk6
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-jobs.exp7
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-jobs.mk43
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp10
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk7
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp10
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk7
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp10
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk7
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-mode.exp30
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-mode.mk41
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-pid.mk18
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-ppid.mk25
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-save_dollars.mk130
-rw-r--r--contrib/bmake/unit-tests/varname-dot-makeflags.exp11
-rw-r--r--contrib/bmake/unit-tests/varname-dot-makeflags.mk42
-rw-r--r--contrib/bmake/unit-tests/varname-dot-makeoverrides.exp7
-rw-r--r--contrib/bmake/unit-tests/varname-dot-makeoverrides.mk25
-rw-r--r--contrib/bmake/unit-tests/varname-dot-newline.exp3
-rw-r--r--contrib/bmake/unit-tests/varname-dot-newline.mk32
-rw-r--r--contrib/bmake/unit-tests/varname-dot-parsedir.exp6
-rw-r--r--contrib/bmake/unit-tests/varname-dot-parsedir.mk12
-rw-r--r--contrib/bmake/unit-tests/varname-dot-parsefile.exp6
-rw-r--r--contrib/bmake/unit-tests/varname-dot-parsefile.mk12
-rwxr-xr-xcontrib/bmake/unit-tests/varname-dot-shell.exp33
-rw-r--r--contrib/bmake/unit-tests/varname-dot-suffixes.exp39
-rw-r--r--contrib/bmake/unit-tests/varname-dot-suffixes.mk105
-rw-r--r--contrib/bmake/unit-tests/varname-empty.exp31
-rwxr-xr-xcontrib/bmake/unit-tests/varname-empty.mk4
-rw-r--r--contrib/bmake/unit-tests/varname-make_print_var_on_error-jobs.mk4
-rw-r--r--contrib/bmake/unit-tests/varname-makeflags.exp20
-rw-r--r--contrib/bmake/unit-tests/varname-makeflags.mk188
-rw-r--r--contrib/bmake/unit-tests/varname.exp6
-rw-r--r--contrib/bmake/unit-tests/varname.mk49
-rw-r--r--contrib/bmake/unit-tests/varparse-dynamic.exp4
-rw-r--r--contrib/bmake/unit-tests/varparse-dynamic.mk11
-rw-r--r--contrib/bmake/unit-tests/varparse-errors.exp24
-rw-r--r--contrib/bmake/unit-tests/varparse-errors.mk52
-rw-r--r--contrib/bmake/unit-tests/varparse-mod.mk6
-rw-r--r--contrib/bmake/unit-tests/varparse-undef-partial.mk13
-rw-r--r--contrib/bmake/unit-tests/varquote.exp3
-rw-r--r--contrib/bmake/unit-tests/varquote.mk14
-rw-r--r--contrib/bmake/util.c217
-rw-r--r--contrib/bmake/var.c2599
613 files changed, 29051 insertions, 16063 deletions
diff --git a/contrib/bmake/ChangeLog b/contrib/bmake/ChangeLog
index 35235e1f8205..1ce033887c8f 100644
--- a/contrib/bmake/ChangeLog
+++ b/contrib/bmake/ChangeLog
@@ -1,3 +1,1100 @@
+2024-05-07 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240508
+ Merge with NetBSD make, pick up
+ o make: ensure variables set on command line get added to
+ .MAKEOVERRIDES (even if they start with '.') so they are passed to
+ sub-makes.
+
+2024-04-30 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240430
+ Merge with NetBSD make, pick up
+ o main.c: ensure '.include <makefile>' respects MAKESYSPATH.
+ Dir_FindFile will search .CURDIR first unless ".DOTLAST" is seen.
+
+2024-04-28 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240428
+ Merge with NetBSD make, pick up
+ o simplify freeing of lists
+ o arch.c: trim pointless comments
+ o var.c: delay variable assignments until actually needed
+ don't reallocate memory after evaluating an expression, result is
+ almost always short-lived.
+
+2024-04-26 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240426
+ Merge with NetBSD make, pick up
+ o job.c: in debug output, print the directory in which a job
+ failed at same time as failed target so it is more easily found in
+ build log.
+
+2024-04-24 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240424
+ Merge with NetBSD make, pick up
+ o clean up comments, code and tests
+
+2024-04-23 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240422
+ Merge with NetBSD make, pick up
+ o var.c: avoid LazyBuf for :*time modifiers.
+ LazyBuf's are not nul terminated so not suitable for passing to
+ functions that expect that. These modifiers are used sparingly so
+ an extra allocation is not a problem.
+
+2024-04-20 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240420
+ Merge with NetBSD make, pick up
+ o provide more context information for parse/evaluate errors
+
+2024-04-14 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240414
+ Merge with NetBSD make, pick up
+ o parse.c: print -dp debug info earlier so we see which
+ .if or .for line is being parsed.
+
+2024-04-04 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240404
+ Merge with NetBSD make, pick up
+ o fix some unit tests for Cygwin
+ o parse.c: exit immediately after reading a null byte from a makefile
+
+ * fix generation of bmake.cat1
+
+2024-03-19 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240314
+ Add/Improve support for Cygwin
+ o uname -s output isn't useful so allow configure to
+ set FORCE_MAKE_OS - to force the value of .MAKE.OS
+ and use Cygwin which matches uname -o
+ o fix some unit-tests for Cygwin
+
+2024-03-10 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * boot-strap: tests can take a long time; use a cookie to
+ skip them if bmake has not been updated since tests last
+ ran successfully.
+
+ * Makefile: Cygwin handles MANTARGET man
+
+ * unit-tests/Makefile: set BROKEN_TESTS for Cygwin
+
+2024-03-09 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240309
+ Merge with NetBSD make, pick up
+ o set .ERROR_EXIT to the exit status of .ERROR_TARGET
+ this allows a .ERROR target to ignore the case of
+ .ERROR_EXIT==6 which just means that the build actually
+ failed somewhere else.
+
+2024-03-04 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240303
+
+ * var.c: on IRIX we need both inttypes.h and stdint.h
+
+2024-03-01 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240301
+ Merge with NetBSD make, pick up
+ o export variables with value from target scope
+ when appropriate.
+
+2024-02-12 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240212
+ Merge with NetBSD make, pick up
+ o remove unneeded conditional-compilation toggles
+ INCLUDES, LIBRARIES, POSIX, SYSVINCLUDE, SYSVVARSUB,
+ GMAKEEXPORT NO_REGEX and SUNSHCMD
+
+ * configure.in: add check for regex.h
+
+ * var.c: replace use of NO_REGEX with HAVE_REGEX_H
+
+2024-02-04 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240204
+ Merge with NetBSD make, pick up
+ o var.c: fix some lint (-dL) mode parsing issues
+
+2024-02-02 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION: (_MAKE_VERSION): 20240202
+ Merge with NetBSD make, pick up
+ o make.1: note that arg to :D and :U can be empty
+ o var.c: $$ is not a parse error when .MAKE.SAVE_DOLLARS=no
+
+2024-01-08 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240108
+ Merge with NetBSD make, pick up
+ o miscellaneous cleanups
+
+2024-01-06 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240106
+ Merge with NetBSD make, pick up
+ o fix duplicate progname when reporting an unknown target
+ o unit tests for Cmd_Exec using temp file
+
+2024-01-05 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240105
+ Merge with NetBSD make, pick up
+ o main.c: Cmd_Exec write cmd to a file if too big
+ avoid blowing commandline/env limits
+
+2024-01-02 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240101
+ o util.c: flesh out more of strftime
+ * configure.in: add --with-bmake-strftime
+ it is not a full implementation but enough to pass all
+ the unit-tests.
+ * parse.c: LoadFile do not append \n to empty buffer.
+
+2023-12-30 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20231230
+ Merge with NetBSD make, pick up
+ o simplify memory allocation for string buffers
+ o fix declared types of list nodes
+ o suff.c: clean up freeing of suffixes
+ o var.c: simplify debug message for the ':@var@...@' modifier
+ clean up variable handling
+
+2023-12-26 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20231226
+ Merge with NetBSD make, pick up
+ o compat.c: ensure make's output is correctly ordered with that of
+ the target when not going to a tty
+ o main.c: check for shellPath whether to call Shell_Init()
+
+2023-12-24 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20231224
+ Merge with NetBSD make, pick up
+ o compat.c: check for shellPath whether to call Shell_Init()
+ tweak the unit test to detect the bug thus fixed.
+ o make.1: do not claim .SHELL is only used by jobs mode.
+
+2023-12-22 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20231220
+ Merge with NetBSD make, pick up
+ o str.c: speed up pattern matching in the ':M' modifier
+ o var.c: fix confusing debug logging when deleting a variable
+ use consistent debug messages style when ignoring variables
+
+2023-12-10 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20231210
+ Merge with NetBSD make, pick up
+ o var.c: avoid segfault on empty :C match expression
+ explain in debug log why variable assignment is ignored.
+
+2023-12-08 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20231208
+ Merge with NetBSD make, pick up
+ o var.c: ensure fromCmd is set correctly for variables set on
+ command line.
+
+2023-11-26 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * configure.in: disable generation of 'makefile' for
+ Darwin by default.
+
+ * boot-strap: docuement --without-makefile
+
+2023-11-24 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20231124
+ Merge with NetBSD make, pick up
+ o main.c: cleanup processing of -j
+ fix lint warning about strchr
+ o var.c: more accurate error message for invalid ':mtime' argument
+ cleanup :[...] modifier
+ avoid reading beyond substring when comparing
+ o unit-tests cover all cases of :mtime, test and explain exporting
+ of variables
+ o cleanup comments
+
+2023-09-17 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * bsd.after-import.mk (ECHO_TAG): FreeBSD no longer uses
+ $FreeBSD$ tag, so avoid adding it.
+
+2023-09-09 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230909
+ Merge with NetBSD make, pick up
+ o main.c: allow -j to compute a multiple of ncpu
+ If _SC_NPROCESSORS_ONLN is supported; and -j arg is a floating
+ point number or ends in 'C' compute .MAKE.JOBS as a multiple of
+ _SC_NPROCESSORS_ONLN
+ .MAKE.JOBS.C will be "yes" if -jC is supported
+
+2023-08-20 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230820
+ Merge with NetBSD make, pick up
+ o make.1: note that :localtime is better for %s
+ o parse.c: improve error messages for invalid input.
+ o var.c: fix for %s:L:gmtime - set TZ=UTC and use localtime to get
+ correct result, it is still better to use %s:L:localtime.
+
+2023-08-18 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230818
+ Merge with NetBSD make, pick up
+ o meta.c: meta_ignore - check raw path against metaIgnorePaths
+ to potentially skip call to realpath.
+ o var.c: be strict when parsing the argument of the ':mtime' modifier
+ o unit-tests/varmod-mtime.mk: document why '${%s:L:localtime}'
+ should be used to get an equivalent value to time(3).
+
+2023-08-16 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230816
+ Merge with NetBSD make, pick up
+ o cond.c: clean up multiple-inclusion guards
+
+2023-07-25 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * unit-tests/Makefile: addd varmod-localtime to BROKEN_TESTS
+ if configure cannot work out how to control TZ.
+ Remove varmod-localtime from BROKEN_TESTS for IRIX*
+
+2023-07-24 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230723
+
+ * configure.in: fix the test for wether TZ=Europe/Berlin works.
+ Depending on the time of year, if run between 22:00 and 00:00 UTC
+ the check in configure would fail incorrectly.
+ Take the day into account as well.
+
+2023-07-18 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230711
+ Merge with NetBSD make, pick up
+ o make.1: clean up wording, clarify scope of '!' in conditions
+
+2023-07-15 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * make-bootstrap.sh.in: set prefix
+ If configure is run using ksh we get unexpanded ${prefix} in
+ DEFAULT_SYS_PATH, by ensuring prefix is set we should still get
+ correct result.
+
+2023-07-13 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230711
+ bump version for IRIX tweaks
+
+ * make.h: undef OP_NONE if defined
+
+ * unit-tests/Makefile: set BROKEN_TESTS for IRIX
+
+ * configure.in: override INSTALL on IRIX
+
+2023-06-27 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * boot-strap op_test: ensure we set TEST_MAKE as we want it.
+
+2023-06-22 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230622
+ Merge with NetBSD make, pick up
+ o optimize string matching for ':M' and ':N'
+ o warn about malformed patterns in ':M', ':N' and '.if make(...)'
+
+2023-06-21 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230621
+ Merge with NetBSD make, pick up
+ o more extensive tests for include guards
+ o parse.c: if a guard is already defined a file that uses the same
+ guard is still guarded by it.
+
+2023-06-20 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230620
+ Merge with NetBSD make, pick up
+ o allow guards to be targets as well as variables
+ The guard targets may include variable references like
+ __${.PARSEDIR:tA}/${.PARSEFILE}__
+
+2023-06-19 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230619
+ Merge with NetBSD make, pick up
+ o unit test for .undef of readOnly vars
+ o optimization for makefiles protected from multiple-inclusion
+ skip even opening the file after first include.
+ Initially this only handles makefiles guarded by a variable
+ target guards are next.
+
+2023-06-16 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230616
+ Merge with NetBSD make, pick up
+ o var.c: do not allow delete of readOnly variable
+
+2023-06-03 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230601
+ Merge with NetBSD make, pick up
+ o parse.c: .break takes no args
+ o lots of unit test updates
+
+2023-05-29 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * unit-tests/Makefile: skip tests that require /dev/filemon
+ if it does not exists - issue a warning.
+
+2023-05-22 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230522
+ Fix building on darwin ppc
+
+ * os.sh (MACHINE): Darwin powerpc cannot use `uname -m`
+ also recent NetBSD uses x86_64 for MACHINE_ARCH so conform.
+
+2023-05-15 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230515
+
+ * Makefile (COPTS.filemon_ktrace.c): NetBSD 7 needs help to
+ compile filemon_ktrace.c
+
+2023-05-13 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230512
+ o sys.dirdeps.mk - broke after-import target
+
+2023-05-10 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230510
+ Merge with NetBSD make, pick up
+ o parse.c: don't print null filename in stack traces
+ o var.c: :mtime operate on each word in variable value
+
+2023-05-09 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230509
+ Merge with NetBSD make, pick up
+ o for.c: skip syntactically wrong .for loops
+ o var.c: allow for :gmtime=${mtime}
+ add :mtime[=timestamp] where timestamp is used if stat(2)
+ fails, if :mtime=error stat(2) failure causes error.
+
+2023-05-05 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230504
+ Merge with NetBSD make, pick up
+ o compat.c: fix compile on NetBSD 7.2
+ o make.1: fix documentation of .PREFIX to match reality and POSIX
+ o unit-tests: improved var-scope-local
+
+2023-04-14 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230414
+ Merge with NetBSD make, pick up
+ o minor cleanup
+
+2023-03-25 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * main.c: on some systems (eg OS/X) setting RLIMIT_NOFILE to
+ unlimited results in an insane number (0x7fffffffffffffff).
+ If BMAKE_NOFILE_MAX is defined, use that instead.
+
+2023-03-22 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230321
+ Merge with NetBSD make, pick up
+ * make.1: document seemingly unexplained Error code 6.
+
+2023-03-18 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230317
+ Merge with NetBSD make, pick up
+ o compat.c: CompatDeleteTarget skip .PHONY targets to be
+ consistent with JobDeleteTarget.
+ o job.c: fix memory leak in handling sysv :from=to modifiers
+
+2023-03-04 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230303
+ Merge with NetBSD make, pick up
+ o several updated unit-tests
+
+2023-02-22 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230222
+ Merge with NetBSD make, pick up
+ o unit tests for .MAKE.META.IGNORE_{FILTER,PATHS,PATTERNS}
+
+2023-02-20 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230218
+ Merge with NetBSD make, pick up
+ o var.c: fix parsing of unevaluated subexpressions with
+ unbalanced '{}'
+
+2023-02-17 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230215
+ Merge with NetBSD make, pick up
+ o inline macros for some variable names
+ o cond.c: reduce complexity of evaluating expressions
+
+2023-02-08 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230208
+ Merge with NetBSD make, pick up
+ o var.c: always use SCOPE_GLOBAL for :_ to avoid problems
+ when it has been used within conditional expressions
+
+2023-01-27 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230127
+
+ * install-sh: if making directories ensure umask is set
+ to match mode.
+
+ * Makefile: use DIRMODE for directories and
+ NONBINMODE for man pages and mk files
+
+2023-01-26 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230126
+ Merge with NetBSD make, pick up
+ o variables like .newline and .MAKE.{GID,PID,PPID,UID}
+ should be read-only.
+
+2023-01-23 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230123
+ Merge with NetBSD make, pick up
+ o .[NO]READONLY: for control of read-only variables
+ o .SYSPATH: for controlling the path searched for makefiles
+
+2023-01-20 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230120
+ Merge with NetBSD make, pick up
+ o allow for white-space between command specifiers @+-
+ o add more details to warning 'Extra targets ignored'
+
+2023-01-12 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * machine.sh: leverage os.sh rather than duplicate
+ also dispence with the $OS.$MACHINE values - we have $HOST_TARGET
+ for that purpose for the past decade or so.
+ We invariably get MACHINE and MACHINE_ARCH at runtime anyway.
+
+2023-01-02 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20230101
+ Merge with NetBSD make, pick up
+ o cleanup comments, inline some LazyBuf_ methods
+ o unit-tests/ add/improve comments in tests
+ o make.1: sync list of built-in variables with reality
+ sort list of built-in variables
+ reduce indentation of the long list of variable names
+ use consistent markup for boolean flags
+ move description of .MAKE.MODE below the .MAKE.META block
+ clarify in which case an expression may omit braces
+
+2022-11-08 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20221024
+ Merge with NetBSD make, pick up
+ o change return type of unlink_file back to int
+
+2022-10-07 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * Makefile: Darwin and Linux can handle MANTARGET=man
+
+2022-09-28 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220928
+ Merge with NetBSD make, pick up
+ o fix more ignored returns from snprintf
+ o compile with higher warnings
+
+2022-09-26 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * main.c meta.c: do not ignore return from snprintf
+
+ * meta.c strlcpy.c: we need prototype for strlcpy
+
+ * sigcompat.c: fix unused function warnings
+
+2022-09-24 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220924
+ Merge with NetBSD make, pick up
+ o fix bug in .break reset of conditional depth
+ o overhaul and simplify tracking of conditional depth
+
+2022-09-17 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220912
+ Merge with NetBSD make, pick up
+ o man page updates
+
+2022-09-09 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220909
+ Merge with NetBSD make, pick up
+ o update unit-tests to handle deprecation of egrep
+ o cond.c: add more details to error message for numeric comparison
+
+ * configure.in: allow for deprecation of egrep
+
+ * Makefile: Linux can handle MANTARGET=man
+
+2022-09-03 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220903
+ Merge with NetBSD make, pick up
+ o job.c: fix handling of null bytes in output
+
+2022-09-02 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220902
+ Merge with NetBSD make, pick up
+ o Allow .break to terminate a .for loop early
+
+2022-09-01 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220901
+ Merge with NetBSD make, pick up
+ o var.c: fix out-of-bounds errors when parsing
+
+2022-08-24 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220824
+ Merge with NetBSD make, pick up
+ o var.c: revert change to modifier parsing that breaks
+ shell variable references within ':@var@body@'
+ o adjust unit-tests
+
+2022-08-18 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220818
+ Merge with NetBSD make, pick up
+ o fix exit status for '-q' (since 1994)
+
+2022-08-08 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220808
+ Merge with NetBSD make, pick up
+ o var.c: fix parsing of modifiers containing unbalanced subexpressions
+ extract parsing of ':D' and ':U' modifiers into separate function
+
+2022-07-26 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220726
+
+ * Auto-create objdir for bmake/unit-tests if appropriate
+
+2022-07-24 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220724
+ Merge with NetBSD make, pick up
+ o make.1: describe variable assignment and evaluation more precisely
+ o parse.c: fix out-of-bounds read when parsing an invalid line
+ o var.c: simplify return type of IsShortVarnameValid
+
+2022-06-12 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220612
+ Merge with NetBSD make, pick up
+ o allow to randomize build order of targets
+ .MAKE.MODE += randomize-targets can help uncover dependency bugs
+ within a makefile.
+ o compat.c: rename Compat_Run to Compat_MakeAll
+ o make.c: inline MakeBuildParent
+ inline make_abort, improve error details
+ o parse.c: reorganize Parse_Error
+ fix memory leak in wildcard targets and sources
+ separate cases in HandleDependencyTargetMundane
+ extract HandleSingleDependencyTargetMundane
+ rename loadfile to LoadFile
+ split IncludeFile into separate functions
+ condense code for searching a file in the paths
+ fix off-by-one error in buffer for .WAIT nodes
+ o str.c: condense Str_Match
+ make code for string matching syntactically more consistent
+
+2022-04-18 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220418
+ Merge with NetBSD make, pick up
+ o ignore '.POSIX:' if not in first non-comment line
+ of Makefile as specified by POSIX.
+ add unit-tests for above.
+ o meta.c: make it easier to find usage of identifiers
+ o targ.c: add .USEBEFORE to Targ_PrintType
+
+2022-04-14 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220414
+
+ * unit-tests/Makefile: simplify checks for shells with
+ BROKEN_TESTS, this helps with other Linux distros that
+ use dash.
+
+2022-03-30 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220330
+ Merge with NetBSD make, pick up
+ o var.c: fix spacing, and a typo in a test
+
+2022-03-26 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220326
+ Merge with NetBSD make, pick up
+ o parse.c: try to include 'posix.mk' the first time
+ .POSIX: is encountered, to allow for beter POSIX compliance.
+ o var.c: make debug logs more readable
+ prefer 'long long' over 'long' on 32-bit C99 platforms
+ fix crash on .undef of an environment variable
+
+2022-03-03 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220303
+ Merge with NetBSD make, pick up
+ o tell meta mode unit tests not to expect filemon
+ o cond.c: make debug logging for comparisons less technical
+ o lst.c: fix mem leak in Lst_Remove
+ o str.c: make code for string matching syntactically more consistent
+ o var.c: simplify ParseModifier_Match
+
+2022-02-14 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * unit-tests/Makefile: control MAKESYSPATH for deptgt-phony
+
+ * VERSION (_MAKE_VERSION): 20220214
+ Merge with NetBSD make, pick up
+ o cond.c: simplify control flow in CondParser_Comparison
+ o job.c: fix echoing of command with '-' in silent target in jobs mode
+ o main.c: prefix the warning about read-only .OBJDIR with a colon
+ o parse.c: remove redundant conditions
+ o var.c: simplify control flow in ModifyWord_SysVSubst
+
+2022-02-08 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * unit-tests/Makefile: disable opt-debug-x-trace on Linux if there
+ is any chance we have dash as .SHELL
+
+ * VERSION (_MAKE_VERSION): 20220208
+ Merge with NetBSD make, pick up
+ o more unit tests
+ o meta.c: use a variable to hold command line to be filtered
+ to avoid any side effects from content of command line.
+
+2022-02-04 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220204
+ Merge with NetBSD make, pick up
+ o use unsigned consistently for line numbers, avoid the need for %z
+ o parse.c: do not step off end of input in Parse_IsVar
+ when checking for target local variable assignments
+
+2022-02-02 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220202
+ Merge with NetBSD make, pick up
+ o remove redundant declaration of HashIter_Init
+ o make DEBUG0 simpler
+
+2022-01-30 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * cast gn->lineno to avoid %z
+
+ * VERSION (_MAKE_VERSION): 20220130
+ Merge with NetBSD make, pick up
+ o more unit tests
+ o make GNode lineno unsigned to please lint
+ o print location of recursive variable references in commands
+ o print "stack trace" (makefile includes) on fatal errors
+ o make.1: refine documentation for target local assignments
+
+2022-01-28 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220128
+ Merge with NetBSD make, pick up
+ o inline functions called only once
+ o for.c: clean up AddEscape for building the body of a .for loop
+ o hash.c: merge duplicate code for finding an entry in a hash table
+ replace HashEntry_KeyEquals with strncmp
+ o make.1: document quirks of target local variable assignments.
+ o parse.c: cleanup white-space
+
+2022-01-26 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220126
+ Merge with NetBSD make, pick up
+ o allow setting target local variables
+ o more unit tests
+ o add missing newline after "cannot continue" message
+ o meta.c: clean up eat_dots
+ o parse.c: fix filename in warning about duplicate script
+ o var.c: when expanding nested variables, check simple things first
+
+2022-01-16 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220116
+ Merge with NetBSD make, pick up
+ o fix for unit-tests/varname-makeflags on non-BSD systems
+ o use Var_Exists rather than Var_Value where appropriate
+ o remove unnecessary functions for expanding variable names
+ o cond.c: inline EvalBare
+ o main.c: lint cleanup
+ o parse.c: condense code in Parse_IsVar
+ use islower for parsing directives (none have upper case)
+
+2022-01-12 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220112
+ Merge with NetBSD make, pick up
+ o meta.c: add .MAKE.META.CMP_FILTER for filtering commands before
+ comparion, rarely needed but useful when it is.
+
+2022-01-10 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220110
+ Merge with NetBSD make, pick up
+ o inline Buf_Clear
+ o remove redundant braces
+ o rename and inline Targ_Precious
+ o cond.c: remove redundant initializer in CondParser_ComparisonOrLeaf
+ o for.c: clean up handling of .for loops
+ fix reported line numbers of continuation lines
+ add details about .for loop variables to stack traces
+ o job.c: reduce code for initializing error handling in shell
+ o main.c: in Cmd_Exec, return error message instead of format string
+ have as few statements as possible between va_start and va_end
+ add debug logging for capturing the output of external commands
+ o make.c: use consistent variable names for varargs
+ o make_malloc.c: remove duplicate code from bmake_strdup
+ o parse.c: add missing printflike annotations
+ remove redundant lines from stack traces
+ fix stack traces in -dp mode
+ reduce confusing code in ParseForLoop
+ fix line number in debug log after returning from a file
+ rename IFile and its fields to match their actual content
+ clean up ParseDependencySources
+ o var.c: shorten ApplyModifier_Assign
+ rename is_shell_metachar, fix character conversion warning
+ merge calls to ApplyModifier_Time
+ merge duplicate code for modifiers 'gmtime' and 'localtime'
+
+2022-01-04 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * parse.c: loadfile restore extra byte in buffer.
+
+2022-01-01 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20220101
+ Merge with NetBSD make, pick up
+ o more unit-tests
+ o remove unnecessary words from command line options in CmdOpts
+ o rename eunlink to unlink_file
+ o cond.c: make ParseWord in condition parser simpler
+ internally return false for irrelevant leaves in conditions
+ replace table for function lookup in conditions with simple code
+ merge duplicate types CondEvalResult and CondResult
+ o for.c: clean up handling of .for loops and .include directives
+ o main.c: constify cached_realpath
+ clean up Cmd_Exec
+ o parse.c: sync API documentation
+ fix error message when reading more than 1 GB from stdin
+ clean up parsing of makefiles
+ fix line number in error message about open conditionals
+ unexport types VarAssignOp and VarAssign
+ clean up function names
+ remove redundant parameters in dependency parsing functions
+ reduce scope of the list of wildcard target names
+ extract OP_NOTARGET into separate function
+ clean up variable names for parsing dependency lines
+ make debug logging a bit more human-friendly
+ o var.c: condense code in ApplyModifier_Assign
+
+2021-12-21 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20211221
+ Merge with NetBSD make, pick up
+ o more unit-tests
+ o style cleanup
+ o in CLEANUP mode, free interned strings at the very end
+ o fix memory leak for filenames in .for loops
+ o buf.c: avoid memory leak
+ o cond.c: condense CondParser_ComparisonOp
+ o hash.c: change return type of HashTable_Set to void
+ o job.c: change return type of Compat_RunCommand from int to bool
+ o main.c: remove bmake_free
+ o parse.c: condense repetetive code in ParseDirective
+ remove dead code for handling traditional include directives
+ clean up parsing of variable assignments
+ remove unreachable code for parsing the dependency operator
+ clean up loading of files
+ fix memory leak in IncludeFile
+ o var.c: fix memory leak when parsing a variable name
+ fix memory leak from ${.SUFFIXES}
+ reduce memory allocation in modifier ':?' and ':C'
+ condense RegexReplace for the modifier ':C' and avoid strlen
+ merge duplicate code for memory handling in Var_Parse
+ distinguish between short-lived and environment variables
+ rename VarFreeEnv to VarFreeShortLived
+
+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
@@ -1157,10 +2254,10 @@
* VERSION (_MAKE_VERSION): 20200418
- * configure.in: use_makefile=no for cygwin et al.
+ * configure.in: use_makefile=no for Cygwin et al.
case insensitive filesystems just don't work if both
makefile and Makefile exist.
- NOTE: bmake does not support cygwin and likely never will,
+ NOTE: bmake does not support Cygwin and likely never will,
but if brave souls want to try it - help them out.
2020-04-02 Simon J Gerraty <sjg@beast.crufty.net>
diff --git a/contrib/bmake/FILES b/contrib/bmake/FILES
index dc0f6f33c763..e557147c85b1 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
@@ -54,13 +53,14 @@ metachar.c
metachar.h
missing/sys/cdefs.h
mkdeps.sh
-nonints.h
os.sh
parse.c
pathnames.h
ranlib.h
realpath.c
setenv.c
+sigact.h
+sigaction.c
sigcompat.c
str.c
str.h
@@ -158,8 +158,6 @@ unit-tests/cond-token-var.exp
unit-tests/cond-token-var.mk
unit-tests/cond-undef-lint.exp
unit-tests/cond-undef-lint.mk
-unit-tests/cond1.exp
-unit-tests/cond1.mk
unit-tests/counter-append.exp
unit-tests/counter-append.mk
unit-tests/counter.exp
@@ -172,10 +170,14 @@ unit-tests/dep-double-colon-indep.exp
unit-tests/dep-double-colon-indep.mk
unit-tests/dep-double-colon.exp
unit-tests/dep-double-colon.mk
+unit-tests/dep-duplicate.exp
+unit-tests/dep-duplicate.mk
unit-tests/dep-exclam.exp
unit-tests/dep-exclam.mk
unit-tests/dep-none.exp
unit-tests/dep-none.mk
+unit-tests/dep-op-missing.exp
+unit-tests/dep-op-missing.mk
unit-tests/dep-percent.exp
unit-tests/dep-percent.mk
unit-tests/dep-var.exp
@@ -270,10 +272,14 @@ unit-tests/deptgt-path.exp
unit-tests/deptgt-path.mk
unit-tests/deptgt-phony.exp
unit-tests/deptgt-phony.mk
+unit-tests/deptgt-posix.exp
+unit-tests/deptgt-posix.mk
unit-tests/deptgt-precious.exp
unit-tests/deptgt-precious.mk
unit-tests/deptgt-shell.exp
unit-tests/deptgt-shell.mk
+unit-tests/deptgt-silent-jobs.exp
+unit-tests/deptgt-silent-jobs.mk
unit-tests/deptgt-silent.exp
unit-tests/deptgt-silent.mk
unit-tests/deptgt-stale.exp
@@ -316,12 +322,18 @@ unit-tests/directive-export-literal.exp
unit-tests/directive-export-literal.mk
unit-tests/directive-export.exp
unit-tests/directive-export.mk
+unit-tests/directive-for-break.exp
+unit-tests/directive-for-break.mk
+unit-tests/directive-for-empty.exp
+unit-tests/directive-for-empty.mk
unit-tests/directive-for-errors.exp
unit-tests/directive-for-errors.mk
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
@@ -344,6 +356,8 @@ unit-tests/directive-ifnmake.exp
unit-tests/directive-ifnmake.mk
unit-tests/directive-include-fatal.exp
unit-tests/directive-include-fatal.mk
+unit-tests/directive-include-guard.exp
+unit-tests/directive-include-guard.mk
unit-tests/directive-include.exp
unit-tests/directive-include.mk
unit-tests/directive-info.exp
@@ -368,8 +382,6 @@ unit-tests/doterror.exp
unit-tests/doterror.mk
unit-tests/dotwait.exp
unit-tests/dotwait.mk
-unit-tests/envfirst.exp
-unit-tests/envfirst.mk
unit-tests/error.exp
unit-tests/error.mk
unit-tests/escape.exp
@@ -382,10 +394,6 @@ unit-tests/export-variants.exp
unit-tests/export-variants.mk
unit-tests/export.exp
unit-tests/export.mk
-unit-tests/forloop.exp
-unit-tests/forloop.mk
-unit-tests/forsubst.exp
-unit-tests/forsubst.mk
unit-tests/gnode-submake.exp
unit-tests/gnode-submake.mk
unit-tests/hanoi-include.exp
@@ -394,8 +402,8 @@ unit-tests/impsrc.exp
unit-tests/impsrc.mk
unit-tests/include-main.exp
unit-tests/include-main.mk
-unit-tests/include-sub.mk
-unit-tests/include-subsub.mk
+unit-tests/include-sub.inc
+unit-tests/include-subsub.inc
unit-tests/job-flags.exp
unit-tests/job-flags.mk
unit-tests/job-output-long-lines.exp
@@ -418,16 +426,11 @@ unit-tests/make-exported.exp
unit-tests/make-exported.mk
unit-tests/meta-cmd-cmp.exp
unit-tests/meta-cmd-cmp.mk
+unit-tests/meta-ignore.inc
unit-tests/moderrs.exp
unit-tests/moderrs.mk
-unit-tests/modmatch.exp
-unit-tests/modmatch.mk
unit-tests/modmisc.exp
unit-tests/modmisc.mk
-unit-tests/modts.exp
-unit-tests/modts.mk
-unit-tests/modword.exp
-unit-tests/modword.mk
unit-tests/objdir-writable.exp
unit-tests/objdir-writable.mk
unit-tests/opt-backwards.exp
@@ -502,6 +505,8 @@ unit-tests/opt-jobs-no-action.exp
unit-tests/opt-jobs-no-action.mk
unit-tests/opt-jobs.exp
unit-tests/opt-jobs.mk
+unit-tests/opt-keep-going-indirect.exp
+unit-tests/opt-keep-going-indirect.mk
unit-tests/opt-keep-going-multiple.exp
unit-tests/opt-keep-going-multiple.mk
unit-tests/opt-keep-going.exp
@@ -532,6 +537,8 @@ unit-tests/opt-var-expanded.exp
unit-tests/opt-var-expanded.mk
unit-tests/opt-var-literal.exp
unit-tests/opt-var-literal.mk
+unit-tests/opt-version.exp
+unit-tests/opt-version.mk
unit-tests/opt-warnings-as-errors.exp
unit-tests/opt-warnings-as-errors.mk
unit-tests/opt-where-am-i.exp
@@ -544,6 +551,8 @@ unit-tests/order.exp
unit-tests/order.mk
unit-tests/parse-var.exp
unit-tests/parse-var.mk
+unit-tests/parse.exp
+unit-tests/parse.mk
unit-tests/phony-end.exp
unit-tests/phony-end.mk
unit-tests/posix.exp
@@ -612,6 +621,8 @@ unit-tests/suff-transform-expand.exp
unit-tests/suff-transform-expand.mk
unit-tests/suff-transform-select.exp
unit-tests/suff-transform-select.mk
+unit-tests/suff-use.exp
+unit-tests/suff-use.mk
unit-tests/sunshcmd.exp
unit-tests/sunshcmd.mk
unit-tests/ternary.exp
@@ -622,18 +633,6 @@ unit-tests/unexport.exp
unit-tests/unexport.mk
unit-tests/use-inference.exp
unit-tests/use-inference.mk
-unit-tests/var-class-cmdline.exp
-unit-tests/var-class-cmdline.mk
-unit-tests/var-class-env.exp
-unit-tests/var-class-env.mk
-unit-tests/var-class-global.exp
-unit-tests/var-class-global.mk
-unit-tests/var-class-local-legacy.exp
-unit-tests/var-class-local-legacy.mk
-unit-tests/var-class-local.exp
-unit-tests/var-class-local.mk
-unit-tests/var-class.exp
-unit-tests/var-class.mk
unit-tests/var-eval-short.exp
unit-tests/var-eval-short.mk
unit-tests/var-op-append.exp
@@ -650,8 +649,22 @@ unit-tests/var-op-sunsh.exp
unit-tests/var-op-sunsh.mk
unit-tests/var-op.exp
unit-tests/var-op.mk
+unit-tests/var-readonly.exp
+unit-tests/var-readonly.mk
unit-tests/var-recursive.exp
unit-tests/var-recursive.mk
+unit-tests/var-scope-cmdline.exp
+unit-tests/var-scope-cmdline.mk
+unit-tests/var-scope-env.exp
+unit-tests/var-scope-env.mk
+unit-tests/var-scope-global.exp
+unit-tests/var-scope-global.mk
+unit-tests/var-scope-local-legacy.exp
+unit-tests/var-scope-local-legacy.mk
+unit-tests/var-scope-local.exp
+unit-tests/var-scope-local.mk
+unit-tests/var-scope.exp
+unit-tests/var-scope.mk
unit-tests/varcmd.exp
unit-tests/varcmd.mk
unit-tests/vardebug.exp
@@ -660,6 +673,8 @@ unit-tests/varfind.exp
unit-tests/varfind.mk
unit-tests/varmisc.exp
unit-tests/varmisc.mk
+unit-tests/varmod-assign-shell.exp
+unit-tests/varmod-assign-shell.mk
unit-tests/varmod-assign.exp
unit-tests/varmod-assign.mk
unit-tests/varmod-defined.exp
@@ -684,6 +699,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
@@ -692,12 +709,18 @@ unit-tests/varmod-match-escape.exp
unit-tests/varmod-match-escape.mk
unit-tests/varmod-match.exp
unit-tests/varmod-match.mk
+unit-tests/varmod-mtime.exp
+unit-tests/varmod-mtime.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 +837,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
@@ -842,8 +867,6 @@ unit-tests/varparse-mod.exp
unit-tests/varparse-mod.mk
unit-tests/varparse-undef-partial.exp
unit-tests/varparse-undef-partial.mk
-unit-tests/varquote.exp
-unit-tests/varquote.mk
util.c
var.c
wait.h
diff --git a/contrib/bmake/Makefile b/contrib/bmake/Makefile
index 38ccb8a6a636..65730df7e3df 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.127 2024/03/19 16:03:23 sjg Exp $
PROG= bmake
@@ -8,7 +8,6 @@ SRCS= \
compat.c \
cond.c \
dir.c \
- enum.c \
for.c \
hash.c \
job.c \
@@ -49,7 +48,7 @@ CFLAGS+= -I. -I${srcdir} ${XDEFS} -DMAKE_NATIVE
CFLAGS+= ${COPTS.${.ALLSRC:M*.c:T:u}}
COPTS.main.c+= "-DMAKE_VERSION=\"${_MAKE_VERSION}\""
-.for x in FORCE_MACHINE FORCE_MACHINE_ARCH
+.for x in FORCE_MAKE_OS FORCE_MACHINE FORCE_MACHINE_ARCH
.ifdef $x
COPTS.main.c+= "-D$x=\"${$x}\""
.endif
@@ -69,7 +68,9 @@ FILEMON_H ?= /usr/include/dev/filemon/filemon.h
.if exists(${FILEMON_H}) && ${FILEMON_H:T} == "filemon.h"
COPTS.filemon_dev.c += -DHAVE_FILEMON_H -I${FILEMON_H:H}
.endif
-.endif # USE_FILEMON == dev
+.elif ${USE_FILEMON} == "ktrace"
+COPTS.filemon_ktrace.c += -Wno-error=unused-parameter
+.endif
.endif # USE_FILEMON
@@ -89,13 +90,13 @@ OS := ${.MAKE.OS:U${uname -s:L:sh}}
# are we 4.4BSD ?
isBSD44:=${BSD44_LIST:M${OS}}
-.if ${isBSD44} == ""
-MANTARGET= cat
-INSTALL?=${srcdir}/install-sh
-.if (${MACHINE} == "sun386")
+.if ${isBSD44} == "" && ${OS:NCygwin:NDarwin:NLinux} != ""
+MANTARGET?= cat
+.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 +132,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:
@@ -176,9 +177,7 @@ SHAREDIR= ${SHAREDIR.bmake:U${prefix}/share}
BINDIR= ${BINDIR.bmake:U${prefix}/bin}
MANDIR= ${MANDIR.bmake:U${SHAREDIR}/man}
-.if !exists(.depend)
${OBJS}: config.h
-.endif
# start-delete2 for bsd.after-import.mk
@@ -189,24 +188,25 @@ 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
.endif
beforeinstall:
- test -d ${DESTDIR}${BINDIR} || ${INSTALL} -m 775 -d ${DESTDIR}${BINDIR}
- test -d ${DESTDIR}${MANDEST} || ${INSTALL} -m 775 -d ${DESTDIR}${MANDEST}
+ test -d ${DESTDIR}${BINDIR} || ${INSTALL} -m ${DIRMODE} -d ${DESTDIR}${BINDIR}
+ test -d ${DESTDIR}${MANDEST} || ${INSTALL} -m ${DIRMODE} -d ${DESTDIR}${MANDEST}
install-mk:
.if exists(${MKSRC}/install-mk)
- test -d ${DESTDIR}${SHARE_MK} || ${INSTALL} -m 775 -d ${DESTDIR}${SHARE_MK}
- sh ${MKSRC}/install-mk -v -m 644 ${DESTDIR}${SHARE_MK}
+ test -d ${DESTDIR}${SHARE_MK} || ${INSTALL} -m ${DIRMODE} -d ${DESTDIR}${SHARE_MK}
+ sh ${MKSRC}/install-mk -v -m ${NONBINMODE} ${DESTDIR}${SHARE_MK}
.else
@echo need to unpack mk.tar.gz under ${srcdir} or set MKSRC; false
.endif
@@ -214,7 +214,24 @@ install-mk:
# A simple unit-test driver to help catch regressions
TEST_MAKE ?= ${.OBJDIR}/${PROG:T}
-accept test:
+accept test: .NOMETA
cd ${.CURDIR}/unit-tests && \
MAKEFLAGS= ${TEST_MAKE} -r -m / ${.TARGET} ${TESTS:DTESTS=${TESTS:Q}}
+
+.if make(test) && ${MK_AUTO_OBJ} == "yes"
+# The test target above visits unit-tests with -r -m /
+# which prevents MK_AUTO_OBJ doing its job
+# so do it here
+.if defined(MAKEOBJDIRPREFIX) || ${MAKEOBJDIR:U:M*/*} != ""
+_utobj = ${.OBJDIR}/unit-tests
+.else
+_utobj = ${.CURDIR}/unit-tests/${MAKEOBJDIR:Uobj}
+.endif
+utobj: .NOMETA
+ @test -d ${_utobj} && exit 0; \
+ echo "[Creating ${_utobj}...]"; \
+ umask ${OBJDIR_UMASK:U002}; \
+ mkdir -p ${_utobj}
+test: utobj
+.endif
diff --git a/contrib/bmake/Makefile.config.in b/contrib/bmake/Makefile.config.in
index 55cd60ca80ba..43ad1b9a397a 100644
--- a/contrib/bmake/Makefile.config.in
+++ b/contrib/bmake/Makefile.config.in
@@ -4,11 +4,13 @@ _MAKE_VERSION?=@_MAKE_VERSION@
prefix?= @prefix@
srcdir= @srcdir@
-CC?= @CC@
+CC= @CC@
+@force_make_os@MAKE_OS?= @make_os@
@force_machine@MACHINE?= @machine@
@force_machine_arch@MACHINE_ARCH?= @machine_arch@
DEFAULT_SYS_PATH?= @default_sys_path@
+EGREP = @egrep@
CPPFLAGS+= @CPPFLAGS@
CFLAGS+= ${CPPFLAGS} @DEFS@
LDFLAGS+= @LDFLAGS@
diff --git a/contrib/bmake/README b/contrib/bmake/README
index a782f6dfc5b5..bf59107db2d3 100644
--- a/contrib/bmake/README
+++ b/contrib/bmake/README
@@ -6,12 +6,12 @@ Since 1993 I have run it on AIX, BSDi, Darwin, FreeBSD, HP-UX, IRIX,
Linux, Minix, OSF, Solaris, SunOS and even UTS.
Others have run it on many more systems.
-Currently each release is tested on NetBSD, FreeBSD, Solaris and Linux.
+Currently each release is tested on Darwin, NetBSD, FreeBSD and Linux.
Since 2003 bmake switched to a date based version (first was 20030714)
which generally represents the date it was last merged with NetBSD's
make. Since then, NetBSD's make is imported within a week of any
-interesting changes, so that bmake tracks it very closely.
+*interesting* changes, so that bmake tracks it very closely.
Building
========
@@ -33,20 +33,27 @@ the GNU standard process of::
./configure; make; make install
+This will *not* work on Darwin or any other system with a case
+insensitive filesystem. It depends on a generated ``makefile`` which
+is disabled by default on Darwin.
+
To make much use of bmake you will need the bsd.*.mk macros or my
portable *.mk macros which are included with bmake since 20121212
and separately available from
-http://www.crufty.net/ftp/pub/sjg/mk.tar.gz
-which will be links to the latest versions.
+https://www.crufty.net/ftp/pub/sjg/mk.tar.gz
+both that and
+https://www.crufty.net/ftp/pub/sjg/bmake.tar.gz
+will be links to the latest versions.
Porting
=======
If you encounter a system that bmake does not build or work on *out of
the box*, I welcome patches.
+Even a report of unit tests which fail is appreciated.
If you can provide access to a suitable machine - even better.
-More info can be found at http://www.crufty.net/help/sjg/bmake.htm
+More info can be found at https://www.crufty.net/help/sjg/bmake.htm
--sjg <sjg@crufty.net>
diff --git a/contrib/bmake/VERSION b/contrib/bmake/VERSION
index 7c28f11013b7..60aa9ae5069b 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=20240508
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..4e52532c780a 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.217 2024/04/27 20:41:32 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.217 2024/04/27 20:41:32 rillig Exp $");
typedef struct List ArchList;
typedef struct ListNode ArchListNode;
@@ -155,7 +155,7 @@ typedef struct ListNode ArchListNode;
static ArchList archives; /* The archives we've already examined */
typedef struct Arch {
- char *name; /* Name of archive */
+ char *name;
HashTable members; /* All the members of the archive described
* by <name, struct ar_hdr *> key/value pairs */
char *fnametab; /* Extended name table strings */
@@ -199,12 +199,10 @@ static int ArchSVR4Entry(Arch *, char *, size_t, FILE *);
#ifdef CLEANUP
static void
-ArchFree(void *ap)
+ArchFree(Arch *a)
{
- Arch *a = ap;
HashIter hi;
- /* Free memory from hash entries */
HashIter_Init(&hi, &a->members);
while (HashIter_Next(&hi) != NULL)
free(hi.entry->value);
@@ -217,79 +215,64 @@ ArchFree(void *ap)
#endif
/* Return "archive(member)". */
-static char *
+MAKE_ATTR_NOINLINE static char *
FullName(const char *archive, const char *member)
{
- size_t len1 = strlen(archive);
- size_t len3 = strlen(member);
- char *result = bmake_malloc(len1 + 1 + len3 + 1 + 1);
- memcpy(result, archive, len1);
- memcpy(result + len1, "(", 1);
- memcpy(result + len1 + 1, member, len3);
- memcpy(result + len1 + 1 + len3, ")", 1 + 1);
- return result;
+ Buffer buf;
+ Buf_Init(&buf);
+ Buf_AddStr(&buf, archive);
+ Buf_AddStr(&buf, "(");
+ Buf_AddStr(&buf, member);
+ Buf_AddStr(&buf, ")");
+ return Buf_DoneData(&buf);
}
/*
* Parse an archive specification such as "archive.a(member1 member2.${EXT})",
- * adding nodes for the expanded members to gns. Nodes are created as
- * necessary.
- *
- * Input:
- * pp The start of the specification.
- * gns The list on which to place the nodes.
- * scope The scope in which to expand variables.
- *
- * Output:
- * return True if it was a valid specification.
- * *pp Points to the first non-space after the archive spec.
+ * adding nodes for the expanded members to gns. If successful, advance pp
+ * beyond the archive specification and any trailing whitespace.
*/
bool
Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
{
- char *cp; /* Pointer into line */
+ char *spec; /* For modifying some bytes of *pp */
+ const char *cp; /* Pointer into line */
GNode *gn; /* New node */
- MFStr libName; /* Library-part of specification */
- char *memName; /* Member-part of specification */
+ FStr lib; /* Library-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
- * expanded */
+ bool expandLib; /* Whether the parsed lib contains
+ * expressions that need to be expanded */
- libName = MFStr_InitRefer(*pp);
- expandLibName = false;
+ spec = *pp;
+ lib = FStr_InitRefer(spec);
+ expandLib = false;
- for (cp = libName.str; *cp != '(' && *cp != '\0';) {
+ for (cp = lib.str; *cp != '(' && *cp != '\0';) {
if (*cp == '$') {
- /* Expand nested variable expressions. */
+ /* Expand nested expressions. */
/* XXX: This code can probably be shortened. */
const char *nested_p = cp;
FStr result;
bool isError;
/* XXX: is expanded twice: once here and once below */
- (void)Var_Parse(&nested_p, scope,
- VARE_UNDEFERR, &result);
+ result = Var_Parse(&nested_p, scope, VARE_UNDEFERR);
/* TODO: handle errors */
isError = result.str == var_Error;
FStr_Done(&result);
if (isError)
return false;
- expandLibName = true;
+ expandLib = true;
cp += nested_p - cp;
} else
cp++;
}
- *cp++ = '\0';
- if (expandLibName) {
- char *expanded;
- (void)Var_Subst(libName.str, scope, VARE_UNDEFERR, &expanded);
- /* TODO: handle errors */
- libName = MFStr_InitOwn(expanded);
- }
-
+ spec[cp++ - spec] = '\0';
+ if (expandLib)
+ Var_Expand(&lib, scope, VARE_UNDEFERR);
for (;;) {
/*
@@ -299,19 +282,21 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
*/
bool doSubst = false;
- pp_skip_whitespace(&cp);
+ cpp_skip_whitespace(&cp);
- memName = cp;
+ mem = FStr_InitRefer(cp);
while (*cp != '\0' && *cp != ')' && !ch_isspace(*cp)) {
if (*cp == '$') {
- /* Expand nested variable expressions. */
- /* XXX: This code can probably be shortened. */
+ /* Expand nested expressions. */
+ /*
+ * XXX: This code can probably be shortened.
+ */
FStr result;
bool isError;
const char *nested_p = cp;
- (void)Var_Parse(&nested_p, scope,
- VARE_UNDEFERR, &result);
+ result = Var_Parse(&nested_p, scope,
+ VARE_UNDEFERR);
/* TODO: handle errors */
isError = result.str == var_Error;
FStr_Done(&result);
@@ -326,27 +311,18 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
}
}
- /*
- * If the specification ends without a closing parenthesis,
- * chances are there's something wrong (like a missing
- * backslash), so it's better to return failure than allow
- * such things to happen
- */
if (*cp == '\0') {
Parse_Error(PARSE_FATAL,
- "No closing parenthesis "
- "in archive specification");
+ "No closing parenthesis "
+ "in archive specification");
return false;
}
- /*
- * If we didn't move anywhere, we must be done
- */
- if (cp == memName)
+ if (cp == mem.str)
break;
saveChar = *cp;
- *cp = '\0';
+ spec[cp - spec] = '\0';
/*
* XXX: This should be taken care of intelligently by
@@ -355,35 +331,30 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
*/
/*
* If member contains variables, try and substitute for them.
- * This will slow down archive specs with dynamic sources, of
- * course, since we'll be (non-)substituting them three
- * times, but them's the breaks -- we need to do this since
- * SuffExpandChildren calls us, otherwise we could assume the
- * thing would be taken care of later.
+ * This slows down archive specs with dynamic sources, since
+ * they are (non-)substituted three times, but we need to do
+ * this since SuffExpandChildren calls us, otherwise we could
+ * assume the substitutions would be taken care of later.
*/
if (doSubst) {
char *fullName;
char *p;
- char *unexpandedMemName = memName;
+ const char *unexpandedMem = mem.str;
- (void)Var_Subst(memName, scope, VARE_UNDEFERR,
- &memName);
- /* TODO: handle errors */
+ Var_Expand(&mem, scope, VARE_UNDEFERR);
/*
* Now form an archive spec and recurse to deal with
* nested variables and multi-word variable values.
*/
- fullName = FullName(libName.str, memName);
+ fullName = FullName(lib.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
- * for the thing and let SuffExpandChildren
- * handle it.
+ * and let SuffExpandChildren handle it.
*/
gn = Targ_GetNode(fullName);
gn->type |= OP_ARCHV;
@@ -398,13 +369,13 @@ 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);
- char *fullname = FullName(libName.str, member);
+ char *fullname = FullName(lib.str, member);
free(member);
gn = Targ_GetNode(fullname);
@@ -416,46 +387,28 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
Lst_Done(&members);
} else {
- char *fullname = FullName(libName.str, memName);
+ char *fullname = FullName(lib.str, mem.str);
gn = Targ_GetNode(fullname);
free(fullname);
- /*
- * We've found the node, but have to make sure the
- * rest of the world knows it's an archive member,
- * without having to constantly check for parentheses,
- * so we type the thing with the OP_ARCHV bit before
- * we place it on the end of the provided list.
- */
gn->type |= OP_ARCHV;
Lst_Append(gns, gn);
}
- if (doSubst)
- free(memName);
+ FStr_Done(&mem);
- *cp = saveChar;
+ spec[cp - spec] = saveChar;
}
- MFStr_Done(&libName);
+ FStr_Done(&lib);
cp++; /* skip the ')' */
- /* We promised that pp would be set up at the next non-space. */
- pp_skip_whitespace(&cp);
- *pp = cp;
+ cpp_skip_whitespace(&cp);
+ *pp += cp - *pp;
return true;
}
/*
- * Locate a member of an archive, given the path of the archive and the path
- * of the desired member.
- *
- * Input:
- * archive Path to the archive
- * member Name of member; only its basename is used.
- * addToCache True if archive should be cached if not already so.
- *
- * Results:
- * The ar_hdr for the member, or NULL.
+ * Locate a member in an archive.
*
* See ArchFindMember for an almost identical copy of this code.
*/
@@ -467,15 +420,11 @@ ArchStatMember(const char *archive, const char *member, bool addToCache)
size_t size; /* Size of archive member */
char magic[SARMAG];
ArchListNode *ln;
- Arch *ar; /* Archive descriptor */
- struct ar_hdr arh; /* archive-member header for reading archive */
+ Arch *ar;
+ struct ar_hdr arh;
char memName[MAXPATHLEN + 1];
/* Current member name while hashing. */
- /*
- * Because of space constraints and similar things, files are archived
- * using their basename, not the entire path.
- */
member = str_basename(member);
for (ln = archives.first; ln != NULL; ln = ln->next) {
@@ -507,11 +456,8 @@ ArchStatMember(const char *archive, const char *member, bool addToCache)
if (!addToCache) {
/*
- * Caller doesn't want the thing cached, just use
- * ArchFindMember to read the header for the member out and
- * close down the stream again. Since the archive is not to be
- * cached, we assume there's no need to allocate extra room
- * for the header we're returning, so just declare it static.
+ * Since the archive is not to be cached, assume there's no
+ * need to allocate the header, so just declare it static.
*/
static struct ar_hdr sarh;
@@ -523,18 +469,10 @@ ArchStatMember(const char *archive, const char *member, bool addToCache)
return &sarh;
}
- /*
- * We don't have this archive on the list yet, so we want to find out
- * everything that's in it and cache it so we can get at it quickly.
- */
arch = fopen(archive, "r");
if (arch == NULL)
return NULL;
- /*
- * We use the ARMAG string to make sure this is an archive we
- * can handle...
- */
if (fread(magic, SARMAG, 1, arch) != 1 ||
strncmp(magic, ARMAG, SARMAG) != 0) {
(void)fclose(arch);
@@ -551,17 +489,9 @@ ArchStatMember(const char *archive, const char *member, bool addToCache)
while (fread(&arh, sizeof arh, 1, arch) == 1) {
char *nameend;
- /* If the header is bogus, there's no way we can recover. */
if (strncmp(arh.AR_FMAG, ARFMAG, sizeof arh.AR_FMAG) != 0)
- goto badarch;
+ goto bad_archive;
- /*
- * We need to advance the stream's pointer to the start of the
- * next header. Files are padded with newlines to an even-byte
- * boundary, so we need to extract the size of the file from
- * the 'size' field of the header and round it up during the
- * seek.
- */
arh.AR_SIZE[sizeof arh.AR_SIZE - 1] = '\0';
size = (size_t)strtol(arh.AR_SIZE, NULL, 10);
@@ -580,7 +510,7 @@ ArchStatMember(const char *archive, const char *member, bool addToCache)
/* svr4 magic mode; handle it */
switch (ArchSVR4Entry(ar, memName, size, arch)) {
case -1: /* Invalid data */
- goto badarch;
+ goto bad_archive;
case 0: /* List of files entry */
continue;
default: /* Got the entry */
@@ -600,15 +530,16 @@ 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 = (size_t)atoi(
+ memName + sizeof AR_EFMT1 - 1);
- if ((unsigned int)elen > MAXPATHLEN)
- goto badarch;
- if (fread(memName, (size_t)elen, 1, arch) != 1)
- goto badarch;
+ if (elen > MAXPATHLEN)
+ goto bad_archive;
+ if (fread(memName, elen, 1, arch) != 1)
+ goto bad_archive;
memName[elen] = '\0';
- if (fseek(arch, -elen, SEEK_CUR) != 0)
- goto badarch;
+ if (fseek(arch, -(long)elen, SEEK_CUR) != 0)
+ goto bad_archive;
if (DEBUG(ARCH) || DEBUG(MAKE))
debug_printf(
"ArchStatMember: "
@@ -624,21 +555,18 @@ ArchStatMember(const char *archive, const char *member, bool addToCache)
HashTable_Set(&ar->members, memName, cached_hdr);
}
+ /* Files are padded with newlines to an even-byte boundary. */
if (fseek(arch, ((long)size + 1) & ~1, SEEK_CUR) != 0)
- goto badarch;
+ goto bad_archive;
}
fclose(arch);
Lst_Append(&archives, ar);
- /*
- * Now that the archive has been read and cached, we can look into
- * the addToCache table to find the desired member's header.
- */
return HashTable_FindValue(&ar->members, member);
-badarch:
+bad_archive:
fclose(arch);
HashTable_Done(&ar->members);
free(ar->fnametab);
@@ -673,7 +601,7 @@ ArchSVR4Entry(Arch *ar, char *inout_name, size_t size, FILE *arch)
if (ar->fnametab != NULL) {
DEBUG0(ARCH,
- "Attempted to redefine an SVR4 name table\n");
+ "Attempted to redefine an SVR4 name table\n");
return -1;
}
@@ -694,8 +622,9 @@ ArchSVR4Entry(Arch *ar, char *inout_name, size_t size, FILE *arch)
entry++;
*ptr = '\0';
}
- DEBUG1(ARCH, "Found svr4 archive name table with %lu entries\n",
- (unsigned long)entry);
+ DEBUG1(ARCH,
+ "Found svr4 archive name table with %lu entries\n",
+ (unsigned long)entry);
return 0;
}
@@ -709,7 +638,7 @@ ArchSVR4Entry(Arch *ar, char *inout_name, size_t size, FILE *arch)
}
if (entry >= ar->fnamesize) {
DEBUG2(ARCH, "SVR4 entry offset %s is greater than %lu\n",
- inout_name, (unsigned long)ar->fnamesize);
+ inout_name, (unsigned long)ar->fnamesize);
return 2;
}
@@ -738,39 +667,31 @@ ArchiveMember_HasName(const struct ar_hdr *hdr,
if (ar_name[namelen] == ' ')
return true;
- /* In archives created by GNU binutils 2.27, the member names end with
- * a slash. */
- if (ar_name[namelen] == '/' &&
- (namelen == ar_name_len || ar_name[namelen + 1] == ' '))
+ /*
+ * In archives created by GNU binutils 2.27, the member names end
+ * with a slash.
+ */
+ if (ar_name[namelen] == '/' && ar_name[namelen + 1] == ' ')
return true;
return false;
}
/*
- * Locate a member of an archive, given the path of the archive and the path
- * of the desired member.
+ * Load the header of an archive member. The mode is "r" for read-only
+ * access, "r+" for read-write access.
*
- * Input:
- * archive Path to the archive
- * member Name of member. If it is a path, only the last
- * component is used.
- * out_arh Archive header to be filled in
- * mode "r" for read-only access, "r+" for read-write access
- *
- * Output:
- * return The archive file, positioned at the start of the
- * member's struct ar_hdr, or NULL if the member doesn't
- * exist.
- * *out_arh The current struct ar_hdr for member.
+ * Upon successful return, the archive file is positioned at the start of the
+ * member's struct ar_hdr. In case of a failure or if the member doesn't
+ * exist, return NULL.
*
* See ArchStatMember for an almost identical copy of this code.
*/
static FILE *
-ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh,
- const char *mode)
+ArchFindMember(const char *archive, const char *member,
+ struct ar_hdr *out_arh, const char *mode)
{
- FILE *arch; /* Stream to archive */
+ FILE *arch;
int size; /* Size of archive member */
char magic[SARMAG];
size_t len;
@@ -779,50 +700,30 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh,
if (arch == NULL)
return NULL;
- /*
- * We use the ARMAG string to make sure this is an archive we
- * can handle...
- */
if (fread(magic, SARMAG, 1, arch) != 1 ||
strncmp(magic, ARMAG, SARMAG) != 0) {
fclose(arch);
return NULL;
}
- /*
- * Because of space constraints and similar things, files are archived
- * using their basename, not the entire path.
- */
+ /* Files are archived using their basename, not the entire path. */
member = str_basename(member);
-
len = strlen(member);
while (fread(out_arh, sizeof *out_arh, 1, arch) == 1) {
if (strncmp(out_arh->AR_FMAG, ARFMAG,
sizeof out_arh->AR_FMAG) != 0) {
- /*
- * The header is bogus, so the archive is bad
- * and there's no way we can recover...
- */
fclose(arch);
return NULL;
}
DEBUG5(ARCH, "Reading archive %s member %.*s mtime %.*s\n",
- archive,
- (int)sizeof out_arh->AR_NAME, out_arh->AR_NAME,
- (int)sizeof out_arh->ar_date, out_arh->ar_date);
+ archive,
+ (int)sizeof out_arh->AR_NAME, out_arh->AR_NAME,
+ (int)sizeof out_arh->ar_date, out_arh->ar_date);
if (ArchiveMember_HasName(out_arh, member, len)) {
- /*
- * To make life easier for callers that want to update
- * the archive, we reposition the file at the start of
- * the header we just read before we return the
- * stream. In a more general situation, it might be
- * better to leave the file at the actual member,
- * rather than its header, but not here.
- */
if (fseek(arch, -(long)sizeof *out_arh, SEEK_CUR) !=
0) {
fclose(arch);
@@ -839,14 +740,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 = (size_t)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,30 +761,25 @@ 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;
}
}
#endif
- /*
- * This isn't the member we're after, so we need to advance the
- * stream's pointer to the start of the next header. Files are
- * padded with newlines to an even-byte boundary, so we need to
- * extract the size of the file from the 'size' field of the
- * header and round it up during the seek.
- */
+ /* Advance to the next member. */
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) {
+ /* Files are padded with newlines to an even-byte boundary. */
+ if (fseek(arch, (size + 1) & ~1L, SEEK_CUR) != 0) {
fclose(arch);
return NULL;
}
@@ -893,17 +790,9 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh,
}
/*
- * Touch a member of an archive, on disk.
- * The GNode's modification time is left as-is.
- *
- * The st_mtime of the entire archive is also changed.
- * For a library, it may be required to run ranlib after this.
- *
- * Input:
- * gn Node of member to touch
- *
- * Results:
- * The 'time' field of the member's header is updated.
+ * Update the ar_date of the member of an archive, on disk but not in the
+ * GNode. Update the st_mtime of the entire archive as well. For a library,
+ * it may be required to run ranlib after this.
*/
void
Arch_Touch(GNode *gn)
@@ -912,7 +801,7 @@ Arch_Touch(GNode *gn)
struct ar_hdr arh;
f = ArchFindMember(GNode_VarArchive(gn), GNode_VarMember(gn), &arh,
- "r+");
+ "r+");
if (f == NULL)
return;
@@ -991,12 +880,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.
@@ -1018,9 +907,6 @@ Arch_UpdateMemberMTime(GNode *gn)
* TARGET variable for this node to be the node's name. Otherwise,
* we set the TARGET variable to be the full path of the library,
* as returned by Dir_FindFile.
- *
- * Input:
- * gn Node of library to find
*/
void
Arch_FindLib(GNode *gn, SearchPath *path)
@@ -1029,28 +915,51 @@ Arch_FindLib(GNode *gn, SearchPath *path)
gn->path = Dir_FindFile(libName, path);
free(libName);
-#ifdef LIBRARIES
Var_Set(gn, TARGET, gn->name);
+}
+
+/* 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
- Var_Set(gn, TARGET, GNode_Path(gn));
+ 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.
+ * Decide if a node with the OP_LIB attribute is out-of-date.
* The library is cached if it hasn't been already.
*
- * There are several ways for a library to be out-of-date that are
- * not available to ordinary files. In addition, there are ways
- * that are open to regular files that are not available to
- * libraries.
+ * There are several ways for a library to be out-of-date that are not
+ * available to ordinary files. In addition, there are ways that are open to
+ * regular files that are not available to libraries.
*
- * A library that is only used as a source is never
- * considered out-of-date by itself. This does not preclude the
- * library's modification time from making its parent be out-of-date.
- * A library will be considered out-of-date for any of these reasons,
- * given that it is a target on a dependency line somewhere:
+ * A library that is only used as a source is never considered out-of-date by
+ * itself. This does not preclude the library's modification time from making
+ * its parent be out-of-date. A library will be considered out-of-date for
+ * any of these reasons, given that it is a target on a dependency line
+ * somewhere:
*
* Its modification time is less than that of one of its sources
* (gn->mtime < gn->youngestChild->mtime).
@@ -1069,46 +978,17 @@ Arch_FindLib(GNode *gn, SearchPath *path)
bool
Arch_LibOODate(GNode *gn)
{
- bool oodate;
- if (gn->type & OP_PHONY) {
- oodate = true;
- } else if (!GNode_IsTarget(gn) && Lst_IsEmpty(&gn->children)) {
- oodate = false;
- } else if ((!Lst_IsEmpty(&gn->children) && gn->youngestChild == NULL) ||
+ if (gn->type & OP_PHONY)
+ return true;
+ if (!GNode_IsTarget(gn) && Lst_IsEmpty(&gn->children))
+ return false;
+ if ((!Lst_IsEmpty(&gn->children) && gn->youngestChild == NULL) ||
(gn->mtime > now) ||
(gn->youngestChild != NULL &&
- gn->mtime < gn->youngestChild->mtime)) {
- oodate = 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 oodate;
+ gn->mtime < gn->youngestChild->mtime))
+ return true;
+ return RanlibOODate(gn);
}
/* Initialize the archives module. */
@@ -1123,26 +1003,25 @@ void
Arch_End(void)
{
#ifdef CLEANUP
- Lst_DoneCall(&archives, ArchFree);
+ ArchListNode *ln;
+
+ for (ln = archives.first; ln != NULL; ln = ln->next)
+ ArchFree(ln->datum);
+ Lst_Done(&archives);
#endif
}
bool
Arch_IsLib(GNode *gn)
{
- static const char armag[] = "!<arch>\n";
- char buf[sizeof armag - 1];
+ char buf[8];
int fd;
+ bool isLib;
if ((fd = open(gn->path, O_RDONLY)) == -1)
return false;
-
- if (read(fd, buf, sizeof buf) != sizeof buf) {
- (void)close(fd);
- return false;
- }
-
+ isLib = read(fd, buf, sizeof buf) == sizeof buf
+ && memcmp(buf, "!<arch>\n", sizeof buf) == 0;
(void)close(fd);
-
- return memcmp(buf, armag, sizeof buf) == 0;
+ return isLib;
}
diff --git a/contrib/bmake/bmake.1 b/contrib/bmake/bmake.1
index e0d8267994c1..d4e937424286 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.375 2024/03/10 02:53:37 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 March 9, 2024
.Dt BMAKE 1
.Os
.Sh NAME
@@ -49,8 +49,8 @@
.Op Fl T Ar file
.Op Fl V Ar variable
.Op Fl v Ar variable
-.Op Ar variable=value
-.Op Ar target ...
+.Op Ar variable\| Ns Cm \&= Ns Ar value
+.Op Ar target No ...
.Sh DESCRIPTION
.Nm
is a program designed to simplify the maintenance of other programs.
@@ -58,34 +58,35 @@ Its input is a list of specifications as to the files upon which programs
and other files depend.
If no
.Fl f Ar makefile
-makefile option is given,
+option is given,
.Nm
-will try to open
-.Ql Pa makefile
+tries to open
+.Sq Pa makefile
then
-.Ql Pa Makefile
+.Sq Pa Makefile
in order to find the specifications.
If the file
-.Ql Pa .depend
-exists, it is read (see
-.Xr mkdep 1 ) .
+.Sq Pa .depend
+exists, it is read, see
+.Xr mkdep 1 .
.Pp
This manual page is intended as a reference document only.
For a more thorough description of
.Nm
and makefiles, please refer to
-.%T "PMake \- A Tutorial" .
+.%T "PMake \- A Tutorial"
+(from 1993).
.Pp
.Nm
-will prepend the contents of the
-.Va MAKEFLAGS
+prepends the contents of the
+.Ev MAKEFLAGS
environment variable to the command line arguments before parsing them.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl B
Try to be backwards compatible by executing a single shell per command and
-by executing the commands to make the sources of a dependency line in sequence.
+by making the sources of a dependency line in sequence.
.It Fl C Ar directory
Change to
.Ar directory
@@ -100,92 +101,97 @@ is equivalent to
Define
.Ar variable
to be 1, in the global scope.
-.It Fl d Ar [-]flags
+.It Fl d Oo Cm \- Oc Ns Ar flags
Turn on debugging, and specify which portions of
.Nm
are to print debugging information.
Unless the flags are preceded by
-.Ql \-
+.Ql \- ,
they are added to the
-.Va MAKEFLAGS
-environment variable and will be processed by any child make processes.
+.Ev MAKEFLAGS
+environment variable and are passed on to any child make processes.
By default, debugging information is printed to standard error,
but this can be changed using the
-.Ar F
+.Cm F
debugging flag.
The debugging output is always unbuffered; in addition, if debugging
is enabled but debugging output is not directed to standard output,
-then the standard output is line buffered.
-.Ar Flags
-is one or more of the following:
+the standard output is line buffered.
+The available
+.Ar flags
+are:
.Bl -tag -width Ds
-.It Ar A
+.It Cm A
Print all possible debugging information;
equivalent to specifying all of the debugging flags.
-.It Ar a
+.It Cm a
Print debugging information about archive searching and caching.
-.It Ar C
-Print debugging information about current working directory.
-.It Ar c
+.It Cm C
+Print debugging information about the current working directory.
+.It Cm c
Print debugging information about conditional evaluation.
-.It Ar d
+.It Cm d
Print debugging information about directory searching and caching.
-.It Ar e
+.It Cm e
Print debugging information about failed commands and targets.
-.It Ar F Ns Oo Sy \&+ Oc Ns Ar filename
+.It Cm F Ns Oo Cm \&+ Oc Ns Ar filename
Specify where debugging output is written.
This must be the last flag, because it consumes the remainder of
the argument.
If the character immediately after the
-.Ql F
+.Cm F
flag is
.Ql \&+ ,
-then the file will be opened in append mode;
-otherwise the file will be overwritten.
+the file is opened in append mode;
+otherwise the file is overwritten.
If the file name is
.Ql stdout
or
-.Ql stderr
-then debugging output will be written to the
-standard output or standard error output file descriptors respectively
-(and the
+.Ql stderr ,
+debugging output is written to the standard output or standard error output
+respectively (and the
.Ql \&+
option has no effect).
-Otherwise, the output will be written to the named file.
-If the file name ends
-.Ql .%d
-then the
+Otherwise, the output is written to the named file.
+If the file name ends with
+.Ql .%d ,
+the
.Ql %d
is replaced by the pid.
-.It Ar f
+.It Cm f
Print debugging information about loop evaluation.
-.It Ar "g1"
+.It Cm g1
Print the input graph before making anything.
-.It Ar "g2"
+.It Cm g2
Print the input graph after making everything, or before exiting
on error.
-.It Ar "g3"
+.It Cm g3
Print the input graph before exiting on error.
-.It Ar h
+.It Cm h
Print debugging information about hash table operations.
-.It Ar j
+.It Cm j
Print debugging information about running multiple shells.
-.It Ar L
+.It Cm L
Turn on lint checks.
-This will throw errors for variable assignments that do not parse
-correctly, at the time of assignment so the file and line number
-are available.
-.It Ar l
+This throws errors for variable assignments that do not parse correctly,
+at the time of assignment, so the file and line number are available.
+.It Cm l
Print commands in Makefiles regardless of whether or not they are prefixed by
.Ql @
-or other "quiet" flags.
-Also known as "loud" behavior.
-.It Ar M
-Print debugging information about "meta" mode decisions about targets.
-.It Ar m
+or other
+.Dq quiet
+flags.
+Also known as
+.Dq loud
+behavior.
+.It Cm M
+Print debugging information about
+.Dq meta
+mode decisions about targets.
+.It Cm m
Print debugging information about making targets, including modification
dates.
-.It Ar n
+.It Cm n
Don't delete the temporary command scripts created when running commands.
These temporary scripts are created in the directory
referred to by the
@@ -205,35 +211,36 @@ This can create many files in
or
.Pa /tmp ,
so use with care.
-.It Ar p
+.It Cm p
Print debugging information about makefile parsing.
-.It Ar s
+.It Cm s
Print debugging information about suffix-transformation rules.
-.It Ar t
+.It Cm t
Print debugging information about target list maintenance.
-.It Ar V
+.It Cm V
Force the
.Fl V
-option to print raw values of variables, overriding the default behavior
-set via
+option to print raw values of variables,
+overriding the default behavior set via
.Va .MAKE.EXPAND_VARIABLES .
-.It Ar v
-Print debugging information about variable assignment.
-.It Ar x
+.It Cm v
+Print debugging information about variable assignment and expansion.
+.It Cm x
Run shell commands with
.Fl x
so the actual commands are printed as they are executed.
.El
.It Fl e
-Specify that environment variables override macro assignments within
-makefiles.
+Let environment variables override global variables within makefiles.
.It Fl f Ar makefile
Specify a makefile to read instead of the default
-.Ql Pa makefile .
+.Pa makefile
+or
+.Pa Makefile .
If
.Ar makefile
is
-.Ql Fl ,
+.Ql \&- ,
standard input is read.
Multiple makefiles may be specified, and are read in the order specified.
.It Fl I Ar directory
@@ -244,7 +251,7 @@ option) is automatically included as part of this list.
.It Fl i
Ignore non-zero exit of shell commands in the makefile.
Equivalent to specifying
-.Ql Fl
+.Ql \&-
before each command line in the makefile.
.It Fl J Ar private
This option should
@@ -252,7 +259,7 @@ This option should
be specified by the user.
.Pp
When the
-.Ar j
+.Fl j
option is in use in a recursive build, this option is passed by a make
to child makes to allow all the make processes in the build to
cooperate to avoid overloading the system.
@@ -260,11 +267,19 @@ cooperate to avoid overloading the system.
Specify the maximum number of jobs that
.Nm
may have running at any one time.
-The value is saved in
+If
+.Ar max_jobs
+is a floating point number, or ends with
+.Ql C ,
+then the value is multiplied by the number of CPUs reported online by
+.Xr sysconf 3 .
+The value of
+.Ar max_jobs
+is saved in
.Va .MAKE.JOBS .
Turns compatibility mode off, unless the
-.Ar B
-flag is also specified.
+.Fl B
+option is also specified.
When compatibility mode is off, all commands associated with a
target are executed in a single shell invocation as opposed to the
traditional one shell invocation per line.
@@ -273,60 +288,72 @@ command invocation and then expect to start with a fresh environment
on the next line.
It is more efficient to correct the scripts rather than turn backwards
compatibility on.
+.Pp
+A job token pool with
+.Ar max_jobs
+tokens is used to control the total number of jobs running.
+Each instance of
+.Nm
+will wait for a token from the pool before running a new job.
.It Fl k
Continue processing after errors are encountered, but only on those targets
that do not depend on the target whose creation caused the error.
.It Fl m Ar directory
-Specify a directory in which to search for sys.mk and makefiles included
-via the
+Specify a directory in which to search for
+.Pa sys.mk
+and makefiles included via the
.Li \&< Ns Ar file Ns Li \&> Ns -style
include statement.
The
.Fl m
option can be used multiple times to form a search path.
-This path will override the default system include path: /usr/share/mk.
-Furthermore the system include path will be appended to the search path used
-for
+This path overrides the default system include path
+.Pa /usr/share/mk .
+Furthermore, the system include path is appended to the search path used for
.Li \*q Ns Ar file Ns Li \*q Ns -style
include statements (see the
.Fl I
option).
+The system include path can be referenced via the read-only variable
+.Va .SYSPATH .
.Pp
-If a file or directory name in the
+If a directory name in the
.Fl m
argument (or the
.Ev MAKESYSPATH
environment variable) starts with the string
-.Qq \&.../
-then
+.Ql \&.../ ,
.Nm
-will search for the specified file or directory named in the remaining part
+searches for the specified file or directory named in the remaining part
of the argument string.
-The search starts with the current directory of
-the Makefile and then works upward towards the root of the file system.
-If the search is successful, then the resulting directory replaces the
-.Qq \&.../
+The search starts with the current directory
+and then works upward towards the root of the file system.
+If the search is successful, the resulting directory replaces the
+.Ql \&.../
specification in the
.Fl m
argument.
-If used, this feature allows
+This feature allows
.Nm
-to easily search in the current source tree for customized sys.mk files
-(e.g., by using
-.Qq \&.../mk/sys.mk
+to easily search in the current source tree for customized
+.Pa sys.mk
+files (e.g., by using
+.Ql \&.../mk/sys.mk
as an argument).
.It Fl n
Display the commands that would have been executed, but do not
-actually execute them unless the target depends on the .MAKE special
-source (see below) or the command is prefixed with
-.Ql Ic + .
+actually execute them unless the target depends on the
+.Va .MAKE
+special source (see below) or the command is prefixed with
+.Sq Cm + .
.It Fl N
-Display the commands which would have been executed, but do not
-actually execute any of them; useful for debugging top-level makefiles
+Display the commands that would have been executed,
+but do not actually execute any of them;
+useful for debugging top-level makefiles
without descending into subdirectories.
.It Fl q
-Do not execute any commands, but exit 0 if the specified targets are
-up-to-date and 1, otherwise.
+Do not execute any commands,
+instead exit 0 if the specified targets are up to date, and 1 otherwise.
.It Fl r
Do not use the built-in rules specified in the system makefile.
.It Fl S
@@ -336,7 +363,7 @@ This is the default behavior and the opposite of
.It Fl s
Do not echo any commands as they are executed.
Equivalent to specifying
-.Ql Ic @
+.Sq Ic @
before each command line in the makefile.
.It Fl T Ar tracefile
When used with the
@@ -353,24 +380,25 @@ Print the value of
.Ar variable .
Do not build any targets.
Multiple instances of this option may be specified;
-the variables will be printed one per line,
+the variables are printed one per line,
with a blank line for each null or undefined variable.
The value printed is extracted from the global scope after all
makefiles have been read.
+.Pp
By default, the raw variable contents (which may
include additional unexpanded variable references) are shown.
If
.Ar variable
contains a
-.Ql \&$
-then the value will be recursively expanded to its complete resultant
-text before printing.
-The expanded value will also be printed if
+.Ql \&$ ,
+it is not interpreted as a variable name but rather as an expression.
+Its value is expanded before printing.
+The value is also expanded before printing if
.Va .MAKE.EXPAND_VARIABLES
-is set to true and
-the
+is set to true and the
.Fl dV
option has not been used to override it.
+.Pp
Note that loop-local and target-local variables, as well as values
taken temporarily by global variables during makefile processing, are
not accessible via this option.
@@ -380,8 +408,13 @@ debug mode can be used to see these at the cost of generating
substantial extraneous output.
.It Fl v Ar variable
Like
+.Fl V ,
+but all printed variables are always expanded to their complete value.
+The last occurrence of
.Fl V
-but the variable is always expanded to its complete value.
+or
+.Fl v
+decides whether all variables are expanded or not.
.It Fl W
Treat any warnings during makefile parsing as errors.
.It Fl w
@@ -389,13 +422,12 @@ Print entering and leaving directory messages, pre and post processing.
.It Fl X
Don't export variables passed on the command line to the environment
individually.
-Variables passed on the command line are still exported
-via the
-.Va MAKEFLAGS
+Variables passed on the command line are still exported via the
+.Ev MAKEFLAGS
environment variable.
This option may be useful on systems which have a small limit on the
size of command arguments.
-.It Ar variable=value
+.It Ar variable\| Ns Cm \&= Ns Ar value
Set the value of the variable
.Ar variable
to
@@ -409,12 +441,12 @@ Variable assignments should follow options for POSIX compatibility
but no ordering is enforced.
.El
.Pp
-There are seven different types of lines in a makefile: file dependency
+There are several different types of lines in a makefile: dependency
specifications, shell commands, variable assignments, include statements,
-conditional directives, for loops, and comments.
+conditional directives, for loops, other directives, and comments.
.Pp
-In general, lines may be continued from one line to the next by ending
-them with a backslash
+Lines may be continued from one line to the next
+by ending them with a backslash
.Pq Ql \e .
The trailing newline character and initial whitespace on the following
line are compressed into a single space.
@@ -423,11 +455,10 @@ Dependency lines consist of one or more targets, an operator, and zero
or more sources.
This creates a relationship where the targets
.Dq depend
-on the sources
-and are customarily created from them.
-A target is considered out-of-date if it does not exist, or if its
-modification time is less than that of any of its sources.
-An out-of-date target will be re-created, but not until all sources
+on the sources and are customarily created from them.
+A target is considered out of date if it does not exist,
+or if its modification time is less than that of any of its sources.
+An out-of-date target is re-created, but not until all sources
have been examined and themselves re-created as needed.
Three operators may be used:
.Bl -tag -width flag
@@ -451,15 +482,16 @@ shell commands are run if the target is out of date with respect to
Thus, different groups of the attached shell commands may be run
depending on the circumstances.
Furthermore, unlike
-.Ic \&:,
+.Ic \&: ,
for dependency lines with no sources, the attached shell
commands are always run.
Also unlike
-.Ic \&:,
-the target will not be removed if
+.Ic \&: ,
+the target is not removed if
.Nm
is interrupted.
.El
+.Pp
All dependency lines mentioning a particular target must use the same
operator.
.Pp
@@ -474,206 +506,229 @@ The values
.Ql * ,
and
.Ql []
-may only be used as part of the final
-component of the target or source, and must be used to describe existing
-files.
+may only be used as part of the final component of the target or source,
+and only match existing files.
The value
.Ql {}
need not necessarily be used to describe existing files.
Expansion is in directory order, not alphabetically as done in the shell.
.Sh SHELL COMMANDS
-Each target may have associated with it one or more lines of shell
-commands, normally
-used to create the target.
+Each target may have associated with it one or more lines of shell commands,
+normally used to create the target.
Each of the lines in this script
.Em must
be preceded by a tab.
(For historical reasons, spaces are not accepted.)
-While targets can appear in many dependency lines if desired, by
-default only one of these rules may be followed by a creation
-script.
+While targets can occur in many dependency lines if desired,
+by default only one of these rules may be followed by a creation script.
If the
-.Ql Ic \&::
-operator is used, however, all rules may include scripts and the
-scripts are executed in the order found.
+.Sq Ic \&::
+operator is used, however, all rules may include scripts,
+and the respective scripts are executed in the order found.
.Pp
-Each line is treated as a separate shell command, unless the end of
-line is escaped with a backslash
-.Pq Ql \e
+Each line is treated as a separate shell command,
+unless the end of line is escaped with a backslash
+.Ql \e ,
in which case that line and the next are combined.
-.\" The escaped newline is retained and passed to the shell, which
-.\" normally ignores it.
-.\" However, the tab at the beginning of the following line is removed.
If the first characters of the command are any combination of
-.Ql Ic @ ,
-.Ql Ic + ,
+.Sq Ic @ ,
+.Sq Ic + ,
or
-.Ql Ic \- ,
+.Sq Ic \- ,
the command is treated specially.
-A
-.Ql Ic @
+.Bl -tag -offset indent -width indent
+.It Ic @
causes the command not to be echoed before it is executed.
-A
-.Ql Ic +
+.It Ic +
causes the command to be executed even when
.Fl n
is given.
-This is similar to the effect of the .MAKE special source,
+This is similar to the effect of the
+.Va .MAKE
+special source,
except that the effect can be limited to a single line of a script.
-A
-.Ql Ic \-
+.It Ic \-
in compatibility mode
causes any non-zero exit status of the command line to be ignored.
+.El
.Pp
When
.Nm
is run in jobs mode with
.Fl j Ar max_jobs ,
-the entire script for the target is fed to a
-single instance of the shell.
+the entire script for the target is fed to a single instance of the shell.
In compatibility (non-jobs) mode, each command is run in a separate process.
If the command contains any shell meta characters
-.Pq Ql #=|^(){};&<>*?[]:$`\e\en
-it will be passed to the shell; otherwise
+.Pq Ql #=|^(){};&<>*?[]:$`\e\en ,
+it is passed to the shell; otherwise
.Nm
-will attempt direct execution.
+attempts direct execution.
If a line starts with
-.Ql Ic \-
-and the shell has ErrCtl enabled then failure of the command line
-will be ignored as in compatibility mode.
+.Sq Ic \-
+and the shell has ErrCtl enabled,
+failure of the command line is ignored as in compatibility mode.
Otherwise
-.Ql Ic \-
+.Sq Ic \-
affects the entire job;
-the script will stop at the first command line that fails,
-but the target will not be deemed to have failed.
+the script stops at the first command line that fails,
+but the target is not deemed to have failed.
.Pp
Makefiles should be written so that the mode of
.Nm
operation does not change their behavior.
-For example, any command which needs to use
+For example, any command which uses
.Dq cd
or
.Dq chdir
-without potentially changing the directory for subsequent commands
+without the intention of changing the directory for subsequent commands
should be put in parentheses so it executes in a subshell.
-To force the use of one shell, escape the line breaks so as to make
+To force the use of a single shell, escape the line breaks so as to make
the whole script one command.
For example:
.Bd -literal -offset indent
avoid-chdir-side-effects:
- @echo Building $@ in `pwd`
+ @echo "Building $@ in $$(pwd)"
@(cd ${.CURDIR} && ${MAKE} $@)
- @echo Back in `pwd`
+ @echo "Back in $$(pwd)"
ensure-one-shell-regardless-of-mode:
- @echo Building $@ in `pwd`; \e
+ @echo "Building $@ in $$(pwd)"; \e
(cd ${.CURDIR} && ${MAKE} $@); \e
- echo Back in `pwd`
+ echo "Back in $$(pwd)"
.Ed
.Pp
Since
.Nm
-will
-.Xr chdir 2
-to
-.Ql Va .OBJDIR
-before executing any targets, each child process
-starts with that as its current working directory.
+changes the current working directory to
+.Sq Va .OBJDIR
+before executing any targets,
+each child process starts with that as its current working directory.
.Sh VARIABLE ASSIGNMENTS
-Variables in make are much like variables in the shell, and, by tradition,
-consist of all upper-case letters.
-.Ss Variable assignment modifiers
-The five operators that can be used to assign values to variables are as
-follows:
+Variables in make behave much like macros in the C preprocessor.
+.Pp
+Variable assignments have the form
+.Sq Ar NAME Ar op Ar value ,
+where:
+.Bl -tag -offset Ds -width Ds
+.It Ar NAME
+is a single-word variable name,
+consisting, by tradition, of all upper-case letters,
+.It Ar op
+is one of the variable assignment operators described below, and
+.It Ar value
+is interpreted according to the variable assignment operator.
+.El
+.Pp
+Whitespace around
+.Ar NAME ,
+.Ar op
+and
+.Ar value
+is discarded.
+.Ss Variable assignment operators
+The five operators that assign values to variables are:
.Bl -tag -width Ds
.It Ic \&=
Assign the value to the variable.
-Any previous value is overridden.
+Any previous value is overwritten.
.It Ic \&+=
-Append the value to the current value of the variable.
+Append the value to the current value of the variable,
+separating them by a single space.
.It Ic \&?=
Assign the value to the variable if it is not already defined.
.It Ic \&:=
-Assign with expansion, i.e. expand the value before assigning it
-to the variable.
-Normally, expansion is not done until the variable is referenced.
+Expand the value, then assign it to the variable.
+.Pp
.Em NOTE :
References to undefined variables are
.Em not
expanded.
This can cause problems when variable modifiers are used.
+.\" See var-op-expand.mk, the section with LATER and INDIRECT.
.It Ic \&!=
-Expand the value and pass it to the shell for execution and assign
-the result to the variable.
+Expand the value and pass it to the shell for execution,
+then assign the output from the child's standard output to the variable.
Any newlines in the result are replaced with spaces.
.El
+.Ss Expansion of variables
+In most contexts where variables are expanded,
+.Ql \&$$
+expands to a single dollar sign.
+In other contexts (most variable modifiers, string literals in conditions),
+.Ql \&\e$
+expands to a single dollar sign.
.Pp
-Any white-space before the assigned
-.Ar value
-is removed; if the value is being appended, a single space is inserted
-between the previous contents of the variable and the appended value.
-.Pp
-Variables are expanded by surrounding the variable name with either
-curly braces
-.Pq Ql {}
-or parentheses
-.Pq Ql ()
-and preceding it with
-a dollar sign
-.Pq Ql \&$ .
-If the variable name contains only a single letter, the surrounding
-braces or parentheses are not required.
+References to variables have the form
+.Cm \&${ Ns Ar name Ns Oo Ns Cm \&: Ns Ar modifiers Oc Ns Cm \&}
+or
+.Cm \&$( Ns Ar name Ns Oo Ns Cm \&: Ns Ar modifiers Oc Ns Cm \&) .
+If the variable name consists of only a single character
+and the expression contains no modifiers,
+the surrounding curly braces or parentheses are not required.
This shorter form is not recommended.
.Pp
-If the variable name contains a dollar, then the name itself is expanded first.
+If the variable name contains a dollar, the name itself is expanded first.
This allows almost arbitrary variable names, however names containing dollar,
-braces, parentheses, or whitespace are really best avoided!
+braces, parentheses or whitespace are really best avoided.
.Pp
-If the result of expanding a variable contains a dollar sign
-.Pq Ql \&$
-the string is expanded again.
+If the result of expanding a nested variable expression contains a dollar sign
+.Pq Ql \&$ ,
+the result is subject to further expansion.
.Pp
-Variable substitution occurs at three distinct times, depending on where
+Variable substitution occurs at four distinct times, depending on where
the variable is being used.
.Bl -enum
.It
Variables in dependency lines are expanded as the line is read.
.It
+Variables in conditionals are expanded individually,
+but only as far as necessary to determine the result of the conditional.
+.It
Variables in shell commands are expanded when the shell command is
executed.
.It
-.Dq .for
+.Ic .for
loop index variables are expanded on each loop iteration.
-Note that other variables are not expanded inside loops so
-the following example code:
+Note that other variables are not expanded when composing the body of a loop,
+so the following example code:
.Bd -literal -offset indent
-
-.Dv .for i in 1 2 3
+\&.for i in 1 2 3
a+= ${i}
j= ${i}
b+= ${j}
-.Dv .endfor
+\&.endfor
all:
@echo ${a}
@echo ${b}
-
.Ed
-will print:
+.Pp
+prints:
.Bd -literal -offset indent
1 2 3
3 3 3
-
.Ed
-Because while ${a} contains
-.Dq 1 2 3
-after the loop is executed, ${b}
+.Pp
+After the loop is executed:
+.Bl -tag -offset indent -width indent
+.It Va a
+contains
+.Ql ${:U1} ${:U2} ${:U3} ,
+which expands to
+.Ql 1 2 3 .
+.It Va j
+contains
+.Ql ${:U3} ,
+which expands to
+.Ql 3 .
+.It Va b
contains
-.Dq ${j} ${j} ${j}
+.Ql ${j} ${j} ${j} ,
which expands to
-.Dq 3 3 3
-since after the loop completes ${j} contains
-.Dq 3 .
+.Ql ${:U3} ${:U3} ${:U3}
+and further to
+.Ql 3 3 3 .
+.El
.El
.Ss Variable classes
The four different classes of variables (in order of increasing precedence)
@@ -691,55 +746,85 @@ Variables defined as part of the command line.
Variables that are defined specific to a certain target.
.El
.Pp
-Local variables are all built in and their values vary magically from
-target to target.
-It is not currently possible to define new local variables.
-The seven local variables are as follows:
-.Bl -tag -width ".ARCHIVE" -offset indent
+Local variables can be set on a dependency line, unless
+.Va .MAKE.TARGET_LOCAL_VARIABLES
+is set to
+.Ql false .
+The rest of the line
+(which already has had global variables expanded)
+is the variable value.
+For example:
+.Bd -literal -offset indent
+COMPILER_WRAPPERS= ccache distcc icecc
+
+${OBJS}: .MAKE.META.CMP_FILTER=${COMPILER_WRAPPERS:S,^,N,}
+.Ed
+.Pp
+Only the targets
+.Ql ${OBJS}
+are impacted by that filter (in
+.Dq meta
+mode) and
+simply enabling/disabling any of the compiler wrappers does not render all
+of those targets out-of-date.
+.Pp
+.Em NOTE :
+target-local variable assignments behave differently in that;
+.Bl -tag -width Ds -offset indent
+.It Ic \&+=
+Only appends to a previous local assignment
+for the same target and variable.
+.It Ic \&:=
+Is redundant with respect to global variables,
+which have already been expanded.
+.El
+.Pp
+The seven built-in local variables are:
+.Bl -tag -width ".Va .ARCHIVE" -offset indent
.It Va .ALLSRC
The list of all sources for this target; also known as
-.Ql Va \&> .
+.Sq Va \&> .
.It Va .ARCHIVE
The name of the archive file; also known as
-.Ql Va \&! .
+.Sq Va \&! .
.It Va .IMPSRC
In suffix-transformation rules, the name/path of the source from which the
target is to be transformed (the
.Dq implied
source); also known as
-.Ql Va \&< .
+.Sq Va \&< .
It is not defined in explicit rules.
.It Va .MEMBER
The name of the archive member; also known as
-.Ql Va % .
+.Sq Va % .
.It Va .OODATE
The list of sources for this target that were deemed out-of-date; also
known as
-.Ql Va \&? .
+.Sq Va \&? .
.It Va .PREFIX
-The file prefix of the target, containing only the file portion, no suffix
-or preceding directory components; also known as
-.Ql Va * .
-The suffix must be one of the known suffixes declared with
-.Ic .SUFFIXES
-or it will not be recognized.
+The name of the target with suffix (if declared in
+.Ic .SUFFIXES )
+removed; also known as
+.Sq Va * .
.It Va .TARGET
The name of the target; also known as
-.Ql Va @ .
+.Sq Va @ .
For compatibility with other makes this is an alias for
-.Ic .ARCHIVE
+.Va .ARCHIVE
in archive member rules.
.El
.Pp
The shorter forms
-.Ql ( Va > ,
-.Ql Va \&! ,
-.Ql Va < ,
-.Ql Va % ,
-.Ql Va \&? ,
-.Ql Va * ,
+.Po
+.Sq Va \&> ,
+.Sq Va \&! ,
+.Sq Va \&< ,
+.Sq Va \&% ,
+.Sq Va \&? ,
+.Sq Va \&* ,
and
-.Ql Va @ )
+.Sq Va \&@
+.Pc
are permitted for backward
compatibility with historical makefiles and legacy POSIX make and are
not recommended.
@@ -748,8 +833,8 @@ Variants of these variables with the punctuation followed immediately by
.Ql D
or
.Ql F ,
-e.g.
-.Ql Va $(@D) ,
+e.g.\&
+.Ql $(@D) ,
are legacy forms equivalent to using the
.Ql :H
and
@@ -762,57 +847,84 @@ makefiles and POSIX but are not recommended.
Four of the local variables may be used in sources on dependency lines
because they expand to the proper value for each target on the line.
These variables are
-.Ql Va .TARGET ,
-.Ql Va .PREFIX ,
-.Ql Va .ARCHIVE ,
+.Sq Va .TARGET ,
+.Sq Va .PREFIX ,
+.Sq Va .ARCHIVE ,
and
-.Ql Va .MEMBER .
+.Sq Va .MEMBER .
.Ss Additional built-in variables
In addition,
.Nm
sets or knows about the following variables:
-.Bl -tag -width .MAKEOVERRIDES
-.It Va \&$
-A single dollar sign
-.Ql \&$ ,
-i.e.
-.Ql \&$$
-expands to a single dollar
-sign.
+.Bl -tag
+.\" NB: This list is sorted case-insensitive, ignoring punctuation.
+.\" NB: To find all built-in variables in make's source code,
+.\" NB: search for Var_*, Global_*, SetVarObjdir, GetBooleanExpr,
+.\" NB: and the implementation of Var_SetWithFlags.
+.\" NB: Last synced on 2023-01-01.
.It Va .ALLTARGETS
-The list of all targets encountered in the Makefile.
-If evaluated during
-Makefile parsing, lists only those targets encountered thus far.
+The list of all targets encountered in the makefiles.
+If evaluated during makefile parsing,
+lists only those targets encountered thus far.
.It Va .CURDIR
A path to the directory where
.Nm
was executed.
Refer to the description of
-.Ql Ev PWD
+.Sq Va PWD
for more details.
+.It Va .ERROR_CMD
+Is used in error handling, see
+.Va MAKE_PRINT_VAR_ON_ERROR .
+.It Va .ERROR_CWD
+Is used in error handling, see
+.Va MAKE_PRINT_VAR_ON_ERROR .
+.It Va .ERROR_EXIT
+Is used in error handling, see
+.Va MAKE_PRINT_VAR_ON_ERROR .
+.It Va .ERROR_META_FILE
+Is used in error handling in
+.Dq meta
+mode, see
+.Va MAKE_PRINT_VAR_ON_ERROR .
+.It Va .ERROR_TARGET
+Is used in error handling, see
+.Va MAKE_PRINT_VAR_ON_ERROR .
.It Va .INCLUDEDFROMDIR
-The directory of the file this Makefile was included from.
+The directory of the file this makefile was included from.
.It Va .INCLUDEDFROMFILE
-The filename of the file this Makefile was included from.
-.It Ev MAKE
+The filename of the file this makefile was included from.
+.\" .INCLUDES is intentionally undocumented, as it is obsolete.
+.\" .LIBS is intentionally undocumented, as it is obsolete.
+.It Va MACHINE
+The machine hardware name, see
+.Xr uname 1 .
+.It Va MACHINE_ARCH
+The machine processor architecture name, see
+.Xr uname 1 .
+.It Va MAKE
The name that
.Nm
was executed with
.Pq Va argv[0] .
-For compatibility
-.Nm
-also sets
-.Va .MAKE
-with the same value.
+.It Va .MAKE
+The same as
+.Va MAKE ,
+for compatibility.
The preferred variable to use is the environment variable
.Ev MAKE
-because it is more compatible with other versions of
-.Nm
+because it is more compatible with other make variants
and cannot be confused with the special target with the same name.
+.\" '.MAKE.cmd_filtered' is intentionally undocumented,
+.\" as it is an internal implementation detail.
.It Va .MAKE.DEPENDFILE
Names the makefile (default
-.Ql Pa .depend )
+.Sq Pa .depend )
from which generated dependencies are read.
+.It Va .MAKE.DIE_QUIETLY
+If set to
+.Ql true ,
+do not print error information at the end.
.It Va .MAKE.EXPAND_VARIABLES
A boolean that controls the default behavior of the
.Fl V
@@ -824,174 +936,236 @@ include additional unexpanded variable references) are shown.
.It Va .MAKE.EXPORTED
The list of variables exported by
.Nm .
-.It Va .MAKE.JOBS
-The argument to the
-.Fl j
-option.
+.It Va MAKEFILE
+The top-level makefile that is currently read,
+as given in the command line.
+.It Va .MAKEFLAGS
+The environment variable
+.Sq Ev MAKEFLAGS
+may contain anything that
+may be specified on
+.Nm Ns 's
+command line.
+Anything specified on
+.Nm Ns 's
+command line is appended to the
+.Va .MAKEFLAGS
+variable, which is then added to the environment for all programs that
+.Nm
+executes.
+.It Va .MAKE.GID
+The numeric group ID of the user running
+.Nm .
+It is read-only.
.It Va .MAKE.JOB.PREFIX
If
.Nm
is run with
-.Ar j
-then output for each target is prefixed with a token
-.Ql --- target ---
+.Fl j ,
+the output for each target is prefixed with a token
+.Dl --- Ar target Li ---
the first part of which can be controlled via
.Va .MAKE.JOB.PREFIX .
If
.Va .MAKE.JOB.PREFIX
is empty, no token is printed.
-.br
-For example:
-.Li .MAKE.JOB.PREFIX=${.newline}---${.MAKE:T}[${.MAKE.PID}]
+For example, setting
+.Va .MAKE.JOB.PREFIX
+to
+.Ql ${.newline}---${.MAKE:T}[${.MAKE.PID}]
would produce tokens like
-.Ql ---make[1234] target ---
+.Dl ---make[1234] Ar target Li ---
making it easier to track the degree of parallelism being achieved.
-.It Ev MAKEFLAGS
-The environment variable
-.Ql Ev MAKEFLAGS
-may contain anything that
-may be specified on
-.Nm Ns 's
-command line.
-Anything specified on
-.Nm Ns 's
-command line is appended to the
-.Ql Ev MAKEFLAGS
-variable which is then
-entered into the environment for all programs which
-.Nm
-executes.
+.It Va .MAKE.JOBS
+The argument to the
+.Fl j
+option.
+.It Va .MAKE.JOBS.C
+A read-only boolean that indicates whether the
+.Fl j
+option supports use of
+.Ql C .
.It Va .MAKE.LEVEL
The recursion depth of
.Nm .
-The initial instance of
+The top-level instance of
.Nm
-will be 0, and an incremented value is put into the environment
-to be seen by the next generation.
+has level 0, and each child make has its parent level plus 1.
This allows tests like:
.Li .if ${.MAKE.LEVEL} == 0
-to protect things which should only be evaluated in the initial instance of
+to protect things which should only be evaluated in the top-level instance of
+.Nm .
+.It Va .MAKE.LEVEL.ENV
+The name of the environment variable that stores the level of nested calls to
.Nm .
.It Va .MAKE.MAKEFILE_PREFERENCE
The ordered list of makefile names
(default
-.Ql Pa makefile ,
-.Ql Pa Makefile )
+.Sq Pa makefile ,
+.Sq Pa Makefile )
that
.Nm
-will look for.
+looks for.
.It Va .MAKE.MAKEFILES
The list of makefiles read by
.Nm ,
which is useful for tracking dependencies.
Each makefile is recorded only once, regardless of the number of times read.
+.It Va .MAKE.META.BAILIWICK
+In
+.Dq meta
+mode, provides a list of prefixes which
+match the directories controlled by
+.Nm .
+If a file that was generated outside of
+.Va .OBJDIR
+but within said bailiwick is missing,
+the current target is considered out-of-date.
+.It Va .MAKE.META.CMP_FILTER
+In
+.Dq meta
+mode, it can (very rarely!) be useful to filter command
+lines before comparison.
+This variable can be set to a set of modifiers that are applied to
+each line of the old and new command that differ, if the filtered
+commands still differ, the target is considered out-of-date.
+.It Va .MAKE.META.CREATED
+In
+.Dq meta
+mode, this variable contains a list of all the meta files
+updated.
+If not empty, it can be used to trigger processing of
+.Va .MAKE.META.FILES .
+.It Va .MAKE.META.FILES
+In
+.Dq meta
+mode, this variable contains a list of all the meta files
+used (updated or not).
+This list can be used to process the meta files to extract dependency
+information.
+.It Va .MAKE.META.IGNORE_FILTER
+Provides a list of variable modifiers to apply to each pathname.
+Ignore if the expansion is an empty string.
+.It Va .MAKE.META.IGNORE_PATHS
+Provides a list of path prefixes that should be ignored;
+because the contents are expected to change over time.
+The default list includes:
+.Sq Pa /dev /etc /proc /tmp /var/run /var/tmp
+.It Va .MAKE.META.IGNORE_PATTERNS
+Provides a list of patterns to match against pathnames.
+Ignore any that match.
+.It Va .MAKE.META.PREFIX
+Defines the message printed for each meta file updated in
+.Dq meta verbose
+mode.
+The default value is:
+.Dl Building ${.TARGET:H:tA}/${.TARGET:T}
.It Va .MAKE.MODE
Processed after reading all makefiles.
-Can affect the mode that
+Affects the mode that
.Nm
runs in.
-It can contain a number of keywords:
-.Bl -hang -width missing-filemon=bf.
-.It Pa compat
+It can contain these keywords:
+.Bl -tag -width indent
+.It Cm compat
Like
.Fl B ,
puts
.Nm
-into "compat" mode.
-.It Pa meta
+into
+.Dq compat
+mode.
+.It Cm meta
Puts
.Nm
-into "meta" mode, where meta files are created for each target
-to capture the command run, the output generated and if
+into
+.Dq meta
+mode, where meta files are created for each target
+to capture the command run, the output generated, and if
.Xr filemon 4
is available, the system calls which are of interest to
.Nm .
-The captured output can be very useful when diagnosing errors.
-.It Pa curdirOk= Ar bf
-Normally
+The captured output can be useful when diagnosing errors.
+.It Cm curdirOk= Ns Ar bf
+By default,
.Nm
-will not create .meta files in
-.Ql Va .CURDIR .
+does not create
+.Pa .meta
+files in
+.Sq Va .CURDIR .
This can be overridden by setting
-.Va bf
-to a value which represents True.
-.It Pa missing-meta= Ar bf
+.Ar bf
+to a value which represents true.
+.It Cm missing-meta= Ns Ar bf
If
-.Va bf
-is True, then a missing .meta file makes the target out-of-date.
-.It Pa missing-filemon= Ar bf
+.Ar bf
+is true, a missing
+.Pa .meta
+file makes the target out-of-date.
+.It Cm missing-filemon= Ns Ar bf
If
-.Va bf
-is True, then missing filemon data makes the target out-of-date.
-.It Pa nofilemon
+.Ar bf
+is true, missing filemon data makes the target out-of-date.
+.It Cm nofilemon
Do not use
.Xr filemon 4 .
-.It Pa env
+.It Cm env
For debugging, it can be useful to include the environment
-in the .meta file.
-.It Pa verbose
-If in "meta" mode, print a clue about the target being built.
+in the
+.Pa .meta
+file.
+.It Cm verbose
+If in
+.Dq meta
+mode, print a clue about the target being built.
This is useful if the build is otherwise running silently.
-The message printed the value of:
+The message printed is the expanded value of
.Va .MAKE.META.PREFIX .
-.It Pa ignore-cmd
+.It Cm ignore-cmd
Some makefiles have commands which are simply not stable.
This keyword causes them to be ignored for
-determining whether a target is out of date in "meta" mode.
+determining whether a target is out of date in
+.Dq meta
+mode.
See also
.Ic .NOMETA_CMP .
-.It Pa silent= Ar bf
+.It Cm silent= Ns Ar bf
If
-.Va bf
-is True, when a .meta file is created, mark the target
+.Ar bf
+is true, when a .meta file is created, mark the target
.Ic .SILENT .
+.It Cm randomize-targets
+In both compat and parallel mode, do not make the targets in the usual order,
+but instead randomize their order.
+This mode can be used to detect undeclared dependencies between files.
.El
-.It Va .MAKE.META.BAILIWICK
-In "meta" mode, provides a list of prefixes which
-match the directories controlled by
-.Nm .
-If a file that was generated outside of
-.Va .OBJDIR
-but within said bailiwick is missing,
-the current target is considered out-of-date.
-.It Va .MAKE.META.CREATED
-In "meta" mode, this variable contains a list of all the meta files
-updated.
-If not empty, it can be used to trigger processing of
-.Va .MAKE.META.FILES .
-.It Va .MAKE.META.FILES
-In "meta" mode, this variable contains a list of all the meta files
-used (updated or not).
-This list can be used to process the meta files to extract dependency
-information.
-.It Va .MAKE.META.IGNORE_PATHS
-Provides a list of path prefixes that should be ignored;
-because the contents are expected to change over time.
-The default list includes:
-.Ql Pa /dev /etc /proc /tmp /var/run /var/tmp
-.It Va .MAKE.META.IGNORE_PATTERNS
-Provides a list of patterns to match against pathnames.
-Ignore any that match.
-.It Va .MAKE.META.IGNORE_FILTER
-Provides a list of variable modifiers to apply to each pathname.
-Ignore if the expansion is an empty string.
-.It Va .MAKE.META.PREFIX
-Defines the message printed for each meta file updated in "meta verbose" mode.
-The default value is:
-.Dl Building ${.TARGET:H:tA}/${.TARGET:T}
+.It Va MAKEOBJDIR
+Used to create files in a separate directory, see
+.Va .OBJDIR .
+.It Va MAKE_OBJDIR_CHECK_WRITABLE
+Used to force a separate directory for the created files,
+even if that directory is not writable, see
+.Va .OBJDIR .
+.It Va MAKEOBJDIRPREFIX
+Used to create files in a separate directory, see
+.Va .OBJDIR .
+.It Va .MAKE.OS
+The name of the operating system, see
+.Xr uname 1 .
+It is read-only.
.It Va .MAKEOVERRIDES
This variable is used to record the names of variables assigned to
on the command line, so that they may be exported as part of
-.Ql Ev MAKEFLAGS .
+.Sq Ev MAKEFLAGS .
This behavior can be disabled by assigning an empty value to
-.Ql Va .MAKEOVERRIDES
+.Sq Va .MAKEOVERRIDES
within a makefile.
Extra variables can be exported from a makefile
by appending their names to
-.Ql Va .MAKEOVERRIDES .
-.Ql Ev MAKEFLAGS
+.Sq Va .MAKEOVERRIDES .
+.Sq Ev MAKEFLAGS
is re-exported whenever
-.Ql Va .MAKEOVERRIDES
+.Sq Va .MAKEOVERRIDES
is modified.
.It Va .MAKE.PATH_FILEMON
If
@@ -1001,13 +1175,38 @@ was built with
support, this is set to the path of the device node.
This allows makefiles to test for this support.
.It Va .MAKE.PID
-The process-id of
+The process ID of
.Nm .
+It is read-only.
.It Va .MAKE.PPID
-The parent process-id of
+The parent process ID of
.Nm .
+It is read-only.
+.It Va MAKE_PRINT_VAR_ON_ERROR
+When
+.Nm
+stops due to an error, it sets
+.Sq Va .ERROR_TARGET
+to the name of the target that failed,
+.Sq Va .ERROR_EXIT
+to the exit status of the failed target,
+.Sq Va .ERROR_CMD
+to the commands of the failed target,
+and in
+.Dq meta
+mode, it also sets
+.Sq Va .ERROR_CWD
+to the
+.Xr getcwd 3 ,
+and
+.Sq Va .ERROR_META_FILE
+to the path of the meta file (if any) describing the failed target.
+It then prints its name and the value of
+.Sq Va .CURDIR
+as well as the value of any variables named in
+.Sq Va MAKE_PRINT_VAR_ON_ERROR .
.It Va .MAKE.SAVE_DOLLARS
-value should be a boolean that controls whether
+If true,
.Ql $$
are preserved when doing
.Ql :=
@@ -1019,40 +1218,35 @@ If set to false,
becomes
.Ql $
per normal evaluation rules.
+.It Va .MAKE.TARGET_LOCAL_VARIABLES
+If set to
+.Ql false ,
+apparent variable assignments in dependency lines are
+treated as normal sources.
.It Va .MAKE.UID
-The user-id running
-.Nm .
-.It Va .MAKE.GID
-The group-id running
+The numeric ID of the user running
.Nm .
-.It Va MAKE_PRINT_VAR_ON_ERROR
-When
-.Nm
-stops due to an error, it sets
-.Ql Va .ERROR_TARGET
-to the name of the target that failed,
-.Ql Va .ERROR_CMD
-to the commands of the failed target,
-and in "meta" mode, it also sets
-.Ql Va .ERROR_CWD
-to the
-.Xr getcwd 3 ,
-and
-.Ql Va .ERROR_META_FILE
-to the path of the meta file (if any) describing the failed target.
-It then prints its name and the value of
-.Ql Va .CURDIR
-as well as the value of any variables named in
-.Ql Va MAKE_PRINT_VAR_ON_ERROR .
+It is read-only.
+.\" 'MAKE_VERSION' is intentionally undocumented
+.\" since it is only defined in the bmake distribution,
+.\" but not in NetBSD's native make.
+.\" '.meta.%d.lcwd' is intentionally undocumented
+.\" since it is an internal implementation detail.
+.\" '.meta.%d.ldir' is intentionally undocumented
+.\" since it is an internal implementation detail.
+.\" 'MFLAGS' is intentionally undocumented
+.\" since it is obsolete.
.It Va .newline
This variable is simply assigned a newline character as its value.
+It is read-only.
This allows expansions using the
.Cm \&:@
modifier to put a newline between
iterations of the loop rather than a space.
-For example, the printing of
-.Ql Va MAKE_PRINT_VAR_ON_ERROR
-could be done as ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}.
+For example, in case of an error,
+.Nm
+prints the variable names and their values using:
+.Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}
.It Va .OBJDIR
A path to the directory where the targets are built.
Its value is determined by trying to
@@ -1060,186 +1254,222 @@ Its value is determined by trying to
to the following directories in order and using the first match:
.Bl -enum
.It
-.Ev ${MAKEOBJDIRPREFIX}${.CURDIR}
+.Cm ${MAKEOBJDIRPREFIX} Ns Cm ${.CURDIR}
.Pp
(Only if
-.Ql Ev MAKEOBJDIRPREFIX
+.Sq Ev MAKEOBJDIRPREFIX
is set in the environment or on the command line.)
.It
-.Ev ${MAKEOBJDIR}
+.Cm ${MAKEOBJDIR}
.Pp
(Only if
-.Ql Ev MAKEOBJDIR
+.Sq Ev MAKEOBJDIR
is set in the environment or on the command line.)
.It
-.Ev ${.CURDIR} Ns Pa /obj. Ns Ev ${MACHINE}
+.Cm ${.CURDIR} Ns Pa /obj. Ns Cm ${MACHINE}
.It
-.Ev ${.CURDIR} Ns Pa /obj
+.Cm ${.CURDIR} Ns Pa /obj
.It
-.Pa /usr/obj/ Ns Ev ${.CURDIR}
+.Pa /usr/obj/ Ns Cm ${.CURDIR}
.It
-.Ev ${.CURDIR}
+.Cm ${.CURDIR}
.El
.Pp
-Variable expansion is performed on the value before it's used,
+Variable expansion is performed on the value before it is used,
so expressions such as
-.Dl ${.CURDIR:S,^/usr/src,/var/obj,}
+.Cm ${.CURDIR:S,^/usr/src,/var/obj,}
may be used.
This is especially useful with
-.Ql Ev MAKEOBJDIR .
+.Sq Ev MAKEOBJDIR .
.Pp
-.Ql Va .OBJDIR
+.Sq Va .OBJDIR
may be modified in the makefile via the special target
-.Ql Ic .OBJDIR .
+.Sq Ic .OBJDIR .
In all cases,
.Nm
-will
-.Xr chdir 2
-to the specified directory if it exists, and set
-.Ql Va .OBJDIR
+changes to the specified directory if it exists, and sets
+.Sq Va .OBJDIR
and
-.Ql Ev PWD
+.Sq Va PWD
to that directory before executing any targets.
.Pp
Except in the case of an explicit
-.Ql Ic .OBJDIR
+.Sq Ic .OBJDIR
target,
.Nm
-will check that the specified directory is writable and ignore it if not.
+checks that the specified directory is writable and ignores it if not.
This check can be skipped by setting the environment variable
-.Ql Ev MAKE_OBJDIR_CHECK_WRITABLE
-to "no".
-.
+.Sq Ev MAKE_OBJDIR_CHECK_WRITABLE
+to
+.Dq no .
.It Va .PARSEDIR
-A path to the directory of the current
-.Ql Pa Makefile
-being parsed.
+The directory name of the current makefile being parsed.
.It Va .PARSEFILE
-The basename of the current
-.Ql Pa Makefile
-being parsed.
+The basename of the current makefile being parsed.
This variable and
-.Ql Va .PARSEDIR
-are both set only while the
-.Ql Pa Makefiles
-are being parsed.
-If you want to retain their current values, assign them to a variable
-using assignment with expansion:
-.Pq Ql Cm \&:= .
+.Sq Va .PARSEDIR
+are both set only while the makefiles are being parsed.
+To retain their current values,
+assign them to a variable using assignment with expansion
+.Sq Cm \&:= .
.It Va .PATH
-A variable that represents the list of directories that
+The space-separated list of directories that
.Nm
-will search for files.
-The search list should be updated using the target
-.Ql Va .PATH
-rather than the variable.
-.It Ev PWD
+searches for files.
+To update this search list, use the special target
+.Sq Ic .PATH
+rather than modifying the variable directly.
+.It Va %POSIX
+Is set in POSIX mode, see the special
+.Ql Va .POSIX
+target.
+.\" XXX: There is no make variable named 'PWD',
+.\" XXX: make only reads and writes the environment variable 'PWD'.
+.It Va PWD
Alternate path to the current directory.
.Nm
normally sets
-.Ql Va .CURDIR
+.Sq Va .CURDIR
to the canonical path given by
.Xr getcwd 3 .
However, if the environment variable
-.Ql Ev PWD
-is set and gives a path to the current directory, then
+.Sq Ev PWD
+is set and gives a path to the current directory,
.Nm
sets
-.Ql Va .CURDIR
+.Sq Va .CURDIR
to the value of
-.Ql Ev PWD
+.Sq Ev PWD
instead.
This behavior is disabled if
-.Ql Ev MAKEOBJDIRPREFIX
+.Sq Ev MAKEOBJDIRPREFIX
is set or
-.Ql Ev MAKEOBJDIR
+.Sq Ev MAKEOBJDIR
contains a variable transform.
-.Ql Ev PWD
+.Sq Va PWD
is set to the value of
-.Ql Va .OBJDIR
+.Sq Va .OBJDIR
for all programs which
.Nm
executes.
-.It Ev .SHELL
+.It Va .SHELL
The pathname of the shell used to run target scripts.
It is read-only.
-.It Ev .TARGETS
+.It Va .SUFFIXES
+The list of known suffixes.
+It is read-only.
+.It Va .SYSPATH
+The space-separated list of directories that
+.Nm
+searches for makefiles, referred to as the system include path.
+To update this search list, use the special target
+.Sq Ic .SYSPATH
+rather than modifying the variable which is read-only.
+.It Va .TARGETS
The list of targets explicitly specified on the command line, if any.
-.It Ev VPATH
-Colon-separated
+.It Va VPATH
+The colon-separated
.Pq Dq \&:
-lists of directories that
+list of directories that
.Nm
-will search for files.
-The variable is supported for compatibility with old make programs only,
-use
-.Ql Va .PATH
+searches for files.
+This variable is supported for compatibility with old make programs only, use
+.Sq Va .PATH
instead.
.El
.Ss Variable modifiers
-Variable expansion may be modified to select or modify each word of the
-variable (where a
-.Dq word
-is white-space delimited sequence of characters).
-The general format of a variable expansion is as follows:
+The general format of a variable expansion is:
.Pp
-.Dl ${variable[:modifier[:...]]}
+.Sm off
+.D1 Ic \&${ Ar variable\| Oo Ic \&: Ar modifier\| Oo Ic \&: No ... Oc Oc Ic \&}
+.Sm on
.Pp
-Each modifier begins with a colon,
-which may be escaped with a backslash
-.Pq Ql \e .
+Each modifier begins with a colon.
+To escape a colon, precede it with a backslash
+.Ql \e .
.Pp
-A set of modifiers can be specified via a variable, as follows:
+A list of indirect modifiers can be specified via a variable, as follows:
.Pp
-.Dl modifier_variable=modifier[:...]
-.Dl ${variable:${modifier_variable}[:...]}
+.Bd -literal -offset indent
+.Ar modifier_variable\^ Li \&= Ar modifier Ns Oo Ic \&: Ns No ... Oc
+
+.Sm off
+.Ic \&${ Ar variable Ic \&:${ Ar modifier_variable Ic \&} Oo Ic \&: No ... Oc Ic \&}
+.Sm on
+.Ed
.Pp
-In this case the first modifier in the modifier_variable does not
-start with a colon, since that must appear in the referencing
-variable.
-If any of the modifiers in the modifier_variable contain a dollar sign
+In this case, the first modifier in the
+.Ar modifier_variable
+does not start with a colon,
+since that colon already occurs in the referencing variable.
+If any of the modifiers in the
+.Ar modifier_variable
+contains a dollar sign
.Pq Ql $ ,
these must be doubled to avoid early expansion.
.Pp
+Some modifiers interpret the expression value as a single string,
+others treat the expression value as a whitespace-separated list of words.
+When splitting a string into words,
+whitespace can be escaped using double quotes, single quotes and backslashes,
+like in the shell.
+The quotes and backslashes are retained in the words.
+.Pp
The supported modifiers are:
.Bl -tag -width EEE
.It Cm \&:E
-Replaces each word in the variable with its suffix.
+Replaces each word with its suffix.
.It Cm \&:H
-Replaces each word in the variable with everything but the last component.
-.It Cm \&:M Ns Ar pattern
+Replaces each word with its dirname.
+.It Cm \&:M\| Ns Ar pattern
Selects only those words that match
.Ar pattern .
The standard shell wildcard characters
.Pf ( Ql * ,
.Ql \&? ,
and
-.Ql Oo Oc )
+.Ql \&[] )
may
be used.
The wildcard characters may be escaped with a backslash
.Pq Ql \e .
As a consequence of the way values are split into words, matched,
-and then joined, a construct like
-.Dl ${VAR:M*}
-will normalize the inter-word spacing, removing all leading and
-trailing space, and converting multiple consecutive spaces
-to single spaces.
-.
-.It Cm \&:N Ns Ar pattern
-This is identical to
-.Ql Cm \&:M ,
-but selects all words which do not match
+and then joined, the construct
+.Ql ${VAR:M*}
+removes all leading and trailing whitespace
+and normalizes the inter-word spacing to a single space.
+.It Cm \&:N\| Ns Ar pattern
+This is the opposite of
+.Sq Cm \&:M ,
+selecting all words which do
+.Em not
+match
.Ar pattern .
.It Cm \&:O
-Orders every word in variable alphabetically.
+Orders the words lexicographically.
+.It Cm \&:On
+Orders the words numerically.
+A number followed by one of
+.Ql k ,
+.Ql M
+or
+.Ql G
+is multiplied by the appropriate factor, which is 1024 for
+.Ql k ,
+1048576 for
+.Ql M ,
+or 1073741824 for
+.Ql G .
+Both upper- and lower-case letters are accepted.
.It Cm \&:Or
-Orders every word in variable in reverse alphabetical order.
+Orders the words in reverse lexicographical order.
+.It Cm \&:Orn
+Orders the words in reverse numerical order.
.It Cm \&:Ox
-Shuffles the words in variable.
-The results will be different each time you are referring to the
+Shuffles the words.
+The results are different each time you are referring to the
modified variable; use the assignment with expansion
-.Pq Ql Cm \&:=
+.Sq Cm \&:=
to prevent such behavior.
For example,
.Bd -literal -offset indent
@@ -1261,86 +1491,110 @@ due uno quattro tre
due uno quattro tre
.Ed
.It Cm \&:Q
-Quotes every shell meta-character in the variable, so that it can be passed
+Quotes every shell meta-character in the value, so that it can be passed
safely to the shell.
.It Cm \&:q
-Quotes every shell meta-character in the variable, and also doubles
+Quotes every shell meta-character in the value, and also doubles
.Sq $
characters so that it can be passed
safely through recursive invocations of
.Nm .
-This is equivalent to:
-.Sq \&:S/\e\&$/&&/g:Q .
+This is equivalent to
+.Sq Cm \&:S/\e\&$/&&/g:Q .
.It Cm \&:R
-Replaces each word in the variable with everything but its suffix.
-.It Cm \&:range[=count]
+Replaces each word with everything but its suffix.
+.It Cm \&:range Ns Oo Cm = Ns Ar count Oc
The value is an integer sequence representing the words of the original
value, or the supplied
-.Va count .
-.It Cm \&:gmtime[=utc]
-The value is a format string for
+.Ar count .
+.It Cm \&:gmtime Ns Oo Cm = Ns Ar timestamp Oc
+The value is interpreted as a format string for
.Xr strftime 3 ,
using
-.Xr gmtime 3 .
+.Xr gmtime 3 ,
+producing the formatted timestamp.
+Note: the
+.Ql %s
+format should only be used with
+.Sq Cm \&:localtime .
If a
-.Va utc
+.Ar timestamp
value is not provided or is 0, the current time is used.
.It Cm \&:hash
-Computes a 32-bit hash of the value and encode it as hex digits.
-.It Cm \&:localtime[=utc]
-The value is a format string for
+Computes a 32-bit hash of the value and encodes it as 8 hex digits.
+.It Cm \&:localtime Ns Oo Cm = Ns Ar timestamp Oc
+The value is interpreted as a format string for
.Xr strftime 3 ,
using
-.Xr localtime 3 .
+.Xr localtime 3 ,
+producing the formatted timestamp.
If a
-.Va utc
+.Ar timestamp
value is not provided or is 0, the current time is used.
+.It Cm \&:mtime Ns Oo Cm = Ns Ar timestamp Oc
+Call
+.Xr stat 2
+with each word as pathname;
+use
+.Ql st_mtime
+as the new value.
+If
+.Xr stat 2
+fails; use
+.Ar timestamp
+or current time.
+If
+.Ar timestamp
+is set to
+.Ql error ,
+then
+.Xr stat 2
+failure will cause an error.
.It Cm \&:tA
-Attempts to convert variable to an absolute path using
-.Xr realpath 3 ,
-if that fails, the value is unchanged.
+Attempts to convert the value to an absolute path using
+.Xr realpath 3 .
+If that fails, the value is unchanged.
.It Cm \&:tl
-Converts variable to lower-case letters.
+Converts the value to lower-case letters.
.It Cm \&:ts Ns Ar c
-Words in the variable are normally separated by a space on expansion.
-This modifier sets the separator to the character
+When joining the words after a modifier that treats the value as words,
+the words are normally separated by a space.
+This modifier changes the separator to the character
.Ar c .
If
.Ar c
-is omitted, then no separator is used.
+is omitted, no separator is used.
The common escapes (including octal numeric codes) work as expected.
.It Cm \&:tu
-Converts variable to upper-case letters.
+Converts the value to upper-case letters.
.It Cm \&:tW
-Causes the value to be treated as a single word
-(possibly containing embedded white space).
+Causes subsequent modifiers to treat the value as a single word
+(possibly containing embedded whitespace).
See also
-.Ql Cm \&:[*] .
+.Sq Cm \&:[*] .
.It Cm \&:tw
-Causes the value to be treated as a sequence of
-words delimited by white space.
+Causes the value to be treated as a list of words.
See also
-.Ql Cm \&:[@] .
+.Sq Cm \&:[@] .
.Sm off
-.It Cm \&:S No \&/ Ar old_string No \&/ Ar new_string No \&/ Op Cm 1gW
+.It Cm \&:S\| No \&/ Ar old_string\| No \&/ Ar new_string\| No \&/ Op Cm 1gW
.Sm on
Modifies the first occurrence of
.Ar old_string
-in each word of the variable's value, replacing it with
+in each word of the value, replacing it with
.Ar new_string .
If a
.Ql g
-is appended to the last delimiter of the pattern, all occurrences
-in each word are replaced.
+is appended to the last delimiter of the pattern,
+all occurrences in each word are replaced.
If a
.Ql 1
-is appended to the last delimiter of the pattern, only the first occurrence
-is affected.
+is appended to the last delimiter of the pattern,
+only the first occurrence is affected.
If a
.Ql W
is appended to the last delimiter of the pattern,
-then the value is treated as a single word
-(possibly containing embedded white space).
+the value is treated as a single word.
If
.Ar old_string
begins with a caret
@@ -1358,39 +1612,37 @@ an ampersand
.Pq Ql &
is replaced by
.Ar old_string
-(without any
+(without the anchoring
.Ql ^
or
.Ql \&$ ) .
-Any character may be used as a delimiter for the parts of the modifier
+Any character may be used as the delimiter for the parts of the modifier
string.
-The anchoring, ampersand and delimiter characters may be escaped with a
+The anchoring, ampersand and delimiter characters can be escaped with a
backslash
.Pq Ql \e .
.Pp
-Variable expansion occurs in the normal fashion inside both
+Both
.Ar old_string
and
.Ar new_string
-with the single exception that a backslash is used to prevent the expansion
-of a dollar sign
-.Pq Ql \&$ ,
-not a preceding dollar sign as is usual.
+may contain nested expressions.
+To prevent a dollar sign from starting a nested expression,
+escape it with a backslash.
.Sm off
-.It Cm \&:C No \&/ Ar pattern No \&/ Ar replacement No \&/ Op Cm 1gW
+.It Cm \&:C\| No \&/ Ar pattern\| No \&/ Ar replacement\| No \&/ Op Cm 1gW
.Sm on
The
.Cm \&:C
-modifier is just like the
+modifier works like the
.Cm \&:S
modifier except that the old and new strings, instead of being
-simple strings, are an extended regular expression (see
-.Xr regex 3 )
-string
+simple strings, are an extended regular expression
.Ar pattern
+(see
+.Xr regex 3 )
and an
.Xr ed 1 Ns \-style
-string
.Ar replacement .
Normally, the first occurrence of the pattern
.Ar pattern
@@ -1406,7 +1658,7 @@ search pattern
as occur in the word or words it is found in; the
.Ql W
modifier causes the value to be treated as a single word
-(possibly containing embedded white space).
+(possibly containing embedded whitespace).
.Pp
As for the
.Cm \&:S
@@ -1417,103 +1669,99 @@ and
are subjected to variable expansion before being parsed as
regular expressions.
.It Cm \&:T
-Replaces each word in the variable with its last path component.
+Replaces each word with its last path component (basename).
.It Cm \&:u
Removes adjacent duplicate words (like
.Xr uniq 1 ) .
.Sm off
-.It Cm \&:\&? Ar true_string Cm \&: Ar false_string
+.It Cm \&:\&?\| Ar true_string\| Cm \&: Ar false_string
.Sm on
-If the variable name (not its value), when parsed as a .if conditional
-expression, evaluates to true, return as its value the
+If the variable name (not its value), when parsed as a
+.Cm .if
+conditional expression, evaluates to true, return as its value the
.Ar true_string ,
otherwise return the
.Ar false_string .
-Since the variable name is used as the expression, \&:\&? must be the
-first modifier after the variable name itself - which will, of course,
-usually contain variable expansions.
+Since the variable name is used as the expression,
+\&:\&? must be the first modifier after the variable name
+.No itself Ns \^\(em\^ Ns
+which, of course, usually contains variable expansions.
A common error is trying to use expressions like
.Dl ${NUMBERS:M42:?match:no}
-which actually tests defined(NUMBERS),
-to determine if any words match "42" you need to use something like:
+which actually tests defined(NUMBERS).
+To determine if any words match
+.Dq 42 ,
+you need to use something like:
.Dl ${"${NUMBERS:M42}" != \&"\&":?match:no} .
-.It Ar :old_string=new_string
+.It Cm :\| Ns Ar old_string\| Ns Cm = Ns Ar new_string
This is the
.At V
-style variable substitution.
-It must be the last modifier specified.
-If
+style substitution.
+It can only be the last modifier specified,
+as a
+.Ql \&:
+in either
.Ar old_string
or
.Ar new_string
-do not contain the pattern matching character
-.Ar %
-then it is assumed that they are
-anchored at the end of each word, so only suffixes or entire
-words may be replaced.
-Otherwise
-.Ar %
-is the substring of
-.Ar old_string
-to be replaced in
-.Ar new_string .
-If only
+is treated as a regular character, not as the end of the modifier.
+.Pp
+If
.Ar old_string
-contains the pattern matching character
-.Ar % ,
-and
+does not contain the pattern matching character
+.Ql % ,
+and the word ends with
.Ar old_string
-matches, then the result is the
+or equals it,
+that suffix is replaced with
.Ar new_string .
-If only the
-.Ar new_string
-contains the pattern matching character
-.Ar % ,
-then it is not treated specially and it is printed as a literal
-.Ar %
-on match.
-If there is more than one pattern matching character
-.Ar ( % )
-in either the
+.Pp
+Otherwise, the first
+.Ql %
+in
+.Ar old_string
+matches a possibly empty substring of arbitrary characters,
+and if the whole pattern is found in the word,
+the matching part is replaced with
+.Ar new_string ,
+and the first occurrence of
+.Ql %
+in
.Ar new_string
-or
-.Ar old_string ,
-only the first instance is treated specially (as the pattern character);
-all subsequent instances are treated as regular characters.
+(if any) is replaced with the substring matched by the
+.Ql % .
.Pp
-Variable expansion occurs in the normal fashion inside both
+Both
.Ar old_string
and
.Ar new_string
-with the single exception that a backslash is used to prevent the
-expansion of a dollar sign
-.Pq Ql \&$ ,
-not a preceding dollar sign as is usual.
+may contain nested expressions.
+To prevent a dollar sign from starting a nested expression,
+escape it with a backslash.
.Sm off
-.It Cm \&:@ Ar temp Cm @ Ar string Cm @
+.It Cm \&:@ Ar varname\| Cm @ Ar string\| Cm @
.Sm on
This is the loop expansion mechanism from the OSF Development
Environment (ODE) make.
Unlike
.Cm \&.for
loops, expansion occurs at the time of reference.
-Assigns
-.Ar temp
-to each word in the variable and evaluates
+For each word in the value, assign the word to the variable named
+.Ar varname
+and evaluate
.Ar string .
The ODE convention is that
-.Ar temp
-should start and end with a period.
-For example.
+.Ar varname
+should start and end with a period, for example:
.Dl ${LINKS:@.LINK.@${LN} ${TARGET} ${.LINK.}@}
.Pp
-However a single character variable is often more readable:
+However, a single-letter variable is often more readable:
.Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}
-.It Cm \&:_[=var]
+.It Cm \&:_ Ns Oo Cm = Ns Ar var Oc
Saves the current variable value in
.Ql $_
or the named
-.Va var
+.Ar var
for later reference.
Example usage:
.Bd -literal -offset indent
@@ -1530,59 +1778,53 @@ is used to save the result of the
.Ql :S
modifier which is later referenced using the index values from
.Ql :range .
-.It Cm \&:U Ns Ar newval
+.It Cm \&:U\| Ns Ar newval
If the variable is undefined,
+the optional
.Ar newval
-is the value.
+(which may be empty) is the value.
If the variable is defined, the existing value is returned.
This is another ODE make feature.
It is handy for setting per-target CFLAGS for instance:
.Dl ${_${.TARGET:T}_CFLAGS:U${DEF_CFLAGS}}
If a value is only required if the variable is undefined, use:
.Dl ${VAR:D:Unewval}
-.It Cm \&:D Ns Ar newval
+.It Cm \&:D\| Ns Ar newval
If the variable is defined,
.Ar newval
-is the value.
+(which may be empty) is the value.
.It Cm \&:L
The name of the variable is the value.
.It Cm \&:P
-The path of the node which has the same name as the variable
-is the value.
-If no such node exists or its path is null, then the
-name of the variable is used.
+The path of the node which has the same name as the variable is the value.
+If no such node exists or its path is null, the name of the variable is used.
In order for this modifier to work, the name (node) must at least have
-appeared on the rhs of a dependency.
+appeared on the right-hand side of a dependency.
.Sm off
-.It Cm \&:\&! Ar cmd Cm \&!
+.It Cm \&:\&! Ar cmd\| Cm \&!
.Sm on
The output of running
.Ar cmd
is the value.
.It Cm \&:sh
-If the variable is non-empty it is run as a command and the output
-becomes the new value.
+The value is run as a command, and the output becomes the new value.
.It Cm \&::= Ns Ar str
The variable is assigned the value
.Ar str
after substitution.
-This modifier and its variations are useful in
-obscure situations such as wanting to set a variable when shell commands
-are being parsed.
-These assignment modifiers always expand to
-nothing, so if appearing in a rule line by themselves should be
-preceded with something to keep
-.Nm
-happy.
+This modifier and its variations are useful in obscure situations
+such as wanting to set a variable
+at a point where a target's shell commands are being parsed.
+These assignment modifiers always expand to nothing.
.Pp
The
-.Ql Cm \&::
+.Sq Cm \&::
helps avoid false matches with the
.At V
style
-.Cm \&:=
-modifier and since substitution always occurs the
-.Cm \&::=
+.Ql \&:=
+modifier and since substitution always occurs, the
+.Ql \&::=
form is vaguely appropriate.
.It Cm \&::?= Ns Ar str
As for
@@ -1599,17 +1841,12 @@ to the variable.
.It Cm \&:\&[ Ns Ar range Ns Cm \&]
Selects one or more words from the value,
or performs other operations related to the way in which the
-value is divided into words.
+value is split into words.
.Pp
-Ordinarily, a value is treated as a sequence of words
-delimited by white space.
-Some modifiers suppress this behavior,
-causing a value to be treated as a single word
-(possibly containing embedded white space).
An empty value, or a value that consists entirely of white-space,
is treated as a single word.
For the purposes of the
-.Ql Cm \&:[]
+.Sq Cm \&:[]
modifier, the words are indexed both forwards using positive integers
(where index 1 represents the first word),
and backwards using negative integers
@@ -1631,52 +1868,55 @@ to
.Ar end ,
inclusive.
For example,
-.Ql Cm \&:[2..-1]
+.Sq Cm \&:[2..-1]
selects all words from the second word to the last word.
If
.Ar start
is greater than
.Ar end ,
-then the words are output in reverse order.
+the words are output in reverse order.
For example,
-.Ql Cm \&:[-1..1]
+.Sq Cm \&:[-1..1]
selects all the words from last to first.
-If the list is already ordered, then this effectively reverses
-the list, but it is more efficient to use
-.Ql Cm \&:Or
+If the list is already ordered,
+this effectively reverses the list,
+but it is more efficient to use
+.Sq Cm \&:Or
instead of
-.Ql Cm \&:O:[-1..1] .
+.Sq Cm \&:O:[-1..1] .
.\" :[*]
.It Cm \&*
Causes subsequent modifiers to treat the value as a single word
-(possibly containing embedded white space).
+(possibly containing embedded whitespace).
Analogous to the effect of
-\&"$*\&"
+.Li \&$*
in Bourne shell.
.\" :[0]
.It 0
Means the same as
-.Ql Cm \&:[*] .
+.Sq Cm \&:[*] .
.\" :[*]
.It Cm \&@
Causes subsequent modifiers to treat the value as a sequence of words
-delimited by white space.
+delimited by whitespace.
Analogous to the effect of
-\&"$@\&"
+.Li \&$@
in Bourne shell.
.\" :[#]
.It Cm \&#
Returns the number of words in the value.
.El \" :[range]
.El
-.Sh INCLUDE STATEMENTS, CONDITIONALS AND FOR LOOPS
-Makefile inclusion, conditional structures and for loops reminiscent
-of the C programming language are provided in
-.Nm .
-All such structures are identified by a line beginning with a single
-dot
+.Sh DIRECTIVES
+.Nm
+offers directives for including makefiles, conditionals and for loops.
+All these directives are identified by a line beginning with a single dot
.Pq Ql \&.
-character.
+character, followed by the keyword of the directive, such as
+.Cm include
+or
+.Cm if .
+.Ss File inclusion
Files are included with either
.Cm \&.include \&< Ns Ar file Ns Cm \&>
or
@@ -1688,36 +1928,28 @@ the system makefile directory.
If double quotes are used, the including makefile's directory and any
directories specified using the
.Fl I
-option are searched before the system
-makefile directory.
-For compatibility with other versions of
-.Nm
-.Ql include file ...
+option are searched before the system makefile directory.
+.Pp
+For compatibility with other make variants,
+.Sq Cm include Ar file No ...
+(without leading dot)
is also accepted.
.Pp
If the include statement is written as
.Cm .-include
or as
-.Cm .sinclude
-then errors locating and/or opening include files are ignored.
+.Cm .sinclude ,
+errors locating and/or opening include files are ignored.
.Pp
If the include statement is written as
-.Cm .dinclude
+.Cm .dinclude ,
not only are errors locating and/or opening include files ignored,
-but stale dependencies within the included file will be ignored
-just like
+but stale dependencies within the included file are ignored just like in
.Va .MAKE.DEPENDFILE .
-.Pp
-Conditional expressions are also preceded by a single dot as the first
-character of a line.
-The possible conditionals are as follows:
+.Ss Exporting variables
+The directives for exporting and unexporting variables are:
.Bl -tag -width Ds
-.It Ic .error Ar message
-The message is printed along with the name of the makefile and line number,
-then
-.Nm
-will exit immediately.
-.It Ic .export Ar variable ...
+.It Ic .export Ar variable No ...
Export the specified global variable.
If no variable list is provided, all globals are exported
except for internal variables (those that start with
@@ -1725,16 +1957,14 @@ except for internal variables (those that start with
This is not affected by the
.Fl X
flag, so should be used with caution.
-For compatibility with other
-.Nm
-programs
-.Ql export variable=value
-is also accepted.
+For compatibility with other make programs,
+.Cm export Ar variable\| Ns Cm \&= Ns Ar value
+(without leading dot) is also accepted.
.Pp
Appending a variable name to
.Va .MAKE.EXPORTED
is equivalent to exporting a variable.
-.It Ic .export-env Ar variable ...
+.It Ic .export-env Ar variable No ...
The same as
.Ql .export ,
except that the variable is not appended to
@@ -1743,21 +1973,16 @@ This allows exporting a value to the environment which is different from that
used by
.Nm
internally.
-.It Ic .export-literal Ar variable ...
+.It Ic .export-literal Ar variable No ...
The same as
.Ql .export-env ,
except that variables in the value are not expanded.
-.It Ic .info Ar message
-The message is printed along with the name of the makefile and line number.
-.It Ic .undef Ar variable ...
-Un-define the specified global variables.
-Only global variables can be un-defined.
-.It Ic .unexport Ar variable ...
+.It Ic .unexport Ar variable No ...
The opposite of
.Ql .export .
The specified global
-.Va variable
-will be removed from
+.Ar variable
+is removed from
.Va .MAKE.EXPORTED .
If no variable list is provided, all globals are unexported,
and
@@ -1766,11 +1991,11 @@ deleted.
.It Ic .unexport-env
Unexport all globals previously exported and
clear the environment inherited from the parent.
-This operation will cause a memory leak of the original environment,
+This operation causes a memory leak of the original environment,
so should be used sparingly.
Testing for
.Va .MAKE.LEVEL
-being 0, would make sense.
+being 0 would make sense.
Also note that any variables which originated in the parent environment
should be explicitly preserved if desired.
For example:
@@ -1783,52 +2008,68 @@ PATH := ${PATH}
.Pp
.Ed
Would result in an environment containing only
-.Ql Ev PATH ,
+.Sq Ev PATH ,
which is the minimal useful environment.
+.\" TODO: Check the below sentence, environment variables don't start with '.'.
Actually
-.Ql Ev .MAKE.LEVEL
-will also be pushed into the new environment.
+.Sq Va .MAKE.LEVEL
+is also pushed into the new environment.
+.El
+.Ss Messages
+The directives for printing messages to the output are:
+.Bl -tag -width Ds
+.It Ic .info Ar message
+The message is printed along with the name of the makefile and line number.
.It Ic .warning Ar message
The message prefixed by
-.Ql Pa warning:
+.Sq Li warning:
is printed along with the name of the makefile and line number.
-.It Ic \&.if Oo \&! Oc Ns Ar expression Op Ar operator expression ...
+.It Ic .error Ar message
+The message is printed along with the name of the makefile and line number,
+.Nm
+exits immediately.
+.El
+.Ss Conditionals
+The directives for conditionals are:
+.ds maybenot Oo Ic \&! Oc Ns
+.Bl -tag
+.It Ic .if \*[maybenot] Ar expression Op Ar operator expression No ...
Test the value of an expression.
-.It Ic .ifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ...
-Test the value of a variable.
-.It Ic .ifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ...
-Test the value of a variable.
-.It Ic .ifmake Oo \&! Oc Ns Ar target Op Ar operator target ...
-Test the target being built.
-.It Ic .ifnmake Oo \&! Ns Oc Ar target Op Ar operator target ...
-Test the target being built.
+.It Ic .ifdef \*[maybenot] Ar variable Op Ar operator variable No ...
+Test whether a variable is defined.
+.It Ic .ifndef \*[maybenot] Ar variable Op Ar operator variable No ...
+Test whether a variable is not defined.
+.It Ic .ifmake \*[maybenot] Ar target Op Ar operator target No ...
+Test the target being requested.
+.It Ic .ifnmake \*[maybenot] Ar target Op Ar operator target No ...
+Test the target being requested.
.It Ic .else
Reverse the sense of the last conditional.
-.It Ic .elif Oo \&! Ns Oc Ar expression Op Ar operator expression ...
+.It Ic .elif \*[maybenot] Ar expression Op Ar operator expression No ...
A combination of
-.Ql Ic .else
+.Sq Ic .else
followed by
-.Ql Ic .if .
-.It Ic .elifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ...
+.Sq Ic .if .
+.It Ic .elifdef \*[maybenot] Ar variable Op Ar operator variable No ...
A combination of
-.Ql Ic .else
+.Sq Ic .else
followed by
-.Ql Ic .ifdef .
-.It Ic .elifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ...
+.Sq Ic .ifdef .
+.It Ic .elifndef \*[maybenot] Ar variable Op Ar operator variable No ...
A combination of
-.Ql Ic .else
+.Sq Ic .else
followed by
-.Ql Ic .ifndef .
-.It Ic .elifmake Oo \&! Oc Ns Ar target Op Ar operator target ...
+.Sq Ic .ifndef .
+.It Ic .elifmake \*[maybenot] Ar target Op Ar operator target No ...
A combination of
-.Ql Ic .else
+.Sq Ic .else
followed by
-.Ql Ic .ifmake .
-.It Ic .elifnmake Oo \&! Oc Ns Ar target Op Ar operator target ...
+.Sq Ic .ifmake .
+.It Ic .elifnmake \*[maybenot] Ar target Op Ar operator target No ...
A combination of
-.Ql Ic .else
+.Sq Ic .else
followed by
-.Ql Ic .ifnmake .
+.Sq Ic .ifnmake .
.It Ic .endif
End the body of the conditional.
.El
@@ -1836,133 +2077,158 @@ End the body of the conditional.
The
.Ar operator
may be any one of the following:
-.Bl -tag -width "Cm XX"
-.It Cm \&|\&|
+.Bl -tag
+.It Ic \&|\&|
Logical OR.
-.It Cm \&&&
-Logical
-.Tn AND ;
-of higher precedence than
-.Dq \&|\&| .
+.It Ic \&&&
+Logical AND; of higher precedence than
+.Sq Ic \&|\&| .
.El
.Pp
-As in C,
.Nm
-will only evaluate a conditional as far as is necessary to determine
-its value.
-Parentheses may be used to change the order of evaluation.
+only evaluates a conditional as far as is necessary to determine its value.
+Parentheses can be used to override the operator precedence.
The boolean operator
-.Ql Ic \&!
-may be used to logically negate an entire
-conditional.
+.Sq Ic \&!
+may be used to logically negate an expression, typically a function call.
It is of higher precedence than
-.Ql Ic \&&& .
+.Sq Ic \&&& .
.Pp
The value of
.Ar expression
-may be any of the following:
-.Bl -tag -width defined
-.It Ic defined
-Takes a variable name as an argument and evaluates to true if the variable
+may be any of the following function call expressions:
+.Bl -tag
+.Sm off
+.It Ic defined Li \&( Ar varname Li \&)
+.Sm on
+Evaluates to true if the variable
+.Ar varname
has been defined.
-.It Ic make
-Takes a target name as an argument and evaluates to true if the target
-was specified as part of
+.Sm off
+.It Ic make Li \&( Ar target Li \&)
+.Sm on
+Evaluates to true if the target was specified as part of
.Nm Ns 's
command line or was declared the default target (either implicitly or
explicitly, see
.Va .MAIN )
before the line containing the conditional.
-.It Ic empty
-Takes a variable, with possible modifiers, and evaluates to true if
-the expansion of the variable would result in an empty string.
-.It Ic exists
-Takes a file name as an argument and evaluates to true if the file exists.
-The file is searched for on the system search path (see
+.Sm off
+.It Ic empty Li \&( Ar varname Oo Li : Ar modifiers Oc Li \&)
+.Sm on
+Evaluates to true if the expansion of the variable,
+after applying the modifiers, results in an empty string.
+.Sm off
+.It Ic exists Li \&( Ar pathname Li \&)
+.Sm on
+Evaluates to true if the given pathname exists.
+If relative, the pathname is searched for on the system search path (see
.Va .PATH ) .
-.It Ic target
-Takes a target name as an argument and evaluates to true if the target
-has been defined.
-.It Ic commands
-Takes a target name as an argument and evaluates to true if the target
-has been defined and has commands associated with it.
+.Sm off
+.It Ic target Li \&( Ar target Li \&)
+.Sm on
+Evaluates to true if the target has been defined.
+.Sm off
+.It Ic commands Li \&( Ar target Li \&)
+.Sm on
+Evaluates to true if the target has been defined
+and has commands associated with it.
.El
.Pp
.Ar Expression
may also be an arithmetic or string comparison.
-Variable expansion is
-performed on both sides of the comparison, after which the numerical
-values are compared.
-A value is interpreted as hexadecimal if it is
-preceded by 0x, otherwise it is decimal; octal numbers are not supported.
-The standard C relational operators are all supported.
-If after
-variable expansion, either the left or right hand side of a
-.Ql Ic ==
-or
-.Ql Ic "!="
-operator is not a numerical value, then
-string comparison is performed between the expanded
-variables.
-If no relational operator is given, it is assumed that the expanded
-variable is being compared against 0, or an empty string in the case
-of a string comparison.
+Variable expansion is performed on both sides of the comparison.
+If both sides are numeric and neither is enclosed in quotes,
+the comparison is done numerically, otherwise lexicographically.
+A string is interpreted as a hexadecimal integer if it is preceded by
+.Li 0x ,
+otherwise it is interpreted as a decimal floating-point number;
+octal numbers are not supported.
+.Pp
+All comparisons may use the operators
+.Sq Ic \&==
+and
+.Sq Ic \&!= .
+Numeric comparisons may also use the operators
+.Sq Ic \&< ,
+.Sq Ic \&<= ,
+.Sq Ic \&>
+and
+.Sq Ic \&>= .
+.Pp
+If the comparison has neither a comparison operator nor a right side,
+the expression evaluates to true if it is nonempty
+and its numeric value (if any) is not zero.
.Pp
When
.Nm
is evaluating one of these conditional expressions, and it encounters
-a (white-space separated) word it doesn't recognize, either the
+a (whitespace-separated) word it doesn't recognize, either the
.Dq make
or
.Dq defined
-expression is applied to it, depending on the form of the conditional.
+function is applied to it, depending on the form of the conditional.
If the form is
-.Ql Ic .ifdef ,
-.Ql Ic .ifndef ,
+.Sq Ic .ifdef ,
+.Sq Ic .ifndef
or
-.Ql Ic .if
+.Sq Ic .if ,
the
.Dq defined
-expression is applied.
+function is applied.
Similarly, if the form is
-.Ql Ic .ifmake
+.Sq Ic .ifmake
or
-.Ql Ic .ifnmake ,
+.Sq Ic .ifnmake ,
the
.Dq make
-expression is applied.
+function is applied.
.Pp
-If the conditional evaluates to true the parsing of the makefile continues
-as before.
-If it evaluates to false, the following lines are skipped.
-In both cases this continues until a
-.Ql Ic .else
+If the conditional evaluates to true,
+parsing of the makefile continues as before.
+If it evaluates to false, the following lines until the corresponding
+.Sq Ic .elif
+variant,
+.Sq Ic .else
or
-.Ql Ic .endif
-is found.
-.Pp
+.Sq Ic .endif
+are skipped.
+.Ss For loops
For loops are typically used to apply a set of rules to a list of files.
The syntax of a for loop is:
.Pp
.Bl -tag -compact -width Ds
-.It Ic \&.for Ar variable Oo Ar variable ... Oc Ic in Ar expression
-.It Aq make-lines
+.It Ic \&.for Ar variable Oo Ar variable No ... Oc Ic in Ar expression
+.It Aq Ar make-lines
.It Ic \&.endfor
.El
.Pp
-After the for
-.Ic expression
-is evaluated, it is split into words.
+The
+.Ar expression
+is expanded and then split into words.
On each iteration of the loop, one word is taken and assigned to each
-.Ic variable ,
+.Ar variable ,
in order, and these
-.Ic variables
+.Ar variables
are substituted into the
-.Ic make-lines
+.Ar make-lines
inside the body of the for loop.
The number of words must come out even; that is, if there are three
iteration variables, the number of words provided must be a multiple
of three.
+.Pp
+If
+.Sq Ic .break
+is encountered within a
+.Cm \&.for
+loop, it causes early termination of the loop, otherwise a parse error.
+.\" TODO: Describe limitations with defined/empty.
+.Ss Other directives
+.Bl -tag -width Ds
+.It Ic .undef Ar variable No ...
+Un-define the specified global variables.
+Only global variables can be un-defined.
+.El
.Sh COMMENTS
Comments begin with a hash
.Pq Ql \&#
@@ -1981,7 +2247,7 @@ as if they all were preceded by a dash
.\" .It Ic .JOIN
.\" XXX
.It Ic .MADE
-Mark all sources of this target as being up-to-date.
+Mark all sources of this target as being up to date.
.It Ic .MAKE
Execute the commands associated with this target even if the
.Fl n
@@ -1999,7 +2265,9 @@ or
Usage in conjunction with
.Ic .MAKE
is the most likely case.
-In "meta" mode, the target is out-of-date if the meta file is missing.
+In
+.Dq meta
+mode, the target is out-of-date if the meta file is missing.
.It Ic .NOMETA
Do not create a meta file for the target.
Meta files are also not created for
@@ -2011,16 +2279,17 @@ targets.
.It Ic .NOMETA_CMP
Ignore differences in commands when deciding if target is out of date.
This is useful if the command contains a value which always changes.
-If the number of commands change, though, the target will still be out of date.
+If the number of commands change, though,
+the target is still considered out of date.
The same effect applies to any command line that uses the variable
.Va .OODATE ,
which can be used for that purpose even when not otherwise needed or desired:
.Bd -literal -offset indent
skip-compare-for-some:
- @echo this will be compared
- @echo this will not ${.OODATE:M.NOMETA_CMP}
- @echo this will also be compared
+ @echo this is compared
+ @echo this is not ${.OODATE:M.NOMETA_CMP}
+ @echo this is also compared
.Ed
The
@@ -2028,7 +2297,7 @@ The
pattern suppresses any expansion of the unwanted variable.
.It Ic .NOPATH
Do not search for the target in the directories specified by
-.Ic .PATH .
+.Va .PATH .
.It Ic .NOTMAIN
Normally
.Nm
@@ -2038,12 +2307,12 @@ This source prevents this target from being selected.
.It Ic .OPTIONAL
If a target is marked with this attribute and
.Nm
-can't figure out how to create it, it will ignore this fact and assume
+can't figure out how to create it, it ignores this fact and assumes
the file isn't needed or already exists.
.It Ic .PHONY
-The target does not
-correspond to an actual file; it is always considered to be out of date,
-and will not be created with the
+The target does not correspond to an actual file;
+it is always considered to be out of date,
+and is not created with the
.Fl t
option.
Suffix-transformation rules are not applied to
@@ -2075,9 +2344,9 @@ If the target already has commands, the
target's commands are appended
to them.
.It Ic .USEBEFORE
-Exactly like
+Like
.Ic .USE ,
-but prepend the
+but instead of appending, prepend the
.Ic .USEBEFORE
target commands to the target.
.It Ic .WAIT
@@ -2105,7 +2374,7 @@ the output is always
.Ql b1 ,
.Ql b ,
.Ql x .
-.br
+.Pp
The ordering imposed by
.Ic .WAIT
is only relevant for parallel makes.
@@ -2120,17 +2389,15 @@ else is done.
.It Ic .DEFAULT
This is sort of a
.Ic .USE
-rule for any target (that was used only as a
-source) that
+rule for any target (that was used only as a source) that
.Nm
can't figure out any other way to create.
Only the shell script is used.
The
-.Ic .IMPSRC
+.Va .IMPSRC
variable of a target that inherits
.Ic .DEFAULT Ns 's
-commands is set
-to the target's own name.
+commands is set to the target's own name.
.It Ic .DELETE_ON_ERROR
If this target is present in the makefile, it globally causes make to
delete targets whose commands fail.
@@ -2141,14 +2408,12 @@ This setting can be used to help prevent half-finished or malformed
targets from being left around and corrupting future rebuilds.
.It Ic .END
Any command lines attached to this target are executed after everything
-else is done.
+else is done successfully.
.It Ic .ERROR
Any command lines attached to this target are executed when another target fails.
-The
-.Ic .ERROR_TARGET
-variable is set to the target that failed.
-See also
-.Ic MAKE_PRINT_VAR_ON_ERROR .
+See
+.Va MAKE_PRINT_VAR_ON_ERROR
+for the variables that will be set.
.It Ic .IGNORE
Mark each of the sources with the
.Ic .IGNORE
@@ -2159,24 +2424,24 @@ option.
.It Ic .INTERRUPT
If
.Nm
-is interrupted, the commands for this target will be executed.
+is interrupted, the commands for this target are executed.
.It Ic .MAIN
If no target is specified when
.Nm
-is invoked, this target will be built.
+is invoked, this target is built.
.It Ic .MAKEFLAGS
This target provides a way to specify flags for
.Nm
-when the makefile is used.
+at the time when the makefiles are read.
The flags are as if typed to the shell, though the
.Fl f
-option will have
+option has
no effect.
.\" XXX: NOT YET!!!!
.\" .It Ic .NOTPARALLEL
.\" The named targets are executed in non parallel mode.
.\" If no targets are
-.\" specified, then all targets are executed in non parallel mode.
+.\" specified, all targets are executed in non parallel mode.
.It Ic .NOPATH
Apply the
.Ic .NOPATH
@@ -2187,18 +2452,19 @@ Disable parallel mode.
Synonym for
.Ic .NOTPARALLEL ,
for compatibility with other pmake variants.
+.It Ic .NOREADONLY
+clear the read-only attribute from the global variables specified as sources.
.It Ic .OBJDIR
The source is a new value for
-.Ql Va .OBJDIR .
+.Sq Va .OBJDIR .
If it exists,
.Nm
-will
-.Xr chdir 2
-to it and update the value of
-.Ql Va .OBJDIR .
+changes the current working directory to it and updates the value of
+.Sq Va .OBJDIR .
.It Ic .ORDER
-The named targets are made in sequence.
+In parallel mode, the named targets are made in sequence.
This ordering does not add targets to the list of targets to be made.
+.Pp
Since the dependents of a target do not get built until the target itself
could be built, unless
.Ql a
@@ -2209,24 +2475,20 @@ the following is a dependency loop:
b: a
.Ed
.Pp
-The ordering imposed by
-.Ic .ORDER
-is only relevant for parallel makes.
.\" XXX: NOT YET!!!!
.\" .It Ic .PARALLEL
.\" The named targets are executed in parallel mode.
.\" If no targets are
-.\" specified, then all targets are executed in parallel mode.
+.\" specified, all targets are executed in parallel mode.
.It Ic .PATH
The sources are directories which are to be searched for files not
found in the current directory.
-If no sources are specified, any previously specified directories are
-deleted.
+If no sources are specified,
+any previously specified directories are removed from the search path.
If the source is the special
.Ic .DOTLAST
-target, then the current working
-directory is searched last.
-.It Ic .PATH. Ns Va suffix
+target, the current working directory is searched last.
+.It Ic .PATH. Ns Ar suffix
Like
.Ic .PATH
but applies only to files with a particular suffix.
@@ -2236,52 +2498,70 @@ The suffix must have been previously declared with
Apply the
.Ic .PHONY
attribute to any specified sources.
+.It Ic .POSIX
+If this is the first non-comment line in the main makefile,
+the variable
+.Va %POSIX
+is set to the value
+.Ql 1003.2
+and the makefile
+.Ql <posix.mk>
+is included if it exists,
+to provide POSIX-compatible default rules.
+If
+.Nm
+is run with the
+.Fl r
+flag, only
+.Ql posix.mk
+contributes to the default rules.
.It Ic .PRECIOUS
Apply the
.Ic .PRECIOUS
attribute to any specified sources.
If no sources are specified, the
.Ic .PRECIOUS
-attribute is applied to every
-target in the file.
+attribute is applied to every target in the file.
+.It Ic .READONLY
+set the read-only attribute on the global variables specified as sources.
.It Ic .SHELL
Sets the shell that
.Nm
-will use to execute commands.
+uses to execute commands.
The sources are a set of
-.Ar field=value
+.Ar field\| Ns Cm \&= Ns Ar value
pairs.
-.Bl -tag -width hasErrCtls
-.It Ar name
+.Bl -tag -width ".Li hasErrCtls"
+.It Li name
This is the minimal specification, used to select one of the built-in
shell specs;
-.Ar sh ,
-.Ar ksh ,
+.Li sh ,
+.Li ksh ,
and
-.Ar csh .
-.It Ar path
-Specifies the path to the shell.
-.It Ar hasErrCtl
+.Li csh .
+.It Li path
+Specifies the absolute path to the shell.
+.It Li hasErrCtl
Indicates whether the shell supports exit on error.
-.It Ar check
+.It Li check
The command to turn on error checking.
-.It Ar ignore
+.It Li ignore
The command to disable error checking.
-.It Ar echo
+.It Li echo
The command to turn on echoing of commands executed.
-.It Ar quiet
+.It Li quiet
The command to turn off echoing of commands executed.
-.It Ar filter
+.It Li filter
The output to filter after issuing the
-.Ar quiet
+.Li quiet
command.
It is typically identical to
-.Ar quiet .
-.It Ar errFlag
+.Li quiet .
+.It Li errFlag
The flag to pass the shell to enable error checking.
-.It Ar echoFlag
+.It Li echoFlag
The flag to pass the shell to enable command echoing.
-.It Ar newline
+.It Li newline
The string literal to pass the shell that results in a single newline
character when used outside of any quoting characters.
.El
@@ -2312,10 +2592,18 @@ It allows the creation of suffix-transformation rules.
.Pp
Example:
.Bd -literal
-\&.SUFFIXES: .o
+\&.SUFFIXES: .c .o
\&.c.o:
cc \-o ${.TARGET} \-c ${.IMPSRC}
.Ed
+.It Ic .SYSPATH
+The sources are directories which are to be added to the system
+include path which
+.Nm
+searches for makefiles.
+If no sources are specified,
+any previously specified directories are removed from the system
+include path.
.El
.Sh ENVIRONMENT
.Nm
@@ -2338,23 +2626,23 @@ may only be set in the environment or on the command line to
.Nm
and not as makefile variables;
see the description of
-.Ql Va .OBJDIR
+.Sq Va .OBJDIR
for more details.
.Sh FILES
.Bl -tag -width /usr/share/mk -compact
.It .depend
list of dependencies
-.It Makefile
-list of dependencies
.It makefile
-list of dependencies
+first default makefile if no makefile is specified on the command line
+.It Makefile
+second default makefile if no makefile is specified on the command line
.It sys.mk
system makefile
.It /usr/share/mk
system makefile directory
.El
.Sh COMPATIBILITY
-The basic make syntax is compatible between different versions of make;
+The basic make syntax is compatible between different make variants;
however the special variables, variable modifiers and conditionals are not.
.Ss Older versions
An incomplete list of changes in older versions of
@@ -2383,13 +2671,15 @@ The
and
.Ic .ORDER
declarations and most functionality pertaining to parallelization.
-(GNU make supports parallelization but lacks these features needed to
+(GNU make supports parallelization but lacks the features needed to
control it effectively.)
.It
Directives, including for loops and conditionals and most of the
forms of include files.
(GNU make has its own incompatible and less powerful syntax for
conditionals.)
+.\" The "less powerful" above means that GNU make does not have the
+.\" make(target), target(target) and commands(target) functions.
.It
All built-in variables that begin with a dot.
.It
@@ -2401,7 +2691,7 @@ and
.Ic .SUFFIXES .
.It
Variable modifiers, except for the
-.Dl :old=new
+.Ql :old=new
string substitution, which does not portably support globbing with
.Ql %
and historically only works on declared suffixes.
@@ -2418,7 +2708,7 @@ Some features are somewhat more portable, such as assignment with
and
.Ic != .
The
-.Ic .PATH
+.Va .PATH
functionality is based on an older feature
.Ic VPATH
found in GNU make and many versions of SVR4 make; however,
@@ -2449,22 +2739,22 @@ command appeared in
.At v7 .
This
make
-implementation is based on Adam De Boor's pmake program which was written
-for Sprite at Berkeley.
+implementation is based on Adam de Boor's pmake program,
+which was written for Sprite at Berkeley.
It was designed to be a parallel distributed make running jobs on different
machines using a daemon called
.Dq customs .
.Pp
Historically the target/dependency
-.Dq FRC
+.Ic FRC
has been used to FoRCe rebuilding (since the target/dependency
-does not exist... unless someone creates an
-.Dq FRC
+does not exist ... unless someone creates an
+.Pa FRC
file).
.Sh BUGS
The
make
-syntax is difficult to parse without actually acting on the data.
+syntax is difficult to parse.
For instance, finding the end of a variable's use should involve scanning
each of the modifiers, using the correct terminator for each field.
In many places
@@ -2472,3 +2762,13 @@ make
just counts {} and () in order to find the end of a variable expansion.
.Pp
There is no way of escaping a space character in a filename.
+.Pp
+In jobs mode, when a target fails;
+make
+will put an error token into the job token pool.
+This will cause all other instances of
+make
+using that token pool to abort the build and exit with error code 6.
+Sometimes the attempt to suppress a cascade of unnecessary errors,
+can result in a seemingly unexplained
+.Ql *** Error code 6
diff --git a/contrib/bmake/bmake.cat1 b/contrib/bmake/bmake.cat1
index d9723b35cf7f..f2b05878e7e2 100644
--- a/contrib/bmake/bmake.cat1
+++ b/contrib/bmake/bmake.cat1
@@ -1,412 +1,446 @@
BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
-NAME
- bmake -- maintain program dependencies
-
-SYNOPSIS
- bmake [-BeikNnqrSstWwX] [-C directory] [-D variable] [-d flags]
- [-f makefile] [-I directory] [-J private] [-j max_jobs]
- [-m directory] [-T file] [-V variable] [-v variable]
- [variable=value] [target ...]
-
-DESCRIPTION
- bmake is a program designed to simplify the maintenance of other pro-
- grams. Its input is a list of specifications as to the files upon which
- programs and other files depend. If no -f makefile makefile option is
- given, bmake will try to open `makefile' then `Makefile' in order to find
- the specifications. If the file `.depend' exists, it is read (see
- mkdep(1)).
+NNAAMMEE
+ bbmmaakkee - maintain program dependencies
+
+SSYYNNOOPPSSIISS
+ bbmmaakkee [--BBeeiikkNNnnqqrrSSssttWWwwXX] [--CC _d_i_r_e_c_t_o_r_y] [--DD _v_a_r_i_a_b_l_e] [--dd _f_l_a_g_s]
+ [--ff _m_a_k_e_f_i_l_e] [--II _d_i_r_e_c_t_o_r_y] [--JJ _p_r_i_v_a_t_e] [--jj _m_a_x___j_o_b_s]
+ [--mm _d_i_r_e_c_t_o_r_y] [--TT _f_i_l_e] [--VV _v_a_r_i_a_b_l_e] [--vv _v_a_r_i_a_b_l_e]
+ [_v_a_r_i_a_b_l_e==_v_a_l_u_e] [_t_a_r_g_e_t ...]
+
+DDEESSCCRRIIPPTTIIOONN
+ bbmmaakkee is a program designed to simplify the maintenance of other
+ programs. Its input is a list of specifications as to the files upon
+ which programs and other files depend. If no --ff _m_a_k_e_f_i_l_e option is
+ given, bbmmaakkee tries to open `_m_a_k_e_f_i_l_e' then `_M_a_k_e_f_i_l_e' in order to find
+ the specifications. If the file `_._d_e_p_e_n_d' exists, it is read, see
+ mkdep(1).
This manual page is intended as a reference document only. For a more
- thorough description of bmake and makefiles, please refer to PMake - A
- Tutorial.
+ thorough description of bbmmaakkee and makefiles, please refer to _P_M_a_k_e _- _A
+ _T_u_t_o_r_i_a_l (from 1993).
- bmake will prepend the contents of the MAKEFLAGS environment variable to
- the command line arguments before parsing them.
+ bbmmaakkee prepends the contents of the MAKEFLAGS environment variable to the
+ command line arguments before parsing them.
The options are as follows:
- -B Try to be backwards compatible by executing a single shell per
- command and by executing the commands to make the sources of a
- dependency line in sequence.
+ --BB Try to be backwards compatible by executing a single shell per
+ command and by making the sources of a dependency line in
+ sequence.
- -C directory
- Change to directory before reading the makefiles or doing any-
- thing else. If multiple -C options are specified, each is inter-
- preted relative to the previous one: -C / -C etc is equivalent to
- -C /etc.
+ --CC _d_i_r_e_c_t_o_r_y
+ Change to _d_i_r_e_c_t_o_r_y before reading the makefiles or doing
+ anything else. If multiple --CC options are specified, each is
+ interpreted relative to the previous one: --CC _/ --CC _e_t_c is
+ equivalent to --CC _/_e_t_c.
- -D variable
- Define variable to be 1, in the global scope.
+ --DD _v_a_r_i_a_b_l_e
+ Define _v_a_r_i_a_b_l_e to be 1, in the global scope.
- -d [-]flags
- Turn on debugging, and specify which portions of bmake are to
+ --dd [--]_f_l_a_g_s
+ Turn on debugging, and specify which portions of bbmmaakkee are to
print debugging information. Unless the flags are preceded by
- `-' they are added to the MAKEFLAGS environment variable and will
- be processed by any child make processes. By default, debugging
+ `-', they are added to the MAKEFLAGS environment variable and are
+ passed on to any child make processes. By default, debugging
information is printed to standard error, but this can be changed
- using the F debugging flag. The debugging output is always un-
- buffered; in addition, if debugging is enabled but debugging out-
- put is not directed to standard output, then the standard output
- is line buffered. Flags is one or more of the following:
+ using the FF debugging flag. The debugging output is always
+ unbuffered; in addition, if debugging is enabled but debugging
+ output is not directed to standard output, the standard output is
+ line buffered. The available _f_l_a_g_s are:
- A Print all possible debugging information; equivalent to
+ AA Print all possible debugging information; equivalent to
specifying all of the debugging flags.
- a Print debugging information about archive searching and
+ aa Print debugging information about archive searching and
caching.
- C Print debugging information about current working direc-
- tory.
+ CC Print debugging information about the current working
+ directory.
- c Print debugging information about conditional evaluation.
+ cc Print debugging information about conditional evaluation.
- d Print debugging information about directory searching and
+ dd Print debugging information about directory searching and
caching.
- e Print debugging information about failed commands and
+ ee Print debugging information about failed commands and
targets.
- F[+]filename
+ FF[++]_f_i_l_e_n_a_m_e
Specify where debugging output is written. This must be
the last flag, because it consumes the remainder of the
- argument. If the character immediately after the `F'
- flag is `+', then the file will be opened in append mode;
- otherwise the file will be overwritten. If the file name
- is `stdout' or `stderr' then debugging output will be
- written to the standard output or standard error output
- file descriptors respectively (and the `+' option has no
- effect). Otherwise, the output will be written to the
- named file. If the file name ends `.%d' then the `%d' is
- replaced by the pid.
+ argument. If the character immediately after the FF flag
+ is `+', the file is opened in append mode; otherwise the
+ file is overwritten. If the file name is `stdout' or
+ `stderr', debugging output is written to the standard
+ output or standard error output respectively (and the `+'
+ option has no effect). Otherwise, the output is written
+ to the named file. If the file name ends with `.%d', the
+ `%d' is replaced by the pid.
- f Print debugging information about loop evaluation.
+ ff Print debugging information about loop evaluation.
- g1 Print the input graph before making anything.
+ gg11 Print the input graph before making anything.
- g2 Print the input graph after making everything, or before
+ gg22 Print the input graph after making everything, or before
exiting on error.
- g3 Print the input graph before exiting on error.
+ gg33 Print the input graph before exiting on error.
- h Print debugging information about hash table operations.
+ hh Print debugging information about hash table operations.
- j Print debugging information about running multiple
+ jj Print debugging information about running multiple
shells.
- L Turn on lint checks. This will throw errors for variable
+ LL Turn on lint checks. This throws errors for variable
assignments that do not parse correctly, at the time of
- assignment so the file and line number are available.
+ assignment, so the file and line number are available.
- l Print commands in Makefiles regardless of whether or not
+ ll Print commands in Makefiles regardless of whether or not
they are prefixed by `@' or other "quiet" flags. Also
known as "loud" behavior.
- M Print debugging information about "meta" mode decisions
+ MM Print debugging information about "meta" mode decisions
about targets.
- m Print debugging information about making targets, includ-
- ing modification dates.
+ mm Print debugging information about making targets,
+ including modification dates.
- n Don't delete the temporary command scripts created when
+ nn Don't delete the temporary command scripts created when
running commands. These temporary scripts are created in
- the directory referred to by the TMPDIR environment vari-
- able, or in /tmp if TMPDIR is unset or set to the empty
- string. The temporary scripts are created by mkstemp(3),
- and have names of the form makeXXXXXX. NOTE: This can
- create many files in TMPDIR or /tmp, so use with care.
+ the directory referred to by the TMPDIR environment
+ variable, or in _/_t_m_p if TMPDIR is unset or set to the
+ empty string. The temporary scripts are created by
+ mkstemp(3), and have names of the form _m_a_k_e_X_X_X_X_X_X. _N_O_T_E:
+ This can create many files in TMPDIR or _/_t_m_p, so use with
+ care.
- p Print debugging information about makefile parsing.
+ pp Print debugging information about makefile parsing.
- s Print debugging information about suffix-transformation
+ ss Print debugging information about suffix-transformation
rules.
- t Print debugging information about target list mainte-
- nance.
+ tt Print debugging information about target list
+ maintenance.
- V Force the -V option to print raw values of variables,
+ VV Force the --VV option to print raw values of variables,
overriding the default behavior set via
- .MAKE.EXPAND_VARIABLES.
+ _._M_A_K_E_._E_X_P_A_N_D___V_A_R_I_A_B_L_E_S.
- v Print debugging information about variable assignment.
+ vv Print debugging information about variable assignment and
+ expansion.
- x Run shell commands with -x so the actual commands are
+ xx Run shell commands with --xx so the actual commands are
printed as they are executed.
- -e Specify that environment variables override macro assignments
- within makefiles.
+ --ee Let environment variables override global variables within
+ makefiles.
- -f makefile
- Specify a makefile to read instead of the default `makefile'. If
- makefile is `-', standard input is read. Multiple makefiles may
- be specified, and are read in the order specified.
+ --ff _m_a_k_e_f_i_l_e
+ Specify a makefile to read instead of the default _m_a_k_e_f_i_l_e or
+ _M_a_k_e_f_i_l_e. If _m_a_k_e_f_i_l_e is `-', standard input is read. Multiple
+ makefiles may be specified, and are read in the order specified.
- -I directory
+ --II _d_i_r_e_c_t_o_r_y
Specify a directory in which to search for makefiles and included
makefiles. The system makefile directory (or directories, see
- the -m option) is automatically included as part of this list.
+ the --mm option) is automatically included as part of this list.
- -i Ignore non-zero exit of shell commands in the makefile. Equiva-
- lent to specifying `-' before each command line in the makefile.
+ --ii Ignore non-zero exit of shell commands in the makefile.
+ Equivalent to specifying `-' before each command line in the
+ makefile.
- -J private
- This option should not be specified by the user.
+ --JJ _p_r_i_v_a_t_e
+ This option should _n_o_t be specified by the user.
- When the j option is in use in a recursive build, this option is
+ When the --jj option is in use in a recursive build, this option is
passed by a make to child makes to allow all the make processes
in the build to cooperate to avoid overloading the system.
- -j max_jobs
- Specify the maximum number of jobs that bmake may have running at
- any one time. The value is saved in .MAKE.JOBS. Turns compati-
- bility mode off, unless the B flag is also specified. When com-
- patibility mode is off, all commands associated with a target are
- executed in a single shell invocation as opposed to the tradi-
- tional one shell invocation per line. This can break traditional
- scripts which change directories on each command invocation and
- then expect to start with a fresh environment on the next line.
- It is more efficient to correct the scripts rather than turn
- backwards compatibility on.
-
- -k Continue processing after errors are encountered, but only on
+ --jj _m_a_x___j_o_b_s
+ Specify the maximum number of jobs that bbmmaakkee may have running at
+ any one time. If _m_a_x___j_o_b_s is a floating point number, or ends
+ with `C', then the value is multiplied by the number of CPUs
+ reported online by sysconf(3). The value of _m_a_x___j_o_b_s is saved in
+ _._M_A_K_E_._J_O_B_S. Turns compatibility mode off, unless the --BB option
+ is also specified. When compatibility mode is off, all commands
+ associated with a target are executed in a single shell
+ invocation as opposed to the traditional one shell invocation per
+ line. This can break traditional scripts which change
+ directories on each command invocation and then expect to start
+ with a fresh environment on the next line. It is more efficient
+ to correct the scripts rather than turn backwards compatibility
+ on.
+
+ A job token pool with _m_a_x___j_o_b_s tokens is used to control the
+ total number of jobs running. Each instance of bbmmaakkee will wait
+ for a token from the pool before running a new job.
+
+ --kk Continue processing after errors are encountered, but only on
those targets that do not depend on the target whose creation
caused the error.
- -m directory
- Specify a directory in which to search for sys.mk and makefiles
- included via the <file>-style include statement. The -m option
- can be used multiple times to form a search path. This path will
- override the default system include path: /usr/share/mk. Fur-
- thermore the system include path will be appended to the search
- path used for "file"-style include statements (see the -I op-
- tion).
-
- If a file or directory name in the -m argument (or the
- MAKESYSPATH environment variable) starts with the string ".../"
- then bmake will search for the specified file or directory named
- in the remaining part of the argument string. The search starts
- with the current directory of the Makefile and then works upward
- towards the root of the file system. If the search is success-
- ful, then the resulting directory replaces the ".../" specifica-
- tion in the -m argument. If used, this feature allows bmake to
- easily search in the current source tree for customized sys.mk
- files (e.g., by using ".../mk/sys.mk" as an argument).
-
- -n Display the commands that would have been executed, but do not
- actually execute them unless the target depends on the .MAKE spe-
- cial source (see below) or the command is prefixed with `+'.
-
- -N Display the commands which would have been executed, but do not
+ --mm _d_i_r_e_c_t_o_r_y
+ Specify a directory in which to search for _s_y_s_._m_k and makefiles
+ included via the <_f_i_l_e>-style include statement. The --mm option
+ can be used multiple times to form a search path. This path
+ overrides the default system include path _/_u_s_r_/_s_h_a_r_e_/_m_k.
+ Furthermore, the system include path is appended to the search
+ path used for "_f_i_l_e"-style include statements (see the --II
+ option). The system include path can be referenced via the read-
+ only variable _._S_Y_S_P_A_T_H.
+
+ If a directory name in the --mm argument (or the MAKESYSPATH
+ environment variable) starts with the string `.../', bbmmaakkee
+ searches for the specified file or directory named in the
+ remaining part of the argument string. The search starts with
+ the current directory and then works upward towards the root of
+ the file system. If the search is successful, the resulting
+ directory replaces the `.../' specification in the --mm argument.
+ This feature allows bbmmaakkee to easily search in the current source
+ tree for customized _s_y_s_._m_k files (e.g., by using `.../mk/sys.mk'
+ as an argument).
+
+ --nn Display the commands that would have been executed, but do not
+ actually execute them unless the target depends on the _._M_A_K_E
+ special source (see below) or the command is prefixed with `++'.
+
+ --NN Display the commands that would have been executed, but do not
actually execute any of them; useful for debugging top-level
makefiles without descending into subdirectories.
- -q Do not execute any commands, but exit 0 if the specified targets
- are up-to-date and 1, otherwise.
+ --qq Do not execute any commands, instead exit 0 if the specified
+ targets are up to date, and 1 otherwise.
- -r Do not use the built-in rules specified in the system makefile.
+ --rr Do not use the built-in rules specified in the system makefile.
- -S Stop processing if an error is encountered. This is the default
- behavior and the opposite of -k.
+ --SS Stop processing if an error is encountered. This is the default
+ behavior and the opposite of --kk.
- -s Do not echo any commands as they are executed. Equivalent to
- specifying `@' before each command line in the makefile.
+ --ss Do not echo any commands as they are executed. Equivalent to
+ specifying `@@' before each command line in the makefile.
- -T tracefile
- When used with the -j flag, append a trace record to tracefile
+ --TT _t_r_a_c_e_f_i_l_e
+ When used with the --jj flag, append a trace record to _t_r_a_c_e_f_i_l_e
for each job started and completed.
- -t Rather than re-building a target as specified in the makefile,
+ --tt Rather than re-building a target as specified in the makefile,
create it or update its modification time to make it appear up-
to-date.
- -V variable
- Print the value of variable. Do not build any targets. Multiple
- instances of this option may be specified; the variables will be
- printed one per line, with a blank line for each null or unde-
- fined variable. The value printed is extracted from the global
- scope after all makefiles have been read. By default, the raw
- variable contents (which may include additional unexpanded vari-
- able references) are shown. If variable contains a `$' then the
- value will be recursively expanded to its complete resultant text
- before printing. The expanded value will also be printed if
- .MAKE.EXPAND_VARIABLES is set to true and the -dV option has not
- been used to override it. Note that loop-local and target-local
- variables, as well as values taken temporarily by global vari-
- ables during makefile processing, are not accessible via this op-
- tion. The -dv debug mode can be used to see these at the cost of
- generating substantial extraneous output.
-
- -v variable
- Like -V but the variable is always expanded to its complete
- value.
-
- -W Treat any warnings during makefile parsing as errors.
-
- -w Print entering and leaving directory messages, pre and post pro-
- cessing.
-
- -X Don't export variables passed on the command line to the environ-
- ment individually. Variables passed on the command line are
- still exported via the MAKEFLAGS environment variable. This op-
- tion may be useful on systems which have a small limit on the
+ --VV _v_a_r_i_a_b_l_e
+ Print the value of _v_a_r_i_a_b_l_e. Do not build any targets. Multiple
+ instances of this option may be specified; the variables are
+ printed one per line, with a blank line for each null or
+ undefined variable. The value printed is extracted from the
+ global scope after all makefiles have been read.
+
+ By default, the raw variable contents (which may include
+ additional unexpanded variable references) are shown. If
+ _v_a_r_i_a_b_l_e contains a `$', it is not interpreted as a variable name
+ but rather as an expression. Its value is expanded before
+ printing. The value is also expanded before printing if
+ _._M_A_K_E_._E_X_P_A_N_D___V_A_R_I_A_B_L_E_S is set to true and the --ddVV option has not
+ been used to override it.
+
+ Note that loop-local and target-local variables, as well as
+ values taken temporarily by global variables during makefile
+ processing, are not accessible via this option. The --ddvv debug
+ mode can be used to see these at the cost of generating
+ substantial extraneous output.
+
+ --vv _v_a_r_i_a_b_l_e
+ Like --VV, but all printed variables are always expanded to their
+ complete value. The last occurrence of --VV or --vv decides whether
+ all variables are expanded or not.
+
+ --WW Treat any warnings during makefile parsing as errors.
+
+ --ww Print entering and leaving directory messages, pre and post
+ processing.
+
+ --XX Don't export variables passed on the command line to the
+ environment individually. Variables passed on the command line
+ are still exported via the MAKEFLAGS environment variable. This
+ option may be useful on systems which have a small limit on the
size of command arguments.
- variable=value
- Set the value of the variable variable to value. Normally, all
+ _v_a_r_i_a_b_l_e==_v_a_l_u_e
+ Set the value of the variable _v_a_r_i_a_b_l_e to _v_a_l_u_e. Normally, all
values passed on the command line are also exported to sub-makes
- in the environment. The -X flag disables this behavior. Vari-
- able assignments should follow options for POSIX compatibility
- but no ordering is enforced.
+ in the environment. The --XX flag disables this behavior.
+ Variable assignments should follow options for POSIX
+ compatibility but no ordering is enforced.
- There are seven different types of lines in a makefile: file dependency
+ There are several different types of lines in a makefile: dependency
specifications, shell commands, variable assignments, include statements,
- conditional directives, for loops, and comments.
+ conditional directives, for loops, other directives, and comments.
- In general, lines may be continued from one line to the next by ending
- them with a backslash (`\'). The trailing newline character and initial
- whitespace on the following line are compressed into a single space.
+ Lines may be continued from one line to the next by ending them with a
+ backslash (`\'). The trailing newline character and initial whitespace
+ on the following line are compressed into a single space.
-FILE DEPENDENCY SPECIFICATIONS
+FFIILLEE DDEEPPEENNDDEENNCCYY SSPPEECCIIFFIICCAATTIIOONNSS
Dependency lines consist of one or more targets, an operator, and zero or
more sources. This creates a relationship where the targets "depend" on
- the sources and are customarily created from them. A target is consid-
- ered out-of-date if it does not exist, or if its modification time is
- less than that of any of its sources. An out-of-date target will be re-
+ the sources and are customarily created from them. A target is
+ considered out of date if it does not exist, or if its modification time
+ is less than that of any of its sources. An out-of-date target is re-
created, but not until all sources have been examined and themselves re-
created as needed. Three operators may be used:
- : Many dependency lines may name this target but only one may have
+ :: Many dependency lines may name this target but only one may have
attached shell commands. All sources named in all dependency lines
are considered together, and if needed the attached shell commands
- are run to create or re-create the target. If bmake is inter-
- rupted, the target is removed.
+ are run to create or re-create the target. If bbmmaakkee is
+ interrupted, the target is removed.
- ! The same, but the target is always re-created whether or not it is
+ !! The same, but the target is always re-created whether or not it is
out of date.
- :: Any dependency line may have attached shell commands, but each one
- is handled independently: its sources are considered and the at-
- tached shell commands are run if the target is out of date with re-
- spect to (only) those sources. Thus, different groups of the at-
- tached shell commands may be run depending on the circumstances.
- Furthermore, unlike :, for dependency lines with no sources, the
- attached shell commands are always run. Also unlike :, the target
- will not be removed if bmake is interrupted.
- All dependency lines mentioning a particular target must use the same op-
- erator.
+ :::: Any dependency line may have attached shell commands, but each one
+ is handled independently: its sources are considered and the
+ attached shell commands are run if the target is out of date with
+ respect to (only) those sources. Thus, different groups of the
+ attached shell commands may be run depending on the circumstances.
+ Furthermore, unlike ::, for dependency lines with no sources, the
+ attached shell commands are always run. Also unlike ::, the target
+ is not removed if bbmmaakkee is interrupted.
+
+ All dependency lines mentioning a particular target must use the same
+ operator.
Targets and sources may contain the shell wildcard values `?', `*', `[]',
and `{}'. The values `?', `*', and `[]' may only be used as part of the
- final component of the target or source, and must be used to describe ex-
- isting files. The value `{}' need not necessarily be used to describe
- existing files. Expansion is in directory order, not alphabetically as
- done in the shell.
-
-SHELL COMMANDS
- Each target may have associated with it one or more lines of shell com-
- mands, normally used to create the target. Each of the lines in this
- script must be preceded by a tab. (For historical reasons, spaces are
- not accepted.) While targets can appear in many dependency lines if de-
- sired, by default only one of these rules may be followed by a creation
- script. If the `::' operator is used, however, all rules may include
- scripts and the scripts are executed in the order found.
+ final component of the target or source, and only match existing files.
+ The value `{}' need not necessarily be used to describe existing files.
+ Expansion is in directory order, not alphabetically as done in the shell.
+
+SSHHEELLLL CCOOMMMMAANNDDSS
+ Each target may have associated with it one or more lines of shell
+ commands, normally used to create the target. Each of the lines in this
+ script _m_u_s_t be preceded by a tab. (For historical reasons, spaces are
+ not accepted.) While targets can occur in many dependency lines if
+ desired, by default only one of these rules may be followed by a creation
+ script. If the `::::' operator is used, however, all rules may include
+ scripts, and the respective scripts are executed in the order found.
Each line is treated as a separate shell command, unless the end of line
- is escaped with a backslash (`\') in which case that line and the next
- are combined. If the first characters of the command are any combination
- of `@', `+', or `-', the command is treated specially. A `@' causes the
- command not to be echoed before it is executed. A `+' causes the command
- to be executed even when -n is given. This is similar to the effect of
- the .MAKE special source, except that the effect can be limited to a sin-
- gle line of a script. A `-' in compatibility mode causes any non-zero
- exit status of the command line to be ignored.
-
- When bmake is run in jobs mode with -j max_jobs, the entire script for
+ is escaped with a backslash `\', in which case that line and the next are
+ combined. If the first characters of the command are any combination of
+ `@@', `++', or `--', the command is treated specially.
+
+ @@ causes the command not to be echoed before it is executed.
+
+ ++ causes the command to be executed even when --nn is given.
+ This is similar to the effect of the _._M_A_K_E special source,
+ except that the effect can be limited to a single line of a
+ script.
+
+ -- in compatibility mode causes any non-zero exit status of
+ the command line to be ignored.
+
+ When bbmmaakkee is run in jobs mode with --jj _m_a_x___j_o_b_s, the entire script for
the target is fed to a single instance of the shell. In compatibility
- (non-jobs) mode, each command is run in a separate process. If the com-
- mand contains any shell meta characters (`#=|^(){};&<>*?[]:$`\\n') it
- will be passed to the shell; otherwise bmake will attempt direct execu-
- tion. If a line starts with `-' and the shell has ErrCtl enabled then
- failure of the command line will be ignored as in compatibility mode.
- Otherwise `-' affects the entire job; the script will stop at the first
- command line that fails, but the target will not be deemed to have
- failed.
-
- Makefiles should be written so that the mode of bmake operation does not
- change their behavior. For example, any command which needs to use "cd"
- or "chdir" without potentially changing the directory for subsequent com-
- mands should be put in parentheses so it executes in a subshell. To
- force the use of one shell, escape the line breaks so as to make the
+ (non-jobs) mode, each command is run in a separate process. If the
+ command contains any shell meta characters (`#=|^(){};&<>*?[]:$`\\n'), it
+ is passed to the shell; otherwise bbmmaakkee attempts direct execution. If a
+ line starts with `--' and the shell has ErrCtl enabled, failure of the
+ command line is ignored as in compatibility mode. Otherwise `--' affects
+ the entire job; the script stops at the first command line that fails,
+ but the target is not deemed to have failed.
+
+ Makefiles should be written so that the mode of bbmmaakkee operation does not
+ change their behavior. For example, any command which uses "cd" or
+ "chdir" without the intention of changing the directory for subsequent
+ commands should be put in parentheses so it executes in a subshell. To
+ force the use of a single shell, escape the line breaks so as to make the
whole script one command. For example:
avoid-chdir-side-effects:
- @echo Building $@ in `pwd`
+ @echo "Building $@ in $$(pwd)"
@(cd ${.CURDIR} && ${MAKE} $@)
- @echo Back in `pwd`
+ @echo "Back in $$(pwd)"
ensure-one-shell-regardless-of-mode:
- @echo Building $@ in `pwd`; \
+ @echo "Building $@ in $$(pwd)"; \
(cd ${.CURDIR} && ${MAKE} $@); \
- echo Back in `pwd`
+ echo "Back in $$(pwd)"
+
+ Since bbmmaakkee changes the current working directory to `_._O_B_J_D_I_R' before
+ executing any targets, each child process starts with that as its current
+ working directory.
+
+VVAARRIIAABBLLEE AASSSSIIGGNNMMEENNTTSS
+ Variables in make behave much like macros in the C preprocessor.
+
+ Variable assignments have the form `_N_A_M_E _o_p _v_a_l_u_e', where:
+
+ _N_A_M_E is a single-word variable name, consisting, by tradition,
+ of all upper-case letters,
+
+ _o_p is one of the variable assignment operators described
+ below, and
- Since bmake will chdir(2) to `.OBJDIR' before executing any targets, each
- child process starts with that as its current working directory.
+ _v_a_l_u_e is interpreted according to the variable assignment
+ operator.
-VARIABLE ASSIGNMENTS
- Variables in make are much like variables in the shell, and, by tradi-
- tion, consist of all upper-case letters.
+ Whitespace around _N_A_M_E, _o_p and _v_a_l_u_e is discarded.
- Variable assignment modifiers
- The five operators that can be used to assign values to variables are as
- follows:
+ VVaarriiaabbllee aassssiiggnnmmeenntt ooppeerraattoorrss
+ The five operators that assign values to variables are:
- = Assign the value to the variable. Any previous value is overrid-
- den.
+ == Assign the value to the variable. Any previous value is
+ overwritten.
- += Append the value to the current value of the variable.
+ ++== Append the value to the current value of the variable, separating
+ them by a single space.
- ?= Assign the value to the variable if it is not already defined.
+ ??== Assign the value to the variable if it is not already defined.
- := Assign with expansion, i.e. expand the value before assigning it
- to the variable. Normally, expansion is not done until the vari-
- able is referenced. NOTE: References to undefined variables are
- not expanded. This can cause problems when variable modifiers
- are used.
+ ::== Expand the value, then assign it to the variable.
- != Expand the value and pass it to the shell for execution and as-
- sign the result to the variable. Any newlines in the result are
- replaced with spaces.
+ _N_O_T_E: References to undefined variables are _n_o_t expanded. This
+ can cause problems when variable modifiers are used.
- Any white-space before the assigned value is removed; if the value is be-
- ing appended, a single space is inserted between the previous contents of
- the variable and the appended value.
+ !!== Expand the value and pass it to the shell for execution, then
+ assign the output from the child's standard output to the
+ variable. Any newlines in the result are replaced with spaces.
- Variables are expanded by surrounding the variable name with either curly
- braces (`{}') or parentheses (`()') and preceding it with a dollar sign
- (`$'). If the variable name contains only a single letter, the surround-
- ing braces or parentheses are not required. This shorter form is not
+ EExxppaannssiioonn ooff vvaarriiaabblleess
+ In most contexts where variables are expanded, `$$' expands to a single
+ dollar sign. In other contexts (most variable modifiers, string literals
+ in conditions), `\$' expands to a single dollar sign.
+
+ References to variables have the form $${{_n_a_m_e[::_m_o_d_i_f_i_e_r_s]}} or
+ $$((_n_a_m_e[::_m_o_d_i_f_i_e_r_s])). If the variable name consists of only a single
+ character and the expression contains no modifiers, the surrounding curly
+ braces or parentheses are not required. This shorter form is not
recommended.
- If the variable name contains a dollar, then the name itself is expanded
- first. This allows almost arbitrary variable names, however names con-
- taining dollar, braces, parentheses, or whitespace are really best
- avoided!
+ If the variable name contains a dollar, the name itself is expanded
+ first. This allows almost arbitrary variable names, however names
+ containing dollar, braces, parentheses or whitespace are really best
+ avoided.
- If the result of expanding a variable contains a dollar sign (`$') the
- string is expanded again.
+ If the result of expanding a nested variable expression contains a dollar
+ sign (`$'), the result is subject to further expansion.
- Variable substitution occurs at three distinct times, depending on where
+ Variable substitution occurs at four distinct times, depending on where
the variable is being used.
1. Variables in dependency lines are expanded as the line is read.
- 2. Variables in shell commands are expanded when the shell command is
- executed.
+ 2. Variables in conditionals are expanded individually, but only as far
+ as necessary to determine the result of the conditional.
- 3. ".for" loop index variables are expanded on each loop iteration.
- Note that other variables are not expanded inside loops so the fol-
- lowing example code:
+ 3. Variables in shell commands are expanded when the shell command is
+ executed.
+ 4. ..ffoorr loop index variables are expanded on each loop iteration. Note
+ that other variables are not expanded when composing the body of a
+ loop, so the following example code:
.for i in 1 2 3
a+= ${i}
@@ -418,21 +452,27 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
@echo ${a}
@echo ${b}
- will print:
+ prints:
1 2 3
3 3 3
- Because while ${a} contains "1 2 3" after the loop is executed, ${b}
- contains "${j} ${j} ${j}" which expands to "3 3 3" since after the
- loop completes ${j} contains "3".
+ After the loop is executed:
+
+ _a contains `${:U1} ${:U2} ${:U3}', which expands to `1 2
+ 3'.
+
+ _j contains `${:U3}', which expands to `3'.
- Variable classes
- The four different classes of variables (in order of increasing prece-
- dence) are:
+ _b contains `${j} ${j} ${j}', which expands to `${:U3}
+ ${:U3} ${:U3}' and further to `3 3 3'.
+
+ VVaarriiaabbllee ccllaasssseess
+ The four different classes of variables (in order of increasing
+ precedence) are:
Environment variables
- Variables defined as part of bmake's environment.
+ Variables defined as part of bbmmaakkee's environment.
Global variables
Variables defined in the makefile or in included makefiles.
@@ -443,377 +483,477 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
Local variables
Variables that are defined specific to a certain target.
- Local variables are all built in and their values vary magically from
- target to target. It is not currently possible to define new local vari-
- ables. The seven local variables are as follows:
+ Local variables can be set on a dependency line, unless
+ _._M_A_K_E_._T_A_R_G_E_T___L_O_C_A_L___V_A_R_I_A_B_L_E_S is set to `false'. The rest of the line
+ (which already has had global variables expanded) is the variable value.
+ For example:
+
+ COMPILER_WRAPPERS= ccache distcc icecc
+
+ ${OBJS}: .MAKE.META.CMP_FILTER=${COMPILER_WRAPPERS:S,^,N,}
+
+ Only the targets `${OBJS}' are impacted by that filter (in "meta" mode)
+ and simply enabling/disabling any of the compiler wrappers does not
+ render all of those targets out-of-date.
- .ALLSRC The list of all sources for this target; also known as
- `>'.
+ _N_O_T_E: target-local variable assignments behave differently in that;
- .ARCHIVE The name of the archive file; also known as `!'.
+ ++== Only appends to a previous local assignment for the same
+ target and variable.
- .IMPSRC In suffix-transformation rules, the name/path of the
- source from which the target is to be transformed (the
- "implied" source); also known as `<'. It is not defined
- in explicit rules.
+ ::== Is redundant with respect to global variables, which have
+ already been expanded.
- .MEMBER The name of the archive member; also known as `%'.
+ The seven built-in local variables are:
- .OODATE The list of sources for this target that were deemed out-
- of-date; also known as `?'.
+ _._A_L_L_S_R_C The list of all sources for this target; also known
+ as `_>'.
- .PREFIX The file prefix of the target, containing only the file
- portion, no suffix or preceding directory components;
- also known as `*'. The suffix must be one of the known
- suffixes declared with .SUFFIXES or it will not be recog-
- nized.
+ _._A_R_C_H_I_V_E The name of the archive file; also known as `_!'.
- .TARGET The name of the target; also known as `@'. For compati-
- bility with other makes this is an alias for .ARCHIVE in
- archive member rules.
+ _._I_M_P_S_R_C In suffix-transformation rules, the name/path of the
+ source from which the target is to be transformed
+ (the "implied" source); also known as `_<'. It is not
+ defined in explicit rules.
- The shorter forms (`>', `!', `<', `%', `?', `*', and `@') are permitted
+ _._M_E_M_B_E_R The name of the archive member; also known as `_%'.
+
+ _._O_O_D_A_T_E The list of sources for this target that were deemed
+ out-of-date; also known as `_?'.
+
+ _._P_R_E_F_I_X The name of the target with suffix (if declared in
+ ..SSUUFFFFIIXXEESS) removed; also known as `_*'.
+
+ _._T_A_R_G_E_T The name of the target; also known as `_@'. For
+ compatibility with other makes this is an alias for
+ _._A_R_C_H_I_V_E in archive member rules.
+
+ The shorter forms (`_>', `_!', `_<', `_%', `_?', `_*', and `_@') are permitted
for backward compatibility with historical makefiles and legacy POSIX
make and are not recommended.
Variants of these variables with the punctuation followed immediately by
- `D' or `F', e.g. `$(@D)', are legacy forms equivalent to using the `:H'
+ `D' or `F', e.g. `$(@D)', are legacy forms equivalent to using the `:H'
and `:T' modifiers. These forms are accepted for compatibility with AT&T
System V UNIX makefiles and POSIX but are not recommended.
Four of the local variables may be used in sources on dependency lines
because they expand to the proper value for each target on the line.
- These variables are `.TARGET', `.PREFIX', `.ARCHIVE', and `.MEMBER'.
-
- Additional built-in variables
- In addition, bmake sets or knows about the following variables:
-
- $ A single dollar sign `$', i.e. `$$' expands to a single
- dollar sign.
-
- .ALLTARGETS The list of all targets encountered in the Makefile. If
- evaluated during Makefile parsing, lists only those tar-
- gets encountered thus far.
-
- .CURDIR A path to the directory where bmake was executed. Refer
- to the description of `PWD' for more details.
-
- .INCLUDEDFROMDIR
- The directory of the file this Makefile was included
- from.
-
- .INCLUDEDFROMFILE
- The filename of the file this Makefile was included from.
-
- MAKE The name that bmake was executed with (argv[0]). For
- compatibility bmake also sets .MAKE with the same value.
- The preferred variable to use is the environment variable
- MAKE because it is more compatible with other versions of
- bmake and cannot be confused with the special target with
- the same name.
-
- .MAKE.DEPENDFILE
- Names the makefile (default `.depend') from which gener-
- ated dependencies are read.
-
- .MAKE.EXPAND_VARIABLES
- A boolean that controls the default behavior of the -V
- option. If true, variable values printed with -V are
- fully expanded; if false, the raw variable contents
- (which may include additional unexpanded variable refer-
- ences) are shown.
-
- .MAKE.EXPORTED The list of variables exported by bmake.
-
- .MAKE.JOBS The argument to the -j option.
-
- .MAKE.JOB.PREFIX
- If bmake is run with j then output for each target is
- prefixed with a token `--- target ---' the first part of
- which can be controlled via .MAKE.JOB.PREFIX. If
- .MAKE.JOB.PREFIX is empty, no token is printed.
- For example:
- .MAKE.JOB.PREFIX=${.newline}---${.MAKE:T}[${.MAKE.PID}]
- would produce tokens like `---make[1234] target ---' mak-
- ing it easier to track the degree of parallelism being
- achieved.
-
- MAKEFLAGS The environment variable `MAKEFLAGS' may contain anything
- that may be specified on bmake's command line. Anything
- specified on bmake's command line is appended to the
- `MAKEFLAGS' variable which is then entered into the envi-
- ronment for all programs which bmake executes.
-
- .MAKE.LEVEL The recursion depth of bmake. The initial instance of
- bmake will be 0, and an incremented value is put into the
- environment to be seen by the next generation. This al-
- lows tests like: .if ${.MAKE.LEVEL} == 0 to protect
- things which should only be evaluated in the initial in-
- stance of bmake.
-
- .MAKE.MAKEFILE_PREFERENCE
- The ordered list of makefile names (default `makefile',
- `Makefile') that bmake will look for.
-
- .MAKE.MAKEFILES
- The list of makefiles read by bmake, which is useful for
- tracking dependencies. Each makefile is recorded only
- once, regardless of the number of times read.
-
- .MAKE.MODE Processed after reading all makefiles. Can affect the
- mode that bmake runs in. It can contain a number of key-
- words:
-
- compat Like -B, puts bmake into "compat"
- mode.
-
- meta Puts bmake into "meta" mode, where
- meta files are created for each tar-
- get to capture the command run, the
- output generated and if filemon(4)
- is available, the system calls which
- are of interest to bmake. The cap-
- tured output can be very useful when
- diagnosing errors.
-
- curdirOk= bf Normally bmake will not create .meta
- files in `.CURDIR'. This can be
- overridden by setting bf to a value
- which represents True.
-
- missing-meta= bf If bf is True, then a missing .meta
- file makes the target out-of-date.
-
- missing-filemon= bf If bf is True, then missing filemon
- data makes the target out-of-date.
-
- nofilemon Do not use filemon(4).
-
- env For debugging, it can be useful to
- include the environment in the .meta
- file.
-
- verbose If in "meta" mode, print a clue
- about the target being built. This
- is useful if the build is otherwise
- running silently. The message
- printed the value of:
- .MAKE.META.PREFIX.
-
- ignore-cmd Some makefiles have commands which
- are simply not stable. This keyword
- causes them to be ignored for deter-
- mining whether a target is out of
- date in "meta" mode. See also
- .NOMETA_CMP.
-
- silent= bf If bf is True, when a .meta file is
- created, mark the target .SILENT.
-
- .MAKE.META.BAILIWICK
- In "meta" mode, provides a list of prefixes which match
- the directories controlled by bmake. If a file that was
- generated outside of .OBJDIR but within said bailiwick is
- missing, the current target is considered out-of-date.
-
- .MAKE.META.CREATED
- In "meta" mode, this variable contains a list of all the
- meta files updated. If not empty, it can be used to
- trigger processing of .MAKE.META.FILES.
-
- .MAKE.META.FILES
- In "meta" mode, this variable contains a list of all the
- meta files used (updated or not). This list can be used
- to process the meta files to extract dependency informa-
- tion.
-
- .MAKE.META.IGNORE_PATHS
- Provides a list of path prefixes that should be ignored;
- because the contents are expected to change over time.
- The default list includes: `/dev /etc /proc /tmp /var/run
- /var/tmp'
-
- .MAKE.META.IGNORE_PATTERNS
- Provides a list of patterns to match against pathnames.
- Ignore any that match.
-
- .MAKE.META.IGNORE_FILTER
- Provides a list of variable modifiers to apply to each
- pathname. Ignore if the expansion is an empty string.
-
- .MAKE.META.PREFIX
- Defines the message printed for each meta file updated in
- "meta verbose" mode. The default value is:
- Building ${.TARGET:H:tA}/${.TARGET:T}
-
- .MAKEOVERRIDES This variable is used to record the names of variables
- assigned to on the command line, so that they may be ex-
- ported as part of `MAKEFLAGS'. This behavior can be dis-
- abled by assigning an empty value to `.MAKEOVERRIDES'
- within a makefile. Extra variables can be exported from
- a makefile by appending their names to `.MAKEOVERRIDES'.
- `MAKEFLAGS' is re-exported whenever `.MAKEOVERRIDES' is
- modified.
-
- .MAKE.PATH_FILEMON
- If bmake was built with filemon(4) support, this is set
- to the path of the device node. This allows makefiles to
- test for this support.
-
- .MAKE.PID The process-id of bmake.
-
- .MAKE.PPID The parent process-id of bmake.
-
- .MAKE.SAVE_DOLLARS
- value should be a boolean that controls whether `$$' are
- preserved when doing `:=' assignments. The default is
- false, for backwards compatibility. Set to true for com-
- patability with other makes. If set to false, `$$' be-
- comes `$' per normal evaluation rules.
-
- .MAKE.UID The user-id running bmake.
-
- .MAKE.GID The group-id running bmake.
-
- MAKE_PRINT_VAR_ON_ERROR
- When bmake stops due to an error, it sets `.ERROR_TARGET'
- to the name of the target that failed, `.ERROR_CMD' to
- the commands of the failed target, and in "meta" mode, it
- also sets `.ERROR_CWD' to the getcwd(3), and
- `.ERROR_META_FILE' to the path of the meta file (if any)
- describing the failed target. It then prints its name
- and the value of `.CURDIR' as well as the value of any
- variables named in `MAKE_PRINT_VAR_ON_ERROR'.
-
- .newline This variable is simply assigned a newline character as
- its value. This allows expansions using the :@ modifier
- to put a newline between iterations of the loop rather
- than a space. For example, the printing of
- `MAKE_PRINT_VAR_ON_ERROR' could be done as
- ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}.
-
- .OBJDIR A path to the directory where the targets are built. Its
- value is determined by trying to chdir(2) to the follow-
- ing directories in order and using the first match:
-
- 1. ${MAKEOBJDIRPREFIX}${.CURDIR}
-
- (Only if `MAKEOBJDIRPREFIX' is set in the environ-
- ment or on the command line.)
-
- 2. ${MAKEOBJDIR}
-
- (Only if `MAKEOBJDIR' is set in the environment or
- on the command line.)
-
- 3. ${.CURDIR}/obj.${MACHINE}
-
- 4. ${.CURDIR}/obj
-
- 5. /usr/obj/${.CURDIR}
-
- 6. ${.CURDIR}
-
- Variable expansion is performed on the value before it's
- used, so expressions such as
- ${.CURDIR:S,^/usr/src,/var/obj,}
- may be used. This is especially useful with
- `MAKEOBJDIR'.
-
- `.OBJDIR' may be modified in the makefile via the special
- target `.OBJDIR'. In all cases, bmake will chdir(2) to
- the specified directory if it exists, and set `.OBJDIR'
- and `PWD' to that directory before executing any targets.
-
- Except in the case of an explicit `.OBJDIR' target, bmake
- will check that the specified directory is writable and
- ignore it if not. This check can be skipped by setting
- the environment variable `MAKE_OBJDIR_CHECK_WRITABLE' to
- "no".
+ These variables are `_._T_A_R_G_E_T', `_._P_R_E_F_I_X', `_._A_R_C_H_I_V_E', and `_._M_E_M_B_E_R'.
+
+ AAddddiittiioonnaall bbuuiilltt--iinn vvaarriiaabblleess
+ In addition, bbmmaakkee sets or knows about the following variables:
+
+ _._A_L_L_T_A_R_G_E_T_S
+ The list of all targets encountered in the makefiles. If
+ evaluated during makefile parsing, lists only those targets
+ encountered thus far.
+
+ _._C_U_R_D_I_R
+ A path to the directory where bbmmaakkee was executed. Refer to the
+ description of `_P_W_D' for more details.
+
+ _._E_R_R_O_R___C_M_D
+ Is used in error handling, see _M_A_K_E___P_R_I_N_T___V_A_R___O_N___E_R_R_O_R.
+
+ _._E_R_R_O_R___C_W_D
+ Is used in error handling, see _M_A_K_E___P_R_I_N_T___V_A_R___O_N___E_R_R_O_R.
+
+ _._E_R_R_O_R___E_X_I_T
+ Is used in error handling, see _M_A_K_E___P_R_I_N_T___V_A_R___O_N___E_R_R_O_R.
+
+ _._E_R_R_O_R___M_E_T_A___F_I_L_E
+ Is used in error handling in "meta" mode, see
+ _M_A_K_E___P_R_I_N_T___V_A_R___O_N___E_R_R_O_R.
+
+ _._E_R_R_O_R___T_A_R_G_E_T
+ Is used in error handling, see _M_A_K_E___P_R_I_N_T___V_A_R___O_N___E_R_R_O_R.
+
+ _._I_N_C_L_U_D_E_D_F_R_O_M_D_I_R
+ The directory of the file this makefile was included from.
+
+ _._I_N_C_L_U_D_E_D_F_R_O_M_F_I_L_E
+ The filename of the file this makefile was included from.
+
+ _M_A_C_H_I_N_E
+ The machine hardware name, see uname(1).
+
+ _M_A_C_H_I_N_E___A_R_C_H
+ The machine processor architecture name, see uname(1).
+
+ _M_A_K_E The name that bbmmaakkee was executed with (_a_r_g_v_[_0_]).
+
+ _._M_A_K_E The same as _M_A_K_E, for compatibility. The preferred variable to
+ use is the environment variable MAKE because it is more
+ compatible with other make variants and cannot be confused with
+ the special target with the same name.
+
+ _._M_A_K_E_._D_E_P_E_N_D_F_I_L_E
+ Names the makefile (default `_._d_e_p_e_n_d') from which generated
+ dependencies are read.
+
+ _._M_A_K_E_._D_I_E___Q_U_I_E_T_L_Y
+ If set to `true', do not print error information at the end.
+
+ _._M_A_K_E_._E_X_P_A_N_D___V_A_R_I_A_B_L_E_S
+ A boolean that controls the default behavior of the --VV option.
+ If true, variable values printed with --VV are fully expanded; if
+ false, the raw variable contents (which may include additional
+ unexpanded variable references) are shown.
+
+ _._M_A_K_E_._E_X_P_O_R_T_E_D
+ The list of variables exported by bbmmaakkee.
+
+ _M_A_K_E_F_I_L_E
+ The top-level makefile that is currently read, as given in the
+ command line.
+
+ _._M_A_K_E_F_L_A_G_S
+ The environment variable `MAKEFLAGS' may contain anything that
+ may be specified on bbmmaakkee's command line. Anything specified on
+ bbmmaakkee's command line is appended to the _._M_A_K_E_F_L_A_G_S variable,
+ which is then added to the environment for all programs that
+ bbmmaakkee executes.
+
+ _._M_A_K_E_._G_I_D
+ The numeric group ID of the user running bbmmaakkee. It is read-only.
+
+ _._M_A_K_E_._J_O_B_._P_R_E_F_I_X
+ If bbmmaakkee is run with --jj, the output for each target is prefixed
+ with a token
+ --- _t_a_r_g_e_t ---
+ the first part of which can be controlled via _._M_A_K_E_._J_O_B_._P_R_E_F_I_X.
+ If _._M_A_K_E_._J_O_B_._P_R_E_F_I_X is empty, no token is printed. For example,
+ setting _._M_A_K_E_._J_O_B_._P_R_E_F_I_X to
+ `${.newline}---${.MAKE:T}[${.MAKE.PID}]' would produce tokens
+ like
+ ---make[1234] _t_a_r_g_e_t ---
+ making it easier to track the degree of parallelism being
+ achieved.
+
+ _._M_A_K_E_._J_O_B_S
+ The argument to the --jj option.
+
+ _._M_A_K_E_._J_O_B_S_._C
+ A read-only boolean that indicates whether the --jj option supports
+ use of `C'.
+
+ _._M_A_K_E_._L_E_V_E_L
+ The recursion depth of bbmmaakkee. The top-level instance of bbmmaakkee
+ has level 0, and each child make has its parent level plus 1.
+ This allows tests like: .if ${.MAKE.LEVEL} == 0 to protect things
+ which should only be evaluated in the top-level instance of
+ bbmmaakkee.
+
+ _._M_A_K_E_._L_E_V_E_L_._E_N_V
+ The name of the environment variable that stores the level of
+ nested calls to bbmmaakkee.
+
+ _._M_A_K_E_._M_A_K_E_F_I_L_E___P_R_E_F_E_R_E_N_C_E
+ The ordered list of makefile names (default `_m_a_k_e_f_i_l_e',
+ `_M_a_k_e_f_i_l_e') that bbmmaakkee looks for.
+
+ _._M_A_K_E_._M_A_K_E_F_I_L_E_S
+ The list of makefiles read by bbmmaakkee, which is useful for tracking
+ dependencies. Each makefile is recorded only once, regardless of
+ the number of times read.
+
+ _._M_A_K_E_._M_E_T_A_._B_A_I_L_I_W_I_C_K
+ In "meta" mode, provides a list of prefixes which match the
+ directories controlled by bbmmaakkee. If a file that was generated
+ outside of _._O_B_J_D_I_R but within said bailiwick is missing, the
+ current target is considered out-of-date.
+
+ _._M_A_K_E_._M_E_T_A_._C_M_P___F_I_L_T_E_R
+ In "meta" mode, it can (very rarely!) be useful to filter command
+ lines before comparison. This variable can be set to a set of
+ modifiers that are applied to each line of the old and new
+ command that differ, if the filtered commands still differ, the
+ target is considered out-of-date.
+
+ _._M_A_K_E_._M_E_T_A_._C_R_E_A_T_E_D
+ In "meta" mode, this variable contains a list of all the meta
+ files updated. If not empty, it can be used to trigger
+ processing of _._M_A_K_E_._M_E_T_A_._F_I_L_E_S.
+
+ _._M_A_K_E_._M_E_T_A_._F_I_L_E_S
+ In "meta" mode, this variable contains a list of all the meta
+ files used (updated or not). This list can be used to process
+ the meta files to extract dependency information.
+
+ _._M_A_K_E_._M_E_T_A_._I_G_N_O_R_E___F_I_L_T_E_R
+ Provides a list of variable modifiers to apply to each pathname.
+ Ignore if the expansion is an empty string.
+
+ _._M_A_K_E_._M_E_T_A_._I_G_N_O_R_E___P_A_T_H_S
+ Provides a list of path prefixes that should be ignored; because
+ the contents are expected to change over time. The default list
+ includes: `_/_d_e_v _/_e_t_c _/_p_r_o_c _/_t_m_p _/_v_a_r_/_r_u_n _/_v_a_r_/_t_m_p'
+
+ _._M_A_K_E_._M_E_T_A_._I_G_N_O_R_E___P_A_T_T_E_R_N_S
+ Provides a list of patterns to match against pathnames. Ignore
+ any that match.
+
+ _._M_A_K_E_._M_E_T_A_._P_R_E_F_I_X
+ Defines the message printed for each meta file updated in "meta
+ verbose" mode. The default value is:
+ Building ${.TARGET:H:tA}/${.TARGET:T}
+
+ _._M_A_K_E_._M_O_D_E
+ Processed after reading all makefiles. Affects the mode that
+ bbmmaakkee runs in. It can contain these keywords:
+
+ ccoommppaatt Like --BB, puts bbmmaakkee into "compat" mode.
+
+ mmeettaa Puts bbmmaakkee into "meta" mode, where meta files are created
+ for each target to capture the command run, the output
+ generated, and if filemon(4) is available, the system
+ calls which are of interest to bbmmaakkee. The captured
+ output can be useful when diagnosing errors.
+
+ ccuurrddiirrOOkk==_b_f
+ By default, bbmmaakkee does not create _._m_e_t_a files in
+ `_._C_U_R_D_I_R'. This can be overridden by setting _b_f to a
+ value which represents true.
+
+ mmiissssiinngg--mmeettaa==_b_f
+ If _b_f is true, a missing _._m_e_t_a file makes the target out-
+ of-date.
+
+ mmiissssiinngg--ffiilleemmoonn==_b_f
+ If _b_f is true, missing filemon data makes the target out-
+ of-date.
+
+ nnooffiilleemmoonn
+ Do not use filemon(4).
+
+ eennvv For debugging, it can be useful to include the
+ environment in the _._m_e_t_a file.
+
+ vveerrbboossee
+ If in "meta" mode, print a clue about the target being
+ built. This is useful if the build is otherwise running
+ silently. The message printed is the expanded value of
+ _._M_A_K_E_._M_E_T_A_._P_R_E_F_I_X.
+
+ iiggnnoorree--ccmmdd
+ Some makefiles have commands which are simply not stable.
+ This keyword causes them to be ignored for determining
+ whether a target is out of date in "meta" mode. See also
+ ..NNOOMMEETTAA__CCMMPP.
+
+ ssiilleenntt==_b_f
+ If _b_f is true, when a .meta file is created, mark the
+ target ..SSIILLEENNTT.
+
+ rraannddoommiizzee--ttaarrggeettss
+ In both compat and parallel mode, do not make the targets
+ in the usual order, but instead randomize their order.
+ This mode can be used to detect undeclared dependencies
+ between files.
+
+ _M_A_K_E_O_B_J_D_I_R
+ Used to create files in a separate directory, see _._O_B_J_D_I_R.
+
+ _M_A_K_E___O_B_J_D_I_R___C_H_E_C_K___W_R_I_T_A_B_L_E
+ Used to force a separate directory for the created files, even if
+ that directory is not writable, see _._O_B_J_D_I_R.
+
+ _M_A_K_E_O_B_J_D_I_R_P_R_E_F_I_X
+ Used to create files in a separate directory, see _._O_B_J_D_I_R.
+
+ _._M_A_K_E_._O_S
+ The name of the operating system, see uname(1). It is read-only.
+
+ _._M_A_K_E_O_V_E_R_R_I_D_E_S
+ This variable is used to record the names of variables assigned
+ to on the command line, so that they may be exported as part of
+ `MAKEFLAGS'. This behavior can be disabled by assigning an empty
+ value to `_._M_A_K_E_O_V_E_R_R_I_D_E_S' within a makefile. Extra variables can
+ be exported from a makefile by appending their names to
+ `_._M_A_K_E_O_V_E_R_R_I_D_E_S'. `MAKEFLAGS' is re-exported whenever
+ `_._M_A_K_E_O_V_E_R_R_I_D_E_S' is modified.
+
+ _._M_A_K_E_._P_A_T_H___F_I_L_E_M_O_N
+ If bbmmaakkee was built with filemon(4) support, this is set to the
+ path of the device node. This allows makefiles to test for this
+ support.
+
+ _._M_A_K_E_._P_I_D
+ The process ID of bbmmaakkee. It is read-only.
+
+ _._M_A_K_E_._P_P_I_D
+ The parent process ID of bbmmaakkee. It is read-only.
+
+ _M_A_K_E___P_R_I_N_T___V_A_R___O_N___E_R_R_O_R
+ When bbmmaakkee stops due to an error, it sets `_._E_R_R_O_R___T_A_R_G_E_T' to the
+ name of the target that failed, `_._E_R_R_O_R___E_X_I_T' to the exit status
+ of the failed target, `_._E_R_R_O_R___C_M_D' to the commands of the failed
+ target, and in "meta" mode, it also sets `_._E_R_R_O_R___C_W_D' to the
+ getcwd(3), and `_._E_R_R_O_R___M_E_T_A___F_I_L_E' to the path of the meta file
+ (if any) describing the failed target. It then prints its name
+ and the value of `_._C_U_R_D_I_R' as well as the value of any variables
+ named in `_M_A_K_E___P_R_I_N_T___V_A_R___O_N___E_R_R_O_R'.
+
+ _._M_A_K_E_._S_A_V_E___D_O_L_L_A_R_S
+ If true, `$$' are preserved when doing `:=' assignments. The
+ default is false, for backwards compatibility. Set to true for
+ compatability with other makes. If set to false, `$$' becomes
+ `$' per normal evaluation rules.
+
+ _._M_A_K_E_._T_A_R_G_E_T___L_O_C_A_L___V_A_R_I_A_B_L_E_S
+ If set to `false', apparent variable assignments in dependency
+ lines are treated as normal sources.
+
+ _._M_A_K_E_._U_I_D
+ The numeric ID of the user running bbmmaakkee. It is read-only.
+
+ _._n_e_w_l_i_n_e
+ This variable is simply assigned a newline character as its
+ value. It is read-only. This allows expansions using the ::@@
+ modifier to put a newline between iterations of the loop rather
+ than a space. For example, in case of an error, bbmmaakkee prints the
+ variable names and their values using:
+ ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}
+
+ _._O_B_J_D_I_R
+ A path to the directory where the targets are built. Its value
+ is determined by trying to chdir(2) to the following directories
+ in order and using the first match:
+
+ 1. $${{MMAAKKEEOOBBJJDDIIRRPPRREEFFIIXX}}$${{..CCUURRDDIIRR}}
+
+ (Only if `MAKEOBJDIRPREFIX' is set in the environment or on
+ the command line.)
+
+ 2. $${{MMAAKKEEOOBBJJDDIIRR}}
+
+ (Only if `MAKEOBJDIR' is set in the environment or on the
+ command line.)
+
+ 3. $${{..CCUURRDDIIRR}}_/_o_b_j_.$${{MMAACCHHIINNEE}}
+
+ 4. $${{..CCUURRDDIIRR}}_/_o_b_j
+
+ 5. _/_u_s_r_/_o_b_j_/$${{..CCUURRDDIIRR}}
+
+ 6. $${{..CCUURRDDIIRR}}
+
+ Variable expansion is performed on the value before it is used,
+ so expressions such as $${{..CCUURRDDIIRR::SS,,^^//uussrr//ssrrcc,,//vvaarr//oobbjj,,}} may be
+ used. This is especially useful with `MAKEOBJDIR'.
- .PARSEDIR A path to the directory of the current `Makefile' being
- parsed.
-
- .PARSEFILE The basename of the current `Makefile' being parsed.
- This variable and `.PARSEDIR' are both set only while the
- `Makefiles' are being parsed. If you want to retain
- their current values, assign them to a variable using as-
- signment with expansion: (`:=').
+ `_._O_B_J_D_I_R' may be modified in the makefile via the special target
+ `..OOBBJJDDIIRR'. In all cases, bbmmaakkee changes to the specified
+ directory if it exists, and sets `_._O_B_J_D_I_R' and `_P_W_D' to that
+ directory before executing any targets.
- .PATH A variable that represents the list of directories that
- bmake will search for files. The search list should be
- updated using the target `.PATH' rather than the vari-
- able.
+ Except in the case of an explicit `..OOBBJJDDIIRR' target, bbmmaakkee checks
+ that the specified directory is writable and ignores it if not.
+ This check can be skipped by setting the environment variable
+ `MAKE_OBJDIR_CHECK_WRITABLE' to "no".
- PWD Alternate path to the current directory. bmake normally
- sets `.CURDIR' to the canonical path given by getcwd(3).
- However, if the environment variable `PWD' is set and
- gives a path to the current directory, then bmake sets
- `.CURDIR' to the value of `PWD' instead. This behavior
- is disabled if `MAKEOBJDIRPREFIX' is set or `MAKEOBJDIR'
- contains a variable transform. `PWD' is set to the value
- of `.OBJDIR' for all programs which bmake executes.
+ _._P_A_R_S_E_D_I_R
+ The directory name of the current makefile being parsed.
- .SHELL The pathname of the shell used to run target scripts. It
- is read-only.
+ _._P_A_R_S_E_F_I_L_E
+ The basename of the current makefile being parsed. This variable
+ and `_._P_A_R_S_E_D_I_R' are both set only while the makefiles are being
+ parsed. To retain their current values, assign them to a
+ variable using assignment with expansion `::=='.
- .TARGETS The list of targets explicitly specified on the command
- line, if any.
+ _._P_A_T_H The space-separated list of directories that bbmmaakkee searches for
+ files. To update this search list, use the special target
+ `..PPAATTHH' rather than modifying the variable directly.
- VPATH Colon-separated (":") lists of directories that bmake
- will search for files. The variable is supported for
- compatibility with old make programs only, use `.PATH'
- instead.
+ _%_P_O_S_I_X Is set in POSIX mode, see the special `_._P_O_S_I_X' target.
- Variable modifiers
- Variable expansion may be modified to select or modify each word of the
- variable (where a "word" is white-space delimited sequence of charac-
- ters). The general format of a variable expansion is as follows:
+ _P_W_D Alternate path to the current directory. bbmmaakkee normally sets
+ `_._C_U_R_D_I_R' to the canonical path given by getcwd(3). However, if
+ the environment variable `PWD' is set and gives a path to the
+ current directory, bbmmaakkee sets `_._C_U_R_D_I_R' to the value of `PWD'
+ instead. This behavior is disabled if `MAKEOBJDIRPREFIX' is set
+ or `MAKEOBJDIR' contains a variable transform. `_P_W_D' is set to
+ the value of `_._O_B_J_D_I_R' for all programs which bbmmaakkee executes.
- ${variable[:modifier[:...]]}
+ _._S_H_E_L_L The pathname of the shell used to run target scripts. It is
+ read-only.
- Each modifier begins with a colon, which may be escaped with a backslash
- (`\').
+ _._S_U_F_F_I_X_E_S
+ The list of known suffixes. It is read-only.
- A set of modifiers can be specified via a variable, as follows:
+ _._S_Y_S_P_A_T_H
+ The space-separated list of directories that bbmmaakkee searches for
+ makefiles, referred to as the system include path. To update
+ this search list, use the special target `..SSYYSSPPAATTHH' rather than
+ modifying the variable which is read-only.
- modifier_variable=modifier[:...]
- ${variable:${modifier_variable}[:...]}
+ _._T_A_R_G_E_T_S
+ The list of targets explicitly specified on the command line, if
+ any.
- In this case the first modifier in the modifier_variable does not start
- with a colon, since that must appear in the referencing variable. If any
- of the modifiers in the modifier_variable contain a dollar sign (`$'),
- these must be doubled to avoid early expansion.
+ _V_P_A_T_H The colon-separated (":") list of directories that bbmmaakkee searches
+ for files. This variable is supported for compatibility with old
+ make programs only, use `_._P_A_T_H' instead.
+
+ VVaarriiaabbllee mmooddiiffiieerrss
+ The general format of a variable expansion is:
+
+ $${{_v_a_r_i_a_b_l_e[::_m_o_d_i_f_i_e_r[::...]]}}
+
+ Each modifier begins with a colon. To escape a colon, precede it with a
+ backslash `\'.
+
+ A list of indirect modifiers can be specified via a variable, as follows:
+
+ _m_o_d_i_f_i_e_r___v_a_r_i_a_b_l_e = _m_o_d_i_f_i_e_r[::...]
+
+ $${{_v_a_r_i_a_b_l_e::$${{_m_o_d_i_f_i_e_r___v_a_r_i_a_b_l_e}}[::...]}}
+
+ In this case, the first modifier in the _m_o_d_i_f_i_e_r___v_a_r_i_a_b_l_e does not start
+ with a colon, since that colon already occurs in the referencing
+ variable. If any of the modifiers in the _m_o_d_i_f_i_e_r___v_a_r_i_a_b_l_e contains a
+ dollar sign (`$'), these must be doubled to avoid early expansion.
+
+ Some modifiers interpret the expression value as a single string, others
+ treat the expression value as a whitespace-separated list of words. When
+ splitting a string into words, whitespace can be escaped using double
+ quotes, single quotes and backslashes, like in the shell. The quotes and
+ backslashes are retained in the words.
The supported modifiers are:
- :E Replaces each word in the variable with its suffix.
+ ::EE Replaces each word with its suffix.
- :H Replaces each word in the variable with everything but the last com-
- ponent.
+ ::HH Replaces each word with its dirname.
- :Mpattern
- Selects only those words that match pattern. The standard shell
+ ::MM_p_a_t_t_e_r_n
+ Selects only those words that match _p_a_t_t_e_r_n. The standard shell
wildcard characters (`*', `?', and `[]') may be used. The wildcard
characters may be escaped with a backslash (`\'). As a consequence
- of the way values are split into words, matched, and then joined, a
- construct like
- ${VAR:M*}
- will normalize the inter-word spacing, removing all leading and
- trailing space, and converting multiple consecutive spaces to single
- spaces.
+ of the way values are split into words, matched, and then joined,
+ the construct `${VAR:M*}' removes all leading and trailing
+ whitespace and normalizes the inter-word spacing to a single space.
+
+ ::NN_p_a_t_t_e_r_n
+ This is the opposite of `::MM', selecting all words which do _n_o_t match
+ _p_a_t_t_e_r_n.
- :Npattern
- This is identical to `:M', but selects all words which do not match
- pattern.
+ ::OO Orders the words lexicographically.
- :O Orders every word in variable alphabetically.
+ ::OOnn Orders the words numerically. A number followed by one of `k', `M'
+ or `G' is multiplied by the appropriate factor, which is 1024 for
+ `k', 1048576 for `M', or 1073741824 for `G'. Both upper- and lower-
+ case letters are accepted.
- :Or Orders every word in variable in reverse alphabetical order.
+ ::OOrr Orders the words in reverse lexicographical 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,
+ ::OOrrnn
+ Orders the words in reverse numerical order.
+
+ ::OOxx Shuffles the words. The results are different each time you are
+ referring to the modified variable; use the assignment with
+ expansion `::==' to prevent such behavior. For example,
LIST= uno due tre quattro
RANDOM_LIST= ${LIST:Ox}
@@ -831,136 +971,143 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
due uno quattro tre
due uno quattro tre
- :Q Quotes every shell meta-character in the variable, so that it can be
+ ::QQ Quotes every shell meta-character in the value, so that it can be
passed safely to the shell.
- :q Quotes every shell meta-character in the variable, and also doubles
- `$' characters so that it can be passed safely through recursive in-
- vocations of bmake. This is equivalent to: `:S/\$/&&/g:Q'.
-
- :R Replaces each word in the variable with everything but its suffix.
-
- :range[=count]
- The value is an integer sequence representing the words of the orig-
- inal value, or the supplied count.
-
- :gmtime[=utc]
- The value is a format string for strftime(3), using gmtime(3). If a
- utc value is not provided or is 0, the current time is used.
-
- :hash
- Computes a 32-bit hash of the value and encode it as hex digits.
-
- :localtime[=utc]
- The value is a format string for strftime(3), using localtime(3).
- If a utc value is not provided or is 0, the current time is used.
-
- :tA Attempts to convert variable to an absolute path using realpath(3),
- if that fails, the value is unchanged.
-
- :tl Converts variable to lower-case letters.
-
- :tsc
- Words in the variable are normally separated by a space on expan-
- sion. This modifier sets the separator to the character c. If c is
- omitted, then no separator is used. The common escapes (including
- octal numeric codes) work as expected.
-
- :tu Converts variable to upper-case letters.
-
- :tW Causes the value to be treated as a single word (possibly containing
- embedded white space). See also `:[*]'.
-
- :tw Causes the value to be treated as a sequence of words delimited by
- white space. See also `:[@]'.
-
- :S/old_string/new_string/[1gW]
- Modifies the first occurrence of old_string in each word of the
- variable's value, replacing it with new_string. If a `g' is ap-
- pended to the last delimiter of the pattern, all occurrences in each
- word are replaced. If a `1' is appended to the last delimiter of
- the pattern, only the first occurrence is affected. If a `W' is ap-
- pended to the last delimiter of the pattern, then the value is
- treated as a single word (possibly containing embedded white space).
- If old_string begins with a caret (`^'), old_string is anchored at
- the beginning of each word. If old_string ends with a dollar sign
- (`$'), it is anchored at the end of each word. Inside new_string,
- an ampersand (`&') is replaced by old_string (without any `^' or
- `$'). Any character may be used as a delimiter for the parts of the
- modifier string. The anchoring, ampersand and delimiter characters
- may be escaped with a backslash (`\').
-
- Variable expansion occurs in the normal fashion inside both
- old_string and new_string with the single exception that a backslash
- is used to prevent the expansion of a dollar sign (`$'), not a pre-
- ceding dollar sign as is usual.
-
- :C/pattern/replacement/[1gW]
- The :C modifier is just like the :S modifier except that the old and
- new strings, instead of being simple strings, are an extended regu-
- lar expression (see regex(3)) string pattern and an ed(1)-style
- string replacement. Normally, the first occurrence of the pattern
- pattern in each word of the value is substituted with replacement.
- The `1' modifier causes the substitution to apply to at most one
- word; the `g' modifier causes the substitution to apply to as many
- instances of the search pattern pattern as occur in the word or
- words it is found in; the `W' modifier causes the value to be
- treated as a single word (possibly containing embedded white space).
-
- As for the :S modifier, the pattern and replacement are subjected to
+ ::qq Quotes every shell meta-character in the value, and also doubles `$'
+ characters so that it can be passed safely through recursive
+ invocations of bbmmaakkee. This is equivalent to `::SS//\\$$//&&&&//gg::QQ'.
+
+ ::RR Replaces each word with everything but its suffix.
+
+ ::rraannggee[==_c_o_u_n_t]
+ The value is an integer sequence representing the words of the
+ original value, or the supplied _c_o_u_n_t.
+
+ ::ggmmttiimmee[==_t_i_m_e_s_t_a_m_p]
+ The value is interpreted as a format string for strftime(3), using
+ gmtime(3), producing the formatted timestamp. Note: the `%s' format
+ should only be used with `::llooccaallttiimmee'. If a _t_i_m_e_s_t_a_m_p value is not
+ provided or is 0, the current time is used.
+
+ ::hhaasshh
+ Computes a 32-bit hash of the value and encodes it as 8 hex digits.
+
+ ::llooccaallttiimmee[==_t_i_m_e_s_t_a_m_p]
+ The value is interpreted as a format string for strftime(3), using
+ localtime(3), producing the formatted timestamp. If a _t_i_m_e_s_t_a_m_p
+ value is not provided or is 0, the current time is used.
+
+ ::mmttiimmee[==_t_i_m_e_s_t_a_m_p]
+ Call stat(2) with each word as pathname; use `st_mtime' as the new
+ value. If stat(2) fails; use _t_i_m_e_s_t_a_m_p or current time. If
+ _t_i_m_e_s_t_a_m_p is set to `error', then stat(2) failure will cause an
+ error.
+
+ ::ttAA Attempts to convert the value to an absolute path using realpath(3).
+ If that fails, the value is unchanged.
+
+ ::ttll Converts the value to lower-case letters.
+
+ ::ttss_c
+ When joining the words after a modifier that treats the value as
+ words, the words are normally separated by a space. This modifier
+ changes the separator to the character _c. If _c is omitted, no
+ separator is used. The common escapes (including octal numeric
+ codes) work as expected.
+
+ ::ttuu Converts the value to upper-case letters.
+
+ ::ttWW Causes subsequent modifiers to treat the value as a single word
+ (possibly containing embedded whitespace). See also `::[[**]]'.
+
+ ::ttww Causes the value to be treated as a list of words. See also `::[[@@]]'.
+
+ ::SS/_o_l_d___s_t_r_i_n_g/_n_e_w___s_t_r_i_n_g/[11ggWW]
+ Modifies the first occurrence of _o_l_d___s_t_r_i_n_g in each word of the
+ value, replacing it with _n_e_w___s_t_r_i_n_g. If a `g' is appended to the
+ last delimiter of the pattern, all occurrences in each word are
+ replaced. If a `1' is appended to the last delimiter of the
+ pattern, only the first occurrence is affected. If a `W' is
+ appended to the last delimiter of the pattern, the value is treated
+ as a single word. If _o_l_d___s_t_r_i_n_g begins with a caret (`^'),
+ _o_l_d___s_t_r_i_n_g is anchored at the beginning of each word. If _o_l_d___s_t_r_i_n_g
+ ends with a dollar sign (`$'), it is anchored at the end of each
+ word. Inside _n_e_w___s_t_r_i_n_g, an ampersand (`&') is replaced by
+ _o_l_d___s_t_r_i_n_g (without the anchoring `^' or `$'). Any character may be
+ used as the delimiter for the parts of the modifier string. The
+ anchoring, ampersand and delimiter characters can be escaped with a
+ backslash (`\').
+
+ Both _o_l_d___s_t_r_i_n_g and _n_e_w___s_t_r_i_n_g may contain nested expressions. To
+ prevent a dollar sign from starting a nested expression, escape it
+ with a backslash.
+
+ ::CC/_p_a_t_t_e_r_n/_r_e_p_l_a_c_e_m_e_n_t/[11ggWW]
+ The ::CC modifier works like the ::SS modifier except that the old and
+ new strings, instead of being simple strings, are an extended
+ regular expression _p_a_t_t_e_r_n (see regex(3)) and an ed(1)-style
+ _r_e_p_l_a_c_e_m_e_n_t. Normally, the first occurrence of the pattern _p_a_t_t_e_r_n
+ in each word of the value is substituted with _r_e_p_l_a_c_e_m_e_n_t. The `1'
+ modifier causes the substitution to apply to at most one word; the
+ `g' modifier causes the substitution to apply to as many instances
+ of the search pattern _p_a_t_t_e_r_n as occur in the word or words it is
+ found in; the `W' modifier causes the value to be treated as a
+ single word (possibly containing embedded whitespace).
+
+ As for the ::SS modifier, the _p_a_t_t_e_r_n and _r_e_p_l_a_c_e_m_e_n_t are subjected to
variable expansion before being parsed as regular expressions.
- :T Replaces each word in the variable with its last path component.
+ ::TT Replaces each word with its last path component (basename).
- :u Removes adjacent duplicate words (like uniq(1)).
+ ::uu Removes adjacent duplicate words (like uniq(1)).
- :?true_string:false_string
- If the variable name (not its value), when parsed as a .if condi-
- tional expression, evaluates to true, return as its value the
- true_string, otherwise return the false_string. Since the variable
+ ::??_t_r_u_e___s_t_r_i_n_g::_f_a_l_s_e___s_t_r_i_n_g
+ If the variable name (not its value), when parsed as a ..iiff
+ conditional expression, evaluates to true, return as its value the
+ _t_r_u_e___s_t_r_i_n_g, otherwise return the _f_a_l_s_e___s_t_r_i_n_g. Since the variable
name is used as the expression, :? must be the first modifier after
- the variable name itself - which will, of course, usually contain
+ the variable name itself--which, of course, usually contains
variable expansions. A common error is trying to use expressions
like
${NUMBERS:M42:?match:no}
- which actually tests defined(NUMBERS), to determine if any words
- match "42" you need to use something like:
+ which actually tests defined(NUMBERS). To determine if any words
+ match "42", you need to use something like:
${"${NUMBERS:M42}" != "":?match:no}.
- :old_string=new_string
- This is the AT&T System V UNIX style variable substitution. It must
- be the last modifier specified. If old_string or new_string do not
- contain the pattern matching character % then it is assumed that
- they are anchored at the end of each word, so only suffixes or en-
- tire words may be replaced. Otherwise % is the substring of
- old_string to be replaced in new_string. If only old_string con-
- tains the pattern matching character %, and old_string matches, then
- the result is the new_string. If only the new_string contains the
- pattern matching character %, then it is not treated specially and
- it is printed as a literal % on match. If there is more than one
- pattern matching character (%) in either the new_string or
- old_string, only the first instance is treated specially (as the
- pattern character); all subsequent instances are treated as regular
- characters.
-
- Variable expansion occurs in the normal fashion inside both
- old_string and new_string with the single exception that a backslash
- is used to prevent the expansion of a dollar sign (`$'), not a pre-
- ceding dollar sign as is usual.
-
- :@temp@string@
- This is the loop expansion mechanism from the OSF Development Envi-
- ronment (ODE) make. Unlike .for loops, expansion occurs at the time
- of reference. Assigns temp to each word in the variable and evalu-
- ates string. The ODE convention is that temp should start and end
- with a period. For example.
+ ::_o_l_d___s_t_r_i_n_g==_n_e_w___s_t_r_i_n_g
+ This is the AT&T System V UNIX style substitution. It can only be
+ the last modifier specified, as a `:' in either _o_l_d___s_t_r_i_n_g or
+ _n_e_w___s_t_r_i_n_g is treated as a regular character, not as the end of the
+ modifier.
+
+ If _o_l_d___s_t_r_i_n_g does not contain the pattern matching character `%',
+ and the word ends with _o_l_d___s_t_r_i_n_g or equals it, that suffix is
+ replaced with _n_e_w___s_t_r_i_n_g.
+
+ Otherwise, the first `%' in _o_l_d___s_t_r_i_n_g matches a possibly empty
+ substring of arbitrary characters, and if the whole pattern is found
+ in the word, the matching part is replaced with _n_e_w___s_t_r_i_n_g, and the
+ first occurrence of `%' in _n_e_w___s_t_r_i_n_g (if any) is replaced with the
+ substring matched by the `%'.
+
+ Both _o_l_d___s_t_r_i_n_g and _n_e_w___s_t_r_i_n_g may contain nested expressions. To
+ prevent a dollar sign from starting a nested expression, escape it
+ with a backslash.
+
+ ::@@_v_a_r_n_a_m_e@@_s_t_r_i_n_g@@
+ This is the loop expansion mechanism from the OSF Development
+ Environment (ODE) make. Unlike ..ffoorr loops, expansion occurs at the
+ time of reference. For each word in the value, assign the word to
+ the variable named _v_a_r_n_a_m_e and evaluate _s_t_r_i_n_g. The ODE convention
+ is that _v_a_r_n_a_m_e should start and end with a period, for example:
${LINKS:@.LINK.@${LN} ${TARGET} ${.LINK.}@}
- However a single character variable is often more readable:
+ However, a single-letter variable is often more readable:
${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}
- :_[=var]
- Saves the current variable value in `$_' or the named var for later
+ ::__[==_v_a_r]
+ Saves the current variable value in `$_' or the named _v_a_r for later
reference. Example usage:
M_cmpv.units = 1 1000 1000000
@@ -972,157 +1119,150 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
Here `$_' is used to save the result of the `:S' modifier which is
later referenced using the index values from `:range'.
- :Unewval
- If the variable is undefined, newval is the value. If the variable
- is defined, the existing value is returned. This is another ODE
- make feature. It is handy for setting per-target CFLAGS for in-
- stance:
+ ::UU_n_e_w_v_a_l
+ If the variable is undefined, the optional _n_e_w_v_a_l (which may be
+ empty) is the value. If the variable is defined, the existing value
+ is returned. This is another ODE make feature. It is handy for
+ setting per-target CFLAGS for instance:
${_${.TARGET:T}_CFLAGS:U${DEF_CFLAGS}}
If a value is only required if the variable is undefined, use:
${VAR:D:Unewval}
- :Dnewval
- If the variable is defined, newval is the value.
+ ::DD_n_e_w_v_a_l
+ If the variable is defined, _n_e_w_v_a_l (which may be empty) is the
+ value.
- :L The name of the variable is the value.
+ ::LL The name of the variable is the value.
- :P The path of the node which has the same name as the variable is the
- value. If no such node exists or its path is null, then the name of
- the variable is used. In order for this modifier to work, the name
- (node) must at least have appeared on the rhs of a dependency.
+ ::PP The path of the node which has the same name as the variable is the
+ value. If no such node exists or its path is null, the name of the
+ variable is used. In order for this modifier to work, the name
+ (node) must at least have appeared on the right-hand side of a
+ dependency.
- :!cmd!
- The output of running cmd is the value.
+ ::!!_c_m_d!!
+ The output of running _c_m_d is the value.
- :sh If the variable is non-empty it is run as a command and the output
- becomes the new value.
+ ::sshh The value is run as a command, and the output becomes the new value.
- ::=str
- The variable is assigned the value str after substitution. This
+ ::::==_s_t_r
+ The variable is assigned the value _s_t_r after substitution. This
modifier and its variations are useful in obscure situations such as
- wanting to set a variable when shell commands are being parsed.
- These assignment modifiers always expand to nothing, so if appearing
- in a rule line by themselves should be preceded with something to
- keep bmake happy.
-
- The `::' helps avoid false matches with the AT&T System V UNIX style
- := modifier and since substitution always occurs the ::= form is
- vaguely appropriate.
-
- ::?=str
- As for ::= but only if the variable does not already have a value.
-
- ::+=str
- Append str to the variable.
-
- ::!=cmd
- Assign the output of cmd to the variable.
-
- :[range]
- Selects one or more words from the value, or performs other opera-
- tions related to the way in which the value is divided into words.
-
- Ordinarily, a value is treated as a sequence of words delimited by
- white space. Some modifiers suppress this behavior, causing a value
- to be treated as a single word (possibly containing embedded white
- space). An empty value, or a value that consists entirely of white-
- space, is treated as a single word. For the purposes of the `:[]'
- modifier, the words are indexed both forwards using positive inte-
- gers (where index 1 represents the first word), and backwards using
- negative integers (where index -1 represents the last word).
-
- The range is subjected to variable expansion, and the expanded re-
- sult is then interpreted as follows:
-
- index Selects a single word from the value.
-
- start..end
- Selects all words from start to end, inclusive. For example,
- `:[2..-1]' selects all words from the second word to the last
- word. If start is greater than end, then the words are out-
- put in reverse order. For example, `:[-1..1]' selects all
- the words from last to first. If the list is already or-
- dered, then this effectively reverses the list, but it is
- more efficient to use `:Or' instead of `:O:[-1..1]'.
-
- * Causes subsequent modifiers to treat the value as a single
- word (possibly containing embedded white space). Analogous
- to the effect of "$*" in Bourne shell.
-
- 0 Means the same as `:[*]'.
-
- @ Causes subsequent modifiers to treat the value as a sequence
- of words delimited by white space. Analogous to the effect
- of "$@" in Bourne shell.
-
- # Returns the number of words in the value.
-
-INCLUDE STATEMENTS, CONDITIONALS AND FOR LOOPS
- Makefile inclusion, conditional structures and for loops reminiscent of
- the C programming language are provided in bmake. All such structures
- are identified by a line beginning with a single dot (`.') character.
- Files are included with either .include <file> or .include "file". Vari-
- ables between the angle brackets or double quotes are expanded to form
- the file name. If angle brackets are used, the included makefile is ex-
- pected to be in the system makefile directory. If double quotes are
+ wanting to set a variable at a point where a target's shell commands
+ are being parsed. These assignment modifiers always expand to
+ nothing.
+
+ The `::::' helps avoid false matches with the AT&T System V UNIX style
+ `:=' modifier and since substitution always occurs, the `::=' form
+ is vaguely appropriate.
+
+ ::::??==_s_t_r
+ As for ::::== but only if the variable does not already have a value.
+
+ ::::++==_s_t_r
+ Append _s_t_r to the variable.
+
+ ::::!!==_c_m_d
+ Assign the output of _c_m_d to the variable.
+
+ ::[[_r_a_n_g_e]]
+ Selects one or more words from the value, or performs other
+ operations related to the way in which the value is split into
+ words.
+
+ An empty value, or a value that consists entirely of white-space, is
+ treated as a single word. For the purposes of the `::[[]]' modifier,
+ the words are indexed both forwards using positive integers (where
+ index 1 represents the first word), and backwards using negative
+ integers (where index -1 represents the last word).
+
+ The _r_a_n_g_e is subjected to variable expansion, and the expanded
+ result is then interpreted as follows:
+
+ _i_n_d_e_x Selects a single word from the value.
+
+ _s_t_a_r_t...._e_n_d
+ Selects all words from _s_t_a_r_t to _e_n_d, inclusive. For example,
+ `::[[22....--11]]' selects all words from the second word to the last
+ word. If _s_t_a_r_t is greater than _e_n_d, the words are output in
+ reverse order. For example, `::[[--11....11]]' selects all the words
+ from last to first. If the list is already ordered, this
+ effectively reverses the list, but it is more efficient to
+ use `::OOrr' instead of `::OO::[[--11....11]]'.
+
+ ** Causes subsequent modifiers to treat the value as a single
+ word (possibly containing embedded whitespace). Analogous to
+ the effect of $* in Bourne shell.
+
+ 0 Means the same as `::[[**]]'.
+
+ @@ Causes subsequent modifiers to treat the value as a sequence
+ of words delimited by whitespace. Analogous to the effect of
+ $@ in Bourne shell.
+
+ ## Returns the number of words in the value.
+
+DDIIRREECCTTIIVVEESS
+ bbmmaakkee offers directives for including makefiles, conditionals and for
+ loops. All these directives are identified by a line beginning with a
+ single dot (`.') character, followed by the keyword of the directive,
+ such as iinncclluuddee or iiff.
+
+ FFiillee iinncclluussiioonn
+ Files are included with either ..iinncclluuddee <<_f_i_l_e>> or ..iinncclluuddee ""_f_i_l_e"".
+ Variables between the angle brackets or double quotes are expanded to
+ form the file name. If angle brackets are used, the included makefile is
+ expected to be in the system makefile directory. If double quotes are
used, the including makefile's directory and any directories specified
- using the -I option are searched before the system makefile directory.
- For compatibility with other versions of bmake `include file ...' is also
- accepted.
+ using the --II option are searched before the system makefile directory.
- If the include statement is written as .-include or as .sinclude then er-
- rors locating and/or opening include files are ignored.
+ For compatibility with other make variants, `iinncclluuddee _f_i_l_e ...' (without
+ leading dot) is also accepted.
- If the include statement is written as .dinclude not only are errors lo-
- cating and/or opening include files ignored, but stale dependencies
- within the included file will be ignored just like .MAKE.DEPENDFILE.
+ If the include statement is written as ..--iinncclluuddee or as ..ssiinncclluuddee, errors
+ locating and/or opening include files are ignored.
- Conditional expressions are also preceded by a single dot as the first
- character of a line. The possible conditionals are as follows:
+ If the include statement is written as ..ddiinncclluuddee, not only are errors
+ locating and/or opening include files ignored, but stale dependencies
+ within the included file are ignored just like in _._M_A_K_E_._D_E_P_E_N_D_F_I_L_E.
- .error message
- The message is printed along with the name of the makefile and
- line number, then bmake will exit immediately.
+ EExxppoorrttiinngg vvaarriiaabblleess
+ The directives for exporting and unexporting variables are:
- .export variable ...
+ ..eexxppoorrtt _v_a_r_i_a_b_l_e ...
Export the specified global variable. If no variable list is
provided, all globals are exported except for internal variables
- (those that start with `.'). This is not affected by the -X
+ (those that start with `.'). This is not affected by the --XX
flag, so should be used with caution. For compatibility with
- other bmake programs `export variable=value' is also accepted.
+ other make programs, eexxppoorrtt _v_a_r_i_a_b_l_e==_v_a_l_u_e (without leading dot)
+ is also accepted.
- Appending a variable name to .MAKE.EXPORTED is equivalent to ex-
- porting a variable.
+ Appending a variable name to _._M_A_K_E_._E_X_P_O_R_T_E_D is equivalent to
+ exporting a variable.
- .export-env variable ...
+ ..eexxppoorrtt--eennvv _v_a_r_i_a_b_l_e ...
The same as `.export', except that the variable is not appended
- to .MAKE.EXPORTED. This allows exporting a value to the environ-
- ment which is different from that used by bmake internally.
+ to _._M_A_K_E_._E_X_P_O_R_T_E_D. This allows exporting a value to the
+ environment which is different from that used by bbmmaakkee
+ internally.
- .export-literal variable ...
+ ..eexxppoorrtt--lliitteerraall _v_a_r_i_a_b_l_e ...
The same as `.export-env', except that variables in the value are
not expanded.
- .info message
- The message is printed along with the name of the makefile and
- line number.
+ ..uunneexxppoorrtt _v_a_r_i_a_b_l_e ...
+ The opposite of `.export'. The specified global _v_a_r_i_a_b_l_e is
+ removed from _._M_A_K_E_._E_X_P_O_R_T_E_D. If no variable list is provided,
+ all globals are unexported, and _._M_A_K_E_._E_X_P_O_R_T_E_D deleted.
- .undef variable ...
- Un-define the specified global variables. Only global variables
- can be un-defined.
-
- .unexport variable ...
- The opposite of `.export'. The specified global variable will be
- removed from .MAKE.EXPORTED. If no variable list is provided,
- all globals are unexported, and .MAKE.EXPORTED deleted.
-
- .unexport-env
- Unexport all globals previously exported and clear the environ-
- ment inherited from the parent. This operation will cause a mem-
- ory leak of the original environment, so should be used spar-
- ingly. Testing for .MAKE.LEVEL being 0, would make sense. Also
- note that any variables which originated in the parent environ-
- ment should be explicitly preserved if desired. For example:
+ ..uunneexxppoorrtt--eennvv
+ Unexport all globals previously exported and clear the
+ environment inherited from the parent. This operation causes a
+ memory leak of the original environment, so should be used
+ sparingly. Testing for _._M_A_K_E_._L_E_V_E_L being 0 would make sense.
+ Also note that any variables which originated in the parent
+ environment should be explicitly preserved if desired. For
+ example:
.if ${.MAKE.LEVEL} == 0
PATH := ${PATH}
@@ -1131,207 +1271,234 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
.endif
Would result in an environment containing only `PATH', which is
- the minimal useful environment. Actually `.MAKE.LEVEL' will also
- be pushed into the new environment.
+ the minimal useful environment. Actually `_._M_A_K_E_._L_E_V_E_L' is also
+ pushed into the new environment.
+
+ MMeessssaaggeess
+ The directives for printing messages to the output are:
- .warning message
- The message prefixed by `warning:' is printed along with the name
+ ..iinnffoo _m_e_s_s_a_g_e
+ The message is printed along with the name of the makefile and
+ line number.
+
+ ..wwaarrnniinngg _m_e_s_s_a_g_e
+ The message prefixed by `warning:' is printed along with the name
of the makefile and line number.
- .if [!]expression [operator expression ...]
+ ..eerrrroorr _m_e_s_s_a_g_e
+ The message is printed along with the name of the makefile and
+ line number, bbmmaakkee exits immediately.
+
+ CCoonnddiittiioonnaallss
+ The directives for conditionals are:
+
+ ..iiff [!!]_e_x_p_r_e_s_s_i_o_n [_o_p_e_r_a_t_o_r _e_x_p_r_e_s_s_i_o_n ...]
Test the value of an expression.
- .ifdef [!]variable [operator variable ...]
- Test the value of a variable.
+ ..iiffddeeff [!!]_v_a_r_i_a_b_l_e [_o_p_e_r_a_t_o_r _v_a_r_i_a_b_l_e ...]
+ Test whether a variable is defined.
+
+ ..iiffnnddeeff [!!]_v_a_r_i_a_b_l_e [_o_p_e_r_a_t_o_r _v_a_r_i_a_b_l_e ...]
+ Test whether a variable is not defined.
- .ifndef [!]variable [operator variable ...]
- Test the value of a variable.
+ ..iiffmmaakkee [!!]_t_a_r_g_e_t [_o_p_e_r_a_t_o_r _t_a_r_g_e_t ...]
+ Test the target being requested.
- .ifmake [!]target [operator target ...]
- Test the target being built.
+ ..iiffnnmmaakkee [!!]_t_a_r_g_e_t [_o_p_e_r_a_t_o_r _t_a_r_g_e_t ...]
+ Test the target being requested.
- .ifnmake [!] target [operator target ...]
- Test the target being built.
+ ..eellssee Reverse the sense of the last conditional.
- .else Reverse the sense of the last conditional.
+ ..eelliiff [!!]_e_x_p_r_e_s_s_i_o_n [_o_p_e_r_a_t_o_r _e_x_p_r_e_s_s_i_o_n ...]
+ A combination of `..eellssee' followed by `..iiff'.
- .elif [!] expression [operator expression ...]
- A combination of `.else' followed by `.if'.
+ ..eelliiffddeeff [!!]_v_a_r_i_a_b_l_e [_o_p_e_r_a_t_o_r _v_a_r_i_a_b_l_e ...]
+ A combination of `..eellssee' followed by `..iiffddeeff'.
- .elifdef [!]variable [operator variable ...]
- A combination of `.else' followed by `.ifdef'.
+ ..eelliiffnnddeeff [!!]_v_a_r_i_a_b_l_e [_o_p_e_r_a_t_o_r _v_a_r_i_a_b_l_e ...]
+ A combination of `..eellssee' followed by `..iiffnnddeeff'.
- .elifndef [!]variable [operator variable ...]
- A combination of `.else' followed by `.ifndef'.
+ ..eelliiffmmaakkee [!!]_t_a_r_g_e_t [_o_p_e_r_a_t_o_r _t_a_r_g_e_t ...]
+ A combination of `..eellssee' followed by `..iiffmmaakkee'.
- .elifmake [!]target [operator target ...]
- A combination of `.else' followed by `.ifmake'.
+ ..eelliiffnnmmaakkee [!!]_t_a_r_g_e_t [_o_p_e_r_a_t_o_r _t_a_r_g_e_t ...]
+ A combination of `..eellssee' followed by `..iiffnnmmaakkee'.
- .elifnmake [!]target [operator target ...]
- A combination of `.else' followed by `.ifnmake'.
+ ..eennddiiff End the body of the conditional.
- .endif End the body of the conditional.
+ The _o_p_e_r_a_t_o_r may be any one of the following:
- The operator may be any one of the following:
+ |||| Logical OR.
- || Logical OR.
+ &&&& Logical AND; of higher precedence than `||||'.
- && Logical AND; of higher precedence than "||".
+ bbmmaakkee only evaluates a conditional as far as is necessary to determine
+ its value. Parentheses can be used to override the operator precedence.
+ The boolean operator `!!' may be used to logically negate an expression,
+ typically a function call. It is of higher precedence than `&&&&'.
- As in C, bmake will only evaluate a conditional as far as is necessary to
- determine its value. Parentheses may be used to change the order of
- evaluation. The boolean operator `!' may be used to logically negate an
- entire conditional. It is of higher precedence than `&&'.
+ The value of _e_x_p_r_e_s_s_i_o_n may be any of the following function call
+ expressions:
- The value of expression may be any of the following:
+ ddeeffiinneedd(_v_a_r_n_a_m_e)
+ Evaluates to true if the variable _v_a_r_n_a_m_e has been defined.
- defined Takes a variable name as an argument and evaluates to true if
- the variable has been defined.
+ mmaakkee(_t_a_r_g_e_t)
+ Evaluates to true if the target was specified as part of bbmmaakkee's
+ command line or was declared the default target (either
+ implicitly or explicitly, see _._M_A_I_N) before the line containing
+ the conditional.
- make Takes a target name as an argument and evaluates to true if the
- target was specified as part of bmake's command line or was de-
- clared the default target (either implicitly or explicitly, see
- .MAIN) before the line containing the conditional.
+ eemmppttyy(_v_a_r_n_a_m_e[:_m_o_d_i_f_i_e_r_s])
+ Evaluates to true if the expansion of the variable, after
+ applying the modifiers, results in an empty string.
- empty Takes a variable, with possible modifiers, and evaluates to true
- if the expansion of the variable would result in an empty
- string.
+ eexxiissttss(_p_a_t_h_n_a_m_e)
+ Evaluates to true if the given pathname exists. If relative, the
+ pathname is searched for on the system search path (see _._P_A_T_H).
- exists Takes a file name as an argument and evaluates to true if the
- file exists. The file is searched for on the system search path
- (see .PATH).
+ ttaarrggeett(_t_a_r_g_e_t)
+ Evaluates to true if the target has been defined.
- target Takes a target name as an argument and evaluates to true if the
- target has been defined.
+ ccoommmmaannddss(_t_a_r_g_e_t)
+ Evaluates to true if the target has been defined and has commands
+ associated with it.
- commands
- Takes a target name as an argument and evaluates to true if the
- target has been defined and has commands associated with it.
+ _E_x_p_r_e_s_s_i_o_n may also be an arithmetic or string comparison. Variable
+ expansion is performed on both sides of the comparison. If both sides
+ are numeric and neither is enclosed in quotes, the comparison is done
+ numerically, otherwise lexicographically. A string is interpreted as a
+ hexadecimal integer if it is preceded by 0x, otherwise it is interpreted
+ as a decimal floating-point number; octal numbers are not supported.
- Expression may also be an arithmetic or string comparison. Variable ex-
- pansion is performed on both sides of the comparison, after which the nu-
- merical values are compared. A value is interpreted as hexadecimal if it
- is preceded by 0x, otherwise it is decimal; octal numbers are not sup-
- ported. The standard C relational operators are all supported. If after
- variable expansion, either the left or right hand side of a `==' or `!='
- operator is not a numerical value, then string comparison is performed
- between the expanded variables. If no relational operator is given, it
- is assumed that the expanded variable is being compared against 0, or an
- empty string in the case of a string comparison.
+ All comparisons may use the operators `====' and `!!=='. Numeric comparisons
+ may also use the operators `<<', `<<==', `>>' and `>>=='.
- When bmake is evaluating one of these conditional expressions, and it en-
- counters a (white-space separated) word it doesn't recognize, either the
- "make" or "defined" expression is applied to it, depending on the form of
- the conditional. If the form is `.ifdef', `.ifndef', or `.if' the
- "defined" expression is applied. Similarly, if the form is `.ifmake' or
- `.ifnmake', the "make" expression is applied.
+ If the comparison has neither a comparison operator nor a right side, the
+ expression evaluates to true if it is nonempty and its numeric value (if
+ any) is not zero.
- If the conditional evaluates to true the parsing of the makefile contin-
- ues as before. If it evaluates to false, the following lines are
- skipped. In both cases this continues until a `.else' or `.endif' is
- found.
+ When bbmmaakkee is evaluating one of these conditional expressions, and it
+ encounters a (whitespace-separated) word it doesn't recognize, either the
+ "make" or "defined" function is applied to it, depending on the form of
+ the conditional. If the form is `..iiffddeeff', `..iiffnnddeeff' or `..iiff', the
+ "defined" function is applied. Similarly, if the form is `..iiffmmaakkee' or
+ `..iiffnnmmaakkee', the "make" function is applied.
+ If the conditional evaluates to true, parsing of the makefile continues
+ as before. If it evaluates to false, the following lines until the
+ corresponding `..eelliiff' variant, `..eellssee' or `..eennddiiff' are skipped.
+
+ FFoorr llooooppss
For loops are typically used to apply a set of rules to a list of files.
The syntax of a for loop is:
- .for variable [variable ...] in expression
- <make-lines>
- .endfor
+ ..ffoorr _v_a_r_i_a_b_l_e [_v_a_r_i_a_b_l_e ...] iinn _e_x_p_r_e_s_s_i_o_n
+ <_m_a_k_e_-_l_i_n_e_s>
+ ..eennddffoorr
+
+ The _e_x_p_r_e_s_s_i_o_n is expanded and then split into words. On each iteration
+ of the loop, one word is taken and assigned to each _v_a_r_i_a_b_l_e, in order,
+ and these _v_a_r_i_a_b_l_e_s are substituted into the _m_a_k_e_-_l_i_n_e_s inside the body
+ of the for loop. The number of words must come out even; that is, if
+ there are three iteration variables, the number of words provided must be
+ a multiple of three.
- After the for expression is evaluated, it is split into words. On each
- iteration of the loop, one word is taken and assigned to each variable,
- in order, and these variables are substituted into the make-lines inside
- the body of the for loop. The number of words must come out even; that
- is, if there are three iteration variables, the number of words provided
- must be a multiple of three.
+ If `..bbrreeaakk' is encountered within a ..ffoorr loop, it causes early
+ termination of the loop, otherwise a parse error.
-COMMENTS
- Comments begin with a hash (`#') character, anywhere but in a shell com-
- mand line, and continue to the end of an unescaped new line.
+ OOtthheerr ddiirreeccttiivveess
+ ..uunnddeeff _v_a_r_i_a_b_l_e ...
+ Un-define the specified global variables. Only global variables
+ can be un-defined.
+
+CCOOMMMMEENNTTSS
+ Comments begin with a hash (`#') character, anywhere but in a shell
+ command line, and continue to the end of an unescaped new line.
-SPECIAL SOURCES (ATTRIBUTES)
- .EXEC Target is never out of date, but always execute commands any-
- way.
+SSPPEECCIIAALL SSOOUURRCCEESS ((AATTTTRRIIBBUUTTEESS))
+ ..EEXXEECC Target is never out of date, but always execute commands
+ anyway.
- .IGNORE Ignore any errors from the commands associated with this tar-
- get, exactly as if they all were preceded by a dash (`-').
+ ..IIGGNNOORREE Ignore any errors from the commands associated with this
+ target, exactly as if they all were preceded by a dash (`-').
- .MADE Mark all sources of this target as being up-to-date.
+ ..MMAADDEE Mark all sources of this target as being up to date.
- .MAKE Execute the commands associated with this target even if the -n
- or -t options were specified. Normally used to mark recursive
- bmakes.
+ ..MMAAKKEE Execute the commands associated with this target even if the --nn
+ or --tt options were specified. Normally used to mark recursive
+ bbmmaakkees.
- .META Create a meta file for the target, even if it is flagged as
- .PHONY, .MAKE, or .SPECIAL. Usage in conjunction with .MAKE is
+ ..MMEETTAA Create a meta file for the target, even if it is flagged as
+ ..PPHHOONNYY, ..MMAAKKEE, or ..SSPPEECCIIAALL. Usage in conjunction with ..MMAAKKEE is
the most likely case. In "meta" mode, the target is out-of-
date if the meta file is missing.
- .NOMETA Do not create a meta file for the target. Meta files are also
- not created for .PHONY, .MAKE, or .SPECIAL targets.
+ ..NNOOMMEETTAA Do not create a meta file for the target. Meta files are also
+ not created for ..PPHHOONNYY, ..MMAAKKEE, or ..SSPPEECCIIAALL targets.
- .NOMETA_CMP
+ ..NNOOMMEETTAA__CCMMPP
Ignore differences in commands when deciding if target is out
of date. This is useful if the command contains a value which
always changes. If the number of commands change, though, the
- target will still be out of date. The same effect applies to
- any command line that uses the variable .OODATE, which can be
- used for that purpose even when not otherwise needed or de-
- sired:
+ target is still considered out of date. The same effect
+ applies to any command line that uses the variable _._O_O_D_A_T_E,
+ which can be used for that purpose even when not otherwise
+ needed or desired:
skip-compare-for-some:
- @echo this will be compared
- @echo this will not ${.OODATE:M.NOMETA_CMP}
- @echo this will also be compared
+ @echo this is compared
+ @echo this is not ${.OODATE:M.NOMETA_CMP}
+ @echo this is also compared
- The :M pattern suppresses any expansion of the unwanted vari-
- able.
+ The ::MM pattern suppresses any expansion of the unwanted
+ variable.
- .NOPATH Do not search for the target in the directories specified by
- .PATH.
+ ..NNOOPPAATTHH Do not search for the target in the directories specified by
+ _._P_A_T_H.
- .NOTMAIN Normally bmake selects the first target it encounters as the
+ ..NNOOTTMMAAIINN Normally bbmmaakkee selects the first target it encounters as the
default target to be built if no target was specified. This
source prevents this target from being selected.
- .OPTIONAL
- If a target is marked with this attribute and bmake can't fig-
- ure out how to create it, it will ignore this fact and assume
+ ..OOPPTTIIOONNAALL
+ If a target is marked with this attribute and bbmmaakkee can't
+ figure out how to create it, it ignores this fact and assumes
the file isn't needed or already exists.
- .PHONY The target does not correspond to an actual file; it is always
- considered to be out of date, and will not be created with the
- -t option. Suffix-transformation rules are not applied to
- .PHONY targets.
+ ..PPHHOONNYY The target does not correspond to an actual file; it is always
+ considered to be out of date, and is not created with the --tt
+ option. Suffix-transformation rules are not applied to ..PPHHOONNYY
+ targets.
- .PRECIOUS
- When bmake is interrupted, it normally removes any partially
- made targets. This source prevents the target from being re-
- moved.
+ ..PPRREECCIIOOUUSS
+ When bbmmaakkee is interrupted, it normally removes any partially
+ made targets. This source prevents the target from being
+ removed.
- .RECURSIVE
- Synonym for .MAKE.
+ ..RREECCUURRSSIIVVEE
+ Synonym for ..MMAAKKEE.
- .SILENT Do not echo any of the commands associated with this target,
+ ..SSIILLEENNTT Do not echo any of the commands associated with this target,
exactly as if they all were preceded by an at sign (`@').
- .USE Turn the target into bmake's version of a macro. When the tar-
- get is used as a source for another target, the other target
+ ..UUSSEE Turn the target into bbmmaakkee's version of a macro. When the
+ target is used as a source for another target, the other target
acquires the commands, sources, and attributes (except for
- .USE) of the source. If the target already has commands, the
- .USE target's commands are appended to them.
+ ..UUSSEE) of the source. If the target already has commands, the
+ ..UUSSEE target's commands are appended to them.
- .USEBEFORE
- Exactly like .USE, but prepend the .USEBEFORE target commands
- to the target.
+ ..UUSSEEBBEEFFOORREE
+ Like ..UUSSEE, but instead of appending, prepend the ..UUSSEEBBEEFFOORREE
+ target commands to the target.
- .WAIT If .WAIT appears in a dependency line, the sources that precede
+ ..WWAAIITT If ..WWAAIITT appears in a dependency line, the sources that precede
it are made before the sources that succeed it in the line.
- Since the dependents of files are not made until the file it-
- self could be made, this also stops the dependents being built
- unless they are needed for another branch of the dependency
- tree. So given:
+ Since the dependents of files are not made until the file
+ itself could be made, this also stops the dependents being
+ built unless they are needed for another branch of the
+ dependency tree. So given:
x: a .WAIT b
echo x
@@ -1343,125 +1510,144 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
echo b1
the output is always `a', `b1', `b', `x'.
- The ordering imposed by .WAIT is only relevant for parallel
+
+ The ordering imposed by ..WWAAIITT is only relevant for parallel
makes.
-SPECIAL TARGETS
+SSPPEECCIIAALL TTAARRGGEETTSS
Special targets may not be included with other targets, i.e. they must be
the only target specified.
- .BEGIN Any command lines attached to this target are executed before
+ ..BBEEGGIINN Any command lines attached to this target are executed before
anything else is done.
- .DEFAULT
- This is sort of a .USE rule for any target (that was used only
- as a source) that bmake can't figure out any other way to cre-
- ate. Only the shell script is used. The .IMPSRC variable of a
- target that inherits .DEFAULT's commands is set to the target's
- own name.
+ ..DDEEFFAAUULLTT
+ This is sort of a ..UUSSEE rule for any target (that was used only
+ as a source) that bbmmaakkee can't figure out any other way to
+ create. Only the shell script is used. The _._I_M_P_S_R_C variable of
+ a target that inherits ..DDEEFFAAUULLTT's commands is set to the
+ target's own name.
- .DELETE_ON_ERROR
+ ..DDEELLEETTEE__OONN__EERRRROORR
If this target is present in the makefile, it globally causes
make to delete targets whose commands fail. (By default, only
targets whose commands are interrupted during execution are
deleted. This is the historical behavior.) This setting can be
- used to help prevent half-finished or malformed targets from be-
- ing left around and corrupting future rebuilds.
+ used to help prevent half-finished or malformed targets from
+ being left around and corrupting future rebuilds.
- .END Any command lines attached to this target are executed after ev-
- erything else is done.
+ ..EENNDD Any command lines attached to this target are executed after
+ everything else is done successfully.
- .ERROR Any command lines attached to this target are executed when an-
- other target fails. The .ERROR_TARGET variable is set to the
- target that failed. See also MAKE_PRINT_VAR_ON_ERROR.
+ ..EERRRROORR Any command lines attached to this target are executed when
+ another target fails. See _M_A_K_E___P_R_I_N_T___V_A_R___O_N___E_R_R_O_R for the
+ variables that will be set.
- .IGNORE Mark each of the sources with the .IGNORE attribute. If no
+ ..IIGGNNOORREE Mark each of the sources with the ..IIGGNNOORREE attribute. If no
sources are specified, this is the equivalent of specifying the
- -i option.
+ --ii option.
- .INTERRUPT
- If bmake is interrupted, the commands for this target will be
+ ..IINNTTEERRRRUUPPTT
+ If bbmmaakkee is interrupted, the commands for this target are
executed.
- .MAIN If no target is specified when bmake is invoked, this target
- will be built.
+ ..MMAAIINN If no target is specified when bbmmaakkee is invoked, this target is
+ built.
- .MAKEFLAGS
- This target provides a way to specify flags for bmake when the
- makefile is used. The flags are as if typed to the shell,
- though the -f option will have no effect.
+ ..MMAAKKEEFFLLAAGGSS
+ This target provides a way to specify flags for bbmmaakkee at the
+ time when the makefiles are read. The flags are as if typed to
+ the shell, though the --ff option has no effect.
- .NOPATH Apply the .NOPATH attribute to any specified sources.
+ ..NNOOPPAATTHH Apply the ..NNOOPPAATTHH attribute to any specified sources.
- .NOTPARALLEL
+ ..NNOOTTPPAARRAALLLLEELL
Disable parallel mode.
- .NO_PARALLEL
- Synonym for .NOTPARALLEL, for compatibility with other pmake
+ ..NNOO__PPAARRAALLLLEELL
+ Synonym for ..NNOOTTPPAARRAALLLLEELL, for compatibility with other pmake
variants.
- .OBJDIR The source is a new value for `.OBJDIR'. If it exists, bmake
- will chdir(2) to it and update the value of `.OBJDIR'.
+ ..NNOORREEAADDOONNLLYY
+ clear the read-only attribute from the global variables
+ specified as sources.
+
+ ..OOBBJJDDIIRR The source is a new value for `_._O_B_J_D_I_R'. If it exists, bbmmaakkee
+ changes the current working directory to it and updates the
+ value of `_._O_B_J_D_I_R'.
- .ORDER The named targets are made in sequence. This ordering does not
- add targets to the list of targets to be made. Since the depen-
- dents of a target do not get built until the target itself could
- be built, unless `a' is built by another part of the dependency
- graph, the following is a dependency loop:
+ ..OORRDDEERR In parallel mode, the named targets are made in sequence. This
+ ordering does not add targets to the list of targets to be made.
+
+ Since the dependents of a target do not get built until the
+ target itself could be built, unless `a' is built by another
+ part of the dependency graph, the following is a dependency
+ loop:
.ORDER: b a
b: a
- The ordering imposed by .ORDER is only relevant for parallel
- makes.
+ ..PPAATTHH The sources are directories which are to be searched for files
+ not found in the current directory. If no sources are
+ specified, any previously specified directories are removed from
+ the search path. If the source is the special ..DDOOTTLLAASSTT target,
+ the current working directory is searched last.
+
+ ..PPAATTHH.._s_u_f_f_i_x
+ Like ..PPAATTHH but applies only to files with a particular suffix.
+ The suffix must have been previously declared with ..SSUUFFFFIIXXEESS.
- .PATH The sources are directories which are to be searched for files
- not found in the current directory. If no sources are speci-
- fied, any previously specified directories are deleted. If the
- source is the special .DOTLAST target, then the current working
- directory is searched last.
+ ..PPHHOONNYY Apply the ..PPHHOONNYY attribute to any specified sources.
- .PATH.suffix
- Like .PATH but applies only to files with a particular suffix.
- The suffix must have been previously declared with .SUFFIXES.
+ ..PPOOSSIIXX If this is the first non-comment line in the main makefile, the
+ variable _%_P_O_S_I_X is set to the value `1003.2' and the makefile
+ `<posix.mk>' is included if it exists, to provide POSIX-
+ compatible default rules. If bbmmaakkee is run with the --rr flag,
+ only `posix.mk' contributes to the default rules.
- .PHONY Apply the .PHONY attribute to any specified sources.
+ ..PPRREECCIIOOUUSS
+ Apply the ..PPRREECCIIOOUUSS attribute to any specified sources. If no
+ sources are specified, the ..PPRREECCIIOOUUSS attribute is applied to
+ every target in the file.
- .PRECIOUS
- Apply the .PRECIOUS attribute to any specified sources. If no
- sources are specified, the .PRECIOUS attribute is applied to ev-
- ery target in the file.
+ ..RREEAADDOONNLLYY
+ set the read-only attribute on the global variables specified as
+ sources.
- .SHELL Sets the shell that bmake will use to execute commands. The
- sources are a set of field=value pairs.
+ ..SSHHEELLLL Sets the shell that bbmmaakkee uses to execute commands. The sources
+ are a set of _f_i_e_l_d==_v_a_l_u_e pairs.
- name This is the minimal specification, used to select
- one of the built-in shell specs; sh, ksh, and csh.
+ name This is the minimal specification, used to
+ select one of the built-in shell specs; sh, ksh,
+ and csh.
- path Specifies the path to the shell.
+ path Specifies the absolute path to the shell.
- hasErrCtl Indicates whether the shell supports exit on error.
+ hasErrCtl Indicates whether the shell supports exit on
+ error.
- check The command to turn on error checking.
+ check The command to turn on error checking.
- ignore The command to disable error checking.
+ ignore The command to disable error checking.
- echo The command to turn on echoing of commands executed.
+ echo The command to turn on echoing of commands
+ executed.
- quiet The command to turn off echoing of commands exe-
- cuted.
+ quiet The command to turn off echoing of commands
+ executed.
- filter The output to filter after issuing the quiet com-
- mand. It is typically identical to quiet.
+ filter The output to filter after issuing the quiet
+ command. It is typically identical to quiet.
- errFlag The flag to pass the shell to enable error checking.
+ errFlag The flag to pass the shell to enable error
+ checking.
- echoFlag The flag to pass the shell to enable command echo-
- ing.
+ echoFlag The flag to pass the shell to enable command
+ echoing.
- newline The string literal to pass the shell that results in
- a single newline character when used outside of any
- quoting characters.
+ newline The string literal to pass the shell that
+ results in a single newline character when used
+ outside of any quoting characters.
Example:
.SHELL: name=ksh path=/bin/ksh hasErrCtl=true \
@@ -1469,116 +1655,129 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
echo="set -v" quiet="set +v" filter="set +v" \
echoFlag=v errFlag=e newline="'\n'"
- .SILENT Apply the .SILENT attribute to any specified sources. If no
- sources are specified, the .SILENT attribute is applied to every
+ ..SSIILLEENNTT Apply the ..SSIILLEENNTT attribute to any specified sources. If no
+ sources are specified, the ..SSIILLEENNTT attribute is applied to every
command in the file.
- .STALE This target gets run when a dependency file contains stale en-
- tries, having .ALLSRC set to the name of that dependency file.
+ ..SSTTAALLEE This target gets run when a dependency file contains stale
+ entries, having _._A_L_L_S_R_C set to the name of that dependency file.
- .SUFFIXES
- Each source specifies a suffix to bmake. If no sources are
+ ..SSUUFFFFIIXXEESS
+ Each source specifies a suffix to bbmmaakkee. If no sources are
specified, any previously specified suffixes are deleted. It
allows the creation of suffix-transformation rules.
Example:
- .SUFFIXES: .o
+ .SUFFIXES: .c .o
.c.o:
cc -o ${.TARGET} -c ${.IMPSRC}
-ENVIRONMENT
- bmake uses the following environment variables, if they exist: MACHINE,
+ ..SSYYSSPPAATTHH
+ The sources are directories which are to be added to the system
+ include path which bbmmaakkee searches for makefiles. If no sources
+ are specified, any previously specified directories are removed
+ from the system include path.
+
+EENNVVIIRROONNMMEENNTT
+ bbmmaakkee uses the following environment variables, if they exist: MACHINE,
MACHINE_ARCH, MAKE, MAKEFLAGS, MAKEOBJDIR, MAKEOBJDIRPREFIX, MAKESYSPATH,
PWD, and TMPDIR.
MAKEOBJDIRPREFIX and MAKEOBJDIR may only be set in the environment or on
- the command line to bmake and not as makefile variables; see the descrip-
- tion of `.OBJDIR' for more details.
+ the command line to bbmmaakkee and not as makefile variables; see the
+ description of `_._O_B_J_D_I_R' for more details.
-FILES
+FFIILLEESS
.depend list of dependencies
- Makefile list of dependencies
- makefile list of dependencies
+ makefile first default makefile if no makefile is specified on the
+ command line
+ Makefile second default makefile if no makefile is specified on the
+ command line
sys.mk system makefile
/usr/share/mk system makefile directory
-COMPATIBILITY
- The basic make syntax is compatible between different versions of make;
+CCOOMMPPAATTIIBBIILLIITTYY
+ The basic make syntax is compatible between different make variants;
however the special variables, variable modifiers and conditionals are
not.
- Older versions
- An incomplete list of changes in older versions of bmake:
+ OOllddeerr vveerrssiioonnss
+ An incomplete list of changes in older versions of bbmmaakkee:
The way that .for loop variables are substituted changed after NetBSD 5.0
so that they still appear to be variable expansions. In particular this
- stops them being treated as syntax, and removes some obscure problems us-
- ing them in .if statements.
+ stops them being treated as syntax, and removes some obscure problems
+ using them in .if statements.
The way that parallel makes are scheduled changed in NetBSD 4.0 so that
- .ORDER and .WAIT apply recursively to the dependent nodes. The algo-
- rithms used may change again in the future.
+ .ORDER and .WAIT apply recursively to the dependent nodes. The
+ algorithms used may change again in the future.
- Other make dialects
- Other make dialects (GNU make, SVR4 make, POSIX make, etc.) do not sup-
- port most of the features of bmake as described in this manual. Most no-
- tably:
+ OOtthheerr mmaakkee ddiiaalleeccttss
+ Other make dialects (GNU make, SVR4 make, POSIX make, etc.) do not
+ support most of the features of bbmmaakkee as described in this manual. Most
+ notably:
- +o The .WAIT and .ORDER declarations and most functionality per-
- taining to parallelization. (GNU make supports parallelization
- but lacks these features needed to control it effectively.)
+ ++oo The ..WWAAIITT and ..OORRDDEERR declarations and most functionality
+ pertaining to parallelization. (GNU make supports
+ parallelization but lacks the features needed to control it
+ effectively.)
- +o Directives, including for loops and conditionals and most of
+ ++oo Directives, including for loops and conditionals and most of
the forms of include files. (GNU make has its own incompatible
and less powerful syntax for conditionals.)
- +o All built-in variables that begin with a dot.
+ ++oo All built-in variables that begin with a dot.
- +o Most of the special sources and targets that begin with a dot,
- with the notable exception of .PHONY, .PRECIOUS, and .SUFFIXES.
+ ++oo Most of the special sources and targets that begin with a dot,
+ with the notable exception of ..PPHHOONNYY, ..PPRREECCIIOOUUSS, and ..SSUUFFFFIIXXEESS.
- +o Variable modifiers, except for the
- :old=new
- string substitution, which does not portably support globbing
- with `%' and historically only works on declared suffixes.
+ ++oo Variable modifiers, except for the `:old=new' string
+ substitution, which does not portably support globbing with `%'
+ and historically only works on declared suffixes.
- +o The $> variable even in its short form; most makes support this
+ ++oo The $$>> variable even in its short form; most makes support this
functionality but its name varies.
- Some features are somewhat more portable, such as assignment with +=, ?=,
- and !=. The .PATH functionality is based on an older feature VPATH found
- in GNU make and many versions of SVR4 make; however, historically its be-
- havior is too ill-defined (and too buggy) to rely upon.
+ Some features are somewhat more portable, such as assignment with ++==, ??==,
+ and !!==. The _._P_A_T_H functionality is based on an older feature VVPPAATTHH found
+ in GNU make and many versions of SVR4 make; however, historically its
+ behavior is too ill-defined (and too buggy) to rely upon.
- The $@ and $< variables are more or less universally portable, as is the
- $(MAKE) variable. Basic use of suffix rules (for files only in the cur-
- rent directory, not trying to chain transformations together, etc.) is
+ The $$@@ and $$<< variables are more or less universally portable, as is the
+ $$((MMAAKKEE)) variable. Basic use of suffix rules (for files only in the
+ current directory, not trying to chain transformations together, etc.) is
also reasonably portable.
-SEE ALSO
+SSEEEE AALLSSOO
mkdep(1)
-HISTORY
- bmake is derived from NetBSD make(1). It uses autoconf to facilitate
+HHIISSTTOORRYY
+ bbmmaakkee is derived from NetBSD make(1). It uses autoconf to facilitate
portability to other platforms.
A make command appeared in Version 7 AT&T UNIX. This make implementation
- is based on Adam De Boor's pmake program which was written for Sprite at
+ is based on Adam de Boor's pmake program, which was written for Sprite at
Berkeley. It was designed to be a parallel distributed make running jobs
on different machines using a daemon called "customs".
- Historically the target/dependency "FRC" has been used to FoRCe rebuild-
- ing (since the target/dependency does not exist... unless someone creates
- an "FRC" file).
+ Historically the target/dependency FFRRCC has been used to FoRCe rebuilding
+ (since the target/dependency does not exist ... unless someone creates an
+ _F_R_C file).
-BUGS
- The make syntax is difficult to parse without actually acting on the
- data. For instance, finding the end of a variable's use should involve
- scanning each of the modifiers, using the correct terminator for each
- field. In many places make just counts {} and () in order to find the
- end of a variable expansion.
+BBUUGGSS
+ The make syntax is difficult to parse. For instance, finding the end of
+ a variable's use should involve scanning each of the modifiers, using the
+ correct terminator for each field. In many places make just counts {}
+ and () in order to find the end of a variable expansion.
There is no way of escaping a space character in a filename.
-FreeBSD 13.0 December 22, 2020 FreeBSD 13.0
+ In jobs mode, when a target fails; make will put an error token into the
+ job token pool. This will cause all other instances of make using that
+ token pool to abort the build and exit with error code 6. Sometimes the
+ attempt to suppress a cascade of unnecessary errors, can result in a
+ seemingly unexplained `*** Error code 6'
+
+FreeBSD 13.2-RELEASE-p10 March 9, 2024 FreeBSD 13.2-RELEASE-p10
diff --git a/contrib/bmake/boot-strap b/contrib/bmake/boot-strap
index d251649db670..b025de52a82b 100755
--- a/contrib/bmake/boot-strap
+++ b/contrib/bmake/boot-strap
@@ -82,9 +82,23 @@
#
# Possibly useful configure_args:
#
+# --without-makefile
+# do not generate 'makefile'.
+#
+# 'makefile' is used to enable the classic
+# './configure; make; make install' dance, but on
+# systems with case insensitive filesystems it can lead
+# to infinite recursion.
+#
+# It is disabled by default on Darwin, and Cygwin.
+#
# --without-meta
# disable use of meta mode.
#
+# Even without filemon(9) meta mode is very useful
+# both for debugging build and improving reliability of
+# update builds.
+#
# --without-filemon
# disable use of filemon(9) which is currently only
# available for NetBSD and FreeBSD.
@@ -119,7 +133,7 @@
# Simon J. Gerraty <sjg@crufty.net>
# RCSid:
-# $Id: boot-strap,v 1.54 2020/11/13 21:47:25 sjg Exp $
+# $Id: boot-strap,v 1.61 2024/03/10 17:51:10 sjg Exp $
#
# @(#) Copyright (c) 2001 Simon J. Gerraty
#
@@ -315,6 +329,14 @@ FindHereOrAbove() {
)
}
+# is $1 newer than $2 ...
+is_newer() {
+ case `'ls' -1td "$@" 2> /dev/null | head -1` in
+ $1) return 0;;
+ esac
+ return 1
+}
+
# is $1 missing from $2 (or PATH) ?
no_path() {
eval "__p=\$${2:-PATH}"
@@ -405,17 +427,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
}
@@ -425,13 +436,18 @@ op_build() {
chmod 755 make-bootstrap.sh || exit 1
./make-bootstrap.sh || exit 1
case "$op" in
- build) op_test;;
+ build) rm -f tested; op_test;;
esac
}
op_test() {
[ -x bmake ] || op_build
- Bmake test || exit 1
+ case "$op" in
+ test) ;;
+ *) is_newer bmake tested || return;;
+ esac
+ Bmake test TEST_MAKE=$objdir/bmake "$@" || exit 1
+ touch tested
}
op_clean() {
@@ -483,5 +499,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..418caeaa58a6 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.18 2023/09/18 05:29:23 sjg Exp $
# This makefile is for use when integrating bmake into a BSD build
# system. Use this makefile after importing bmake.
@@ -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; }' \
@@ -67,18 +68,25 @@ MAKEFILE_SED = sed -e '/^MACHINE/d' \
# These are the simple files we want to capture
configured_files= config.h Makefile.config unit-tests/Makefile.config
+# FreeBSD has dropped their tag with svn
+.if ${HOST_OS:NFreeBSD} == ""
+ECHO_TAG= :
+.else
+ECHO_TAG?= echo
+.endif
+
after-import: bootstrap ${MAKEFILE}
.for f in ${configured_files:M*.[ch]}
@echo Capturing $f
@mkdir -p ${${.CURDIR}/$f:L:H}
- @(echo '/* $$${HOST_OS}$$ */'; cat ${HOST_OS}/$f) > ${.CURDIR}/$f
+ @(${ECHO_TAG} '/* $$${HOST_OS}$$ */'; cat ${HOST_OS}/$f) > ${.CURDIR}/$f
.endfor
.for f in ${configured_files:M*Makefile*}
@echo Capturing $f
@mkdir -p ${${.CURDIR}/$f:L:H}
@(echo '# This is a generated file, do NOT edit!'; \
echo '# See ${_this:S,${SRCTOP}/,,}'; \
- echo '#'; echo '# $$${HOST_OS}$$'; echo; \
+ echo '#'; ${ECHO_TAG} '# $$${HOST_OS}$$'; echo; \
echo 'SRCTOP?= $${.CURDIR:${${.CURDIR}/$f:L:H:S,${SRCTOP}/,,:C,[^/]+,H,g:S,/,:,g}}'; echo; \
${MAKEFILE_SED} ${HOST_OS}/$f ) > ${.CURDIR}/$f
.endfor
@@ -88,7 +96,7 @@ _makefile: bootstrap ${MAKEFILE}
@echo Generating ${.CURDIR}/Makefile
@(echo '# This is a generated file, do NOT edit!'; \
echo '# See ${_this:S,${SRCTOP}/,,}'; \
- echo '#'; echo '# $$${HOST_OS}$$'; \
+ echo '#'; ${ECHO_TAG} '# $$${HOST_OS}$$'; \
echo; echo 'SRCTOP?= $${.CURDIR:${.CURDIR:S,${SRCTOP}/,,:C,[^/]+,H,g:S,/,:,g}}'; \
echo; echo '# look here first for config.h'; \
echo 'CFLAGS+= -I$${.CURDIR}'; echo; \
@@ -114,7 +122,7 @@ _utmakefile: bootstrap ${MAKEFILE}
@mkdir -p ${.CURDIR}/unit-tests
@(echo '# This is a generated file, do NOT edit!'; \
echo '# See ${_this:S,${SRCTOP}/,,}'; \
- echo '#'; echo '# $$${HOST_OS}$$'; \
+ echo '#'; ${ECHO_TAG} '# $$${HOST_OS}$$'; \
${MAKEFILE_SED} \
-e '/^UNIT_TESTS/s,=.*,= $${srcdir},' \
${BMAKE_SRC}/unit-tests/Makefile ) > ${.TARGET}
diff --git a/contrib/bmake/buf.c b/contrib/bmake/buf.c
index cef082247278..8f4de0c3e2bc 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.58 2024/04/28 15:10:19 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -69,13 +69,13 @@
* SUCH DAMAGE.
*/
-/* Automatically-expanding null-terminated character buffers. */
+/* Automatically growing null-terminated buffers of characters. */
#include <limits.h>
#include "make.h"
/* "@(#)buf.c 8.1 (Berkeley) 6/6/93" */
-MAKE_RCSID("$NetBSD: buf.c,v 1.51 2021/01/30 21:18:14 rillig Exp $");
+MAKE_RCSID("$NetBSD: buf.c,v 1.58 2024/04/28 15:10:19 rillig Exp $");
/* Make space in the buffer for adding at least 16 more bytes. */
void
@@ -106,7 +106,7 @@ Buf_AddBytes(Buffer *buf, const char *bytes, size_t bytes_len)
/* Add the bytes between start and end to the buffer. */
void
-Buf_AddBytesBetween(Buffer *buf, const char *start, const char *end)
+Buf_AddRange(Buffer *buf, const char *start, const char *end)
{
Buf_AddBytes(buf, start, (size_t)(end - start));
}
@@ -122,25 +122,20 @@ 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);
}
-/* Mark the buffer as empty, so it can be filled with data again. */
void
-Buf_Empty(Buffer *buf)
+Buf_AddFlag(Buffer *buf, bool flag, const char *name)
{
- buf->len = 0;
- buf->data[0] = '\0';
+ if (flag) {
+ if (buf->len > 0)
+ Buf_AddByte(buf, '|');
+ Buf_AddBytes(buf, name, strlen(name));
+ }
}
/* Initialize a buffer. */
@@ -192,29 +187,3 @@ Buf_DoneData(Buffer *buf)
return data;
}
-
-#ifndef BUF_COMPACT_LIMIT
-# define BUF_COMPACT_LIMIT 128 /* worthwhile saving */
-#endif
-
-/*
- * Return the data from the buffer.
- * Leave the buffer itself in an indeterminate state.
- *
- * If the buffer size is much greater than its content,
- * a new buffer will be allocated and the old one freed.
- */
-char *
-Buf_DoneDataCompact(Buffer *buf)
-{
-#if BUF_COMPACT_LIMIT > 0
- if (buf->cap - buf->len >= BUF_COMPACT_LIMIT) {
- /* We trust realloc to be smart */
- char *data = bmake_realloc(buf->data, buf->len + 1);
- data[buf->len] = '\0'; /* XXX: unnecessary */
- Buf_DoneData(buf);
- return data;
- }
-#endif
- return Buf_DoneData(buf);
-}
diff --git a/contrib/bmake/buf.h b/contrib/bmake/buf.h
index 938820e4745f..fad36e932616 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.50 2024/04/28 15:10:19 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -88,7 +88,15 @@ typedef struct Buffer {
void Buf_Expand(Buffer *);
-/* Buf_AddByte adds a single byte to a buffer. */
+/* Mark the buffer as empty, so it can be filled with data again. */
+MAKE_INLINE void
+Buf_Clear(Buffer *buf)
+{
+ buf->len = 0;
+ buf->data[0] = '\0';
+}
+
+/* Adds a single byte to a buffer. */
MAKE_INLINE void
Buf_AddByte(Buffer *buf, char byte)
{
@@ -101,21 +109,20 @@ Buf_AddByte(Buffer *buf, char byte)
end[1] = '\0';
}
-MAKE_INLINE bool
+MAKE_INLINE bool MAKE_ATTR_USE
Buf_EndsWith(const Buffer *buf, char ch)
{
return buf->len > 0 && buf->data[buf->len - 1] == ch;
}
void Buf_AddBytes(Buffer *, const char *, size_t);
-void Buf_AddBytesBetween(Buffer *, const char *, const char *);
+void Buf_AddRange(Buffer *, const char *, const char *);
void Buf_AddStr(Buffer *, const char *);
void Buf_AddInt(Buffer *, int);
-void Buf_Empty(Buffer *);
+void Buf_AddFlag(Buffer *, bool, const char *);
void Buf_Init(Buffer *);
void Buf_InitSize(Buffer *, size_t);
void Buf_Done(Buffer *);
-char *Buf_DoneData(Buffer *);
-char *Buf_DoneDataCompact(Buffer *);
+char *Buf_DoneData(Buffer *) MAKE_ATTR_USE;
-#endif /* MAKE_BUF_H */
+#endif
diff --git a/contrib/bmake/compat.c b/contrib/bmake/compat.c
index f8c47397f3df..3a8b1c4b2640 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.255 2024/04/20 10:18:55 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -70,16 +70,11 @@
*/
/*
- * compat.c --
- * The routines in this file implement the full-compatibility
- * mode of PMake. Most of the special functionality of PMake
- * is available in this mode. Things not supported:
- * - different shells.
- * - friendly variable substitution.
+ * This file implements the full-compatibility mode of make, which makes the
+ * targets without parallelism and without a custom shell.
*
* Interface:
- * Compat_Run Initialize things for this module and recreate
- * thems as need creatin'
+ * Compat_MakeAll Initialize this module and make the given targets.
*/
#ifdef HAVE_CONFIG_H
@@ -99,25 +94,24 @@
#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.255 2024/04/20 10:18:55 rillig Exp $");
static GNode *curTarg = NULL;
static pid_t compatChild;
static int compatSigno;
/*
- * CompatDeleteTarget -- delete the file of a failed, interrupted, or
- * otherwise duffed target if not inhibited by .PRECIOUS.
+ * Delete the file of a failed, interrupted, or otherwise duffed target,
+ * unless inhibited by .PRECIOUS.
*/
static void
CompatDeleteTarget(GNode *gn)
{
- if (gn != NULL && !Targ_Precious(gn)) {
+ if (gn != NULL && !GNode_IsPrecious(gn) &&
+ (gn->type & OP_PHONY) == 0) {
const char *file = GNode_VarTarget(gn);
-
- if (!opts.noExecute && eunlink(file) != -1) {
+ if (!opts.noExecute && unlink_file(file) == 0)
Error("*** %s removed", file);
- }
}
}
@@ -135,15 +129,12 @@ CompatInterrupt(int signo)
{
CompatDeleteTarget(curTarg);
- if (curTarg != NULL && !Targ_Precious(curTarg)) {
- /*
- * Run .INTERRUPT only if hit with interrupt signal
- */
+ if (curTarg != NULL && !GNode_IsPrecious(curTarg)) {
+ /* Run .INTERRUPT only if hit with interrupt signal. */
if (signo == SIGINT) {
GNode *gn = Targ_FindNode(".INTERRUPT");
- if (gn != NULL) {
+ if (gn != NULL)
Compat_Make(gn, gn);
- }
}
}
@@ -168,10 +159,12 @@ DebugFailedTarget(const char *cmd, const GNode *gn)
{
const char *p = cmd;
debug_printf("\n*** Failed target: %s\n*** Failed command: ",
- gn->name);
+ gn->name);
- /* Replace runs of whitespace with a single space, to reduce
- * the amount of whitespace for multi-line command lines. */
+ /*
+ * Replace runs of whitespace with a single space, to reduce the
+ * amount of whitespace for multi-line command lines.
+ */
while (*p != '\0') {
if (ch_isspace(*p)) {
debug_printf(" ");
@@ -187,7 +180,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
@@ -220,36 +213,38 @@ UseShell(const char *cmd MAKE_ATTR_UNUSED)
* ln List node that contains the command
*
* Results:
- * 0 if the command succeeded, 1 if an error occurred.
+ * true if the command succeeded.
*/
-int
+bool
Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
{
char *cmdStart; /* Start of expanded command */
- char *bp;
+ char *volatile bp;
bool silent; /* Don't print command */
bool doIt; /* Execute even if -n */
- volatile bool errCheck; /* Check errors */
+ volatile bool errCheck; /* Check errors */
WAIT_T reason; /* Reason for child's death */
WAIT_T status; /* Description of child's death */
pid_t cpid; /* Child actually found */
pid_t retstat; /* Result of wait */
const char **volatile av; /* Argument vector for thing to exec */
char **volatile mav; /* Copy of the argument vector for freeing */
- bool useShell; /* True if command should be executed
- * using a shell */
+ bool useShell; /* True if command should be executed 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;
- (void)Var_Subst(cmd, gn, VARE_WANTRES, &cmdStart);
+ EvalStack_Push(gn->name, NULL, NULL);
+ cmdStart = Var_Subst(cmd, gn, VARE_WANTRES);
+ EvalStack_Pop();
/* TODO: handle errors */
if (cmdStart[0] == '\0') {
free(cmdStart);
- return 0;
+ return true;
}
cmd = cmdStart;
LstNode_Set(ln, cmdStart);
@@ -269,12 +264,12 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
* usual '$$'.
*/
Lst_Append(&endNode->commands, cmdStart);
- return 0;
+ return true;
}
}
if (strcmp(cmdStart, "...") == 0) {
gn->type |= OP_SAVE_CMDS;
- return 0;
+ return true;
}
for (;;) {
@@ -282,48 +277,35 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
silent = !DEBUG(LOUD);
else if (*cmd == '-')
errCheck = false;
- else if (*cmd == '+') {
+ else if (*cmd == '+')
doIt = true;
- if (shellName == NULL) /* we came here from jobs */
- Shell_Init();
- } else
+ else if (!ch_isspace(*cmd))
+ /* Ignore whitespace for compatibility with gnu make */
break;
cmd++;
}
while (ch_isspace(*cmd))
cmd++;
-
- /*
- * If we did not end up with a command, just skip it.
- */
if (cmd[0] == '\0')
- return 0;
+ return true;
useShell = UseShell(cmd);
- /*
- * Print the command before echoing if we're not supposed to be quiet
- * for this one. We also print the command if -n given.
- */
+
if (!silent || !GNode_ShouldExecute(gn)) {
printf("%s\n", cmd);
fflush(stdout);
}
- /*
- * If we're not supposed to execute any commands, this is as far as
- * we go...
- */
if (!doIt && !GNode_ShouldExecute(gn))
- return 0;
+ return true;
DEBUG1(JOB, "Execute: '%s'\n", cmd);
+ if (useShell && shellPath == NULL)
+ Shell_Init(); /* we need shellPath */
+
if (useShell) {
- /*
- * We need to pass the command off to the shell, typically
- * because the command contains a "meta" character.
- */
static const char *shargv[5];
/* The following work for any of the builtin shell specs. */
@@ -338,11 +320,6 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
bp = NULL;
mav = NULL;
} else {
- /*
- * No meta-characters, so no need to exec a shell. Break the
- * command into words to form an argument vector we can
- * execute.
- */
Words words = Str_Words(cmd, false);
mav = words.words;
bp = words.freeIt;
@@ -350,25 +327,20 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
}
#ifdef USE_META
- if (useMeta) {
+ if (useMeta)
meta_compat_start();
- }
#endif
- Var_ReexportVars();
+ Var_ReexportVars(gn);
- /*
- * Fork and execute the single command. If the fork fails, we abort.
- */
compatChild = cpid = vfork();
- if (cpid < 0) {
+ if (cpid < 0)
Fatal("Could not fork");
- }
+
if (cpid == 0) {
#ifdef USE_META
- if (useMeta) {
+ if (useMeta)
meta_compat_child();
- }
#endif
(void)execvp(av[0], (char *const *)UNCONST(av));
execDie("exec", av[0]);
@@ -382,33 +354,28 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
LstNode_SetNull(ln);
#ifdef USE_META
- if (useMeta) {
+ if (useMeta)
meta_compat_parent(cpid);
- }
#endif
- /*
- * The child is off and running. Now all we can do is wait...
- */
+ /* The child is off and running. Now all we can do is wait... */
while ((retstat = wait(&reason)) != cpid) {
if (retstat > 0)
JobReapChild(retstat, reason, false); /* not ours? */
- if (retstat == -1 && errno != EINTR) {
+ if (retstat == -1 && errno != EINTR)
break;
- }
}
if (retstat < 0)
Fatal("error in wait: %d: %s", retstat, strerror(errno));
if (WIFSTOPPED(reason)) {
- status = WSTOPSIG(reason); /* stopped */
+ status = WSTOPSIG(reason);
} else if (WIFEXITED(reason)) {
- status = WEXITSTATUS(reason); /* exited */
+ status = WEXITSTATUS(reason);
#if defined(USE_META) && defined(USE_FILEMON_ONCE)
- if (useMeta) {
- meta_cmd_finish(NULL);
- }
+ if (useMeta)
+ meta_cmd_finish(NULL);
#endif
if (status != 0) {
if (DEBUG(ERROR))
@@ -416,7 +383,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
printf("*** Error code %d", status);
}
} else {
- status = WTERMSIG(reason); /* signaled */
+ status = WTERMSIG(reason);
printf("*** Signal %d", status);
}
@@ -424,11 +391,12 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
if (!WIFEXITED(reason) || status != 0) {
if (errCheck) {
#ifdef USE_META
- if (useMeta) {
+ if (useMeta)
meta_job_error(NULL, gn, false, status);
- }
#endif
gn->made = ERROR;
+ if (WIFEXITED(reason))
+ gn->exit_status = status;
if (opts.keepgoing) {
/*
* Abort the current target,
@@ -448,6 +416,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
printf(" (ignored)\n");
status = 0;
}
+ fflush(stdout);
}
free(cmdStart);
@@ -457,7 +426,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
kill(myPid, compatSigno);
}
- return status;
+ return status == 0;
}
static void
@@ -467,19 +436,71 @@ RunCommands(GNode *gn)
for (ln = gn->commands.first; ln != NULL; ln = ln->next) {
const char *cmd = ln->datum;
- if (Compat_RunCommand(cmd, gn, ln) != 0)
+ if (!Compat_RunCommand(cmd, gn, ln))
break;
}
}
static void
+MakeInRandomOrder(GNode **gnodes, GNode **end, GNode *pgn)
+{
+ GNode **it;
+ size_t r;
+
+ for (r = (size_t)(end - gnodes); r >= 2; r--) {
+ /* Biased, but irrelevant in practice. */
+ size_t i = (size_t)random() % r;
+ GNode *t = gnodes[r - 1];
+ gnodes[r - 1] = gnodes[i];
+ gnodes[i] = t;
+ }
+
+ for (it = gnodes; it != end; it++)
+ Compat_Make(*it, pgn);
+}
+
+static void
+MakeWaitGroupsInRandomOrder(GNodeList *gnodes, GNode *pgn)
+{
+ Vector vec;
+ GNodeListNode *ln;
+ GNode **nodes;
+ size_t i, n, start;
+
+ Vector_Init(&vec, sizeof(GNode *));
+ for (ln = gnodes->first; ln != NULL; ln = ln->next)
+ *(GNode **)Vector_Push(&vec) = ln->datum;
+ nodes = vec.items;
+ n = vec.len;
+
+ start = 0;
+ for (i = 0; i < n; i++) {
+ if (nodes[i]->type & OP_WAIT) {
+ MakeInRandomOrder(nodes + start, nodes + i, pgn);
+ Compat_Make(nodes[i], pgn);
+ start = i + 1;
+ }
+ }
+ MakeInRandomOrder(nodes + start, nodes + i, pgn);
+
+ Vector_Done(&vec);
+}
+
+static void
MakeNodes(GNodeList *gnodes, GNode *pgn)
{
GNodeListNode *ln;
+ if (Lst_IsEmpty(gnodes))
+ return;
+ if (opts.randomizeTargets) {
+ MakeWaitGroupsInRandomOrder(gnodes, pgn);
+ return;
+ }
+
for (ln = gnodes->first; ln != NULL; ln = ln->next) {
- GNode *cohort = ln->datum;
- Compat_Make(cohort, pgn);
+ GNode *cgn = ln->datum;
+ Compat_Make(cgn, pgn);
}
}
@@ -497,7 +518,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 +526,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;
}
@@ -532,7 +553,7 @@ MakeUnmade(GNode *gn, GNode *pgn)
* to tell him/her "yes".
*/
DEBUG0(MAKE, "out-of-date.\n");
- if (opts.queryFlag)
+ if (opts.query && gn != Targ_GetEndNode())
exit(1);
/*
@@ -547,25 +568,20 @@ MakeUnmade(GNode *gn, GNode *pgn)
*/
if (opts.ignoreErrors)
gn->type |= OP_IGNORE;
- if (opts.beSilent)
+ if (opts.silent)
gn->type |= OP_SILENT;
if (Job_CheckCommands(gn, Fatal)) {
- /*
- * Our commands are ok, but we still have to worry about
- * the -t flag.
- */
- if (!opts.touchFlag || (gn->type & OP_MAKE)) {
+ if (!opts.touch || (gn->type & OP_MAKE)) {
curTarg = gn;
#ifdef USE_META
- if (useMeta && GNode_ShouldExecute(gn)) {
+ if (useMeta && GNode_ShouldExecute(gn))
meta_job_start(NULL, gn);
- }
#endif
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,15 +601,15 @@ 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.");
+ PrintOnError(gn, "\nStop.\n");
exit(1);
}
return true;
@@ -612,11 +628,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 +679,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);
}
@@ -681,7 +697,7 @@ MakeBeginNode(void)
Compat_Make(gn, gn);
if (GNode_IsError(gn)) {
- PrintOnError(gn, "\nStop.");
+ PrintOnError(gn, "\nStop.\n");
exit(1);
}
}
@@ -699,14 +715,8 @@ InitSignals(void)
bmake_signal(SIGQUIT, CompatInterrupt);
}
-/*
- * Initialize this module and start making.
- *
- * Input:
- * targs The target nodes to re-create
- */
void
-Compat_Run(GNodeList *targs)
+Compat_MakeAll(GNodeList *targs)
{
GNode *errorNode = NULL;
@@ -715,12 +725,14 @@ Compat_Run(GNodeList *targs)
InitSignals();
- /* Create the .END node now, to keep the (debug) output of the
- * counter.mk test the same as before 2020-09-23. This implementation
- * detail probably doesn't matter though. */
+ /*
+ * Create the .END node now, to keep the (debug) output of the
+ * counter.mk test the same as before 2020-09-23. This
+ * implementation detail probably doesn't matter though.
+ */
(void)Targ_GetEndNode();
- if (!opts.queryFlag)
+ if (!opts.query)
MakeBeginNode();
/*
@@ -737,13 +749,12 @@ Compat_Run(GNodeList *targs)
printf("`%s' is up to date.\n", gn->name);
} else if (gn->made == ABORTED) {
printf("`%s' not remade because of errors.\n",
- gn->name);
+ gn->name);
}
if (GNode_IsError(gn) && errorNode == NULL)
errorNode = gn;
}
- /* If the user has defined a .END target, run its commands. */
if (errorNode == NULL) {
GNode *endNode = Targ_GetEndNode();
Compat_Make(endNode, endNode);
@@ -756,7 +767,7 @@ Compat_Run(GNodeList *targs)
Targ_PrintGraph(2);
else if (DEBUG(GRAPH3))
Targ_PrintGraph(3);
- PrintOnError(errorNode, "\nStop.");
+ PrintOnError(errorNode, "\nStop.\n");
exit(1);
}
}
diff --git a/contrib/bmake/cond.c b/contrib/bmake/cond.c
index a8d88d1d6816..5001677303e2 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.363 2024/04/23 22:51:28 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -81,12 +81,8 @@
* of one of the .if directives or the condition in a
* ':?then:else' variable modifier.
*
- * Cond_save_depth
- * Cond_restore_depth
- * Save and restore the nesting of the conditions, at
- * the start and end of including another makefile, to
- * ensure that in each makefile the conditional
- * directives are well-balanced.
+ * Cond_EndFile At the end of reading a makefile, ensure that the
+ * conditional directives are well-balanced.
*/
#include <errno.h>
@@ -95,14 +91,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.363 2024/04/23 22:51:28 rillig Exp $");
/*
- * The parsing of conditional expressions is based on this grammar:
- * Or -> And '||' Or
- * Or -> And
- * And -> Term '&&' And
- * And -> Term
+ * Conditional expressions conform to this grammar:
+ * Or -> And ('||' And)*
+ * And -> Term ('&&' Term)*
* Term -> Function '(' Argument ')'
* Term -> Leaf Operator Leaf
* Term -> Leaf
@@ -111,13 +105,13 @@ MAKE_RCSID("$NetBSD: cond.c,v 1.267 2021/06/11 14:52:03 rillig Exp $");
* Leaf -> "string"
* Leaf -> Number
* Leaf -> VariableExpression
- * Leaf -> Symbol
+ * Leaf -> BareWord
* Operator -> '==' | '!=' | '>' | '<' | '>=' | '<='
*
- * 'Symbol' is an unquoted string literal to which the default function is
- * applied.
+ * BareWord is an unquoted string literal, its evaluation depends on the kind
+ * of '.if' directive.
*
- * The tokens are scanned by CondToken, which returns:
+ * The tokens are scanned by CondParser_Token, which returns:
* TOK_AND for '&&'
* TOK_OR for '||'
* TOK_NOT for '!'
@@ -125,18 +119,14 @@ MAKE_RCSID("$NetBSD: cond.c,v 1.267 2021/06/11 14:52:03 rillig Exp $");
* TOK_RPAREN for ')'
*
* Other terminal symbols are evaluated using either the default function or
- * the function given in the terminal, they return either TOK_TRUE or
- * TOK_FALSE.
+ * the function given in the terminal, they return either TOK_TRUE, TOK_FALSE
+ * or TOK_ERROR.
*/
typedef enum Token {
TOK_FALSE, TOK_TRUE, TOK_AND, TOK_OR, TOK_NOT,
TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR
} Token;
-typedef enum CondResult {
- CR_FALSE, CR_TRUE, CR_ERROR
-} CondResult;
-
typedef enum ComparisonOp {
LT, LE, GT, GE, EQ, NE
} ComparisonOp;
@@ -145,50 +135,56 @@ typedef struct CondParser {
/*
* The plain '.if ${VAR}' evaluates to true if the value of the
- * expression has length > 0. The other '.if' variants delegate
- * to evalBare instead.
+ * expression has length > 0 and is not numerically zero. The other
+ * '.if' variants delegate to evalBare instead, for example '.ifdef
+ * ${VAR}' is equivalent to '.if defined(${VAR})', checking whether
+ * the variable named by the expression '${VAR}' is defined.
*/
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 an expression or a plain word.
+ *
+ * In conditional directives like '.if', the left-hand side must
+ * either be an 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 */
/*
* Whether an error message has already been printed for this
- * condition. The first available error message is usually the most
- * specific one, therefore it makes sense to suppress the standard
- * "Malformed conditional" message.
+ * condition.
*/
bool printedError;
} CondParser;
-static CondResult CondParser_Or(CondParser *par, bool);
+static CondResult CondParser_Or(CondParser *, bool);
-static unsigned int cond_depth = 0; /* current .if nesting level */
-static unsigned int cond_min_depth = 0; /* depth at makefile open */
+unsigned int cond_depth = 0; /* current .if nesting level */
-static const char *opname[] = { "<", "<=", ">", ">=", "==", "!=" };
+/* Names for ComparisonOp. */
+static const char opname[][3] = { "<", "<=", ">", ">=", "==", "!=" };
-/*
- * 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;
-
-static bool
-is_token(const char *str, const char *tok, size_t len)
+MAKE_INLINE bool
+skip_string(const char **pp, const char *str)
{
- return strncmp(str, tok, len) == 0 && !ch_isalpha(str[len]);
+ size_t len = strlen(str);
+ bool ok = strncmp(*pp, str, len) == 0;
+ if (ok)
+ *pp += len;
+ return ok;
}
static Token
@@ -197,16 +193,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,136 +200,130 @@ CondParser_SkipWhitespace(CondParser *par)
}
/*
- * Parse the argument of a built-in function.
- *
- * Arguments:
- * *pp initially points at the '(',
- * upon successful return it points right after the ')'.
- *
- * *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.
- *
- * Return the length of the argument, or 0 on error.
+ * 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.
*/
-static size_t
-ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func,
- char **out_arg)
+static char *
+ParseWord(const char **pp, bool doEval)
{
const char *p = *pp;
- Buffer argBuf;
- int paren_depth;
- size_t argLen;
+ Buffer word;
+ int depth;
- if (func != NULL)
- p++; /* Skip opening '(' - verified by caller */
+ Buf_Init(&word);
- if (*p == '\0') {
- *out_arg = NULL; /* Missing closing parenthesis: */
- return 0; /* .if defined( */
- }
-
- cpp_skip_hspace(&p);
-
- Buf_InitSize(&argBuf, 16);
-
- paren_depth = 0;
+ depth = 0;
for (;;) {
char ch = *p;
if (ch == '\0' || ch == ' ' || ch == '\t')
break;
- if ((ch == '&' || ch == '|') && paren_depth == 0)
+ if ((ch == '&' || ch == '|') && depth == 0)
break;
- if (*p == '$') {
- /*
- * Parse the variable expression and install it as
- * part of the argument if it's valid. We tell
- * Var_Parse to complain on an undefined variable,
- * (XXX: but Var_Parse ignores that request)
- * so we don't need to do it. Nor do we return an
- * error, though perhaps we should.
- */
+ if (ch == '$') {
VarEvalMode emode = doEval
? VARE_UNDEFERR
: VARE_PARSE_ONLY;
- FStr nestedVal;
- (void)Var_Parse(&p, SCOPE_CMDLINE, emode, &nestedVal);
+ /*
+ * TODO: make Var_Parse complain about undefined
+ * variables.
+ */
+ FStr nestedVal = Var_Parse(&p, SCOPE_CMDLINE, emode);
/* TODO: handle errors */
- Buf_AddStr(&argBuf, nestedVal.str);
+ Buf_AddStr(&word, nestedVal.str);
FStr_Done(&nestedVal);
continue;
}
if (ch == '(')
- paren_depth++;
- else if (ch == ')' && --paren_depth < 0)
+ depth++;
+ else if (ch == ')' && --depth < 0)
break;
- Buf_AddByte(&argBuf, *p);
+ Buf_AddByte(&word, ch);
p++;
}
- argLen = argBuf.len;
- *out_arg = Buf_DoneData(&argBuf);
+ cpp_skip_hspace(&p);
+ *pp = p;
+ return Buf_DoneData(&word);
+}
+
+/* Parse the function argument, including the surrounding parentheses. */
+static char *
+ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func)
+{
+ const char *p = *pp;
+ char *res;
+
+ p++; /* skip the '(' */
+ cpp_skip_hspace(&p);
+ res = ParseWord(&p, doEval);
cpp_skip_hspace(&p);
- if (func != NULL && *p++ != ')') {
+ if (*p++ != ')') {
+ int len = 0;
+ while (ch_isalpha(func[len]))
+ len++;
+
Parse_Error(PARSE_FATAL,
- "Missing closing parenthesis for %s()", func);
+ "Missing closing parenthesis for %.*s()", len, func);
par->printedError = true;
- return 0;
+ free(res);
+ return NULL;
}
*pp = p;
- return argLen;
+ return res;
}
-/* Test whether the given variable is defined. */
-/*ARGSUSED*/
+/* See if the given variable is defined. */
static bool
-FuncDefined(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
+FuncDefined(const char *var)
{
- FStr value = Var_Value(SCOPE_CMDLINE, arg);
- bool result = value.str != NULL;
- FStr_Done(&value);
- return result;
+ return Var_Exists(SCOPE_CMDLINE, var);
}
-/* See if the given target is being made. */
-/*ARGSUSED*/
+/* See if a target matching targetPattern is requested to be made. */
static bool
-FuncMake(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
+FuncMake(const char *targetPattern)
{
StringListNode *ln;
-
- for (ln = opts.create.first; ln != NULL; ln = ln->next)
- if (Str_Match(ln->datum, arg))
+ bool warned = false;
+
+ for (ln = opts.create.first; ln != NULL; ln = ln->next) {
+ StrMatchResult res = Str_Match(ln->datum, targetPattern);
+ if (res.error != NULL && !warned) {
+ warned = true;
+ Parse_Error(PARSE_WARNING,
+ "%s in pattern argument '%s' to function 'make'",
+ res.error, targetPattern);
+ }
+ if (res.matched)
return true;
+ }
return false;
}
/* See if the given file exists. */
-/*ARGSUSED*/
static bool
-FuncExists(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
+FuncExists(const char *file)
{
bool result;
char *path;
- path = Dir_FindFile(arg, &dirSearchPath);
+ path = Dir_FindFile(file, &dirSearchPath);
DEBUG2(COND, "exists(%s) result is \"%s\"\n",
- arg, path != NULL ? path : "");
+ file, path != NULL ? path : "");
result = path != NULL;
free(path);
return result;
}
/* 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 *node)
{
- GNode *gn = Targ_FindNode(arg);
+ GNode *gn = Targ_FindNode(node);
return gn != NULL && GNode_IsTarget(gn);
}
@@ -351,22 +331,17 @@ 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 *node)
{
- GNode *gn = Targ_FindNode(arg);
- return gn != NULL && GNode_IsTarget(gn) && !Lst_IsEmpty(&gn->commands);
+ GNode *gn = Targ_FindNode(node);
+ return gn != NULL && GNode_IsTarget(gn) &&
+ !Lst_IsEmpty(&gn->commands);
}
/*
- * Convert the given number into a double.
- * We try a base 10 or 16 integer conversion first, if that fails
- * then we try a floating point conversion instead.
- *
- * Results:
- * Returns true if the conversion succeeded.
- * Sets 'out_value' to the converted number.
+ * Convert the string to a floating point number. Accepted formats are
+ * base-10 integer, base-16 integer and finite floating point numbers.
*/
static bool
TryParseNumber(const char *str, double *out_value)
@@ -375,12 +350,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;
@@ -405,80 +380,56 @@ is_separator(char ch)
}
/*
- * In a quoted or unquoted string literal or a number, parse a variable
- * expression.
+ * In a quoted or unquoted string literal or a number, parse an
+ * expression and add its value to the buffer.
+ *
+ * Return whether to continue parsing the leaf.
*
* Example: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX}
*/
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;
+ const char *p;
+ bool atStart; /* true means an expression outside quotes */
- /* 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;
- atStart = nested_p == start;
- parseResult = Var_Parse(&nested_p, SCOPE_CMDLINE, emode, inout_str);
+ p = par->p;
+ atStart = p == start;
+ *inout_str = Var_Parse(&p, SCOPE_CMDLINE, emode);
/* TODO: handle errors */
if (inout_str->str == var_Error) {
- if (parseResult == VPR_ERR) {
- /*
- * FIXME: Even if an error occurs, there is no
- * guarantee that it is reported.
- *
- * See cond-token-plain.mk $$$$$$$$.
- */
- par->printedError = true;
- }
- /*
- * XXX: Can there be any situation in which a returned
- * var_Error needs to be freed?
- */
FStr_Done(inout_str);
- /*
- * Even if !doEval, we still report syntax errors, which is
- * what getting var_Error back with !doEval means.
- */
*inout_str = FStr_InitRefer(NULL);
return false;
}
- par->p = nested_p;
+ par->p = p;
- /*
- * If the '$' started the string literal (which means no quotes), and
- * the variable expression is followed by a space, looks like a
- * comparison operator or is the end of the expression, we are done.
- */
if (atStart && is_separator(par->p[0]))
return false;
Buf_AddStr(buf, inout_str->str);
FStr_Done(inout_str);
- *inout_str = FStr_InitRefer(NULL); /* not finished yet */
+ *inout_str = FStr_InitRefer(NULL); /* not finished yet */
return true;
}
/*
- * Parse a string from a variable expression or an optionally quoted
- * string. This is called for the left-hand and right-hand sides of
- * comparisons.
+ * Parse a string from an expression or an optionally quoted string,
+ * on the left-hand and right-hand sides of comparisons.
*
- * Results:
- * Returns the string, absent any quotes, or NULL on error.
- * Sets out_quoted if the leaf was a quoted string literal.
+ * Return the string without any enclosing quotes, or NULL on error.
+ * Sets out_quoted if the leaf was a quoted string literal.
*/
-static void
-CondParser_Leaf(CondParser *par, bool doEval, bool strictLHS,
- FStr *out_str, bool *out_quoted)
+static FStr
+CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK,
+ bool *out_quoted)
{
Buffer buf;
FStr str;
@@ -504,7 +455,7 @@ CondParser_Leaf(CondParser *par, bool doEval, bool strictLHS,
case '"':
par->p++;
if (quoted)
- goto got_str; /* skip the closing quote */
+ goto return_buf; /* skip the closing quote */
Buf_AddByte(&buf, '"');
continue;
case ')': /* see is_separator */
@@ -515,42 +466,32 @@ CondParser_Leaf(CondParser *par, bool doEval, bool strictLHS,
case ' ':
case '\t':
if (!quoted)
- goto got_str;
+ goto return_buf;
Buf_AddByte(&buf, par->p[0]);
par->p++;
continue;
case '$':
if (!CondParser_StringExpr(par,
start, doEval, quoted, &buf, &str))
- goto cleanup;
+ goto return_str;
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.
- */
str = FStr_InitRefer(NULL);
- goto cleanup;
+ goto return_str;
}
Buf_AddByte(&buf, par->p[0]);
par->p++;
continue;
}
}
-got_str:
+return_buf:
str = FStr_InitOwn(buf.data);
-cleanup:
- Buf_DoneData(&buf); /* XXX: memory leak on failure? */
- *out_str = str;
-}
-
-static bool
-EvalBare(const CondParser *par, const char *arg, size_t arglen)
-{
- bool res = par->evalBare(arglen, arg);
- return par->negateEvalBare ? !res : res;
+ buf.data = NULL;
+return_str:
+ Buf_Done(&buf);
+ return str;
}
/*
@@ -558,33 +499,24 @@ EvalBare(const CondParser *par, const char *arg, size_t arglen)
* ".if 0".
*/
static bool
-EvalNotEmpty(CondParser *par, const char *value, bool quoted)
+EvalTruthy(CondParser *par, const char *value, bool quoted)
{
double num;
- /* For .ifxxx "...", check for non-empty string. */
if (quoted)
return value[0] != '\0';
-
- /* For .ifxxx <number>, compare against zero */
if (TryParseNumber(value, &num))
return num != 0.0;
-
- /* 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. */
if (par->plain)
return value[0] != '\0';
-
- return EvalBare(par, value, strlen(value));
+ return par->evalBare(value) != par->negateEvalBare;
}
/* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
static bool
EvalCompareNum(double lhs, ComparisonOp op, double rhs)
{
- DEBUG3(COND, "lhs = %f, rhs = %f, op = %.2s\n", lhs, rhs, opname[op]);
+ DEBUG3(COND, "Comparing %f %s %f\n", lhs, opname[op], rhs);
switch (op) {
case LT:
@@ -595,10 +527,10 @@ EvalCompareNum(double lhs, ComparisonOp op, double rhs)
return lhs > rhs;
case GE:
return lhs >= rhs;
- case NE:
- return lhs != rhs;
- default:
+ case EQ:
return lhs == rhs;
+ default:
+ return lhs != rhs;
}
}
@@ -608,13 +540,14 @@ EvalCompareStr(CondParser *par, const char *lhs,
{
if (op != EQ && op != NE) {
Parse_Error(PARSE_FATAL,
- "String comparison operator must be either == or !=");
+ "Comparison with '%s' requires both operands "
+ "'%s' and '%s' to be numeric",
+ opname[op], lhs, rhs);
par->printedError = true;
return TOK_ERROR;
}
- DEBUG3(COND, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
- lhs, rhs, opname[op]);
+ DEBUG3(COND, "Comparing \"%s\" %s \"%s\"\n", lhs, opname[op], rhs);
return ToToken((op == EQ) == (strcmp(lhs, rhs) == 0));
}
@@ -637,33 +570,19 @@ CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op)
{
const char *p = par->p;
- if (p[0] == '<' && p[1] == '=') {
- *out_op = LE;
- goto length_2;
- } else if (p[0] == '<') {
- *out_op = LT;
- goto length_1;
- } else if (p[0] == '>' && p[1] == '=') {
- *out_op = GE;
- goto length_2;
- } else if (p[0] == '>') {
- *out_op = GT;
- goto length_1;
- } else if (p[0] == '=' && p[1] == '=') {
- *out_op = EQ;
- goto length_2;
- } else if (p[0] == '!' && p[1] == '=') {
- *out_op = NE;
- goto length_2;
- }
+ if (p[0] == '<' && p[1] == '=')
+ return par->p += 2, *out_op = LE, true;
+ if (p[0] == '<')
+ return par->p += 1, *out_op = LT, true;
+ if (p[0] == '>' && p[1] == '=')
+ return par->p += 2, *out_op = GE, true;
+ if (p[0] == '>')
+ return par->p += 1, *out_op = GT, true;
+ if (p[0] == '=' && p[1] == '=')
+ return par->p += 2, *out_op = EQ, true;
+ if (p[0] == '!' && p[1] == '=')
+ return par->p += 2, *out_op = NE, true;
return false;
-
-length_2:
- par->p = p + 2;
- return true;
-length_1:
- par->p = p + 1;
- return true;
}
/*
@@ -682,19 +601,14 @@ 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);
+ lhs = CondParser_Leaf(par, doEval, par->leftUnquotedOK, &lhsQuoted);
if (lhs.str == NULL)
goto done_lhs;
CondParser_SkipWhitespace(par);
if (!CondParser_ComparisonOp(par, &op)) {
- /* Unknown operator, compare against an empty string or 0. */
- t = ToToken(doEval && EvalNotEmpty(par, lhs.str, lhsQuoted));
+ t = ToToken(doEval && EvalTruthy(par, lhs.str, lhsQuoted));
goto done_lhs;
}
@@ -702,24 +616,17 @@ 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);
- if (rhs.str == NULL)
- goto done_rhs;
-
- if (!doEval) {
- t = TOK_FALSE;
- goto done_rhs;
- }
-
- t = EvalCompare(par, lhs.str, lhsQuoted, op, rhs.str, rhsQuoted);
-
-done_rhs:
+ rhs = CondParser_Leaf(par, doEval, true, &rhsQuoted);
+ t = rhs.str == NULL ? TOK_ERROR
+ : !doEval ? TOK_FALSE
+ : EvalCompare(par, lhs.str, lhsQuoted, op, rhs.str, rhsQuoted);
FStr_Done(&rhs);
+
done_lhs:
FStr_Done(&lhs);
return t;
@@ -729,146 +636,117 @@ 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 *p = 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 (!skip_string(&p, "empty"))
+ return false;
+
+ cpp_skip_whitespace(&p);
+ if (*p != '(')
+ return false;
- (*pp)--; /* Make (*pp)[1] point to the '('. */
- (void)Var_Parse(pp, SCOPE_CMDLINE,
- doEval ? VARE_WANTRES : VARE_PARSE_ONLY, &val);
+ p--; /* Make p[1] point to the '('. */
+ val = Var_Parse(&p, SCOPE_CMDLINE,
+ doEval ? VARE_WANTRES : VARE_PARSE_ONLY);
/* 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 = ToToken(doEval && val.str[0] == '\0');
}
- /*
- * 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 = p;
+ return true;
}
-/* Parse a function call expression, such as 'defined(${file})'. */
+/* Parse a function call expression, such as 'exists(${file})'. */
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 *);
- } 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 }
- };
- 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;
-
- cp += fn->fn_name_len;
- cpp_skip_whitespace(&cp);
- if (*cp != '(')
- break;
+ char *arg;
+ const char *p = par->p;
+ bool (*fn)(const char *);
+ const char *fn_name = p;
+
+ if (skip_string(&p, "defined"))
+ fn = FuncDefined;
+ else if (skip_string(&p, "make"))
+ fn = FuncMake;
+ else if (skip_string(&p, "exists"))
+ fn = FuncExists;
+ else if (skip_string(&p, "target"))
+ fn = FuncTarget;
+ else if (skip_string(&p, "commands"))
+ fn = FuncCommands;
+ else
+ 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;
- }
+ cpp_skip_whitespace(&p);
+ if (*p != '(')
+ 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;
- }
+ arg = ParseFuncArg(par, &p, doEval, fn_name);
+ *out_token = ToToken(doEval &&
+ arg != NULL && arg[0] != '\0' && fn(arg));
+ free(arg);
- return false;
+ par->p = p;
+ return true;
}
/*
- * Parse a comparison such as '${VAR} == "value"', or a simple leaf without
- * operator, which is a number, a variable expression or a string literal.
+ * 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, an 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;
-
- /* Push anything numeric through the compare expression */
- cp = par->p;
- if (ch_isdigit(cp[0]) || cp[0] == '-' || cp[0] == '+')
+ char *arg;
+ const char *p;
+
+ p = par->p;
+ if (ch_isdigit(p[0]) || p[0] == '-' || p[0] == '+')
return CondParser_Comparison(par, doEval);
/*
- * Most likely we have a naked token to apply the default function to.
- * However ".if a == b" gets here when the "a" is unquoted and doesn't
- * start with a '$'. This surprises people.
+ * Most likely we have a bare word to apply the default function to.
+ * However, ".if a == b" gets here when the "a" is unquoted and
+ * doesn't start with a '$'. This surprises people.
* If what follows the function argument is a '=' or '!' then the
* syntax would be invalid if we did "defined(a)" - so instead treat
* as an expression.
*/
/*
- * XXX: Is it possible to have a variable expression evaluated twice
- * at this point?
+ * XXX: In edge cases, an expression may be evaluated twice,
+ * see cond-token-plain.mk, keyword 'twice'.
*/
- arglen = ParseFuncArg(par, &cp, doEval, NULL, &arg);
- cp1 = cp;
- cpp_skip_whitespace(&cp1);
- if (*cp1 == '=' || *cp1 == '!' || *cp1 == '<' || *cp1 == '>')
+ arg = ParseWord(&p, doEval);
+ assert(arg[0] != '\0');
+
+ if (*p == '=' || *p == '!' || *p == '<' || *p == '>')
return CondParser_Comparison(par, doEval);
- par->p = cp;
+ par->p = p;
/*
* Evaluate the argument using the default function.
* This path always treats .if as .ifdef. To get here, the character
* after .if must have been taken literally, so the argument cannot
- * be empty - even if it contained a variable expansion.
+ * be empty - even if it contained an expression.
*/
- t = ToToken(!doEval || EvalBare(par, arg, arglen));
+ t = ToToken(doEval && par->evalBare(arg) != par->negateEvalBare);
free(arg);
return t;
}
@@ -934,12 +812,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
@@ -951,12 +847,13 @@ CondParser_Term(CondParser *par, bool doEval)
{
CondResult res;
Token t;
+ bool neg = false;
- t = CondParser_Token(par, doEval);
- if (t == TOK_TRUE)
- return CR_TRUE;
- if (t == TOK_FALSE)
- return CR_FALSE;
+ while ((t = CondParser_Token(par, doEval)) == TOK_NOT)
+ neg = !neg;
+
+ if (t == TOK_TRUE || t == TOK_FALSE)
+ return neg == (t == TOK_FALSE) ? CR_TRUE : CR_FALSE;
if (t == TOK_LPAREN) {
res = CondParser_Or(par, doEval);
@@ -964,126 +861,83 @@ CondParser_Term(CondParser *par, bool doEval)
return CR_ERROR;
if (CondParser_Token(par, doEval) != TOK_RPAREN)
return CR_ERROR;
- return res;
- }
-
- if (t == TOK_NOT) {
- res = CondParser_Term(par, doEval);
- if (res == CR_TRUE)
- res = CR_FALSE;
- else if (res == CR_FALSE)
- res = CR_TRUE;
- return res;
+ return neg == (res == CR_FALSE) ? CR_TRUE : CR_FALSE;
}
return CR_ERROR;
}
/*
- * And -> Term '&&' And
- * And -> Term
+ * And -> Term ('&&' Term)*
*/
static CondResult
CondParser_And(CondParser *par, bool doEval)
{
- CondResult res;
- Token op;
+ CondResult res, rhs;
- res = CondParser_Term(par, doEval);
- if (res == CR_ERROR)
- return CR_ERROR;
-
- 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;
-
- res = CondParser_And(par, doEval);
- if (res == CR_ERROR)
- return CR_ERROR;
+ CondResult res, rhs;
- 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;
}
-static CondEvalResult
-CondParser_Eval(CondParser *par, bool *out_value)
-{
- CondResult res;
-
- DEBUG1(COND, "CondParser_Eval: %s\n", par->p);
-
- res = CondParser_Or(par, true);
- if (res == CR_ERROR)
- return COND_INVALID;
-
- if (CondParser_Token(par, false) != TOK_EOF)
- return COND_INVALID;
-
- *out_value = res == CR_TRUE;
- return COND_PARSE;
-}
-
/*
- * Evaluate the condition, including any side effects from the variable
+ * Evaluate the condition, including any side effects from the
* expressions in the condition. The condition consists of &&, ||, !,
* function(arg), comparisons and parenthetical groupings thereof.
- *
- * Results:
- * 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
*/
-static CondEvalResult
-CondEvalExpression(const char *cond, bool *out_value, bool plain,
- bool (*evalBare)(size_t, const char *), bool negate,
- bool eprint, bool strictLHS)
+static CondResult
+CondEvalExpression(const char *cond, bool plain,
+ bool (*evalBare)(const char *), bool negate,
+ bool eprint, bool leftUnquotedOK)
{
CondParser par;
- CondEvalResult rval;
-
- lhsStrict = strictLHS;
+ CondResult rval;
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;
- rval = CondParser_Eval(&par, out_value);
+ DEBUG1(COND, "CondParser_Eval: %s\n", par.p);
+ rval = CondParser_Or(&par, true);
+ if (par.curr != TOK_EOF)
+ rval = CR_ERROR;
- if (rval == COND_INVALID && eprint && !par.printedError)
+ if (rval == CR_ERROR && eprint && !par.printedError)
Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond);
return rval;
@@ -1093,11 +947,11 @@ CondEvalExpression(const char *cond, bool *out_value, bool plain,
* Evaluate a condition in a :? modifier, such as
* ${"${VAR}" == value:?yes:no}.
*/
-CondEvalResult
-Cond_EvalCondition(const char *cond, bool *out_value)
+CondResult
+Cond_EvalCondition(const char *cond)
{
- return CondEvalExpression(cond, out_value, true,
- FuncDefined, false, false, false);
+ return CondEvalExpression(cond, true,
+ FuncDefined, false, false, true);
}
static bool
@@ -1109,39 +963,30 @@ 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;
+ const char *p = *pp + 2;
- p += 2;
*out_plain = false;
*out_evalBare = FuncDefined;
- *out_negate = false;
- if (*p == 'n') {
- p++;
- *out_negate = true;
- }
- if (is_token(p, "def", 3)) { /* .ifdef and .ifndef */
- p += 3;
- } else if (is_token(p, "make", 4)) { /* .ifmake and .ifnmake */
- p += 4;
+ *out_negate = skip_string(&p, "n");
+
+ if (skip_string(&p, "def")) { /* .ifdef and .ifndef */
+ } else if (skip_string(&p, "make")) /* .ifmake and .ifnmake */
*out_evalBare = FuncMake;
- } else if (is_token(p, "", 0) && !*out_negate) { /* plain .if */
+ else if (!*out_negate) /* plain .if */
*out_plain = true;
- } else {
- /*
- * TODO: Add error message about unknown directive,
- * since there is no other known directive that starts
- * with 'el' or 'if'.
- *
- * Example: .elifx 123
- */
- return false;
- }
+ else
+ goto unknown_directive;
+ if (ch_isalpha(*p))
+ goto unknown_directive;
*pp = p;
return true;
+
+unknown_directive:
+ return false;
}
/*
@@ -1165,16 +1010,16 @@ DetermineKindOfConditional(const char **pp, bool *out_plain,
* parenthetical groupings thereof.
*
* Results:
- * COND_PARSE to continue parsing the lines that follow the
+ * CR_TRUE to continue parsing the lines that follow the
* conditional (when <cond> evaluates to true)
- * COND_SKIP to skip the lines after the conditional
+ * CR_FALSE to skip the lines after the conditional
* (when <cond> evaluates to false, or when a previous
- * branch has already been taken)
- * COND_INVALID if the conditional was not valid, either because of
+ * branch was already taken)
+ * CR_ERROR if the conditional was not valid, either because of
* a syntax error or because some variable was undefined
* or because the condition could not be evaluated
*/
-CondEvalResult
+CondResult
Cond_EvalLine(const char *line)
{
typedef enum IfState {
@@ -1182,8 +1027,10 @@ Cond_EvalLine(const char *line)
/* None of the previous <cond> evaluated to true. */
IFS_INITIAL = 0,
- /* The previous <cond> evaluated to true.
- * The lines following this condition are interpreted. */
+ /*
+ * The previous <cond> evaluated to true. The lines following
+ * this condition are interpreted.
+ */
IFS_ACTIVE = 1 << 0,
/* The previous directive was an '.else'. */
@@ -1198,10 +1045,10 @@ 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;
+ CondResult res;
IfState state;
const char *p = line;
@@ -1214,47 +1061,42 @@ Cond_EvalLine(const char *line)
p++; /* skip the leading '.' */
cpp_skip_hspace(&p);
- if (IsEndif(p)) { /* It is an '.endif'. */
+ if (IsEndif(p)) {
if (p[5] != '\0') {
Parse_Error(PARSE_FATAL,
- "The .endif directive does not take arguments.");
+ "The .endif directive does not take arguments");
}
- if (cond_depth == cond_min_depth) {
+ if (cond_depth == CurFile_CondMinDepth()) {
Parse_Error(PARSE_FATAL, "if-less endif");
- return COND_PARSE;
+ return CR_TRUE;
}
/* Return state for previous conditional */
cond_depth--;
+ Parse_GuardEndif();
return cond_states[cond_depth] & IFS_ACTIVE
- ? COND_PARSE : COND_SKIP;
+ ? CR_TRUE : CR_FALSE;
}
/* Parse the name of the directive, such as 'if', 'elif', 'endif'. */
if (p[0] == 'e') {
- if (p[1] != 'l') {
- /*
- * Unknown directive. It might still be a
- * transformation rule like '.elisp.scm',
- * therefore no error message here.
- */
- return COND_INVALID;
- }
+ if (p[1] != 'l')
+ return CR_ERROR;
/* Quite likely this is 'else' or 'elif' */
p += 2;
- if (is_token(p, "se", 2)) { /* It is an 'else'. */
-
+ if (strncmp(p, "se", 2) == 0 && !ch_isalpha(p[2])) {
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) {
+ if (cond_depth == CurFile_CondMinDepth()) {
Parse_Error(PARSE_FATAL, "if-less else");
- return COND_PARSE;
+ return CR_TRUE;
}
+ Parse_GuardElse();
state = cond_states[cond_depth];
if (state == IFS_INITIAL) {
@@ -1262,44 +1104,40 @@ Cond_EvalLine(const char *line)
} else {
if (state & IFS_SEEN_ELSE)
Parse_Error(PARSE_WARNING,
- "extra else");
+ "extra else");
state = IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
}
cond_states[cond_depth] = state;
- return state & IFS_ACTIVE ? COND_PARSE : COND_SKIP;
+ return state & IFS_ACTIVE ? CR_TRUE : CR_FALSE;
}
/* Assume for now it is an elif */
isElif = true;
} else
isElif = false;
- if (p[0] != 'i' || p[1] != 'f') {
- /*
- * Unknown directive. It might still be a transformation rule
- * like '.elisp.scm', therefore no error message here.
- */
- return COND_INVALID; /* Not an ifxxx or elifxxx line */
- }
+ if (p[0] != 'i' || p[1] != 'f')
+ return CR_ERROR;
if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate))
- return COND_INVALID;
+ return CR_ERROR;
if (isElif) {
- if (cond_depth == cond_min_depth) {
+ if (cond_depth == CurFile_CondMinDepth()) {
Parse_Error(PARSE_FATAL, "if-less elif");
- return COND_PARSE;
+ return CR_TRUE;
}
+ Parse_GuardElse();
state = cond_states[cond_depth];
if (state & IFS_SEEN_ELSE) {
Parse_Error(PARSE_WARNING, "extra elif");
cond_states[cond_depth] =
IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
- return COND_SKIP;
+ return CR_FALSE;
}
if (state != IFS_INITIAL) {
cond_states[cond_depth] = IFS_WAS_ACTIVE;
- return COND_SKIP;
+ return CR_FALSE;
}
} else {
/* Normal .if */
@@ -1311,58 +1149,97 @@ Cond_EvalLine(const char *line)
*/
cond_states_cap += 32;
cond_states = bmake_realloc(cond_states,
- cond_states_cap *
- sizeof *cond_states);
+ cond_states_cap * sizeof *cond_states);
}
state = cond_states[cond_depth];
cond_depth++;
if (!(state & IFS_ACTIVE)) {
- /*
- * If we aren't parsing the data,
- * treat as always false.
- */
cond_states[cond_depth] = IFS_WAS_ACTIVE;
- return COND_SKIP;
+ return CR_FALSE;
}
}
- /* And evaluate the conditional expression */
- if (CondEvalExpression(p, &value, plain, evalBare, negate,
- true, true) == COND_INVALID) {
- /* Syntax error in conditional, error message already output. */
- /* Skip everything to matching .endif */
- /* XXX: An extra '.else' is not detected in this case. */
+ res = CondEvalExpression(p, plain, evalBare, negate, true, false);
+ if (res == CR_ERROR) {
+ /* Syntax error, error message already output. */
+ /* Skip everything to the matching '.endif'. */
+ /* An extra '.else' is not detected in this case. */
cond_states[cond_depth] = IFS_WAS_ACTIVE;
- return COND_SKIP;
+ return CR_FALSE;
}
- if (!value) {
- cond_states[cond_depth] = IFS_INITIAL;
- return COND_SKIP;
+ cond_states[cond_depth] = res == CR_TRUE ? IFS_ACTIVE : IFS_INITIAL;
+ return res;
+}
+
+static bool
+ParseVarnameGuard(const char **pp, const char **varname)
+{
+ const char *p = *pp;
+
+ if (ch_isalpha(*p) || *p == '_') {
+ while (ch_isalnum(*p) || *p == '_')
+ p++;
+ *varname = *pp;
+ *pp = p;
+ return true;
}
- cond_states[cond_depth] = IFS_ACTIVE;
- return COND_PARSE;
+ return false;
}
-void
-Cond_restore_depth(unsigned int saved_depth)
+/* Extracts the multiple-inclusion guard from a conditional, if any. */
+Guard *
+Cond_ExtractGuard(const char *line)
{
- unsigned int open_conds = cond_depth - cond_min_depth;
+ const char *p, *varname;
+ Substring dir;
+ Guard *guard;
- if (open_conds != 0 || saved_depth > cond_depth) {
- Parse_Error(PARSE_FATAL, "%u open conditional%s",
- open_conds, open_conds == 1 ? "" : "s");
- cond_depth = cond_min_depth;
+ p = line + 1; /* skip the '.' */
+ cpp_skip_hspace(&p);
+
+ dir.start = p;
+ while (ch_isalpha(*p))
+ p++;
+ dir.end = p;
+ cpp_skip_hspace(&p);
+
+ if (Substring_Equals(dir, "if")) {
+ if (skip_string(&p, "!defined(")) {
+ if (ParseVarnameGuard(&p, &varname)
+ && strcmp(p, ")") == 0)
+ goto found_variable;
+ } else if (skip_string(&p, "!target(")) {
+ const char *arg_p = p;
+ free(ParseWord(&p, false));
+ if (strcmp(p, ")") == 0) {
+ guard = bmake_malloc(sizeof(*guard));
+ guard->kind = GK_TARGET;
+ guard->name = ParseWord(&arg_p, true);
+ return guard;
+ }
+ }
+ } else if (Substring_Equals(dir, "ifndef")) {
+ if (ParseVarnameGuard(&p, &varname) && *p == '\0')
+ goto found_variable;
}
+ return NULL;
- cond_min_depth = saved_depth;
+found_variable:
+ guard = bmake_malloc(sizeof(*guard));
+ guard->kind = GK_VARIABLE;
+ guard->name = bmake_strsedup(varname, p);
+ return guard;
}
-unsigned int
-Cond_save_depth(void)
+void
+Cond_EndFile(void)
{
- unsigned int depth = cond_min_depth;
+ unsigned int open_conds = cond_depth - CurFile_CondMinDepth();
- cond_min_depth = cond_depth;
- return depth;
+ if (open_conds != 0) {
+ Parse_Error(PARSE_FATAL, "%u open conditional%s",
+ open_conds, open_conds == 1 ? "" : "s");
+ cond_depth = CurFile_CondMinDepth();
+ }
}
diff --git a/contrib/bmake/config.h.in b/contrib/bmake/config.h.in
index 18f07aeda37f..3834761a6b87 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,8 +68,11 @@
/* Define to 1 if you have the <limits.h> header file. */
#undef HAVE_LIMITS_H
-/* Define to 1 if you have the <memory.h> header file. */
-#undef HAVE_MEMORY_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 <minix/config.h> header file. */
+#undef HAVE_MINIX_CONFIG_H
/* Define to 1 if you have the `mmap' function. */
#undef HAVE_MMAP
@@ -89,6 +95,9 @@
/* Define to 1 if you have the `realpath' function. */
#undef HAVE_REALPATH
+/* Define to 1 if you have the <regex.h> header file. */
+#undef HAVE_REGEX_H
+
/* Define to 1 if you have the `select' function. */
#undef HAVE_SELECT
@@ -98,21 +107,45 @@
/* 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
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
@@ -143,12 +176,11 @@
/* Define to 1 if you have the `strtol' function. */
#undef HAVE_STRTOL
-/* Define to 1 if `st_rdev' is a member of `struct stat'. */
-#undef HAVE_STRUCT_STAT_ST_RDEV
+/* Define to 1 if you have the `strtoll' function. */
+#undef HAVE_STRTOLL
-/* Define to 1 if your `struct stat' has `st_rdev'. Deprecated, use
- `HAVE_STRUCT_STAT_ST_RDEV' instead. */
-#undef HAVE_ST_RDEV
+/* Define to 1 if you have the `strtoul' function. */
+#undef HAVE_STRTOUL
/* Define to 1 if you have the `sysctl' function. */
#undef HAVE_SYSCTL
@@ -197,6 +229,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
@@ -227,6 +262,9 @@
/* Define to 1 if you have the `warnx' function. */
#undef HAVE_WARNX
+/* Define to 1 if you have the <wchar.h> header file. */
+#undef HAVE_WCHAR_H
+
/* Define to 1 if `fork' works. */
#undef HAVE_WORKING_FORK
@@ -254,18 +292,14 @@
/* Define to the version of this package. */
#undef PACKAGE_VERSION
-/* Define as the return type of signal handlers (`int' or `void'). */
-#undef RETSIGTYPE
-
/* Define to 1 if the `S_IS*' macros in <sys/stat.h> do not work properly. */
#undef STAT_MACROS_BROKEN
-/* Define to 1 if you have the ANSI C header files. */
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+ required in a freestanding environment). This macro is provided for
+ backward compatibility; new code need not use it. */
#undef STDC_HEADERS
-/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
-#undef TIME_WITH_SYS_TIME
-
/* Define to 1 if your <sys/time.h> declares `struct tm'. */
#undef TM_IN_SYS_TIME
@@ -273,21 +307,87 @@
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
+/* Enable general extensions on macOS. */
+#ifndef _DARWIN_C_SOURCE
+# undef _DARWIN_C_SOURCE
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# undef _GNU_SOURCE
#endif
-/* Enable threading extensions on Solaris. */
+/* Enable X/Open compliant socket functions that do not require linking
+ with -lxnet on HP-UX 11.11. */
+#ifndef _HPUX_ALT_XOPEN_SOCKET_API
+# undef _HPUX_ALT_XOPEN_SOCKET_API
+#endif
+/* Identify the host operating system as Minix.
+ This macro does not affect the system headers' behavior.
+ A future release of Autoconf may stop defining this macro. */
+#ifndef _MINIX
+# undef _MINIX
+#endif
+/* Enable general extensions on NetBSD.
+ Enable NetBSD compatibility extensions on Minix. */
+#ifndef _NETBSD_SOURCE
+# undef _NETBSD_SOURCE
+#endif
+/* Enable OpenBSD compatibility extensions on NetBSD.
+ Oddly enough, this does nothing on OpenBSD. */
+#ifndef _OPENBSD_SOURCE
+# undef _OPENBSD_SOURCE
+#endif
+/* Define to 1 if needed for POSIX-compatible behavior. */
+#ifndef _POSIX_SOURCE
+# undef _POSIX_SOURCE
+#endif
+/* Define to 2 if needed for POSIX-compatible behavior. */
+#ifndef _POSIX_1_SOURCE
+# undef _POSIX_1_SOURCE
+#endif
+/* Enable POSIX-compatible threading on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
+/* Enable extensions specified by ISO/IEC TS 18661-5:2014. */
+#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
+# undef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-1:2014. */
+#ifndef __STDC_WANT_IEC_60559_BFP_EXT__
+# undef __STDC_WANT_IEC_60559_BFP_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-2:2015. */
+#ifndef __STDC_WANT_IEC_60559_DFP_EXT__
+# undef __STDC_WANT_IEC_60559_DFP_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */
+#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__
+# undef __STDC_WANT_IEC_60559_FUNCS_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */
+#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
+# undef __STDC_WANT_IEC_60559_TYPES_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TR 24731-2:2010. */
+#ifndef __STDC_WANT_LIB_EXT2__
+# undef __STDC_WANT_LIB_EXT2__
+#endif
+/* Enable extensions specified by ISO/IEC 24747:2009. */
+#ifndef __STDC_WANT_MATH_SPEC_FUNCS__
+# undef __STDC_WANT_MATH_SPEC_FUNCS__
+#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
-/* Enable general extensions on Solaris. */
-#ifndef __EXTENSIONS__
-# undef __EXTENSIONS__
+/* Enable X/Open extensions. Define to 500 only if necessary
+ to make mbstate_t available. */
+#ifndef _XOPEN_SOURCE
+# undef _XOPEN_SOURCE
#endif
@@ -303,16 +403,6 @@
# endif
#endif
-/* Define to 1 if on MINIX. */
-#undef _MINIX
-
-/* Define to 2 if the system does not provide POSIX.1 features except with
- this defined. */
-#undef _POSIX_1_SOURCE
-
-/* Define to 1 if you need to in order for `stat' and other things to work. */
-#undef _POSIX_SOURCE
-
/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
#define below would cause a syntax error. */
@@ -324,13 +414,23 @@
/* 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
/* Define to `long int' if <sys/types.h> does not define. */
#undef off_t
-/* Define to `int' if <sys/types.h> does not define. */
+/* Define as a signed integer type capable of holding a process identifier. */
#undef pid_t
/* type that signal handlers can safely frob */
diff --git a/contrib/bmake/configure b/contrib/bmake/configure
index 6dbf5ad31a00..08a550f3cf86 100755
--- a/contrib/bmake/configure
+++ b/contrib/bmake/configure
@@ -1,11 +1,12 @@
#! /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.71 for bmake 20240314.
#
# Report bugs to <sjg@NetBSD.org>.
#
#
-# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
+# Inc.
#
#
# This configure script is free software; the Free Software Foundation
@@ -16,14 +17,16 @@
# Be more Bourne compatible
DUALCASE=1; export DUALCASE # for MKS sh
-if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+as_nop=:
+if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
emulate sh
NULLCMD=:
# Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
# is contrary to our usage. Disable this feature.
alias -g '${1+"$@"}'='"$@"'
setopt NO_GLOB_SUBST
-else
+else $as_nop
case `(set -o) 2>/dev/null` in #(
*posix*) :
set -o posix ;; #(
@@ -33,46 +36,46 @@ esac
fi
+
+# Reset variables that may have inherited troublesome values from
+# the environment.
+
+# IFS needs to be set, to space, tab, and newline, in precisely that order.
+# (If _AS_PATH_WALK were called with IFS unset, it would have the
+# side effect of setting IFS to empty, thus disabling word splitting.)
+# Quoting is to prevent editors from complaining about space-tab.
as_nl='
'
export as_nl
-# Printing a long string crashes Solaris 7 /usr/bin/printf.
-as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
-as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
-as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
-# Prefer a ksh shell builtin over an external printf program on Solaris,
-# but without wasting forks for bash or zsh.
-if test -z "$BASH_VERSION$ZSH_VERSION" \
- && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
- as_echo='print -r --'
- as_echo_n='print -rn --'
-elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
- as_echo='printf %s\n'
- as_echo_n='printf %s'
-else
- if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
- as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
- as_echo_n='/usr/ucb/echo -n'
- else
- as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
- as_echo_n_body='eval
- arg=$1;
- case $arg in #(
- *"$as_nl"*)
- expr "X$arg" : "X\\(.*\\)$as_nl";
- arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
- esac;
- expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
- '
- export as_echo_n_body
- as_echo_n='sh -c $as_echo_n_body as_echo'
- fi
- export as_echo_body
- as_echo='sh -c $as_echo_body as_echo'
-fi
+IFS=" "" $as_nl"
+
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# Ensure predictable behavior from utilities with locale-dependent output.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# We cannot yet rely on "unset" to work, but we need these variables
+# to be unset--not just set to an empty or harmless value--now, to
+# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct
+# also avoids known problems related to "unset" and subshell syntax
+# in other old shells (e.g. bash 2.01 and pdksh 5.2.14).
+for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH
+do eval test \${$as_var+y} \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+
+# Ensure that fds 0, 1, and 2 are open.
+if (exec 3>&0) 2>/dev/null; then :; else exec 0</dev/null; fi
+if (exec 3>&1) 2>/dev/null; then :; else exec 1>/dev/null; fi
+if (exec 3>&2) ; then :; else exec 2>/dev/null; fi
# The user is always right.
-if test "${PATH_SEPARATOR+set}" != set; then
+if ${PATH_SEPARATOR+false} :; then
PATH_SEPARATOR=:
(PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
(PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
@@ -81,13 +84,6 @@ if test "${PATH_SEPARATOR+set}" != set; then
fi
-# IFS
-# We need space, tab and new line, in precisely that order. Quoting is
-# there to prevent editors from complaining about space-tab.
-# (If _AS_PATH_WALK were called with IFS unset, it would disable word
-# splitting by setting IFS to empty value.)
-IFS=" "" $as_nl"
-
# Find who we are. Look in the path if we contain no directory separator.
as_myself=
case $0 in #((
@@ -96,8 +92,12 @@ case $0 in #((
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ test -r "$as_dir$0" && as_myself=$as_dir$0 && break
done
IFS=$as_save_IFS
@@ -109,30 +109,10 @@ if test "x$as_myself" = x; then
as_myself=$0
fi
if test ! -f "$as_myself"; then
- $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
exit 1
fi
-# Unset variables that we do not need and which cause bugs (e.g. in
-# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
-# suppresses any "Segmentation fault" message there. '((' could
-# trigger a bug in pdksh 5.2.14.
-for as_var in BASH_ENV ENV MAIL MAILPATH
-do eval test x\${$as_var+set} = xset \
- && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
-done
-PS1='$ '
-PS2='> '
-PS4='+ '
-
-# NLS nuisances.
-LC_ALL=C
-export LC_ALL
-LANGUAGE=C
-export LANGUAGE
-
-# CDPATH.
-(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
# Use a proper internal environment variable to ensure we don't fall
# into an infinite loop, continuously re-executing ourselves.
@@ -154,20 +134,22 @@ esac
exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
# Admittedly, this is quite paranoid, since all the known shells bail
# out after a failed `exec'.
-$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
-as_fn_exit 255
+printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
fi
# We don't want this to propagate to other subprocesses.
{ _as_can_reexec=; unset _as_can_reexec;}
if test "x$CONFIG_SHELL" = x; then
- as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+ as_bourne_compatible="as_nop=:
+if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
emulate sh
NULLCMD=:
# Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
# is contrary to our usage. Disable this feature.
alias -g '\${1+\"\$@\"}'='\"\$@\"'
setopt NO_GLOB_SUBST
-else
+else \$as_nop
case \`(set -o) 2>/dev/null\` in #(
*posix*) :
set -o posix ;; #(
@@ -187,42 +169,52 @@ as_fn_success || { exitcode=1; echo as_fn_success failed.; }
as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
-if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+if ( set x; as_fn_ret_success y && test x = \"\$1\" )
+then :
-else
+else \$as_nop
exitcode=1; echo positional parameters were not saved.
fi
test x\$exitcode = x0 || exit 1
+blah=\$(echo \$(echo blah))
+test x\"\$blah\" = xblah || exit 1
test -x / || exit 1"
as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
- test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
-test \$(( 1 + 1 )) = 2 || exit 1"
- if (eval "$as_required") 2>/dev/null; then :
+ test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1"
+ if (eval "$as_required") 2>/dev/null
+then :
as_have_required=yes
-else
+else $as_nop
as_have_required=no
fi
- if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+ if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null
+then :
-else
+else $as_nop
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
as_found=false
for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
as_found=:
case $as_dir in #(
/*)
for as_base in sh bash ksh sh5; do
# Try only shells that exist, to save several forks.
- as_shell=$as_dir/$as_base
+ as_shell=$as_dir$as_base
if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
- { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null
+then :
CONFIG_SHELL=$as_shell as_have_required=yes
- if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null
+then :
break 2
fi
fi
@@ -230,14 +222,21 @@ fi
esac
as_found=false
done
-$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
- { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
- CONFIG_SHELL=$SHELL as_have_required=yes
-fi; }
IFS=$as_save_IFS
+if $as_found
+then :
+
+else $as_nop
+ if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+ as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null
+then :
+ CONFIG_SHELL=$SHELL as_have_required=yes
+fi
+fi
- if test "x$CONFIG_SHELL" != x; then :
+ if test "x$CONFIG_SHELL" != x
+then :
export CONFIG_SHELL
# We cannot yet assume a decent shell, so we have to provide a
# neutralization value for shells without unset; and this also
@@ -255,18 +254,19 @@ esac
exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
# Admittedly, this is quite paranoid, since all the known shells bail
# out after a failed `exec'.
-$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
exit 255
fi
- if test x$as_have_required = xno; then :
- $as_echo "$0: This script requires a shell more modern than all"
- $as_echo "$0: the shells that I found on your system."
- if test x${ZSH_VERSION+set} = xset ; then
- $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
- $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+ if test x$as_have_required = xno
+then :
+ printf "%s\n" "$0: This script requires a shell more modern than all"
+ printf "%s\n" "$0: the shells that I found on your system."
+ if test ${ZSH_VERSION+y} ; then
+ printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+ printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later."
else
- $as_echo "$0: Please tell bug-autoconf@gnu.org and sjg@NetBSD.org
+ printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and sjg@NetBSD.org
$0: about your system, including any error possibly output
$0: before this message. Then install a modern shell, or
$0: manually run the script under such a shell if you do
@@ -294,6 +294,7 @@ as_fn_unset ()
}
as_unset=as_fn_unset
+
# as_fn_set_status STATUS
# -----------------------
# Set $? to STATUS, without forking.
@@ -311,6 +312,14 @@ as_fn_exit ()
as_fn_set_status $1
exit $1
} # as_fn_exit
+# as_fn_nop
+# ---------
+# Do nothing but, unlike ":", preserve the value of $?.
+as_fn_nop ()
+{
+ return $?
+}
+as_nop=as_fn_nop
# as_fn_mkdir_p
# -------------
@@ -325,7 +334,7 @@ as_fn_mkdir_p ()
as_dirs=
while :; do
case $as_dir in #(
- *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
*) as_qdir=$as_dir;;
esac
as_dirs="'$as_qdir' $as_dirs"
@@ -334,7 +343,7 @@ $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$as_dir" : 'X\(//\)[^/]' \| \
X"$as_dir" : 'X\(//\)$' \| \
X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$as_dir" |
+printf "%s\n" X"$as_dir" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
@@ -373,12 +382,13 @@ as_fn_executable_p ()
# advantage of any shell optimizations that allow amortized linear growth over
# repeated appends, instead of the typical quadratic growth present in naive
# implementations.
-if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null
+then :
eval 'as_fn_append ()
{
eval $1+=\$2
}'
-else
+else $as_nop
as_fn_append ()
{
eval $1=\$$1\$2
@@ -390,18 +400,27 @@ fi # as_fn_append
# Perform arithmetic evaluation on the ARGs, and store the result in the
# global $as_val. Take advantage of shells that can avoid forks. The arguments
# must be portable across $(()) and expr.
-if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null
+then :
eval 'as_fn_arith ()
{
as_val=$(( $* ))
}'
-else
+else $as_nop
as_fn_arith ()
{
as_val=`expr "$@" || test $? -eq 1`
}
fi # as_fn_arith
+# as_fn_nop
+# ---------
+# Do nothing but, unlike ":", preserve the value of $?.
+as_fn_nop ()
+{
+ return $?
+}
+as_nop=as_fn_nop
# as_fn_error STATUS ERROR [LINENO LOG_FD]
# ----------------------------------------
@@ -413,9 +432,9 @@ as_fn_error ()
as_status=$1; test $as_status -eq 0 && as_status=1
if test "$4"; then
as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
fi
- $as_echo "$as_me: error: $2" >&2
+ printf "%s\n" "$as_me: error: $2" >&2
as_fn_exit $as_status
} # as_fn_error
@@ -442,7 +461,7 @@ as_me=`$as_basename -- "$0" ||
$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
X"$0" : 'X\(//\)$' \| \
X"$0" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X/"$0" |
+printf "%s\n" X/"$0" |
sed '/^.*\/\([^/][^/]*\)\/*$/{
s//\1/
q
@@ -486,7 +505,7 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits
s/-\n.*//
' >$as_me.lineno &&
chmod +x "$as_me.lineno" ||
- { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+ { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
# If we had to re-execute with $CONFIG_SHELL, we're ensured to have
# already done that, so ensure we don't try to do so again and fall
@@ -500,6 +519,10 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits
exit
}
+
+# Determine whether it's possible to make 'echo' print without a newline.
+# These variables are no longer used directly by Autoconf, but are AC_SUBSTed
+# for compatibility with existing Makefiles.
ECHO_C= ECHO_N= ECHO_T=
case `echo -n x` in #(((((
-n*)
@@ -513,6 +536,13 @@ case `echo -n x` in #(((((
ECHO_N='-n';;
esac
+# For backward compatibility with old third-party macros, we provide
+# the shell variables $as_echo and $as_echo_n. New code should use
+# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively.
+as_echo='printf %s\n'
+as_echo_n='printf %s'
+
+
rm -f conf$$ conf$$.exe conf$$.file
if test -d conf$$.dir; then
rm -f conf$$.dir/conf$$.file
@@ -580,53 +610,52 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='bmake'
PACKAGE_TARNAME='bmake'
-PACKAGE_VERSION='20210201'
-PACKAGE_STRING='bmake 20210201'
+PACKAGE_VERSION='20240314'
+PACKAGE_STRING='bmake 20240314'
PACKAGE_BUGREPORT='sjg@NetBSD.org'
PACKAGE_URL=''
# Factoring default headers for most tests.
ac_includes_default="\
-#include <stdio.h>
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-#ifdef HAVE_SYS_STAT_H
-# include <sys/stat.h>
+#include <stddef.h>
+#ifdef HAVE_STDIO_H
+# include <stdio.h>
#endif
-#ifdef STDC_HEADERS
+#ifdef HAVE_STDLIB_H
# include <stdlib.h>
-# include <stddef.h>
-#else
-# ifdef HAVE_STDLIB_H
-# include <stdlib.h>
-# endif
#endif
#ifdef HAVE_STRING_H
-# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
-# include <memory.h>
-# endif
# include <string.h>
#endif
-#ifdef HAVE_STRINGS_H
-# include <strings.h>
-#endif
#ifdef HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif"
+ac_header_c_list=
+ac_func_c_list=
ac_subst_vars='LTLIBOBJS
+UTC_1
_MAKE_VERSION
filemon_h
use_filemon
use_meta
diff_u
+diff
GCC
INSTALL
default_sys_path
@@ -635,15 +664,15 @@ force_machine_arch
machine_arch
force_machine
machine
+force_make_os
+make_os
+egrep
LIBOBJS
bmake_path_max
ac_exe_suffix
INSTALL_DATA
INSTALL_SCRIPT
INSTALL_PROGRAM
-EGREP
-GREP
-CPP
OBJEXT
EXEEXT
ac_ct_CC
@@ -670,6 +699,7 @@ infodir
docdir
oldincludedir
includedir
+runstatedir
localstatedir
sharedstatedir
sysconfdir
@@ -696,6 +726,7 @@ with_defshell
with_makefile
with_meta
with_filemon
+with_bmake_strftime
with_machine
with_force_machine
with_force_machine_arch
@@ -713,8 +744,7 @@ CC
CFLAGS
LDFLAGS
LIBS
-CPPFLAGS
-CPP'
+CPPFLAGS'
# Initialize some variables set by options.
@@ -753,6 +783,7 @@ datadir='${datarootdir}'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -782,8 +813,6 @@ do
*) ac_optarg=yes ;;
esac
- # Accept the important Cygnus configure options, so we can diagnose typos.
-
case $ac_dashdash$ac_option in
--)
ac_dashdash=yes ;;
@@ -824,9 +853,9 @@ do
ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
- as_fn_error $? "invalid feature name: $ac_useropt"
+ as_fn_error $? "invalid feature name: \`$ac_useropt'"
ac_useropt_orig=$ac_useropt
- ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
*"
"enable_$ac_useropt"
@@ -850,9 +879,9 @@ do
ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
- as_fn_error $? "invalid feature name: $ac_useropt"
+ as_fn_error $? "invalid feature name: \`$ac_useropt'"
ac_useropt_orig=$ac_useropt
- ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
*"
"enable_$ac_useropt"
@@ -1005,6 +1034,15 @@ do
| -silent | --silent | --silen | --sile | --sil)
silent=yes ;;
+ -runstatedir | --runstatedir | --runstatedi | --runstated \
+ | --runstate | --runstat | --runsta | --runst | --runs \
+ | --run | --ru | --r)
+ ac_prev=runstatedir ;;
+ -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+ | --run=* | --ru=* | --r=*)
+ runstatedir=$ac_optarg ;;
+
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1054,9 +1092,9 @@ do
ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
- as_fn_error $? "invalid package name: $ac_useropt"
+ as_fn_error $? "invalid package name: \`$ac_useropt'"
ac_useropt_orig=$ac_useropt
- ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
*"
"with_$ac_useropt"
@@ -1070,9 +1108,9 @@ do
ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
- as_fn_error $? "invalid package name: $ac_useropt"
+ as_fn_error $? "invalid package name: \`$ac_useropt'"
ac_useropt_orig=$ac_useropt
- ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
*"
"with_$ac_useropt"
@@ -1116,9 +1154,9 @@ Try \`$0 --help' for more information"
*)
# FIXME: should be removed in autoconf 3.0.
- $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2
expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
- $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2
: "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
;;
@@ -1134,7 +1172,7 @@ if test -n "$ac_unrecognized_opts"; then
case $enable_option_checking in
no) ;;
fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
- *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
esac
fi
@@ -1142,7 +1180,7 @@ fi
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
- libdir localedir mandir
+ libdir localedir mandir runstatedir
do
eval ac_val=\$$ac_var
# Remove trailing slashes.
@@ -1198,7 +1236,7 @@ $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$as_myself" : 'X\(//\)[^/]' \| \
X"$as_myself" : 'X\(//\)$' \| \
X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$as_myself" |
+printf "%s\n" X"$as_myself" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
@@ -1255,7 +1293,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 20240314 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1295,6 +1333,7 @@ Fine tuning of the installation directories:
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include]
@@ -1316,7 +1355,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 20240314:";;
esac
cat <<\_ACEOF
@@ -1331,10 +1370,14 @@ Optional Features:
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
- --with-defshell=SHELL use SHELL by default - must be sh compatible, use sh or ksh to pick the internal definitions
+ --with-defshell=[name=]SHELL use SHELL by default
+ optional 'name' can be 'sh' to indicate SHELL is sh compatible
+ eg. --with-defshell=sh=/bin/bsh
+ use just 'sh' or 'ksh' to pick the internal definitions
--without-makefile disable use of generated makefile
--without-meta disable use of meta-mode
--with-filemon={no,dev,ktrace,path/filemon.h} indicate filemon method for meta-mode. Path to filemon.h implies dev
+ --with-bmake-strftime force use of bmake strftime
--with-machine=MACHINE explicitly set MACHINE
--with-force-machine=MACHINE set FORCE_MACHINE
--with-force-machine-arch=MACHINE set FORCE_MACHINE_ARCH
@@ -1354,7 +1397,6 @@ Some influential environment variables:
LIBS libraries to pass to the linker, e.g. -l<library>
CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
you have headers in a nonstandard directory <include dir>
- CPP C preprocessor
Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.
@@ -1375,9 +1417,9 @@ if test "$ac_init_help" = "recursive"; then
case "$ac_dir" in
.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
*)
- ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'`
# A ".." for each directory in $ac_dir_suffix.
- ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
case $ac_top_builddir_sub in
"") ac_top_builddir_sub=. ac_top_build_prefix= ;;
*) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
@@ -1405,7 +1447,8 @@ esac
ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
cd "$ac_dir" || { ac_status=$?; continue; }
- # Check for guested configure.
+ # Check for configure.gnu first; this name is used for a wrapper for
+ # Metaconfig's "Configure" on case-insensitive file systems.
if test -f "$ac_srcdir/configure.gnu"; then
echo &&
$SHELL "$ac_srcdir/configure.gnu" --help=recursive
@@ -1413,7 +1456,7 @@ ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
echo &&
$SHELL "$ac_srcdir/configure" --help=recursive
else
- $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2
fi || ac_status=$?
cd "$ac_pwd" || { ac_status=$?; break; }
done
@@ -1422,10 +1465,10 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-bmake configure 20210201
-generated by GNU Autoconf 2.69
+bmake configure 20240314
+generated by GNU Autoconf 2.71
-Copyright (C) 2012 Free Software Foundation, Inc.
+Copyright (C) 2021 Free Software Foundation, Inc.
This configure script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it.
_ACEOF
@@ -1442,14 +1485,14 @@ fi
ac_fn_c_try_compile ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- rm -f conftest.$ac_objext
+ rm -f conftest.$ac_objext conftest.beam
if { { ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_compile") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
@@ -1457,14 +1500,15 @@ $as_echo "$ac_try_echo"; } >&5
cat conftest.er1 >&5
mv -f conftest.er1 conftest.err
fi
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && {
test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
- } && test -s conftest.$ac_objext; then :
+ } && test -s conftest.$ac_objext
+then :
ac_retval=0
-else
- $as_echo "$as_me: failed program was:" >&5
+else $as_nop
+ printf "%s\n" "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
@@ -1474,138 +1518,90 @@ fi
} # ac_fn_c_try_compile
-# ac_fn_c_try_cpp LINENO
-# ----------------------
-# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
-ac_fn_c_try_cpp ()
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+printf %s "checking for $2... " >&6; }
+if eval test \${$3+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ eval "$3=yes"
+else $as_nop
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- if { { ac_try="$ac_cpp conftest.$ac_ext"
+ rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext
+ if { { ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
- (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+printf "%s\n" "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
grep -v '^ *+' conftest.err >conftest.er1
cat conftest.er1 >&5
mv -f conftest.er1 conftest.err
fi
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; } > conftest.i && {
- test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
- }; then :
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ test -x conftest$ac_exeext
+ }
+then :
ac_retval=0
-else
- $as_echo "$as_me: failed program was:" >&5
+else $as_nop
+ printf "%s\n" "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
- ac_retval=1
+ ac_retval=1
fi
+ # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+ # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+ # interfere with the next link command; also delete a directory that is
+ # left behind by Apple's compiler. We do this before executing the actions.
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
-} # ac_fn_c_try_cpp
-
-# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
-# -------------------------------------------------------
-# Tests whether HEADER exists, giving a warning if it cannot be compiled using
-# the include files in INCLUDES and setting the cache variable VAR
-# accordingly.
-ac_fn_c_check_header_mongrel ()
-{
- as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- if eval \${$3+:} false; then :
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
-$as_echo_n "checking for $2... " >&6; }
-if eval \${$3+:} false; then :
- $as_echo_n "(cached) " >&6
-fi
-eval ac_res=\$$3
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
-else
- # Is the header compilable?
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
-$as_echo_n "checking $2 usability... " >&6; }
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-$4
-#include <$2>
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- ac_header_compiler=yes
-else
- ac_header_compiler=no
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
-$as_echo "$ac_header_compiler" >&6; }
-
-# Is the header present?
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
-$as_echo_n "checking $2 presence... " >&6; }
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <$2>
-_ACEOF
-if ac_fn_c_try_cpp "$LINENO"; then :
- ac_header_preproc=yes
-else
- ac_header_preproc=no
-fi
-rm -f conftest.err conftest.i conftest.$ac_ext
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
-$as_echo "$ac_header_preproc" >&6; }
-
-# So? What about this header?
-case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
- yes:no: )
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
-$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
-$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
- ;;
- no:yes:* )
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
-$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5
-$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;}
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
-$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5
-$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;}
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
-$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
-( $as_echo "## ----------------------------- ##
-## Report this to sjg@NetBSD.org ##
-## ----------------------------- ##"
- ) | sed "s/^/$as_me: WARNING: /" >&2
- ;;
-esac
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
-$as_echo_n "checking for $2... " >&6; }
-if eval \${$3+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- eval "$3=\$ac_header_compiler"
-fi
-eval ac_res=\$$3
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
-fi
- eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
-
-} # ac_fn_c_check_header_mongrel
+} # ac_fn_c_try_link
# ac_fn_c_try_run LINENO
# ----------------------
-# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
-# that executables *can* be run.
+# Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that
+# executables *can* be run.
ac_fn_c_try_run ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
@@ -1615,25 +1611,26 @@ case "(($ac_try" in
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_link") 2>&5
ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
{ { case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_try") 2>&5
ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }; }; then :
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }
+then :
ac_retval=0
-else
- $as_echo "$as_me: program exited with status $ac_status" >&5
- $as_echo "$as_me: failed program was:" >&5
+else $as_nop
+ printf "%s\n" "$as_me: program exited with status $ac_status" >&5
+ printf "%s\n" "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=$ac_status
@@ -1644,82 +1641,85 @@ fi
} # ac_fn_c_try_run
-# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
-# -------------------------------------------------------
-# Tests whether HEADER exists and can be compiled using the include files in
-# INCLUDES, setting the cache variable VAR accordingly.
-ac_fn_c_check_header_compile ()
+# 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 $2" >&5
-$as_echo_n "checking for $2... " >&6; }
-if eval \${$3+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5
+printf %s "checking for int$2_t... " >&6; }
+if eval test \${$3+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ 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. */
-$4
-#include <$2>
+$ac_includes_default
+ enum { N = $2 / 2 - 1 };
+int
+main (void)
+{
+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 :
- eval "$3=yes"
-else
- eval "$3=no"
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-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
+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 (void)
+{
+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];
-} # ac_fn_c_check_header_compile
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
-# ac_fn_c_try_link LINENO
-# -----------------------
-# Try to link conftest.$ac_ext, and return whether this succeeded.
-ac_fn_c_try_link ()
-{
- as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- rm -f conftest.$ac_objext conftest$ac_exeext
- if { { ac_try="$ac_link"
-case "(($ac_try" in
- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
- *) ac_try_echo=$ac_try;;
+else $as_nop
+ case $ac_type in #(
+ int$2_t) :
+ eval "$3=yes" ;; #(
+ *) :
+ eval "$3=\$ac_type" ;;
esac
-eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
- (eval "$ac_link") 2>conftest.err
- ac_status=$?
- if test -s conftest.err; then
- grep -v '^ *+' conftest.err >conftest.er1
- cat conftest.er1 >&5
- mv -f conftest.er1 conftest.err
- fi
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; } && {
- test -z "$ac_c_werror_flag" ||
- test ! -s conftest.err
- } && test -s conftest$ac_exeext && {
- test "$cross_compiling" = yes ||
- test -x conftest$ac_exeext
- }; then :
- ac_retval=0
-else
- $as_echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ if eval test \"x\$"$3"\" = x"no"
+then :
- ac_retval=1
+else $as_nop
+ break
fi
- # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
- # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
- # interfere with the next link command; also delete a directory that is
- # left behind by Apple's compiler. We do this before executing the actions.
- rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ done
+fi
+eval ac_res=\$$3
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
- as_fn_set_status $ac_retval
-} # ac_fn_c_try_link
+} # ac_fn_c_find_intX_t
# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
# -------------------------------------------
@@ -1728,17 +1728,18 @@ fi
ac_fn_c_check_type ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
-$as_echo_n "checking for $2... " >&6; }
-if eval \${$3+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+printf %s "checking for $2... " >&6; }
+if eval test \${$3+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
eval "$3=no"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
-main ()
+main (void)
{
if (sizeof ($2))
return 0;
@@ -1746,12 +1747,13 @@ if (sizeof ($2))
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
-main ()
+main (void)
{
if (sizeof (($2)))
return 0;
@@ -1759,18 +1761,19 @@ if (sizeof (($2)))
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
-else
+else $as_nop
eval "$3=yes"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
eval ac_res=\$$3
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_type
@@ -1782,11 +1785,12 @@ $as_echo "$ac_res" >&6; }
ac_fn_c_find_uintX_t ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5
-$as_echo_n "checking for uint$2_t... " >&6; }
-if eval \${$3+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5
+printf %s "checking for uint$2_t... " >&6; }
+if eval test \${$3+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
eval "$3=no"
# Order is important - never check a type that is potentially smaller
# than half of the expected target width.
@@ -1796,7 +1800,7 @@ else
/* end confdefs.h. */
$ac_includes_default
int
-main ()
+main (void)
{
static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)];
test_array [0] = 0;
@@ -1806,7 +1810,8 @@ return test_array [0];
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
case $ac_type in #(
uint$2_t) :
eval "$3=yes" ;; #(
@@ -1814,40 +1819,44 @@ if ac_fn_c_try_compile "$LINENO"; then :
eval "$3=\$ac_type" ;;
esac
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
- if eval test \"x\$"$3"\" = x"no"; then :
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ if eval test \"x\$"$3"\" = x"no"
+then :
-else
+else $as_nop
break
fi
done
fi
eval ac_res=\$$3
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_find_uintX_t
-# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES
-# ---------------------------------------------
+# ac_fn_check_decl LINENO SYMBOL VAR INCLUDES EXTRA-OPTIONS FLAG-VAR
+# ------------------------------------------------------------------
# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
-# accordingly.
-ac_fn_c_check_decl ()
+# accordingly. Pass EXTRA-OPTIONS to the compiler, using FLAG-VAR.
+ac_fn_check_decl ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
as_decl_name=`echo $2|sed 's/ *(.*//'`
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5
+printf %s "checking whether $as_decl_name is declared... " >&6; }
+if eval test \${$3+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'`
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5
-$as_echo_n "checking whether $as_decl_name is declared... " >&6; }
-if eval \${$3+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ eval ac_save_FLAGS=\$$6
+ as_fn_append $6 " $5"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
-main ()
+main (void)
{
#ifndef $as_decl_name
#ifdef __cplusplus
@@ -1861,19 +1870,22 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
eval "$3=yes"
-else
+else $as_nop
eval "$3=no"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ eval $6=\$ac_save_FLAGS
+
fi
eval ac_res=\$$3
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
-} # ac_fn_c_check_decl
+} # ac_fn_check_decl
# ac_fn_c_check_func LINENO FUNC VAR
# ----------------------------------
@@ -1881,11 +1893,12 @@ $as_echo "$ac_res" >&6; }
ac_fn_c_check_func ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
-$as_echo_n "checking for $2... " >&6; }
-if eval \${$3+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+printf %s "checking for $2... " >&6; }
+if eval test \${$3+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
@@ -1893,16 +1906,9 @@ else
#define $2 innocuous_$2
/* System header to define __stub macros and hopefully few prototypes,
- which can conflict with char $2 (); below.
- Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
- <limits.h> exists even on freestanding compilers. */
-
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
-#endif
+ which can conflict with char $2 (); below. */
+#include <limits.h>
#undef $2
/* Override any GCC internal prototype to avoid an error.
@@ -1920,92 +1926,56 @@ choke me
#endif
int
-main ()
+main (void)
{
return $2 ();
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
eval "$3=yes"
-else
+else $as_nop
eval "$3=no"
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
eval ac_res=\$$3
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_func
+ac_configure_args_raw=
+for ac_arg
+do
+ case $ac_arg in
+ *\'*)
+ ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ as_fn_append ac_configure_args_raw " '$ac_arg'"
+done
-# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES
-# ----------------------------------------------------
-# Tries to find if the field MEMBER exists in type AGGR, after including
-# INCLUDES, setting cache variable VAR accordingly.
-ac_fn_c_check_member ()
-{
- as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5
-$as_echo_n "checking for $2.$3... " >&6; }
-if eval \${$4+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-$5
-int
-main ()
-{
-static $2 ac_aggr;
-if (ac_aggr.$3)
-return 0;
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- eval "$4=yes"
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-$5
-int
-main ()
-{
-static $2 ac_aggr;
-if (sizeof ac_aggr.$3)
-return 0;
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- eval "$4=yes"
-else
- eval "$4=no"
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-eval ac_res=\$$4
- { $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
+case $ac_configure_args_raw in
+ *$as_nl*)
+ ac_safe_unquote= ;;
+ *)
+ ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab.
+ ac_unsafe_a="$ac_unsafe_z#~"
+ ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g"
+ ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;;
+esac
-} # ac_fn_c_check_member
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
-generated by GNU Autoconf 2.69. Invocation command line was
+It was created by bmake $as_me 20240314, which was
+generated by GNU Autoconf 2.71. Invocation command line was
- $ $0 $@
+ $ $0$ac_configure_args_raw
_ACEOF
exec 5>>config.log
@@ -2038,8 +2008,12 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- $as_echo "PATH: $as_dir"
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ printf "%s\n" "PATH: $as_dir"
done
IFS=$as_save_IFS
@@ -2074,7 +2048,7 @@ do
| -silent | --silent | --silen | --sile | --sil)
continue ;;
*\'*)
- ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
esac
case $ac_pass in
1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
@@ -2109,11 +2083,13 @@ done
# WARNING: Use '\'' to represent an apostrophe within the trap.
# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
trap 'exit_status=$?
+ # Sanitize IFS.
+ IFS=" "" $as_nl"
# Save into config.log some information that might help in debugging.
{
echo
- $as_echo "## ---------------- ##
+ printf "%s\n" "## ---------------- ##
## Cache variables. ##
## ---------------- ##"
echo
@@ -2124,8 +2100,8 @@ trap 'exit_status=$?
case $ac_val in #(
*${as_nl}*)
case $ac_var in #(
- *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
-$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
esac
case $ac_var in #(
_ | IFS | as_nl) ;; #(
@@ -2149,7 +2125,7 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
)
echo
- $as_echo "## ----------------- ##
+ printf "%s\n" "## ----------------- ##
## Output variables. ##
## ----------------- ##"
echo
@@ -2157,14 +2133,14 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
do
eval ac_val=\$$ac_var
case $ac_val in
- *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
esac
- $as_echo "$ac_var='\''$ac_val'\''"
+ printf "%s\n" "$ac_var='\''$ac_val'\''"
done | sort
echo
if test -n "$ac_subst_files"; then
- $as_echo "## ------------------- ##
+ printf "%s\n" "## ------------------- ##
## File substitutions. ##
## ------------------- ##"
echo
@@ -2172,15 +2148,15 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
do
eval ac_val=\$$ac_var
case $ac_val in
- *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
esac
- $as_echo "$ac_var='\''$ac_val'\''"
+ printf "%s\n" "$ac_var='\''$ac_val'\''"
done | sort
echo
fi
if test -s confdefs.h; then
- $as_echo "## ----------- ##
+ printf "%s\n" "## ----------- ##
## confdefs.h. ##
## ----------- ##"
echo
@@ -2188,8 +2164,8 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
echo
fi
test "$ac_signal" != 0 &&
- $as_echo "$as_me: caught signal $ac_signal"
- $as_echo "$as_me: exit $exit_status"
+ printf "%s\n" "$as_me: caught signal $ac_signal"
+ printf "%s\n" "$as_me: exit $exit_status"
} >&5
rm -f core *.core core.conftest.* &&
rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
@@ -2203,63 +2179,48 @@ ac_signal=0
# confdefs.h avoids OS command line length limits that DEFS can exceed.
rm -f -r conftest* confdefs.h
-$as_echo "/* confdefs.h */" > confdefs.h
+printf "%s\n" "/* confdefs.h */" > confdefs.h
# Predefined preprocessor variables.
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE_NAME "$PACKAGE_NAME"
-_ACEOF
+printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
-_ACEOF
+printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE_VERSION "$PACKAGE_VERSION"
-_ACEOF
+printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE_STRING "$PACKAGE_STRING"
-_ACEOF
+printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
-_ACEOF
+printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE_URL "$PACKAGE_URL"
-_ACEOF
+printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h
# Let the site file select an alternate cache file if it wants to.
# Prefer an explicitly selected file to automatically selected ones.
-ac_site_file1=NONE
-ac_site_file2=NONE
if test -n "$CONFIG_SITE"; then
- # We do not want a PATH search for config.site.
- case $CONFIG_SITE in #((
- -*) ac_site_file1=./$CONFIG_SITE;;
- */*) ac_site_file1=$CONFIG_SITE;;
- *) ac_site_file1=./$CONFIG_SITE;;
- esac
+ ac_site_files="$CONFIG_SITE"
elif test "x$prefix" != xNONE; then
- ac_site_file1=$prefix/share/config.site
- ac_site_file2=$prefix/etc/config.site
+ ac_site_files="$prefix/share/config.site $prefix/etc/config.site"
else
- ac_site_file1=$ac_default_prefix/share/config.site
- ac_site_file2=$ac_default_prefix/etc/config.site
+ ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
fi
-for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+
+for ac_site_file in $ac_site_files
do
- test "x$ac_site_file" = xNONE && continue
- if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
-$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+ case $ac_site_file in #(
+ */*) :
+ ;; #(
+ *) :
+ ac_site_file=./$ac_site_file ;;
+esac
+ if test -f "$ac_site_file" && test -r "$ac_site_file"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;}
sed 's/^/| /' "$ac_site_file" >&5
. "$ac_site_file" \
- || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+ || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "failed to load site script $ac_site_file
See \`config.log' for more details" "$LINENO" 5; }
fi
@@ -2269,19 +2230,441 @@ if test -r "$cache_file"; then
# Some versions of bash will fail to source /dev/null (special files
# actually), so we avoid doing that. DJGPP emulates it as a regular file.
if test /dev/null != "$cache_file" && test -f "$cache_file"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
-$as_echo "$as_me: loading cache $cache_file" >&6;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+printf "%s\n" "$as_me: loading cache $cache_file" >&6;}
case $cache_file in
[\\/]* | ?:[\\/]* ) . "$cache_file";;
*) . "./$cache_file";;
esac
fi
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
-$as_echo "$as_me: creating cache $cache_file" >&6;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+printf "%s\n" "$as_me: creating cache $cache_file" >&6;}
>$cache_file
fi
+as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H"
+# Test code for whether the C compiler supports C89 (global declarations)
+ac_c_conftest_c89_globals='
+/* Does the compiler advertise C89 conformance?
+ Do not test the value of __STDC__, because some compilers set it to 0
+ while being otherwise adequately conformant. */
+#if !defined __STDC__
+# error "Compiler does not advertise C89 conformance"
+#endif
+
+#include <stddef.h>
+#include <stdarg.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */
+struct buf { int x; };
+struct buf * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not \xHH hex character constants.
+ These do not provoke an error unfortunately, instead are silently treated
+ as an "x". The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously \x00 != x always comes out true, for an
+ array size at least. It is necessary to write \x00 == 0 to get something
+ that is true only with -std. */
+int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) '\''x'\''
+int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int),
+ int, int);'
+
+# Test code for whether the C compiler supports C89 (body of main).
+ac_c_conftest_c89_main='
+ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]);
+'
+
+# Test code for whether the C compiler supports C99 (global declarations)
+ac_c_conftest_c99_globals='
+// Does the compiler advertise C99 conformance?
+#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
+# error "Compiler does not advertise C99 conformance"
+#endif
+
+#include <stdbool.h>
+extern int puts (const char *);
+extern int printf (const char *, ...);
+extern int dprintf (int, const char *, ...);
+extern void *malloc (size_t);
+
+// Check varargs macros. These examples are taken from C99 6.10.3.5.
+// dprintf is used instead of fprintf to avoid needing to declare
+// FILE and stderr.
+#define debug(...) dprintf (2, __VA_ARGS__)
+#define showlist(...) puts (#__VA_ARGS__)
+#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__))
+static void
+test_varargs_macros (void)
+{
+ int x = 1234;
+ int y = 5678;
+ debug ("Flag");
+ debug ("X = %d\n", x);
+ showlist (The first, second, and third items.);
+ report (x>y, "x is %d but y is %d", x, y);
+}
+
+// Check long long types.
+#define BIG64 18446744073709551615ull
+#define BIG32 4294967295ul
+#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0)
+#if !BIG_OK
+ #error "your preprocessor is broken"
+#endif
+#if BIG_OK
+#else
+ #error "your preprocessor is broken"
+#endif
+static long long int bignum = -9223372036854775807LL;
+static unsigned long long int ubignum = BIG64;
+
+struct incomplete_array
+{
+ int datasize;
+ double data[];
+};
+
+struct named_init {
+ int number;
+ const wchar_t *name;
+ double average;
+};
+
+typedef const char *ccp;
+
+static inline int
+test_restrict (ccp restrict text)
+{
+ // See if C++-style comments work.
+ // Iterate through items via the restricted pointer.
+ // Also check for declarations in for loops.
+ for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i)
+ continue;
+ return 0;
+}
+
+// Check varargs and va_copy.
+static bool
+test_varargs (const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ va_list args_copy;
+ va_copy (args_copy, args);
+
+ const char *str = "";
+ int number = 0;
+ float fnumber = 0;
+
+ while (*format)
+ {
+ switch (*format++)
+ {
+ case '\''s'\'': // string
+ str = va_arg (args_copy, const char *);
+ break;
+ case '\''d'\'': // int
+ number = va_arg (args_copy, int);
+ break;
+ case '\''f'\'': // float
+ fnumber = va_arg (args_copy, double);
+ break;
+ default:
+ break;
+ }
+ }
+ va_end (args_copy);
+ va_end (args);
+
+ return *str && number && fnumber;
+}
+'
+
+# Test code for whether the C compiler supports C99 (body of main).
+ac_c_conftest_c99_main='
+ // Check bool.
+ _Bool success = false;
+ success |= (argc != 0);
+
+ // Check restrict.
+ if (test_restrict ("String literal") == 0)
+ success = true;
+ char *restrict newvar = "Another string";
+
+ // Check varargs.
+ success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234);
+ test_varargs_macros ();
+
+ // Check flexible array members.
+ struct incomplete_array *ia =
+ malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10));
+ ia->datasize = 10;
+ for (int i = 0; i < ia->datasize; ++i)
+ ia->data[i] = i * 1.234;
+
+ // Check named initializers.
+ struct named_init ni = {
+ .number = 34,
+ .name = L"Test wide string",
+ .average = 543.34343,
+ };
+
+ ni.number = 58;
+
+ int dynamic_array[ni.number];
+ dynamic_array[0] = argv[0][0];
+ dynamic_array[ni.number - 1] = 543;
+
+ // work around unused variable warnings
+ ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\''
+ || dynamic_array[ni.number - 1] != 543);
+'
+
+# Test code for whether the C compiler supports C11 (global declarations)
+ac_c_conftest_c11_globals='
+// Does the compiler advertise C11 conformance?
+#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L
+# error "Compiler does not advertise C11 conformance"
+#endif
+
+// Check _Alignas.
+char _Alignas (double) aligned_as_double;
+char _Alignas (0) no_special_alignment;
+extern char aligned_as_int;
+char _Alignas (0) _Alignas (int) aligned_as_int;
+
+// Check _Alignof.
+enum
+{
+ int_alignment = _Alignof (int),
+ int_array_alignment = _Alignof (int[100]),
+ char_alignment = _Alignof (char)
+};
+_Static_assert (0 < -_Alignof (int), "_Alignof is signed");
+
+// Check _Noreturn.
+int _Noreturn does_not_return (void) { for (;;) continue; }
+
+// Check _Static_assert.
+struct test_static_assert
+{
+ int x;
+ _Static_assert (sizeof (int) <= sizeof (long int),
+ "_Static_assert does not work in struct");
+ long int y;
+};
+
+// Check UTF-8 literals.
+#define u8 syntax error!
+char const utf8_literal[] = u8"happens to be ASCII" "another string";
+
+// Check duplicate typedefs.
+typedef long *long_ptr;
+typedef long int *long_ptr;
+typedef long_ptr long_ptr;
+
+// Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1.
+struct anonymous
+{
+ union {
+ struct { int i; int j; };
+ struct { int k; long int l; } w;
+ };
+ int m;
+} v1;
+'
+
+# Test code for whether the C compiler supports C11 (body of main).
+ac_c_conftest_c11_main='
+ _Static_assert ((offsetof (struct anonymous, i)
+ == offsetof (struct anonymous, w.k)),
+ "Anonymous union alignment botch");
+ v1.i = 2;
+ v1.w.k = 5;
+ ok |= v1.i != 5;
+'
+
+# Test code for whether the C compiler supports C11 (complete).
+ac_c_conftest_c11_program="${ac_c_conftest_c89_globals}
+${ac_c_conftest_c99_globals}
+${ac_c_conftest_c11_globals}
+
+int
+main (int argc, char **argv)
+{
+ int ok = 0;
+ ${ac_c_conftest_c89_main}
+ ${ac_c_conftest_c99_main}
+ ${ac_c_conftest_c11_main}
+ return ok;
+}
+"
+
+# Test code for whether the C compiler supports C99 (complete).
+ac_c_conftest_c99_program="${ac_c_conftest_c89_globals}
+${ac_c_conftest_c99_globals}
+
+int
+main (int argc, char **argv)
+{
+ int ok = 0;
+ ${ac_c_conftest_c89_main}
+ ${ac_c_conftest_c99_main}
+ return ok;
+}
+"
+
+# Test code for whether the C compiler supports C89 (complete).
+ac_c_conftest_c89_program="${ac_c_conftest_c89_globals}
+
+int
+main (int argc, char **argv)
+{
+ int ok = 0;
+ ${ac_c_conftest_c89_main}
+ return ok;
+}
+"
+
+as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H"
+as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H"
+as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H"
+as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H"
+as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H"
+as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H"
+as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H"
+as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H"
+as_fn_append ac_header_c_list " wchar.h wchar_h HAVE_WCHAR_H"
+as_fn_append ac_header_c_list " minix/config.h minix_config_h HAVE_MINIX_CONFIG_H"
+as_fn_append ac_header_c_list " sys/time.h sys_time_h HAVE_SYS_TIME_H"
+as_fn_append ac_header_c_list " vfork.h vfork_h HAVE_VFORK_H"
+as_fn_append ac_func_c_list " fork HAVE_FORK"
+as_fn_append ac_func_c_list " vfork HAVE_VFORK"
+as_fn_append ac_func_c_list " vprintf HAVE_VPRINTF"
+
+# Auxiliary files required by this configure script.
+ac_aux_files="install-sh"
+
+# Locations in which to look for auxiliary files.
+ac_aux_dir_candidates="${srcdir}${PATH_SEPARATOR}${srcdir}/..${PATH_SEPARATOR}${srcdir}/../.."
+
+# Search for a directory containing all of the required auxiliary files,
+# $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates.
+# If we don't find one directory that contains all the files we need,
+# we report the set of missing files from the *first* directory in
+# $ac_aux_dir_candidates and give up.
+ac_missing_aux_files=""
+ac_first_candidate=:
+printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in $ac_aux_dir_candidates
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ as_found=:
+
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5
+ ac_aux_dir_found=yes
+ ac_install_sh=
+ for ac_aux in $ac_aux_files
+ do
+ # As a special case, if "install-sh" is required, that requirement
+ # can be satisfied by any of "install-sh", "install.sh", or "shtool",
+ # and $ac_install_sh is set appropriately for whichever one is found.
+ if test x"$ac_aux" = x"install-sh"
+ then
+ if test -f "${as_dir}install-sh"; then
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5
+ ac_install_sh="${as_dir}install-sh -c"
+ elif test -f "${as_dir}install.sh"; then
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5
+ ac_install_sh="${as_dir}install.sh -c"
+ elif test -f "${as_dir}shtool"; then
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5
+ ac_install_sh="${as_dir}shtool install -c"
+ else
+ ac_aux_dir_found=no
+ if $ac_first_candidate; then
+ ac_missing_aux_files="${ac_missing_aux_files} install-sh"
+ else
+ break
+ fi
+ fi
+ else
+ if test -f "${as_dir}${ac_aux}"; then
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5
+ else
+ ac_aux_dir_found=no
+ if $ac_first_candidate; then
+ ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}"
+ else
+ break
+ fi
+ fi
+ fi
+ done
+ if test "$ac_aux_dir_found" = yes; then
+ ac_aux_dir="$as_dir"
+ break
+ fi
+ ac_first_candidate=false
+
+ as_found=false
+done
+IFS=$as_save_IFS
+if $as_found
+then :
+
+else $as_nop
+ as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5
+fi
+
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+if test -f "${ac_aux_dir}config.guess"; then
+ ac_config_guess="$SHELL ${ac_aux_dir}config.guess"
+fi
+if test -f "${ac_aux_dir}config.sub"; then
+ ac_config_sub="$SHELL ${ac_aux_dir}config.sub"
+fi
+if test -f "$ac_aux_dir/configure"; then
+ ac_configure="$SHELL ${ac_aux_dir}configure"
+fi
+
# Check that the precious variables saved in the cache have kept the same
# value.
ac_cache_corrupted=false
@@ -2292,12 +2675,12 @@ for ac_var in $ac_precious_vars; do
eval ac_new_val=\$ac_env_${ac_var}_value
case $ac_old_set,$ac_new_set in
set,)
- { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
-$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
ac_cache_corrupted=: ;;
,set)
- { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
-$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
ac_cache_corrupted=: ;;
,);;
*)
@@ -2306,24 +2689,24 @@ $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
ac_old_val_w=`echo x $ac_old_val`
ac_new_val_w=`echo x $ac_new_val`
if test "$ac_old_val_w" != "$ac_new_val_w"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
-$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
ac_cache_corrupted=:
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
-$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
eval $ac_var=\$ac_old_val
fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
-$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
- { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
-$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
+printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
+printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;}
fi;;
esac
# Pass precious variables to config.status.
if test "$ac_new_set" = set; then
case $ac_new_val in
- *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
*) ac_arg=$ac_var=$ac_new_val ;;
esac
case " $ac_configure_args " in
@@ -2333,11 +2716,12 @@ $as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
fi
done
if $ac_cache_corrupted; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
- { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
-$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
- as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;}
+ as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file'
+ and start over" "$LINENO" 5
fi
## -------------------- ##
## Main body of script. ##
@@ -2355,35 +2739,72 @@ 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|*/bsh)
+ DEFSHELL_INDEX=DEFSHELL_INDEX_SH;;
+ *=*) # eg. sh=/bin/bsh
+ eval `IFS="="; set -- $1; echo name=$1 defshell_path=$2`
+ case "$name" in
+ csh) DEFSHELL_INDEX=DEFSHELL_INDEX_CSH;;
+ ksh) DEFSHELL_INDEX=DEFSHELL_INDEX_KSH;;
+ sh) DEFSHELL_INDEX=DEFSHELL_INDEX_SH;;
+ *) DEFSHELL_INDEX=DEFSHELL_INDEX_CUSTOM;;
+ esac
+ ;;
+ *) DEFSHELL_INDEX=DEFSHELL_INDEX_CUSTOM
+ defshell_path=$1
+ ;;
+ esac
+ case "$defshell_path,$1" in
+ ,/bin/*) ;;
+ ,*/*) defshell_path=$1;;
+ esac
+}
# Check whether --with-defshell was given.
-if test "${with_defshell+set}" = set; then :
+if test ${with_defshell+y}
+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
+FORCE_MAKE_OS=
+make_os=
case "$OS" in
-CYGWIN*|MINGW*) use_makefile=no;;
+CYGWIN*)
+ use_makefile=no
+ OS=Cygwin
+ FORCE_MAKE_OS=$OS
+ ;;
+Darwin|MINGW*) use_makefile=no;;
*) use_makefile=yes;;
esac
+if test "x$FORCE_MAKE_OS" != x; then
+ force_make_os=FORCE_
+ make_os=${FORCE_MAKE_OS}
+fi
# Check whether --with-makefile was given.
-if test "${with_makefile+set}" = set; then :
+if test ${with_makefile+y}
+then :
withval=$with_makefile; case "${withval}" in
yes|no) use_makefile=${withval};;
*) as_fn_error $? "bad value ${withval} given for makefile" "$LINENO" 5 ;;
@@ -2393,7 +2814,8 @@ fi
use_meta=yes
# Check whether --with-meta was given.
-if test "${with_meta+set}" = set; then :
+if test ${with_meta+y}
+then :
withval=$with_meta; case "${withval}" in
yes|no) use_meta=${withval};;
*) as_fn_error $? "bad value ${withval} given for meta" "$LINENO" 5 ;;
@@ -2402,7 +2824,8 @@ fi
# Check whether --with-filemon was given.
-if test "${with_filemon+set}" = set; then :
+if test ${with_filemon+y}
+then :
withval=$with_filemon; case "/${withval}" in
/no) use_filemon=no;;
/*trace) filemon_h=no use_filemon="${withval}";;
@@ -2414,7 +2837,7 @@ case "$use_filemon,$filemon_h" in
,*.h) use_filemon=dev;;
esac
-else
+else $as_nop
case "$OS" in
NetBSD) filemon_h=no use_filemon=ktrace;;
@@ -2438,6 +2861,15 @@ esac
fi
+
+# Check whether --with-bmake_strftime was given.
+if test ${with_bmake_strftime+y}
+then :
+ withval=$with_bmake_strftime; case "${withval}" in
+yes|no) bmake_strftime=$withval;;
+esac
+fi
+
case "$use_meta" in
yes)
case "$use_filemon" in
@@ -2446,6 +2878,50 @@ 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"
+ FORCE_USE_SHELL=1
+ ;;
+esac
+if test "x$FORCE_USE_SHELL" != x; then
+ CPPFLAGS="${CPPFLAGS} -DFORCE_USE_SHELL"
+fi
+# Not everyone groks TZ=Europe/Berlin
+# which is used by the localtime tests
+echo $ECHO_N "checking whether system has timezone Europe/Berlin... $ECHO_C" >&6
+eval `TZ=UTC date '+utc_H=%H utc_d=%d' 2> /dev/null`
+eval `TZ=Europe/Berlin date '+utc1_H=%H utc1_d=%d' 2> /dev/null`
+if test ${utc_d-0} -lt ${utc1_d-0} -o ${utc_H-0} -lt ${utc1_H-0}; then
+ echo yes >&6
+ UTC_1=Europe/Berlin
+else
+ eval `TZ=UTC-1 date '+utc1_H=%H utc1_d=%d' 2> /dev/null`
+ if test ${utc_d-0} -lt ${utc1_d-0} -o ${utc_H-0} -lt ${utc1_H-0}; then
+ UTC_1=UTC-1
+ echo no, using UTC-1 >&6
+ fi
+fi
+test "x$UTC_1" = x && echo no >&6
+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'
@@ -2454,11 +2930,12 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
set dummy ${ac_tool_prefix}gcc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
@@ -2466,11 +2943,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="${ac_tool_prefix}gcc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -2481,11 +2962,11 @@ fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -2494,11 +2975,12 @@ if test -z "$ac_cv_prog_CC"; then
ac_ct_CC=$CC
# Extract the first word of "gcc", so it can be a program name with args.
set dummy gcc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_ac_ct_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
if test -n "$ac_ct_CC"; then
ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
else
@@ -2506,11 +2988,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CC="gcc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -2521,11 +3007,11 @@ fi
fi
ac_ct_CC=$ac_cv_prog_ac_ct_CC
if test -n "$ac_ct_CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
-$as_echo "$ac_ct_CC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
if test "x$ac_ct_CC" = x; then
@@ -2533,8 +3019,8 @@ fi
else
case $cross_compiling:$ac_tool_warned in
yes:)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
CC=$ac_ct_CC
@@ -2547,11 +3033,12 @@ if test -z "$CC"; then
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
set dummy ${ac_tool_prefix}cc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
@@ -2559,11 +3046,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="${ac_tool_prefix}cc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -2574,11 +3065,11 @@ fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -2587,11 +3078,12 @@ fi
if test -z "$CC"; then
# Extract the first word of "cc", so it can be a program name with args.
set dummy cc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
@@ -2600,15 +3092,19 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
- if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
ac_prog_rejected=yes
continue
fi
ac_cv_prog_CC="cc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -2624,18 +3120,18 @@ if test $ac_prog_rejected = yes; then
# However, it has the same basename, so the bogon will be chosen
# first if we set CC to just the basename; use the full file name.
shift
- ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@"
fi
fi
fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -2646,11 +3142,12 @@ if test -z "$CC"; then
do
# Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
set dummy $ac_tool_prefix$ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
@@ -2658,11 +3155,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -2673,11 +3174,11 @@ fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -2690,11 +3191,12 @@ if test -z "$CC"; then
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_ac_ct_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
if test -n "$ac_ct_CC"; then
ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
else
@@ -2702,11 +3204,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CC="$ac_prog"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -2717,11 +3223,11 @@ fi
fi
ac_ct_CC=$ac_cv_prog_ac_ct_CC
if test -n "$ac_ct_CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
-$as_echo "$ac_ct_CC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -2733,8 +3239,8 @@ done
else
case $cross_compiling:$ac_tool_warned in
yes:)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
CC=$ac_ct_CC
@@ -2742,25 +3248,129 @@ esac
fi
fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args.
+set dummy ${ac_tool_prefix}clang; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}clang"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "clang", so it can be a program name with args.
+set dummy clang; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="clang"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
-test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+fi
+
+
+test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "no acceptable C compiler found in \$PATH
See \`config.log' for more details" "$LINENO" 5; }
# Provide some information about the compiler.
-$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
set X $ac_compile
ac_compiler=$2
-for ac_option in --version -v -V -qversion; do
+for ac_option in --version -v -V -qversion -version; do
{ { ac_try="$ac_compiler $ac_option >&5"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_compiler $ac_option >&5") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
@@ -2770,7 +3380,7 @@ $as_echo "$ac_try_echo"; } >&5
cat conftest.er1 >&5
fi
rm -f conftest.er1 conftest.err
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
done
@@ -2778,7 +3388,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
;
@@ -2790,9 +3400,9 @@ ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
# Try to create an executable without -o first, disregard a.out.
# It will help us diagnose broken compilers, and finding out an intuition
# of exeext.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
-$as_echo_n "checking whether the C compiler works... " >&6; }
-ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+printf %s "checking whether the C compiler works... " >&6; }
+ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
# The possible output files:
ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
@@ -2813,11 +3423,12 @@ case "(($ac_try" in
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_link_default") 2>&5
ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }; then :
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+then :
# Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
# in a Makefile. We should not override ac_cv_exeext if it was cached,
@@ -2834,7 +3445,7 @@ do
# certainly right.
break;;
*.* )
- if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no;
then :; else
ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
fi
@@ -2850,44 +3461,46 @@ do
done
test "$ac_cv_exeext" = no && ac_cv_exeext=
-else
+else $as_nop
ac_file=''
fi
-if test -z "$ac_file"; then :
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-$as_echo "$as_me: failed program was:" >&5
+if test -z "$ac_file"
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+printf "%s\n" "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
-{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error 77 "C compiler cannot create executables
See \`config.log' for more details" "$LINENO" 5; }
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
-$as_echo_n "checking for C compiler default output file name... " >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
-$as_echo "$ac_file" >&6; }
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+printf %s "checking for C compiler default output file name... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+printf "%s\n" "$ac_file" >&6; }
ac_exeext=$ac_cv_exeext
rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
ac_clean_files=$ac_clean_files_save
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
-$as_echo_n "checking for suffix of executables... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+printf %s "checking for suffix of executables... " >&6; }
if { { ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_link") 2>&5
ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }; then :
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+then :
# If both `conftest.exe' and `conftest' are `present' (well, observable)
# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
# work properly (i.e., refer to `conftest.exe'), while it won't with
@@ -2901,15 +3514,15 @@ for ac_file in conftest.exe conftest conftest.*; do
* ) break;;
esac
done
-else
- { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+else $as_nop
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot compute suffix of executables: cannot compile and link
See \`config.log' for more details" "$LINENO" 5; }
fi
rm -f conftest conftest$ac_cv_exeext
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
-$as_echo "$ac_cv_exeext" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+printf "%s\n" "$ac_cv_exeext" >&6; }
rm -f conftest.$ac_ext
EXEEXT=$ac_cv_exeext
@@ -2918,7 +3531,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
int
-main ()
+main (void)
{
FILE *f = fopen ("conftest.out", "w");
return ferror (f) || fclose (f) != 0;
@@ -2930,8 +3543,8 @@ _ACEOF
ac_clean_files="$ac_clean_files conftest.out"
# Check that the compiler produces executables we can run. If not, either
# the compiler is broken, or we cross compile.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
-$as_echo_n "checking whether we are cross compiling... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+printf %s "checking whether we are cross compiling... " >&6; }
if test "$cross_compiling" != yes; then
{ { ac_try="$ac_link"
case "(($ac_try" in
@@ -2939,10 +3552,10 @@ case "(($ac_try" in
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_link") 2>&5
ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
if { ac_try='./conftest$ac_cv_exeext'
{ { case "(($ac_try" in
@@ -2950,39 +3563,40 @@ $as_echo "$ac_try_echo"; } >&5
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_try") 2>&5
ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; }; then
cross_compiling=no
else
if test "$cross_compiling" = maybe; then
cross_compiling=yes
else
- { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "cannot run C compiled programs.
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot run C compiled programs.
If you meant to cross compile, use \`--host'.
See \`config.log' for more details" "$LINENO" 5; }
fi
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
-$as_echo "$cross_compiling" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+printf "%s\n" "$cross_compiling" >&6; }
rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
ac_clean_files=$ac_clean_files_save
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
-$as_echo_n "checking for suffix of object files... " >&6; }
-if ${ac_cv_objext+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+printf %s "checking for suffix of object files... " >&6; }
+if test ${ac_cv_objext+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
;
@@ -2996,11 +3610,12 @@ case "(($ac_try" in
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_compile") 2>&5
ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }; then :
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+then :
for ac_file in conftest.o conftest.obj conftest.*; do
test -f "$ac_file" || continue;
case $ac_file in
@@ -3009,31 +3624,32 @@ $as_echo "$ac_try_echo"; } >&5
break;;
esac
done
-else
- $as_echo "$as_me: failed program was:" >&5
+else $as_nop
+ printf "%s\n" "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
-{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot compute suffix of object files: cannot compile
See \`config.log' for more details" "$LINENO" 5; }
fi
rm -f conftest.$ac_cv_objext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
-$as_echo "$ac_cv_objext" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+printf "%s\n" "$ac_cv_objext" >&6; }
OBJEXT=$ac_cv_objext
ac_objext=$OBJEXT
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
-$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
-if ${ac_cv_c_compiler_gnu+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5
+printf %s "checking whether the compiler supports GNU C... " >&6; }
+if test ${ac_cv_c_compiler_gnu+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
#ifndef __GNUC__
choke me
@@ -3043,29 +3659,33 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_compiler_gnu=yes
-else
+else $as_nop
ac_compiler_gnu=no
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
ac_cv_c_compiler_gnu=$ac_compiler_gnu
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
-$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; }
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
if test $ac_compiler_gnu = yes; then
GCC=yes
else
GCC=
fi
-ac_test_CFLAGS=${CFLAGS+set}
+ac_test_CFLAGS=${CFLAGS+y}
ac_save_CFLAGS=$CFLAGS
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
-$as_echo_n "checking whether $CC accepts -g... " >&6; }
-if ${ac_cv_prog_cc_g+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+printf %s "checking whether $CC accepts -g... " >&6; }
+if test ${ac_cv_prog_cc_g+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_save_c_werror_flag=$ac_c_werror_flag
ac_c_werror_flag=yes
ac_cv_prog_cc_g=no
@@ -3074,57 +3694,60 @@ else
/* end confdefs.h. */
int
-main ()
+main (void)
{
;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_cv_prog_cc_g=yes
-else
+else $as_nop
CFLAGS=""
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
-else
+else $as_nop
ac_c_werror_flag=$ac_save_c_werror_flag
CFLAGS="-g"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_cv_prog_cc_g=yes
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
ac_c_werror_flag=$ac_save_c_werror_flag
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
-$as_echo "$ac_cv_prog_cc_g" >&6; }
-if test "$ac_test_CFLAGS" = set; then
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+printf "%s\n" "$ac_cv_prog_cc_g" >&6; }
+if test $ac_test_CFLAGS; then
CFLAGS=$ac_save_CFLAGS
elif test $ac_cv_prog_cc_g = yes; then
if test "$GCC" = yes; then
@@ -3139,232 +3762,144 @@ else
CFLAGS=
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
-$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
-if ${ac_cv_prog_cc_c89+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- ac_cv_prog_cc_c89=no
+ac_prog_cc_stdc=no
+if test x$ac_prog_cc_stdc = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5
+printf %s "checking for $CC option to enable C11 features... " >&6; }
+if test ${ac_cv_prog_cc_c11+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_prog_cc_c11=no
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-#include <stdarg.h>
-#include <stdio.h>
-struct stat;
-/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
-struct buf { int x; };
-FILE * (*rcsopen) (struct buf *, struct stat *, int);
-static char *e (p, i)
- char **p;
- int i;
-{
- return p[i];
-}
-static char *f (char * (*g) (char **, int), char **p, ...)
-{
- char *s;
- va_list v;
- va_start (v,p);
- s = g (p, va_arg (v,int));
- va_end (v);
- return s;
-}
-
-/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
- function prototypes and stuff, but not '\xHH' hex character constants.
- These don't provoke an error unfortunately, instead are silently treated
- as 'x'. The following induces an error, until -std is added to get
- proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
- array size at least. It's necessary to write '\x00'==0 to get something
- that's true only with -std. */
-int osf4_cc_array ['\x00' == 0 ? 1 : -1];
-
-/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
- inside strings and character constants. */
-#define FOO(x) 'x'
-int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
-
-int test (int i, double x);
-struct s1 {int (*f) (int a);};
-struct s2 {int (*f) (double a);};
-int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
-int argc;
-char **argv;
-int
-main ()
-{
-return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
- ;
- return 0;
-}
+$ac_c_conftest_c11_program
_ACEOF
-for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
- -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+for ac_arg in '' -std=gnu11
do
CC="$ac_save_CC $ac_arg"
- if ac_fn_c_try_compile "$LINENO"; then :
- ac_cv_prog_cc_c89=$ac_arg
+ if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_prog_cc_c11=$ac_arg
fi
-rm -f core conftest.err conftest.$ac_objext
- test "x$ac_cv_prog_cc_c89" != "xno" && break
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+ test "x$ac_cv_prog_cc_c11" != "xno" && break
done
rm -f conftest.$ac_ext
CC=$ac_save_CC
-
fi
-# AC_CACHE_VAL
-case "x$ac_cv_prog_cc_c89" in
- x)
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
-$as_echo "none needed" >&6; } ;;
- xno)
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
-$as_echo "unsupported" >&6; } ;;
- *)
- CC="$CC $ac_cv_prog_cc_c89"
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
-$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
-esac
-if test "x$ac_cv_prog_cc_c89" != xno; then :
-fi
-
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-
-
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
-$as_echo_n "checking how to run the C preprocessor... " >&6; }
-# On Suns, sometimes $CPP names a directory.
-if test -n "$CPP" && test -d "$CPP"; then
- CPP=
-fi
-if test -z "$CPP"; then
- if ${ac_cv_prog_CPP+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- # Double quotes because CPP needs to be expanded
- for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
- do
- ac_preproc_ok=false
-for ac_c_preproc_warn_flag in '' yes
-do
- # Use a header file that comes with gcc, so configuring glibc
- # with a fresh cross-compiler works.
- # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
- # <limits.h> exists even on freestanding compilers.
- # On the NeXT, cc -E runs the code through the compiler's parser,
- # not just through cpp. "Syntax error" is here to catch this case.
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+if test "x$ac_cv_prog_cc_c11" = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+ if test "x$ac_cv_prog_cc_c11" = x
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
+printf "%s\n" "$ac_cv_prog_cc_c11" >&6; }
+ CC="$CC $ac_cv_prog_cc_c11"
+fi
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11
+ ac_prog_cc_stdc=c11
+fi
+fi
+if test x$ac_prog_cc_stdc = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5
+printf %s "checking for $CC option to enable C99 features... " >&6; }
+if test ${ac_cv_prog_cc_c99+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_prog_cc_c99=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
-#endif
- Syntax error
+$ac_c_conftest_c99_program
_ACEOF
-if ac_fn_c_try_cpp "$LINENO"; then :
-
-else
- # Broken: fails on valid input.
-continue
+for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99=
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_prog_cc_c99=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+ test "x$ac_cv_prog_cc_c99" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
fi
-rm -f conftest.err conftest.i conftest.$ac_ext
- # OK, works on sane cases. Now check whether nonexistent headers
- # can be detected and how.
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+if test "x$ac_cv_prog_cc_c99" = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+ if test "x$ac_cv_prog_cc_c99" = x
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
+printf "%s\n" "$ac_cv_prog_cc_c99" >&6; }
+ CC="$CC $ac_cv_prog_cc_c99"
+fi
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
+ ac_prog_cc_stdc=c99
+fi
+fi
+if test x$ac_prog_cc_stdc = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5
+printf %s "checking for $CC option to enable C89 features... " >&6; }
+if test ${ac_cv_prog_cc_c89+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-#include <ac_nonexistent.h>
+$ac_c_conftest_c89_program
_ACEOF
-if ac_fn_c_try_cpp "$LINENO"; then :
- # Broken: success on invalid input.
-continue
-else
- # Passes both tests.
-ac_preproc_ok=:
-break
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_prog_cc_c89=$ac_arg
fi
-rm -f conftest.err conftest.i conftest.$ac_ext
-
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
done
-# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
-rm -f conftest.i conftest.err conftest.$ac_ext
-if $ac_preproc_ok; then :
- break
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
fi
- done
- ac_cv_prog_CPP=$CPP
-
+if test "x$ac_cv_prog_cc_c89" = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+ if test "x$ac_cv_prog_cc_c89" = x
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+printf "%s\n" "$ac_cv_prog_cc_c89" >&6; }
+ CC="$CC $ac_cv_prog_cc_c89"
fi
- CPP=$ac_cv_prog_CPP
-else
- ac_cv_prog_CPP=$CPP
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
+ ac_prog_cc_stdc=c89
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
-$as_echo "$CPP" >&6; }
-ac_preproc_ok=false
-for ac_c_preproc_warn_flag in '' yes
-do
- # Use a header file that comes with gcc, so configuring glibc
- # with a fresh cross-compiler works.
- # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
- # <limits.h> exists even on freestanding compilers.
- # On the NeXT, cc -E runs the code through the compiler's parser,
- # not just through cpp. "Syntax error" is here to catch this case.
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
-#endif
- Syntax error
-_ACEOF
-if ac_fn_c_try_cpp "$LINENO"; then :
-
-else
- # Broken: fails on valid input.
-continue
-fi
-rm -f conftest.err conftest.i conftest.$ac_ext
-
- # OK, works on sane cases. Now check whether nonexistent headers
- # can be detected and how.
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <ac_nonexistent.h>
-_ACEOF
-if ac_fn_c_try_cpp "$LINENO"; then :
- # Broken: success on invalid input.
-continue
-else
- # Passes both tests.
-ac_preproc_ok=:
-break
-fi
-rm -f conftest.err conftest.i conftest.$ac_ext
-
-done
-# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
-rm -f conftest.i conftest.err conftest.$ac_ext
-if $ac_preproc_ok; then :
-
-else
- { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
-See \`config.log' for more details" "$LINENO" 5; }
fi
ac_ext=c
@@ -3374,325 +3909,175 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $
ac_compiler_gnu=$ac_cv_c_compiler_gnu
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
-$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
-if ${ac_cv_path_GREP+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test -z "$GREP"; then
- ac_path_GREP_found=false
- # Loop through the user's path and test for each of PROGNAME-LIST
- as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+ac_header= ac_cache=
+for ac_item in $ac_header_c_list
do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- for ac_prog in grep ggrep; do
- for ac_exec_ext in '' $ac_executable_extensions; do
- ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
- as_fn_executable_p "$ac_path_GREP" || continue
-# Check for GNU ac_path_GREP and select it if it is found.
- # Check for GNU $ac_path_GREP
-case `"$ac_path_GREP" --version 2>&1` in
-*GNU*)
- ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
-*)
- ac_count=0
- $as_echo_n 0123456789 >"conftest.in"
- while :
- do
- cat "conftest.in" "conftest.in" >"conftest.tmp"
- mv "conftest.tmp" "conftest.in"
- cp "conftest.in" "conftest.nl"
- $as_echo 'GREP' >> "conftest.nl"
- "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
- diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
- as_fn_arith $ac_count + 1 && ac_count=$as_val
- if test $ac_count -gt ${ac_path_GREP_max-0}; then
- # Best one so far, save it but keep looking for a better one
- ac_cv_path_GREP="$ac_path_GREP"
- ac_path_GREP_max=$ac_count
+ if test $ac_cache; then
+ ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default"
+ if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then
+ printf "%s\n" "#define $ac_item 1" >> confdefs.h
fi
- # 10*(2^10) chars as input seems more than enough
- test $ac_count -gt 10 && break
- done
- rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
-esac
-
- $ac_path_GREP_found && break 3
- done
- done
- done
-IFS=$as_save_IFS
- if test -z "$ac_cv_path_GREP"; then
- as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ ac_header= ac_cache=
+ elif test $ac_header; then
+ ac_cache=$ac_item
+ else
+ ac_header=$ac_item
fi
-else
- ac_cv_path_GREP=$GREP
-fi
+done
+
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
-$as_echo "$ac_cv_path_GREP" >&6; }
- GREP="$ac_cv_path_GREP"
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
-$as_echo_n "checking for egrep... " >&6; }
-if ${ac_cv_path_EGREP+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
- then ac_cv_path_EGREP="$GREP -E"
- else
- if test -z "$EGREP"; then
- ac_path_EGREP_found=false
- # Loop through the user's path and test for each of PROGNAME-LIST
- as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
-do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- for ac_prog in egrep; do
- for ac_exec_ext in '' $ac_executable_extensions; do
- ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
- as_fn_executable_p "$ac_path_EGREP" || continue
-# Check for GNU ac_path_EGREP and select it if it is found.
- # Check for GNU $ac_path_EGREP
-case `"$ac_path_EGREP" --version 2>&1` in
-*GNU*)
- ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
-*)
- ac_count=0
- $as_echo_n 0123456789 >"conftest.in"
- while :
- do
- cat "conftest.in" "conftest.in" >"conftest.tmp"
- mv "conftest.tmp" "conftest.in"
- cp "conftest.in" "conftest.nl"
- $as_echo 'EGREP' >> "conftest.nl"
- "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
- diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
- as_fn_arith $ac_count + 1 && ac_count=$as_val
- if test $ac_count -gt ${ac_path_EGREP_max-0}; then
- # Best one so far, save it but keep looking for a better one
- ac_cv_path_EGREP="$ac_path_EGREP"
- ac_path_EGREP_max=$ac_count
- fi
- # 10*(2^10) chars as input seems more than enough
- test $ac_count -gt 10 && break
- done
- rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
-esac
- $ac_path_EGREP_found && break 3
- done
- done
- done
-IFS=$as_save_IFS
- if test -z "$ac_cv_path_EGREP"; then
- as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
- fi
-else
- ac_cv_path_EGREP=$EGREP
-fi
- fi
+
+
+if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes
+then :
+
+printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h
+
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
-$as_echo "$ac_cv_path_EGREP" >&6; }
- EGREP="$ac_cv_path_EGREP"
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
-$as_echo_n "checking for ANSI C header files... " >&6; }
-if ${ac_cv_header_stdc+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+
+
+
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5
+printf %s "checking whether it is safe to define __EXTENSIONS__... " >&6; }
+if test ${ac_cv_safe_to_define___extensions__+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <float.h>
+# define __EXTENSIONS__ 1
+ $ac_includes_default
int
-main ()
+main (void)
{
;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- ac_cv_header_stdc=yes
-else
- ac_cv_header_stdc=no
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-
-if test $ac_cv_header_stdc = yes; then
- # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <string.h>
-
-_ACEOF
-if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
- $EGREP "memchr" >/dev/null 2>&1; then :
-
-else
- ac_cv_header_stdc=no
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_safe_to_define___extensions__=yes
+else $as_nop
+ ac_cv_safe_to_define___extensions__=no
fi
-rm -f conftest*
-
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5
+printf "%s\n" "$ac_cv_safe_to_define___extensions__" >&6; }
-if test $ac_cv_header_stdc = yes; then
- # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether _XOPEN_SOURCE should be defined" >&5
+printf %s "checking whether _XOPEN_SOURCE should be defined... " >&6; }
+if test ${ac_cv_should_define__xopen_source+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_should_define__xopen_source=no
+ if test $ac_cv_header_wchar_h = yes
+then :
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-#include <stdlib.h>
-
-_ACEOF
-if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
- $EGREP "free" >/dev/null 2>&1; then :
-else
- ac_cv_header_stdc=no
-fi
-rm -f conftest*
+ #include <wchar.h>
+ mbstate_t x;
+int
+main (void)
+{
-fi
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
-if test $ac_cv_header_stdc = yes; then
- # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
- if test "$cross_compiling" = yes; then :
- :
-else
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-#include <ctype.h>
-#include <stdlib.h>
-#if ((' ' & 0x0FF) == 0x020)
-# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
-# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
-#else
-# define ISLOWER(c) \
- (('a' <= (c) && (c) <= 'i') \
- || ('j' <= (c) && (c) <= 'r') \
- || ('s' <= (c) && (c) <= 'z'))
-# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
-#endif
-#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+ #define _XOPEN_SOURCE 500
+ #include <wchar.h>
+ mbstate_t x;
int
-main ()
+main (void)
{
- int i;
- for (i = 0; i < 256; i++)
- if (XOR (islower (i), ISLOWER (i))
- || toupper (i) != TOUPPER (i))
- return 2;
+
+ ;
return 0;
}
_ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
-
-else
- ac_cv_header_stdc=no
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_should_define__xopen_source=yes
fi
-rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
- conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
-$as_echo "$ac_cv_header_stdc" >&6; }
-if test $ac_cv_header_stdc = yes; then
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_should_define__xopen_source" >&5
+printf "%s\n" "$ac_cv_should_define__xopen_source" >&6; }
-$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+ printf "%s\n" "#define _ALL_SOURCE 1" >>confdefs.h
-fi
+ printf "%s\n" "#define _DARWIN_C_SOURCE 1" >>confdefs.h
-# On IRIX 5.3, sys/types and inttypes.h are conflicting.
-for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
- inttypes.h stdint.h unistd.h
-do :
- as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
-ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
-"
-if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
- cat >>confdefs.h <<_ACEOF
-#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
-_ACEOF
+ printf "%s\n" "#define _GNU_SOURCE 1" >>confdefs.h
-fi
+ printf "%s\n" "#define _HPUX_ALT_XOPEN_SOCKET_API 1" >>confdefs.h
-done
+ printf "%s\n" "#define _NETBSD_SOURCE 1" >>confdefs.h
+ printf "%s\n" "#define _OPENBSD_SOURCE 1" >>confdefs.h
+ printf "%s\n" "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h
- ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default"
-if test "x$ac_cv_header_minix_config_h" = xyes; then :
- MINIX=yes
-else
- MINIX=
-fi
+ printf "%s\n" "#define __STDC_WANT_IEC_60559_ATTRIBS_EXT__ 1" >>confdefs.h
+ printf "%s\n" "#define __STDC_WANT_IEC_60559_BFP_EXT__ 1" >>confdefs.h
- if test "$MINIX" = yes; then
+ printf "%s\n" "#define __STDC_WANT_IEC_60559_DFP_EXT__ 1" >>confdefs.h
-$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h
+ printf "%s\n" "#define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1" >>confdefs.h
+ printf "%s\n" "#define __STDC_WANT_IEC_60559_TYPES_EXT__ 1" >>confdefs.h
-$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h
+ printf "%s\n" "#define __STDC_WANT_LIB_EXT2__ 1" >>confdefs.h
+ printf "%s\n" "#define __STDC_WANT_MATH_SPEC_FUNCS__ 1" >>confdefs.h
-$as_echo "#define _MINIX 1" >>confdefs.h
+ printf "%s\n" "#define _TANDEM_SOURCE 1" >>confdefs.h
- fi
+ if test $ac_cv_header_minix_config_h = yes
+then :
+ MINIX=yes
+ printf "%s\n" "#define _MINIX 1" >>confdefs.h
+ printf "%s\n" "#define _POSIX_SOURCE 1" >>confdefs.h
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5
-$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; }
-if ${ac_cv_safe_to_define___extensions__+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
+ printf "%s\n" "#define _POSIX_1_SOURCE 2" >>confdefs.h
-# define __EXTENSIONS__ 1
- $ac_includes_default
-int
-main ()
-{
-
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- ac_cv_safe_to_define___extensions__=yes
-else
- ac_cv_safe_to_define___extensions__=no
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else $as_nop
+ MINIX=
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5
-$as_echo "$ac_cv_safe_to_define___extensions__" >&6; }
- test $ac_cv_safe_to_define___extensions__ = yes &&
- $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h
-
- $as_echo "#define _ALL_SOURCE 1" >>confdefs.h
-
- $as_echo "#define _GNU_SOURCE 1" >>confdefs.h
+ if test $ac_cv_safe_to_define___extensions__ = yes
+then :
+ printf "%s\n" "#define __EXTENSIONS__ 1" >>confdefs.h
- $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h
-
- $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h
+fi
+ if test $ac_cv_should_define__xopen_source = yes
+then :
+ printf "%s\n" "#define _XOPEN_SOURCE 500" >>confdefs.h
+fi
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
@@ -3702,11 +4087,12 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
set dummy ${ac_tool_prefix}gcc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
@@ -3714,11 +4100,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="${ac_tool_prefix}gcc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -3729,11 +4119,11 @@ fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -3742,11 +4132,12 @@ if test -z "$ac_cv_prog_CC"; then
ac_ct_CC=$CC
# Extract the first word of "gcc", so it can be a program name with args.
set dummy gcc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_ac_ct_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
if test -n "$ac_ct_CC"; then
ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
else
@@ -3754,11 +4145,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CC="gcc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -3769,11 +4164,11 @@ fi
fi
ac_ct_CC=$ac_cv_prog_ac_ct_CC
if test -n "$ac_ct_CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
-$as_echo "$ac_ct_CC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
if test "x$ac_ct_CC" = x; then
@@ -3781,8 +4176,8 @@ fi
else
case $cross_compiling:$ac_tool_warned in
yes:)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
CC=$ac_ct_CC
@@ -3795,11 +4190,12 @@ if test -z "$CC"; then
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
set dummy ${ac_tool_prefix}cc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
@@ -3807,11 +4203,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="${ac_tool_prefix}cc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -3822,11 +4222,11 @@ fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -3835,11 +4235,12 @@ fi
if test -z "$CC"; then
# Extract the first word of "cc", so it can be a program name with args.
set dummy cc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
@@ -3848,15 +4249,19 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
- if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
ac_prog_rejected=yes
continue
fi
ac_cv_prog_CC="cc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -3872,18 +4277,18 @@ if test $ac_prog_rejected = yes; then
# However, it has the same basename, so the bogon will be chosen
# first if we set CC to just the basename; use the full file name.
shift
- ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@"
fi
fi
fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -3894,11 +4299,12 @@ if test -z "$CC"; then
do
# Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
set dummy $ac_tool_prefix$ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
@@ -3906,11 +4312,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -3921,11 +4331,11 @@ fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -3938,11 +4348,12 @@ if test -z "$CC"; then
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_ac_ct_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
if test -n "$ac_ct_CC"; then
ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
else
@@ -3950,11 +4361,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CC="$ac_prog"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -3965,11 +4380,11 @@ fi
fi
ac_ct_CC=$ac_cv_prog_ac_ct_CC
if test -n "$ac_ct_CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
-$as_echo "$ac_ct_CC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -3981,34 +4396,138 @@ done
else
case $cross_compiling:$ac_tool_warned in
yes:)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args.
+set dummy ${ac_tool_prefix}clang; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}clang"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "clang", so it can be a program name with args.
+set dummy clang; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="clang"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
CC=$ac_ct_CC
fi
+else
+ CC="$ac_cv_prog_CC"
fi
fi
-test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "no acceptable C compiler found in \$PATH
See \`config.log' for more details" "$LINENO" 5; }
# Provide some information about the compiler.
-$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
set X $ac_compile
ac_compiler=$2
-for ac_option in --version -v -V -qversion; do
+for ac_option in --version -v -V -qversion -version; do
{ { ac_try="$ac_compiler $ac_option >&5"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_compiler $ac_option >&5") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
@@ -4018,20 +4537,21 @@ $as_echo "$ac_try_echo"; } >&5
cat conftest.er1 >&5
fi
rm -f conftest.er1 conftest.err
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
done
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
-$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
-if ${ac_cv_c_compiler_gnu+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5
+printf %s "checking whether the compiler supports GNU C... " >&6; }
+if test ${ac_cv_c_compiler_gnu+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
#ifndef __GNUC__
choke me
@@ -4041,29 +4561,33 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_compiler_gnu=yes
-else
+else $as_nop
ac_compiler_gnu=no
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
ac_cv_c_compiler_gnu=$ac_compiler_gnu
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
-$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; }
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
if test $ac_compiler_gnu = yes; then
GCC=yes
else
GCC=
fi
-ac_test_CFLAGS=${CFLAGS+set}
+ac_test_CFLAGS=${CFLAGS+y}
ac_save_CFLAGS=$CFLAGS
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
-$as_echo_n "checking whether $CC accepts -g... " >&6; }
-if ${ac_cv_prog_cc_g+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+printf %s "checking whether $CC accepts -g... " >&6; }
+if test ${ac_cv_prog_cc_g+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_save_c_werror_flag=$ac_c_werror_flag
ac_c_werror_flag=yes
ac_cv_prog_cc_g=no
@@ -4072,57 +4596,60 @@ else
/* end confdefs.h. */
int
-main ()
+main (void)
{
;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_cv_prog_cc_g=yes
-else
+else $as_nop
CFLAGS=""
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
-else
+else $as_nop
ac_c_werror_flag=$ac_save_c_werror_flag
CFLAGS="-g"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_cv_prog_cc_g=yes
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
ac_c_werror_flag=$ac_save_c_werror_flag
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
-$as_echo "$ac_cv_prog_cc_g" >&6; }
-if test "$ac_test_CFLAGS" = set; then
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+printf "%s\n" "$ac_cv_prog_cc_g" >&6; }
+if test $ac_test_CFLAGS; then
CFLAGS=$ac_save_CFLAGS
elif test $ac_cv_prog_cc_g = yes; then
if test "$GCC" = yes; then
@@ -4137,309 +4664,155 @@ else
CFLAGS=
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
-$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
-if ${ac_cv_prog_cc_c89+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- ac_cv_prog_cc_c89=no
+ac_prog_cc_stdc=no
+if test x$ac_prog_cc_stdc = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5
+printf %s "checking for $CC option to enable C11 features... " >&6; }
+if test ${ac_cv_prog_cc_c11+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_prog_cc_c11=no
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-#include <stdarg.h>
-#include <stdio.h>
-struct stat;
-/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
-struct buf { int x; };
-FILE * (*rcsopen) (struct buf *, struct stat *, int);
-static char *e (p, i)
- char **p;
- int i;
-{
- return p[i];
-}
-static char *f (char * (*g) (char **, int), char **p, ...)
-{
- char *s;
- va_list v;
- va_start (v,p);
- s = g (p, va_arg (v,int));
- va_end (v);
- return s;
-}
-
-/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
- function prototypes and stuff, but not '\xHH' hex character constants.
- These don't provoke an error unfortunately, instead are silently treated
- as 'x'. The following induces an error, until -std is added to get
- proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
- array size at least. It's necessary to write '\x00'==0 to get something
- that's true only with -std. */
-int osf4_cc_array ['\x00' == 0 ? 1 : -1];
-
-/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
- inside strings and character constants. */
-#define FOO(x) 'x'
-int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
-
-int test (int i, double x);
-struct s1 {int (*f) (int a);};
-struct s2 {int (*f) (double a);};
-int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
-int argc;
-char **argv;
-int
-main ()
-{
-return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
- ;
- return 0;
-}
+$ac_c_conftest_c11_program
_ACEOF
-for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
- -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+for ac_arg in '' -std=gnu11
do
CC="$ac_save_CC $ac_arg"
- if ac_fn_c_try_compile "$LINENO"; then :
- ac_cv_prog_cc_c89=$ac_arg
+ if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_prog_cc_c11=$ac_arg
fi
-rm -f core conftest.err conftest.$ac_objext
- test "x$ac_cv_prog_cc_c89" != "xno" && break
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+ test "x$ac_cv_prog_cc_c11" != "xno" && break
done
rm -f conftest.$ac_ext
CC=$ac_save_CC
-
-fi
-# AC_CACHE_VAL
-case "x$ac_cv_prog_cc_c89" in
- x)
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
-$as_echo "none needed" >&6; } ;;
- xno)
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
-$as_echo "unsupported" >&6; } ;;
- *)
- CC="$CC $ac_cv_prog_cc_c89"
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
-$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
-esac
-if test "x$ac_cv_prog_cc_c89" != xno; then :
-
fi
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5
-$as_echo_n "checking for $CC option to accept ISO C99... " >&6; }
-if ${ac_cv_prog_cc_c99+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+if test "x$ac_cv_prog_cc_c11" = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+ if test "x$ac_cv_prog_cc_c11" = x
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
+printf "%s\n" "$ac_cv_prog_cc_c11" >&6; }
+ CC="$CC $ac_cv_prog_cc_c11"
+fi
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11
+ ac_prog_cc_stdc=c11
+fi
+fi
+if test x$ac_prog_cc_stdc = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5
+printf %s "checking for $CC option to enable C99 features... " >&6; }
+if test ${ac_cv_prog_cc_c99+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_cv_prog_cc_c99=no
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <wchar.h>
-#include <stdio.h>
-
-// Check varargs macros. These examples are taken from C99 6.10.3.5.
-#define debug(...) fprintf (stderr, __VA_ARGS__)
-#define showlist(...) puts (#__VA_ARGS__)
-#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__))
-static void
-test_varargs_macros (void)
-{
- int x = 1234;
- int y = 5678;
- debug ("Flag");
- debug ("X = %d\n", x);
- showlist (The first, second, and third items.);
- report (x>y, "x is %d but y is %d", x, y);
-}
-
-// Check long long types.
-#define BIG64 18446744073709551615ull
-#define BIG32 4294967295ul
-#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0)
-#if !BIG_OK
- your preprocessor is broken;
-#endif
-#if BIG_OK
-#else
- your preprocessor is broken;
-#endif
-static long long int bignum = -9223372036854775807LL;
-static unsigned long long int ubignum = BIG64;
-
-struct incomplete_array
-{
- int datasize;
- double data[];
-};
-
-struct named_init {
- int number;
- const wchar_t *name;
- double average;
-};
-
-typedef const char *ccp;
-
-static inline int
-test_restrict (ccp restrict text)
-{
- // See if C++-style comments work.
- // Iterate through items via the restricted pointer.
- // Also check for declarations in for loops.
- for (unsigned int i = 0; *(text+i) != '\0'; ++i)
- continue;
- return 0;
-}
-
-// Check varargs and va_copy.
-static void
-test_varargs (const char *format, ...)
-{
- va_list args;
- va_start (args, format);
- va_list args_copy;
- va_copy (args_copy, args);
-
- const char *str;
- int number;
- float fnumber;
-
- while (*format)
- {
- switch (*format++)
- {
- case 's': // string
- str = va_arg (args_copy, const char *);
- break;
- case 'd': // int
- number = va_arg (args_copy, int);
- break;
- case 'f': // float
- fnumber = va_arg (args_copy, double);
- break;
- default:
- break;
- }
- }
- va_end (args_copy);
- va_end (args);
-}
-
-int
-main ()
-{
-
- // Check bool.
- _Bool success = false;
-
- // Check restrict.
- if (test_restrict ("String literal") == 0)
- success = true;
- char *restrict newvar = "Another string";
-
- // Check varargs.
- test_varargs ("s, d' f .", "string", 65, 34.234);
- test_varargs_macros ();
-
- // Check flexible array members.
- struct incomplete_array *ia =
- malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10));
- ia->datasize = 10;
- for (int i = 0; i < ia->datasize; ++i)
- ia->data[i] = i * 1.234;
-
- // Check named initializers.
- struct named_init ni = {
- .number = 34,
- .name = L"Test wide string",
- .average = 543.34343,
- };
-
- ni.number = 58;
-
- int dynamic_array[ni.number];
- dynamic_array[ni.number - 1] = 543;
-
- // work around unused variable warnings
- return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x'
- || dynamic_array[ni.number - 1] != 543);
-
- ;
- return 0;
-}
+$ac_c_conftest_c99_program
_ACEOF
-for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99
+for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99=
do
CC="$ac_save_CC $ac_arg"
- if ac_fn_c_try_compile "$LINENO"; then :
+ if ac_fn_c_try_compile "$LINENO"
+then :
ac_cv_prog_cc_c99=$ac_arg
fi
-rm -f core conftest.err conftest.$ac_objext
+rm -f core conftest.err conftest.$ac_objext conftest.beam
test "x$ac_cv_prog_cc_c99" != "xno" && break
done
rm -f conftest.$ac_ext
CC=$ac_save_CC
-
fi
-# AC_CACHE_VAL
-case "x$ac_cv_prog_cc_c99" in
- x)
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
-$as_echo "none needed" >&6; } ;;
- xno)
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
-$as_echo "unsupported" >&6; } ;;
- *)
- CC="$CC $ac_cv_prog_cc_c99"
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
-$as_echo "$ac_cv_prog_cc_c99" >&6; } ;;
-esac
-if test "x$ac_cv_prog_cc_c99" != xno; then :
+if test "x$ac_cv_prog_cc_c99" = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+ if test "x$ac_cv_prog_cc_c99" = x
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
+printf "%s\n" "$ac_cv_prog_cc_c99" >&6; }
+ CC="$CC $ac_cv_prog_cc_c99"
+fi
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
+ ac_prog_cc_stdc=c99
+fi
+fi
+if test x$ac_prog_cc_stdc = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5
+printf %s "checking for $CC option to enable C89 features... " >&6; }
+if test ${ac_cv_prog_cc_c89+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_c_conftest_c89_program
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_prog_cc_c89=$ac_arg
fi
-
-
-ac_aux_dir=
-for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
- if test -f "$ac_dir/install-sh"; then
- ac_aux_dir=$ac_dir
- ac_install_sh="$ac_aux_dir/install-sh -c"
- break
- elif test -f "$ac_dir/install.sh"; then
- ac_aux_dir=$ac_dir
- ac_install_sh="$ac_aux_dir/install.sh -c"
- break
- elif test -f "$ac_dir/shtool"; then
- ac_aux_dir=$ac_dir
- ac_install_sh="$ac_aux_dir/shtool install -c"
- break
- fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
done
-if test -z "$ac_aux_dir"; then
- as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
fi
-# These three variables are undocumented and unsupported,
-# and are intended to be withdrawn in a future Autoconf release.
-# They can cause serious problems if a builder's source tree is in a directory
-# whose full name contains unusual characters.
-ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
-ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
-ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+if test "x$ac_cv_prog_cc_c89" = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+ if test "x$ac_cv_prog_cc_c89" = x
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+printf "%s\n" "$ac_cv_prog_cc_c89" >&6; }
+ CC="$CC $ac_cv_prog_cc_c89"
+fi
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
+ ac_prog_cc_stdc=c89
+fi
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
-# Find a good install program. We prefer a C program (faster),
+ # Find a good install program. We prefer a C program (faster),
# so one script is as good as another. But avoid the broken or
# incompatible versions:
# SysV /etc/install, /usr/sbin/install
@@ -4453,20 +4826,25 @@ ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
# OS/2's system install, which has a completely different semantic
# ./install, which can be erroneously created by make from ./install.sh.
# Reject install programs that cannot install multiple files.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
-$as_echo_n "checking for a BSD-compatible install... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+printf %s "checking for a BSD-compatible install... " >&6; }
if test -z "$INSTALL"; then
-if ${ac_cv_path_install+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+if test ${ac_cv_path_install+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- # Account for people who put trailing slashes in PATH elements.
-case $as_dir/ in #((
- ./ | .// | /[cC]/* | \
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ # Account for fact that we put trailing slashes in our PATH walk.
+case $as_dir in #((
+ ./ | /[cC]/* | \
/etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
/usr/ucb/* ) ;;
@@ -4476,13 +4854,13 @@ case $as_dir/ in #((
# by default.
for ac_prog in ginstall scoinst install; do
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then
if test $ac_prog = install &&
- grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
# AIX install. It has an incompatible calling convention.
:
elif test $ac_prog = install &&
- grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
# program-specific install script used by HP pwplus--don't use.
:
else
@@ -4490,12 +4868,12 @@ case $as_dir/ in #((
echo one > conftest.one
echo two > conftest.two
mkdir conftest.dir
- if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+ if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" &&
test -s conftest.one && test -s conftest.two &&
test -s conftest.dir/conftest.one &&
test -s conftest.dir/conftest.two
then
- ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c"
break 3
fi
fi
@@ -4511,7 +4889,7 @@ IFS=$as_save_IFS
rm -rf conftest.one conftest.two conftest.dir
fi
- if test "${ac_cv_path_install+set}" = set; then
+ if test ${ac_cv_path_install+y}; then
INSTALL=$ac_cv_path_install
else
# As a last resort, use the slow shell script. Don't cache a
@@ -4521,8 +4899,8 @@ fi
INSTALL=$ac_install_sh
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
-$as_echo "$INSTALL" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+printf "%s\n" "$INSTALL" >&6; }
# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
# It thinks the first close brace ends the variable substitution.
@@ -4532,6 +4910,10 @@ test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+# We have to override that on some systems
+case "$OS" in
+IRIX*) ac_INSTALL=$srcdir/install-sh;;
+esac
if test -x /usr/bin/getconf; then
bmake_path_max=`getconf PATH_MAX / 2> /dev/null`
# only a numeric response is useful
@@ -4542,13 +4924,41 @@ if test $bmake_path_max -gt 1024; then
# this is all we expect
bmake_path_max=1024
fi
+if test ${bmake_strftime:-no} = yes; then
+ CPPFLAGS="${CPPFLAGS} -DFORCE_BMAKE_STRFTIME"
+fi
echo "Using: BMAKE_PATH_MAX=$bmake_path_max" >&6
-
-{ $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 :
- $as_echo_n "(cached) " >&6
-else
+# if type does not work which(1) had better!
+# 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
+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
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5
+printf %s "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
+if test ${ac_cv_header_sys_wait_h+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <sys/types.h>
@@ -4561,7 +4971,7 @@ else
#endif
int
-main ()
+main (void)
{
int s;
wait (&s);
@@ -4570,36 +4980,38 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_cv_header_sys_wait_h=yes
-else
+else $as_nop
ac_cv_header_sys_wait_h=no
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5
-$as_echo "$ac_cv_header_sys_wait_h" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5
+printf "%s\n" "$ac_cv_header_sys_wait_h" >&6; }
if test $ac_cv_header_sys_wait_h = yes; then
-$as_echo "#define HAVE_SYS_WAIT_H 1" >>confdefs.h
+printf "%s\n" "#define HAVE_SYS_WAIT_H 1" >>confdefs.h
fi
ac_header_dirent=no
for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
- as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5
-$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; }
-if eval \${$as_ac_Header+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ as_ac_Header=`printf "%s\n" "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5
+printf %s "checking for $ac_hdr that defines DIR... " >&6; }
+if eval test \${$as_ac_Header+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <sys/types.h>
#include <$ac_hdr>
int
-main ()
+main (void)
{
if ((DIR *) 0)
return 0;
@@ -4607,19 +5019,21 @@ return 0;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
eval "$as_ac_Header=yes"
-else
+else $as_nop
eval "$as_ac_Header=no"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
eval ac_res=\$$as_ac_Header
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
-if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Header"\" = x"yes"
+then :
cat >>confdefs.h <<_ACEOF
-#define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1
+#define `printf "%s\n" "HAVE_$ac_hdr" | $as_tr_cpp` 1
_ACEOF
ac_header_dirent=$ac_hdr; break
@@ -4628,11 +5042,12 @@ fi
done
# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
if test $ac_header_dirent = dirent.h; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
-$as_echo_n "checking for library containing opendir... " >&6; }
-if ${ac_cv_search_opendir+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
+printf %s "checking for library containing opendir... " >&6; }
+if test ${ac_cv_search_opendir+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -4640,56 +5055,59 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
-#ifdef __cplusplus
-extern "C"
-#endif
char opendir ();
int
-main ()
+main (void)
{
return opendir ();
;
return 0;
}
_ACEOF
-for ac_lib in '' dir; do
+for ac_lib in '' dir
+do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
- if ac_fn_c_try_link "$LINENO"; then :
+ if ac_fn_c_try_link "$LINENO"
+then :
ac_cv_search_opendir=$ac_res
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext
- if ${ac_cv_search_opendir+:} false; then :
+ if test ${ac_cv_search_opendir+y}
+then :
break
fi
done
-if ${ac_cv_search_opendir+:} false; then :
+if test ${ac_cv_search_opendir+y}
+then :
-else
+else $as_nop
ac_cv_search_opendir=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
-$as_echo "$ac_cv_search_opendir" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
+printf "%s\n" "$ac_cv_search_opendir" >&6; }
ac_res=$ac_cv_search_opendir
-if test "$ac_res" != no; then :
+if test "$ac_res" != no
+then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
-$as_echo_n "checking for library containing opendir... " >&6; }
-if ${ac_cv_search_opendir+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
+printf %s "checking for library containing opendir... " >&6; }
+if test ${ac_cv_search_opendir+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -4697,141 +5115,181 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
-#ifdef __cplusplus
-extern "C"
-#endif
char opendir ();
int
-main ()
+main (void)
{
return opendir ();
;
return 0;
}
_ACEOF
-for ac_lib in '' x; do
+for ac_lib in '' x
+do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
- if ac_fn_c_try_link "$LINENO"; then :
+ if ac_fn_c_try_link "$LINENO"
+then :
ac_cv_search_opendir=$ac_res
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext
- if ${ac_cv_search_opendir+:} false; then :
+ if test ${ac_cv_search_opendir+y}
+then :
break
fi
done
-if ${ac_cv_search_opendir+:} false; then :
+if test ${ac_cv_search_opendir+y}
+then :
-else
+else $as_nop
ac_cv_search_opendir=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
-$as_echo "$ac_cv_search_opendir" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
+printf "%s\n" "$ac_cv_search_opendir" >&6; }
ac_res=$ac_cv_search_opendir
-if test "$ac_res" != no; then :
+if test "$ac_res" != no
+then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
fi
-for ac_header in sys/param.h
-do :
- ac_fn_c_check_header_mongrel "$LINENO" "sys/param.h" "ac_cv_header_sys_param_h" "$ac_includes_default"
-if test "x$ac_cv_header_sys_param_h" = xyes; then :
- cat >>confdefs.h <<_ACEOF
-#define HAVE_SYS_PARAM_H 1
-_ACEOF
+ac_fn_c_check_header_compile "$LINENO" "sys/param.h" "ac_cv_header_sys_param_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_param_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_PARAM_H 1" >>confdefs.h
fi
-done
-
-for ac_header in sys/sysctl.h
-do :
- ac_fn_c_check_header_compile "$LINENO" "sys/sysctl.h" "ac_cv_header_sys_sysctl_h" "#ifdef HAVE_SYS_PARAM_H
+ac_fn_c_check_header_compile "$LINENO" "sys/sysctl.h" "ac_cv_header_sys_sysctl_h" "#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
# endif
"
-if test "x$ac_cv_header_sys_sysctl_h" = xyes; then :
- cat >>confdefs.h <<_ACEOF
-#define HAVE_SYS_SYSCTL_H 1
-_ACEOF
+if test "x$ac_cv_header_sys_sysctl_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_SYSCTL_H 1" >>confdefs.h
fi
-done
+ac_fn_c_check_header_compile "$LINENO" "ar.h" "ac_cv_header_ar_h" "$ac_includes_default"
+if test "x$ac_cv_header_ar_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_AR_H 1" >>confdefs.h
-for ac_header in \
- ar.h \
- err.h \
- fcntl.h \
- libgen.h \
- limits.h \
- paths.h \
- poll.h \
- ranlib.h \
- sys/mman.h \
- sys/select.h \
- sys/socket.h \
- sys/time.h \
- sys/uio.h \
- utime.h \
-
-do :
- as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
-ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
-if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
- cat >>confdefs.h <<_ACEOF
-#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
-_ACEOF
+fi
+ac_fn_c_check_header_compile "$LINENO" "err.h" "ac_cv_header_err_h" "$ac_includes_default"
+if test "x$ac_cv_header_err_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_ERR_H 1" >>confdefs.h
fi
+ac_fn_c_check_header_compile "$LINENO" "fcntl.h" "ac_cv_header_fcntl_h" "$ac_includes_default"
+if test "x$ac_cv_header_fcntl_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_FCNTL_H 1" >>confdefs.h
-done
+fi
+ac_fn_c_check_header_compile "$LINENO" "libgen.h" "ac_cv_header_libgen_h" "$ac_includes_default"
+if test "x$ac_cv_header_libgen_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_LIBGEN_H 1" >>confdefs.h
+fi
+ac_fn_c_check_header_compile "$LINENO" "limits.h" "ac_cv_header_limits_h" "$ac_includes_default"
+if test "x$ac_cv_header_limits_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_LIMITS_H 1" >>confdefs.h
-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
+fi
+ac_fn_c_check_header_compile "$LINENO" "paths.h" "ac_cv_header_paths_h" "$ac_includes_default"
+if test "x$ac_cv_header_paths_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_PATHS_H 1" >>confdefs.h
-_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*
+ac_fn_c_check_header_compile "$LINENO" "poll.h" "ac_cv_header_poll_h" "$ac_includes_default"
+if test "x$ac_cv_header_poll_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_POLL_H 1" >>confdefs.h
-else
- CPPFLAGS="${CPPFLAGS} -I`cd ${srcdir}/missing && pwd`"
fi
+ac_fn_c_check_header_compile "$LINENO" "ranlib.h" "ac_cv_header_ranlib_h" "$ac_includes_default"
+if test "x$ac_cv_header_ranlib_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_RANLIB_H 1" >>confdefs.h
+fi
+ac_fn_c_check_header_compile "$LINENO" "regex.h" "ac_cv_header_regex_h" "$ac_includes_default"
+if test "x$ac_cv_header_regex_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_REGEX_H 1" >>confdefs.h
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/mman.h" "ac_cv_header_sys_mman_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_mman_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_MMAN_H 1" >>confdefs.h
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/select.h" "ac_cv_header_sys_select_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_select_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_SELECT_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/socket.h" "ac_cv_header_sys_socket_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_socket_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_SOCKET_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/time.h" "ac_cv_header_sys_time_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_time_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_TIME_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/uio.h" "ac_cv_header_sys_uio_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_uio_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYS_UIO_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "utime.h" "ac_cv_header_utime_h" "$ac_includes_default"
+if test "x$ac_cv_header_utime_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_UTIME_H 1" >>confdefs.h
+
+fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__" >&5
-$as_echo_n "checking for __attribute__... " >&6; }
-if ${ac_cv___attribute__+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+
+ac_fn_c_check_header_compile "$LINENO" "sys/cdefs.h" "ac_cv_header_sys_cdefs_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_cdefs_h" = xyes
+then :
+
+else $as_nop
+ CPPFLAGS="${CPPFLAGS} -I`cd ${srcdir}/missing && pwd`"
+fi
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for __attribute__" >&5
+printf %s "checking for __attribute__... " >&6; }
+if test ${ac_cv___attribute__+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -4853,28 +5311,30 @@ main(int argc, char **argv)
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
ac_cv___attribute__=yes
-else
+else $as_nop
ac_cv___attribute__=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
if test "$ac_cv___attribute__" = "yes"; then
-$as_echo "#define HAVE___ATTRIBUTE__ 1" >>confdefs.h
+printf "%s\n" "#define HAVE___ATTRIBUTE__ 1" >>confdefs.h
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv___attribute__" >&5
-$as_echo "$ac_cv___attribute__" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv___attribute__" >&5
+printf "%s\n" "$ac_cv___attribute__" >&6; }
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5
-$as_echo_n "checking whether byte ordering is bigendian... " >&6; }
-if ${ac_cv_c_bigendian+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5
+printf %s "checking whether byte ordering is bigendian... " >&6; }
+if test ${ac_cv_c_bigendian+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_cv_c_bigendian=unknown
# See if we're dealing with a universal compiler.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -4885,7 +5345,8 @@ else
typedef int dummy;
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
# Check for potential -arch flags. It is not universal unless
# there are at least two -arch flags with different values.
@@ -4909,7 +5370,7 @@ if ac_fn_c_try_compile "$LINENO"; then :
fi
done
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test $ac_cv_c_bigendian = unknown; then
# See if sys/param.h defines the BYTE_ORDER macro.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -4918,7 +5379,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
#include <sys/param.h>
int
-main ()
+main (void)
{
#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \
&& defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \
@@ -4930,7 +5391,8 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
# It does; now see whether it defined to BIG_ENDIAN or not.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -4938,7 +5400,7 @@ if ac_fn_c_try_compile "$LINENO"; then :
#include <sys/param.h>
int
-main ()
+main (void)
{
#if BYTE_ORDER != BIG_ENDIAN
not big endian
@@ -4948,14 +5410,15 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_cv_c_bigendian=yes
-else
+else $as_nop
ac_cv_c_bigendian=no
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
if test $ac_cv_c_bigendian = unknown; then
# See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris).
@@ -4964,7 +5427,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
#include <limits.h>
int
-main ()
+main (void)
{
#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN)
bogus endian macros
@@ -4974,14 +5437,15 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
# It does; now see whether it defined to _BIG_ENDIAN or not.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <limits.h>
int
-main ()
+main (void)
{
#ifndef _BIG_ENDIAN
not big endian
@@ -4991,31 +5455,33 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_cv_c_bigendian=yes
-else
+else $as_nop
ac_cv_c_bigendian=no
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
if test $ac_cv_c_bigendian = unknown; then
# Compile a test program.
- if test "$cross_compiling" = yes; then :
+ if test "$cross_compiling" = yes
+then :
# Try to guess by grepping values from an object file.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-short int ascii_mm[] =
+unsigned short int ascii_mm[] =
{ 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
- short int ascii_ii[] =
+ unsigned short int ascii_ii[] =
{ 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
int use_ascii (int i) {
return ascii_mm[i] + ascii_ii[i];
}
- short int ebcdic_ii[] =
+ unsigned short int ebcdic_ii[] =
{ 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
- short int ebcdic_mm[] =
+ unsigned short int ebcdic_mm[] =
{ 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
int use_ebcdic (int i) {
return ebcdic_mm[i] + ebcdic_ii[i];
@@ -5023,14 +5489,15 @@ short int ascii_mm[] =
extern int foo;
int
-main ()
+main (void)
{
return use_ascii (foo) == use_ebcdic (foo);
;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then
ac_cv_c_bigendian=yes
fi
@@ -5043,13 +5510,13 @@ if ac_fn_c_try_compile "$LINENO"; then :
fi
fi
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-else
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_includes_default
int
-main ()
+main (void)
{
/* Are we little or big endian? From Harbison&Steele. */
@@ -5065,9 +5532,10 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
+if ac_fn_c_try_run "$LINENO"
+then :
ac_cv_c_bigendian=no
-else
+else $as_nop
ac_cv_c_bigendian=yes
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
@@ -5076,17 +5544,17 @@ fi
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5
-$as_echo "$ac_cv_c_bigendian" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5
+printf "%s\n" "$ac_cv_c_bigendian" >&6; }
case $ac_cv_c_bigendian in #(
yes)
- $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h
+ printf "%s\n" "#define WORDS_BIGENDIAN 1" >>confdefs.h
;; #(
no)
;; #(
universal)
-$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
+printf "%s\n" "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
;; #(
*)
@@ -5094,16 +5562,17 @@ $as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;;
esac
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5
-$as_echo_n "checking for an ANSI C-conforming const... " >&6; }
-if ${ac_cv_c_const+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5
+printf %s "checking for an ANSI C-conforming const... " >&6; }
+if test ${ac_cv_c_const+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
#ifndef __cplusplus
@@ -5116,7 +5585,7 @@ main ()
/* NEC SVR4.0.2 mips cc rejects this. */
struct point {int x, y;};
static struct point const zero = {0,0};
- /* AIX XL C 1.02.0.0 rejects this.
+ /* IBM XL C 1.02.0.0 rejects this.
It does not let you subtract one const X* pointer from another in
an arm of an if-expression whose if-part is not a constant
expression */
@@ -5144,7 +5613,7 @@ main ()
iptr p = 0;
++p;
}
- { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying
+ { /* IBM XL C 1.02.0.0 rejects this sort of thing, saying
"k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
struct s { int j; const int *ap[3]; } bx;
struct s *b = &bx; b->j = 5;
@@ -5160,62 +5629,265 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_cv_c_const=yes
-else
+else $as_nop
ac_cv_c_const=no
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5
-$as_echo "$ac_cv_c_const" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5
+printf "%s\n" "$ac_cv_c_const" >&6; }
if test $ac_cv_c_const = no; then
-$as_echo "#define const /**/" >>confdefs.h
+printf "%s\n" "#define const /**/" >>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 :
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for inline" >&5
+printf %s "checking for inline... " >&6; }
+if test ${ac_cv_c_inline+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ 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 (void) {return 0; }
+$ac_kw foo_t foo (void) {return 0; }
+#endif
-else
+_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.beam conftest.$ac_ext
+ test "$ac_cv_c_inline" != no && break
+done
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5
+printf "%s\n" "$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) ;; #(
+ *)
+
+printf "%s\n" "#define int64_t $ac_cv_c_int64_t" >>confdefs.h
+;;
+esac
+
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for unsigned long long int" >&5
+printf %s "checking for unsigned long long int... " >&6; }
+if test ${ac_cv_type_unsigned_long_long_int+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_type_unsigned_long_long_int=yes
+ case $ac_prog_cc_stdc in
+ no | c89) ;;
+ *)
+ 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 (void)
+{
+/* 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 $as_nop
+ ac_cv_type_unsigned_long_long_int=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext;;
+ esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_unsigned_long_long_int" >&5
+printf "%s\n" "$ac_cv_type_unsigned_long_long_int" >&6; }
+ if test $ac_cv_type_unsigned_long_long_int = yes; then
+
+printf "%s\n" "#define HAVE_UNSIGNED_LONG_LONG_INT 1" >>confdefs.h
+
+ fi
-cat >>confdefs.h <<_ACEOF
-#define mode_t int
+
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for long long int" >&5
+printf %s "checking for long long int... " >&6; }
+if test ${ac_cv_type_long_long_int+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_type_long_long_int=yes
+ case $ac_prog_cc_stdc in
+ no | c89) ;;
+ *)
+ 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 $as_nop
+ 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 (void)
+{
+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 $as_nop
+ 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;;
+ esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_long_long_int" >&5
+printf "%s\n" "$ac_cv_type_long_long_int" >&6; }
+ if test $ac_cv_type_long_long_int = yes; then
+
+printf "%s\n" "#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 :
+
+else $as_nop
+
+printf "%s\n" "#define mode_t int" >>confdefs.h
fi
ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default"
-if test "x$ac_cv_type_off_t" = xyes; then :
+if test "x$ac_cv_type_off_t" = xyes
+then :
-else
+else $as_nop
-cat >>confdefs.h <<_ACEOF
-#define off_t long int
-_ACEOF
+printf "%s\n" "#define off_t long int" >>confdefs.h
fi
-ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default"
-if test "x$ac_cv_type_pid_t" = xyes; then :
-else
+ ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default
+"
+if test "x$ac_cv_type_pid_t" = xyes
+then :
+
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ #if defined _WIN64 && !defined __CYGWIN__
+ LLP64
+ #endif
+
+int
+main (void)
+{
+
+ ;
+ return 0;
+}
-cat >>confdefs.h <<_ACEOF
-#define pid_t int
_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_pid_type='int'
+else $as_nop
+ ac_pid_type='__int64'
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+
+printf "%s\n" "#define pid_t $ac_pid_type" >>confdefs.h
+
fi
+
ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
-if test "x$ac_cv_type_size_t" = xyes; then :
+if test "x$ac_cv_type_size_t" = xyes
+then :
-else
+else $as_nop
-cat >>confdefs.h <<_ACEOF
-#define size_t unsigned int
-_ACEOF
+printf "%s\n" "#define size_t unsigned int" >>confdefs.h
fi
@@ -5224,80 +5896,125 @@ case $ac_cv_c_uint32_t in #(
no|yes) ;; #(
*)
-$as_echo "#define _UINT32_T 1" >>confdefs.h
+printf "%s\n" "#define _UINT32_T 1" >>confdefs.h
-cat >>confdefs.h <<_ACEOF
-#define uint32_t $ac_cv_c_uint32_t
-_ACEOF
+printf "%s\n" "#define uint32_t $ac_cv_c_uint32_t" >>confdefs.h
;;
esac
-ac_fn_c_check_decl "$LINENO" "sys_siglist" "ac_cv_have_decl_sys_siglist" "#include <signal.h>
-/* NetBSD declares sys_siglist in unistd.h. */
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
-"
-if test "x$ac_cv_have_decl_sys_siglist" = xyes; then :
- ac_have_decl=1
-else
- ac_have_decl=0
-fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5
+printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; }
+if test ${ac_cv_c_undeclared_builtin_options+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_save_CFLAGS=$CFLAGS
+ ac_cv_c_undeclared_builtin_options='cannot detect'
+ for ac_arg in '' -fno-builtin; do
+ CFLAGS="$ac_save_CFLAGS $ac_arg"
+ # This test program should *not* compile successfully.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
-cat >>confdefs.h <<_ACEOF
-#define HAVE_DECL_SYS_SIGLIST $ac_have_decl
+int
+main (void)
+{
+(void) strchr;
+ ;
+ return 0;
+}
_ACEOF
-
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5
-$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; }
-if ${ac_cv_header_time+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+else $as_nop
+ # This test program should compile successfully.
+ # No library function is consistently available on
+ # freestanding implementations, so test against a dummy
+ # declaration. Include always-available headers on the
+ # off chance that they somehow elicit warnings.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-#include <sys/types.h>
-#include <sys/time.h>
-#include <time.h>
+#include <float.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+extern void ac_decl (int, char *);
int
-main ()
+main (void)
{
-if ((struct tm *) 0)
-return 0;
+(void) ac_decl (0, (char *) 0);
+ (void) ac_decl;
+
;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- ac_cv_header_time=yes
-else
- ac_cv_header_time=no
+if ac_fn_c_try_compile "$LINENO"
+then :
+ if test x"$ac_arg" = x
+then :
+ ac_cv_c_undeclared_builtin_options='none needed'
+else $as_nop
+ ac_cv_c_undeclared_builtin_options=$ac_arg
+fi
+ break
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5
-$as_echo "$ac_cv_header_time" >&6; }
-if test $ac_cv_header_time = yes; then
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ done
+ CFLAGS=$ac_save_CFLAGS
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_undeclared_builtin_options" >&5
+printf "%s\n" "$ac_cv_c_undeclared_builtin_options" >&6; }
+ case $ac_cv_c_undeclared_builtin_options in #(
+ 'cannot detect') :
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot make $CC report undeclared builtins
+See \`config.log' for more details" "$LINENO" 5; } ;; #(
+ 'none needed') :
+ ac_c_undeclared_builtin_options='' ;; #(
+ *) :
+ ac_c_undeclared_builtin_options=$ac_cv_c_undeclared_builtin_options ;;
+esac
-$as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h
+ac_fn_check_decl "$LINENO" "sys_siglist" "ac_cv_have_decl_sys_siglist" "#include <signal.h>
+/* NetBSD declares sys_siglist in unistd.h. */
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_sys_siglist" = xyes
+then :
+ ac_have_decl=1
+else $as_nop
+ ac_have_decl=0
fi
+printf "%s\n" "#define HAVE_DECL_SYS_SIGLIST $ac_have_decl" >>confdefs.h
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5
-$as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; }
-if ${ac_cv_struct_tm+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5
+printf %s "checking whether struct tm is in sys/time.h or time.h... " >&6; }
+if test ${ac_cv_struct_tm+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <sys/types.h>
#include <time.h>
int
-main ()
+main (void)
{
struct tm tm;
int *p = &tm.tm_sec;
@@ -5306,18 +6023,19 @@ struct tm tm;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_cv_struct_tm=time.h
-else
+else $as_nop
ac_cv_struct_tm=sys/time.h
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5
-$as_echo "$ac_cv_struct_tm" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5
+printf "%s\n" "$ac_cv_struct_tm" >&6; }
if test $ac_cv_struct_tm = sys/time.h; then
-$as_echo "#define TM_IN_SYS_TIME 1" >>confdefs.h
+printf "%s\n" "#define TM_IN_SYS_TIME 1" >>confdefs.h
fi
@@ -5330,87 +6048,48 @@ ac_fn_c_check_type "$LINENO" "sig_atomic_t" "ac_cv_type_sig_atomic_t" "
#include <signal.h>
"
-if test "x$ac_cv_type_sig_atomic_t" = xyes; then :
-
-else
-
-$as_echo "#define sig_atomic_t int" >>confdefs.h
+if test "x$ac_cv_type_sig_atomic_t" = xyes
+then :
-
-fi
+printf "%s\n" "#define HAVE_SIG_ATOMIC_T 1" >>confdefs.h
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5
-$as_echo_n "checking return type of signal handlers... " >&6; }
-if ${ac_cv_type_signal+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <sys/types.h>
-#include <signal.h>
-
-int
-main ()
-{
-return *(signal (0, 0)) (0) == 1;
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- ac_cv_type_signal=int
-else
- ac_cv_type_signal=void
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_signal" >&5
-$as_echo "$ac_cv_type_signal" >&6; }
-cat >>confdefs.h <<_ACEOF
-#define RETSIGTYPE $ac_cv_type_signal
-_ACEOF
-for ac_header in vfork.h
-do :
- ac_fn_c_check_header_mongrel "$LINENO" "vfork.h" "ac_cv_header_vfork_h" "$ac_includes_default"
-if test "x$ac_cv_header_vfork_h" = xyes; then :
- cat >>confdefs.h <<_ACEOF
-#define HAVE_VFORK_H 1
-_ACEOF
-
-fi
-
+ac_func=
+for ac_item in $ac_func_c_list
+do
+ if test $ac_func; then
+ ac_fn_c_check_func "$LINENO" $ac_func ac_cv_func_$ac_func
+ if eval test \"x\$ac_cv_func_$ac_func\" = xyes; then
+ echo "#define $ac_item 1" >> confdefs.h
+ fi
+ ac_func=
+ else
+ ac_func=$ac_item
+ fi
done
-for ac_func in fork vfork
-do :
- as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
-ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
-if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
- cat >>confdefs.h <<_ACEOF
-#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
-_ACEOF
-fi
-done
if test "x$ac_cv_func_fork" = xyes; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5
-$as_echo_n "checking for working fork... " >&6; }
-if ${ac_cv_func_fork_works+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test "$cross_compiling" = yes; then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5
+printf %s "checking for working fork... " >&6; }
+if test ${ac_cv_func_fork_works+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test "$cross_compiling" = yes
+then :
ac_cv_func_fork_works=cross
-else
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_includes_default
int
-main ()
+main (void)
{
/* By Ruediger Kuhlmann. */
@@ -5420,9 +6099,10 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
+if ac_fn_c_try_run "$LINENO"
+then :
ac_cv_func_fork_works=yes
-else
+else $as_nop
ac_cv_func_fork_works=no
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
@@ -5430,8 +6110,8 @@ rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5
-$as_echo "$ac_cv_func_fork_works" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5
+printf "%s\n" "$ac_cv_func_fork_works" >&6; }
else
ac_cv_func_fork_works=$ac_cv_func_fork
@@ -5446,27 +6126,37 @@ if test "x$ac_cv_func_fork_works" = xcross; then
ac_cv_func_fork_works=yes
;;
esac
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5
-$as_echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5
+printf "%s\n" "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;}
fi
ac_cv_func_vfork_works=$ac_cv_func_vfork
if test "x$ac_cv_func_vfork" = xyes; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5
-$as_echo_n "checking for working vfork... " >&6; }
-if ${ac_cv_func_vfork_works+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test "$cross_compiling" = yes; then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5
+printf %s "checking for working vfork... " >&6; }
+if test ${ac_cv_func_vfork_works+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test "$cross_compiling" = yes
+then :
ac_cv_func_vfork_works=cross
-else
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Thanks to Paul Eggert for this test. */
$ac_includes_default
+#include <signal.h>
#include <sys/wait.h>
#ifdef HAVE_VFORK_H
# include <vfork.h>
#endif
+
+static void
+do_nothing (int sig)
+{
+ (void) sig;
+}
+
/* On some sparc systems, changes by the child to local and incoming
argument registers are propagated back to the parent. The compiler
is told about this with #include <vfork.h>, but some compilers
@@ -5474,11 +6164,7 @@ $ac_includes_default
static variable whose address is put into a register that is
clobbered by the vfork. */
static void
-#ifdef __cplusplus
sparc_address_test (int arg)
-# else
-sparc_address_test (arg) int arg;
-#endif
{
static pid_t child;
if (!child) {
@@ -5496,13 +6182,18 @@ sparc_address_test (arg) int arg;
}
int
-main ()
+main (void)
{
pid_t parent = getpid ();
pid_t child;
sparc_address_test (0);
+ /* On Solaris 2.4, changes by the child to the signal handler
+ also munge signal handlers in the parent. To detect this,
+ start by putting the parent's handler in a known state. */
+ signal (SIGTERM, SIG_DFL);
+
child = vfork ();
if (child == 0) {
@@ -5524,6 +6215,10 @@ main ()
|| p != p5 || p != p6 || p != p7)
_exit(1);
+ /* Alter the child's signal handler. */
+ if (signal (SIGTERM, do_nothing) != SIG_DFL)
+ _exit(1);
+
/* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent
from child file descriptors. If the child closes a descriptor
before it execs or exits, this munges the parent's descriptor
@@ -5539,6 +6234,9 @@ main ()
/* Was there some problem with vforking? */
child < 0
+ /* Did the child munge the parent's signal handler? */
+ || signal (SIGTERM, SIG_DFL) != SIG_DFL
+
/* Did the child fail? (This shouldn't happen.) */
|| status
@@ -5551,9 +6249,10 @@ main ()
}
}
_ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
+if ac_fn_c_try_run "$LINENO"
+then :
ac_cv_func_vfork_works=yes
-else
+else $as_nop
ac_cv_func_vfork_works=no
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
@@ -5561,58 +6260,54 @@ rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5
-$as_echo "$ac_cv_func_vfork_works" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5
+printf "%s\n" "$ac_cv_func_vfork_works" >&6; }
fi;
if test "x$ac_cv_func_fork_works" = xcross; then
ac_cv_func_vfork_works=$ac_cv_func_vfork
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5
-$as_echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5
+printf "%s\n" "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;}
fi
if test "x$ac_cv_func_vfork_works" = xyes; then
-$as_echo "#define HAVE_WORKING_VFORK 1" >>confdefs.h
+printf "%s\n" "#define HAVE_WORKING_VFORK 1" >>confdefs.h
else
-$as_echo "#define vfork fork" >>confdefs.h
+printf "%s\n" "#define vfork fork" >>confdefs.h
fi
if test "x$ac_cv_func_fork_works" = xyes; then
-$as_echo "#define HAVE_WORKING_FORK 1" >>confdefs.h
+printf "%s\n" "#define HAVE_WORKING_FORK 1" >>confdefs.h
fi
-for ac_func in vprintf
-do :
- ac_fn_c_check_func "$LINENO" "vprintf" "ac_cv_func_vprintf"
-if test "x$ac_cv_func_vprintf" = xyes; then :
- cat >>confdefs.h <<_ACEOF
-#define HAVE_VPRINTF 1
-_ACEOF
-ac_fn_c_check_func "$LINENO" "_doprnt" "ac_cv_func__doprnt"
-if test "x$ac_cv_func__doprnt" = xyes; then :
-$as_echo "#define HAVE_DOPRNT 1" >>confdefs.h
+if test "x$ac_cv_func_vprintf" = xno
+then :
+ ac_fn_c_check_func "$LINENO" "_doprnt" "ac_cv_func__doprnt"
+if test "x$ac_cv_func__doprnt" = xyes
+then :
-fi
+printf "%s\n" "#define HAVE_DOPRNT 1" >>confdefs.h
fi
-done
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for wait3 that fills in rusage" >&5
-$as_echo_n "checking for wait3 that fills in rusage... " >&6; }
-if ${ac_cv_func_wait3_rusage+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test "$cross_compiling" = yes; then :
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for wait3 that fills in rusage" >&5
+printf %s "checking for wait3 that fills in rusage... " >&6; }
+if test ${ac_cv_func_wait3_rusage+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test "$cross_compiling" = yes
+then :
ac_cv_func_wait3_rusage=no
-else
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_includes_default
@@ -5621,7 +6316,7 @@ $ac_includes_default
#include <sys/wait.h>
/* HP-UX has wait3 but does not fill in rusage at all. */
int
-main ()
+main (void)
{
struct rusage r;
int i;
@@ -5651,9 +6346,10 @@ main ()
}
}
_ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
+if ac_fn_c_try_run "$LINENO"
+then :
ac_cv_func_wait3_rusage=yes
-else
+else $as_nop
ac_cv_func_wait3_rusage=no
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
@@ -5661,62 +6357,251 @@ rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_wait3_rusage" >&5
-$as_echo "$ac_cv_func_wait3_rusage" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_wait3_rusage" >&5
+printf "%s\n" "$ac_cv_func_wait3_rusage" >&6; }
if test $ac_cv_func_wait3_rusage = yes; then
-$as_echo "#define HAVE_WAIT3 1" >>confdefs.h
-
-fi
-
-for ac_func in \
- err \
- errx \
- getcwd \
- getenv \
- getopt \
- getwd \
- killpg \
- mmap \
- putenv \
- select \
- setenv \
- setpgid \
- setsid \
- sigaction \
- sigvec \
- snprintf \
- strerror \
- strftime \
- strsep \
- strtod \
- strtol \
- sysctl \
- unsetenv \
- vsnprintf \
- wait3 \
- wait4 \
- waitpid \
- warn \
- warnx \
-
-do :
- as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
-ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
-if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
- cat >>confdefs.h <<_ACEOF
-#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
-_ACEOF
+printf "%s\n" "#define HAVE_WAIT3 1" >>confdefs.h
fi
-done
+ac_fn_c_check_func "$LINENO" "err" "ac_cv_func_err"
+if test "x$ac_cv_func_err" = xyes
+then :
+ printf "%s\n" "#define HAVE_ERR 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "errx" "ac_cv_func_errx"
+if test "x$ac_cv_func_errx" = xyes
+then :
+ printf "%s\n" "#define HAVE_ERRX 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "getcwd" "ac_cv_func_getcwd"
+if test "x$ac_cv_func_getcwd" = xyes
+then :
+ printf "%s\n" "#define HAVE_GETCWD 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "getenv" "ac_cv_func_getenv"
+if test "x$ac_cv_func_getenv" = xyes
+then :
+ printf "%s\n" "#define HAVE_GETENV 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "getwd" "ac_cv_func_getwd"
+if test "x$ac_cv_func_getwd" = xyes
+then :
+ printf "%s\n" "#define HAVE_GETWD 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "killpg" "ac_cv_func_killpg"
+if test "x$ac_cv_func_killpg" = xyes
+then :
+ printf "%s\n" "#define HAVE_KILLPG 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "mmap" "ac_cv_func_mmap"
+if test "x$ac_cv_func_mmap" = xyes
+then :
+ printf "%s\n" "#define HAVE_MMAP 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "putenv" "ac_cv_func_putenv"
+if test "x$ac_cv_func_putenv" = xyes
+then :
+ printf "%s\n" "#define HAVE_PUTENV 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "select" "ac_cv_func_select"
+if test "x$ac_cv_func_select" = xyes
+then :
+ printf "%s\n" "#define HAVE_SELECT 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "setenv" "ac_cv_func_setenv"
+if test "x$ac_cv_func_setenv" = xyes
+then :
+ printf "%s\n" "#define HAVE_SETENV 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "setpgid" "ac_cv_func_setpgid"
+if test "x$ac_cv_func_setpgid" = xyes
+then :
+ printf "%s\n" "#define HAVE_SETPGID 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "setrlimit" "ac_cv_func_setrlimit"
+if test "x$ac_cv_func_setrlimit" = xyes
+then :
+ printf "%s\n" "#define HAVE_SETRLIMIT 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "setsid" "ac_cv_func_setsid"
+if test "x$ac_cv_func_setsid" = xyes
+then :
+ printf "%s\n" "#define HAVE_SETSID 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "sigaddset" "ac_cv_func_sigaddset"
+if test "x$ac_cv_func_sigaddset" = xyes
+then :
+ printf "%s\n" "#define HAVE_SIGADDSET 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "sigpending" "ac_cv_func_sigpending"
+if test "x$ac_cv_func_sigpending" = xyes
+then :
+ printf "%s\n" "#define HAVE_SIGPENDING 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "sigprocmask" "ac_cv_func_sigprocmask"
+if test "x$ac_cv_func_sigprocmask" = xyes
+then :
+ printf "%s\n" "#define HAVE_SIGPROCMASK 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "sigsetmask" "ac_cv_func_sigsetmask"
+if test "x$ac_cv_func_sigsetmask" = xyes
+then :
+ printf "%s\n" "#define HAVE_SIGSETMASK 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "sigsuspend" "ac_cv_func_sigsuspend"
+if test "x$ac_cv_func_sigsuspend" = xyes
+then :
+ printf "%s\n" "#define HAVE_SIGSUSPEND 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "sigvec" "ac_cv_func_sigvec"
+if test "x$ac_cv_func_sigvec" = xyes
+then :
+ printf "%s\n" "#define HAVE_SIGVEC 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "snprintf" "ac_cv_func_snprintf"
+if test "x$ac_cv_func_snprintf" = xyes
+then :
+ printf "%s\n" "#define HAVE_SNPRINTF 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strerror" "ac_cv_func_strerror"
+if test "x$ac_cv_func_strerror" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRERROR 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "stresep" "ac_cv_func_stresep"
+if test "x$ac_cv_func_stresep" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRESEP 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strftime" "ac_cv_func_strftime"
+if test "x$ac_cv_func_strftime" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRFTIME 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strsep" "ac_cv_func_strsep"
+if test "x$ac_cv_func_strsep" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRSEP 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strtod" "ac_cv_func_strtod"
+if test "x$ac_cv_func_strtod" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRTOD 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strtol" "ac_cv_func_strtol"
+if test "x$ac_cv_func_strtol" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRTOL 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strtoll" "ac_cv_func_strtoll"
+if test "x$ac_cv_func_strtoll" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRTOLL 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strtoul" "ac_cv_func_strtoul"
+if test "x$ac_cv_func_strtoul" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRTOUL 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "sysctl" "ac_cv_func_sysctl"
+if test "x$ac_cv_func_sysctl" = xyes
+then :
+ printf "%s\n" "#define HAVE_SYSCTL 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "unsetenv" "ac_cv_func_unsetenv"
+if test "x$ac_cv_func_unsetenv" = xyes
+then :
+ printf "%s\n" "#define HAVE_UNSETENV 1" >>confdefs.h
+fi
+ac_fn_c_check_func "$LINENO" "vsnprintf" "ac_cv_func_vsnprintf"
+if test "x$ac_cv_func_vsnprintf" = xyes
+then :
+ printf "%s\n" "#define HAVE_VSNPRINTF 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "wait3" "ac_cv_func_wait3"
+if test "x$ac_cv_func_wait3" = xyes
+then :
+ printf "%s\n" "#define HAVE_WAIT3 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "wait4" "ac_cv_func_wait4"
+if test "x$ac_cv_func_wait4" = xyes
+then :
+ printf "%s\n" "#define HAVE_WAIT4 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "waitpid" "ac_cv_func_waitpid"
+if test "x$ac_cv_func_waitpid" = xyes
+then :
+ printf "%s\n" "#define HAVE_WAITPID 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "warn" "ac_cv_func_warn"
+if test "x$ac_cv_func_warn" = xyes
+then :
+ printf "%s\n" "#define HAVE_WARN 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "warnx" "ac_cv_func_warnx"
+if test "x$ac_cv_func_warnx" = xyes
+then :
+ printf "%s\n" "#define HAVE_WARNX 1" >>confdefs.h
+
+fi
+
+
+ac_fn_c_check_func "$LINENO" "getopt" "ac_cv_func_getopt"
+if test "x$ac_cv_func_getopt" = xyes
+then :
+ printf "%s\n" "#define HAVE_GETOPT 1" >>confdefs.h
+
+else $as_nop
+ 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
+if test "x$ac_cv_func_realpath" = xyes
+then :
+ printf "%s\n" "#define HAVE_REALPATH 1" >>confdefs.h
-else
+else $as_nop
case " $LIBOBJS " in
*" realpath.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS realpath.$ac_objext"
@@ -5724,12 +6609,12 @@ else
esac
fi
-
ac_fn_c_check_func "$LINENO" "dirname" "ac_cv_func_dirname"
-if test "x$ac_cv_func_dirname" = xyes; then :
- $as_echo "#define HAVE_DIRNAME 1" >>confdefs.h
+if test "x$ac_cv_func_dirname" = xyes
+then :
+ printf "%s\n" "#define HAVE_DIRNAME 1" >>confdefs.h
-else
+else $as_nop
case " $LIBOBJS " in
*" dirname.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS dirname.$ac_objext"
@@ -5737,12 +6622,25 @@ else
esac
fi
+ac_fn_c_check_func "$LINENO" "sigaction" "ac_cv_func_sigaction"
+if test "x$ac_cv_func_sigaction" = xyes
+then :
+ printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h
+
+else $as_nop
+ 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
+if test "x$ac_cv_func_stresep" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRESEP 1" >>confdefs.h
-else
+else $as_nop
case " $LIBOBJS " in
*" stresep.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS stresep.$ac_objext"
@@ -5750,12 +6648,12 @@ else
esac
fi
-
ac_fn_c_check_func "$LINENO" "strlcpy" "ac_cv_func_strlcpy"
-if test "x$ac_cv_func_strlcpy" = xyes; then :
- $as_echo "#define HAVE_STRLCPY 1" >>confdefs.h
+if test "x$ac_cv_func_strlcpy" = xyes
+then :
+ printf "%s\n" "#define HAVE_STRLCPY 1" >>confdefs.h
-else
+else $as_nop
case " $LIBOBJS " in
*" strlcpy.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS strlcpy.$ac_objext"
@@ -5765,12 +6663,12 @@ esac
fi
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for emalloc in -lutil" >&5
-$as_echo_n "checking for emalloc in -lutil... " >&6; }
-if ${ac_cv_lib_util_emalloc+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for emalloc in -lutil" >&5
+printf %s "checking for emalloc in -lutil... " >&6; }
+if test ${ac_cv_lib_util_emalloc+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_check_lib_save_LIBS=$LIBS
LIBS="-lutil $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -5779,35 +6677,35 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
-#ifdef __cplusplus
-extern "C"
-#endif
char emalloc ();
int
-main ()
+main (void)
{
return emalloc ();
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
ac_cv_lib_util_emalloc=yes
-else
+else $as_nop
ac_cv_lib_util_emalloc=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_emalloc" >&5
-$as_echo "$ac_cv_lib_util_emalloc" >&6; }
-if test "x$ac_cv_lib_util_emalloc" = xyes; then :
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for erealloc in -lutil" >&5
-$as_echo_n "checking for erealloc in -lutil... " >&6; }
-if ${ac_cv_lib_util_erealloc+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_emalloc" >&5
+printf "%s\n" "$ac_cv_lib_util_emalloc" >&6; }
+if test "x$ac_cv_lib_util_emalloc" = xyes
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for erealloc in -lutil" >&5
+printf %s "checking for erealloc in -lutil... " >&6; }
+if test ${ac_cv_lib_util_erealloc+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_check_lib_save_LIBS=$LIBS
LIBS="-lutil $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -5816,35 +6714,35 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
-#ifdef __cplusplus
-extern "C"
-#endif
char erealloc ();
int
-main ()
+main (void)
{
return erealloc ();
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
ac_cv_lib_util_erealloc=yes
-else
+else $as_nop
ac_cv_lib_util_erealloc=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_erealloc" >&5
-$as_echo "$ac_cv_lib_util_erealloc" >&6; }
-if test "x$ac_cv_lib_util_erealloc" = xyes; then :
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for estrdup in -lutil" >&5
-$as_echo_n "checking for estrdup in -lutil... " >&6; }
-if ${ac_cv_lib_util_estrdup+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_erealloc" >&5
+printf "%s\n" "$ac_cv_lib_util_erealloc" >&6; }
+if test "x$ac_cv_lib_util_erealloc" = xyes
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for estrdup in -lutil" >&5
+printf %s "checking for estrdup in -lutil... " >&6; }
+if test ${ac_cv_lib_util_estrdup+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_check_lib_save_LIBS=$LIBS
LIBS="-lutil $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -5853,35 +6751,35 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
-#ifdef __cplusplus
-extern "C"
-#endif
char estrdup ();
int
-main ()
+main (void)
{
return estrdup ();
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
ac_cv_lib_util_estrdup=yes
-else
+else $as_nop
ac_cv_lib_util_estrdup=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_estrdup" >&5
-$as_echo "$ac_cv_lib_util_estrdup" >&6; }
-if test "x$ac_cv_lib_util_estrdup" = xyes; then :
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for estrndup in -lutil" >&5
-$as_echo_n "checking for estrndup in -lutil... " >&6; }
-if ${ac_cv_lib_util_estrndup+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_estrdup" >&5
+printf "%s\n" "$ac_cv_lib_util_estrdup" >&6; }
+if test "x$ac_cv_lib_util_estrdup" = xyes
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for estrndup in -lutil" >&5
+printf %s "checking for estrndup in -lutil... " >&6; }
+if test ${ac_cv_lib_util_estrndup+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_check_lib_save_LIBS=$LIBS
LIBS="-lutil $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -5890,30 +6788,29 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
-#ifdef __cplusplus
-extern "C"
-#endif
char estrndup ();
int
-main ()
+main (void)
{
return estrndup ();
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
ac_cv_lib_util_estrndup=yes
-else
+else $as_nop
ac_cv_lib_util_estrndup=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_estrndup" >&5
-$as_echo "$ac_cv_lib_util_estrndup" >&6; }
-if test "x$ac_cv_lib_util_estrndup" = xyes; then :
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_util_estrndup" >&5
+printf "%s\n" "$ac_cv_lib_util_estrndup" >&6; }
+if test "x$ac_cv_lib_util_estrndup" = xyes
+then :
LIBS="$LIBS -lutil"
CPPFLAGS="$CPPFLAGS -DUSE_EMALLOC"
fi
@@ -5925,11 +6822,12 @@ fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stat file-mode macros are broken" >&5
-$as_echo_n "checking whether stat file-mode macros are broken... " >&6; }
-if ${ac_cv_header_stat_broken+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether stat file-mode macros are broken" >&5
+printf %s "checking whether stat file-mode macros are broken... " >&6; }
+if test ${ac_cv_header_stat_broken+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <sys/types.h>
@@ -5952,34 +6850,22 @@ extern char c4[S_ISSOCK (S_IFREG) ? -1 : 1];
#endif
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_cv_header_stat_broken=no
-else
+else $as_nop
ac_cv_header_stat_broken=yes
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stat_broken" >&5
-$as_echo "$ac_cv_header_stat_broken" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stat_broken" >&5
+printf "%s\n" "$ac_cv_header_stat_broken" >&6; }
if test $ac_cv_header_stat_broken = yes; then
-$as_echo "#define STAT_MACROS_BROKEN 1" >>confdefs.h
+printf "%s\n" "#define STAT_MACROS_BROKEN 1" >>confdefs.h
fi
-ac_fn_c_check_member "$LINENO" "struct stat" "st_rdev" "ac_cv_member_struct_stat_st_rdev" "$ac_includes_default"
-if test "x$ac_cv_member_struct_stat_st_rdev" = xyes; then :
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE_STRUCT_STAT_ST_RDEV 1
-_ACEOF
-
-
-$as_echo "#define HAVE_ST_RDEV 1" >>confdefs.h
-
-fi
-
-
echo "checking if compiler supports __func__" >&6
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
@@ -5991,28 +6877,35 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
const char *func = __func__;
;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
-else
+else $as_nop
-$as_echo "#define __func__ __FUNCTION__" >>confdefs.h
+printf "%s\n" "#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
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+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
echo "checking for MACHINE & MACHINE_ARCH..." >&6
cat > conftest.$ac_ext <<EOF
@@ -6027,7 +6920,7 @@ machine_arch=MACHINE_ARCH
EOF
default_machine=`(eval "$ac_cpp conftest.$ac_ext") 2>&5 |
- egrep machine= | tr -d ' "'`
+ grep machine= | tr -d ' "'`
rm -rf conftest*
if test "$default_machine"; then
eval "$default_machine"
@@ -6037,7 +6930,8 @@ machine_arch=${machine_arch:-`$srcdir/machine.sh arch`}
echo "defaults: MACHINE=$machine, MACHINE_ARCH=$machine_arch" 1>&6
# Check whether --with-machine was given.
-if test "${with_machine+set}" = set; then :
+if test ${with_machine+y}
+then :
withval=$with_machine; case "${withval}" in
yes) as_fn_error $? "bad value ${withval} given for bmake MACHINE" "$LINENO" 5 ;;
no) ;;
@@ -6049,7 +6943,8 @@ fi
force_machine=
# Check whether --with-force_machine was given.
-if test "${with_force_machine+set}" = set; then :
+if test ${with_force_machine+y}
+then :
withval=$with_force_machine; case "${withval}" in
yes) force_machine=FORCE_;;
no) ;;
@@ -6060,7 +6955,8 @@ fi
force_machine_arch=
# Check whether --with-force_machine_arch was given.
-if test "${with_force_machine_arch+set}" = set; then :
+if test ${with_force_machine_arch+y}
+then :
withval=$with_force_machine_arch; case "${withval}" in
yes) force_machine_arch=FORCE_;;
no) ;;
@@ -6070,7 +6966,8 @@ fi
# Check whether --with-machine_arch was given.
-if test "${with_machine_arch+set}" = set; then :
+if test ${with_machine_arch+y}
+then :
withval=$with_machine_arch; case "${withval}" in
yes) as_fn_error $? "bad value ${withval} given for bmake MACHINE_ARCH" "$LINENO" 5 ;;
no) ;;
@@ -6082,7 +6979,8 @@ echo "Using: ${force_machine}MACHINE=$machine, ${force_machine_arch}MACHINE_ARCH
default_sys_path=\${prefix}/share/mk
# Check whether --with-default-sys-path was given.
-if test "${with_default_sys_path+set}" = set; then :
+if test ${with_default_sys_path+y}
+then :
withval=$with_default_sys_path; case "${withval}" in
yes) as_fn_error $? "bad value ${withval} given for bmake _PATH_DEFSYSPATH" "$LINENO" 5 ;;
no) ;;
@@ -6093,7 +6991,8 @@ fi
# Check whether --with-path-objdirprefix was given.
-if test "${with_path_objdirprefix+set}" = set; then :
+if test ${with_path_objdirprefix+y}
+then :
withval=$with_path_objdirprefix; case "${withval}" in
yes) as_fn_error $? "bad value ${withval} given for bmake _PATH_OBJDIRPREFIX" "$LINENO" 5 ;;
no) CPPFLAGS="$CPPFLAGS -DNO_PATH_OBJDIRPREFIX" ;;
@@ -6102,7 +7001,8 @@ esac
fi
# Check whether --enable-pwd-override was given.
-if test "${enable_pwd_override+set}" = set; then :
+if test ${enable_pwd_override+y}
+then :
enableval=$enable_pwd_override; case "${enableval}" in
yes) ;;
no) CPPFLAGS="$CPPFLAGS -DNO_PWD_OVERRIDE" ;;
@@ -6111,7 +7011,8 @@ esac
fi
# Check whether --enable-check-make-chdir was given.
-if test "${enable_check_make_chdir+set}" = set; then :
+if test ${enable_check_make_chdir+y}
+then :
enableval=$enable_check_make_chdir; case "${enableval}" in
yes) ;;
no) CPPFLAGS="$CPPFLAGS -DNO_CHECK_MAKE_CHDIR" ;;
@@ -6121,7 +7022,8 @@ fi
# Check whether --with-mksrc was given.
-if test "${with_mksrc+set}" = set; then :
+if test ${with_mksrc+y}
+then :
withval=$with_mksrc; case "${withval}" in
""|yes|no) ;;
*) test -s $withval/install-mk && mksrc=$withval ||
@@ -6140,24 +7042,45 @@ 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
+printf "%s\n" "#define DEFSHELL_CUSTOM \"$defshell_path\"" >>confdefs.h
+
+ ;;
+/*INDEX*)
+ echo "Using: SHELL=$DEFSHELL_INDEX ($defshell_path)" | sed 's,DEFSHELL_INDEX_,,' >&6
+
+printf "%s\n" "#define DEFSHELL_INDEX $DEFSHELL_INDEX" >>confdefs.h
+
+
+printf "%s\n" "#define DEFSHELL_PATH \"$defshell_path\"" >>confdefs.h
+
+ ;;
+*)
+ echo "Using: SHELL=$DEFSHELL_INDEX" | sed 's,DEFSHELL_INDEX_,,' >&6
+
+printf "%s\n" "#define DEFSHELL_INDEX $DEFSHELL_INDEX" >>confdefs.h
+
+ ;;
+esac
+case "`echo bmake | egrep 'a|b' 2>&1`" in
+bmake) egrep=egrep;;
+*) egrep='grep -E';;
+esac
+
+
-fi
-if test -n "$DEFSHELL_INDEX"; then
-cat >>confdefs.h <<_ACEOF
-#define DEFSHELL_INDEX $DEFSHELL_INDEX
-_ACEOF
-fi
@@ -6175,6 +7098,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
@@ -6204,8 +7139,8 @@ _ACEOF
case $ac_val in #(
*${as_nl}*)
case $ac_var in #(
- *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
-$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
esac
case $ac_var in #(
_ | IFS | as_nl) ;; #(
@@ -6235,15 +7170,15 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
/^ac_cv_env_/b end
t clear
:clear
- s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/
t end
s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
:end' >>confcache
if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
if test -w "$cache_file"; then
if test "x$cache_file" != "x/dev/null"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
-$as_echo "$as_me: updating cache $cache_file" >&6;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+printf "%s\n" "$as_me: updating cache $cache_file" >&6;}
if test ! -f "$cache_file" || test -h "$cache_file"; then
cat confcache >"$cache_file"
else
@@ -6257,8 +7192,8 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
fi
fi
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
-$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;}
fi
fi
rm -f confcache
@@ -6275,7 +7210,7 @@ U=
for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
# 1. Remove the extension, and $U if already installed.
ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
- ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+ ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"`
# 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
# will be set to the directory where LIBOBJS objects are built.
as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
@@ -6292,8 +7227,8 @@ LTLIBOBJS=$ac_ltlibobjs
ac_write_fail=0
ac_clean_files_save=$ac_clean_files
ac_clean_files="$ac_clean_files $CONFIG_STATUS"
-{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
-$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;}
as_write_fail=0
cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
#! $SHELL
@@ -6316,14 +7251,16 @@ cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
# Be more Bourne compatible
DUALCASE=1; export DUALCASE # for MKS sh
-if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+as_nop=:
+if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
emulate sh
NULLCMD=:
# Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
# is contrary to our usage. Disable this feature.
alias -g '${1+"$@"}'='"$@"'
setopt NO_GLOB_SUBST
-else
+else $as_nop
case `(set -o) 2>/dev/null` in #(
*posix*) :
set -o posix ;; #(
@@ -6333,46 +7270,46 @@ esac
fi
+
+# Reset variables that may have inherited troublesome values from
+# the environment.
+
+# IFS needs to be set, to space, tab, and newline, in precisely that order.
+# (If _AS_PATH_WALK were called with IFS unset, it would have the
+# side effect of setting IFS to empty, thus disabling word splitting.)
+# Quoting is to prevent editors from complaining about space-tab.
as_nl='
'
export as_nl
-# Printing a long string crashes Solaris 7 /usr/bin/printf.
-as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
-as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
-as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
-# Prefer a ksh shell builtin over an external printf program on Solaris,
-# but without wasting forks for bash or zsh.
-if test -z "$BASH_VERSION$ZSH_VERSION" \
- && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
- as_echo='print -r --'
- as_echo_n='print -rn --'
-elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
- as_echo='printf %s\n'
- as_echo_n='printf %s'
-else
- if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
- as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
- as_echo_n='/usr/ucb/echo -n'
- else
- as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
- as_echo_n_body='eval
- arg=$1;
- case $arg in #(
- *"$as_nl"*)
- expr "X$arg" : "X\\(.*\\)$as_nl";
- arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
- esac;
- expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
- '
- export as_echo_n_body
- as_echo_n='sh -c $as_echo_n_body as_echo'
- fi
- export as_echo_body
- as_echo='sh -c $as_echo_body as_echo'
-fi
+IFS=" "" $as_nl"
+
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# Ensure predictable behavior from utilities with locale-dependent output.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# We cannot yet rely on "unset" to work, but we need these variables
+# to be unset--not just set to an empty or harmless value--now, to
+# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct
+# also avoids known problems related to "unset" and subshell syntax
+# in other old shells (e.g. bash 2.01 and pdksh 5.2.14).
+for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH
+do eval test \${$as_var+y} \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+
+# Ensure that fds 0, 1, and 2 are open.
+if (exec 3>&0) 2>/dev/null; then :; else exec 0</dev/null; fi
+if (exec 3>&1) 2>/dev/null; then :; else exec 1>/dev/null; fi
+if (exec 3>&2) ; then :; else exec 2>/dev/null; fi
# The user is always right.
-if test "${PATH_SEPARATOR+set}" != set; then
+if ${PATH_SEPARATOR+false} :; then
PATH_SEPARATOR=:
(PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
(PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
@@ -6381,13 +7318,6 @@ if test "${PATH_SEPARATOR+set}" != set; then
fi
-# IFS
-# We need space, tab and new line, in precisely that order. Quoting is
-# there to prevent editors from complaining about space-tab.
-# (If _AS_PATH_WALK were called with IFS unset, it would disable word
-# splitting by setting IFS to empty value.)
-IFS=" "" $as_nl"
-
# Find who we are. Look in the path if we contain no directory separator.
as_myself=
case $0 in #((
@@ -6396,8 +7326,12 @@ case $0 in #((
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ test -r "$as_dir$0" && as_myself=$as_dir$0 && break
done
IFS=$as_save_IFS
@@ -6409,30 +7343,10 @@ if test "x$as_myself" = x; then
as_myself=$0
fi
if test ! -f "$as_myself"; then
- $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
exit 1
fi
-# Unset variables that we do not need and which cause bugs (e.g. in
-# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
-# suppresses any "Segmentation fault" message there. '((' could
-# trigger a bug in pdksh 5.2.14.
-for as_var in BASH_ENV ENV MAIL MAILPATH
-do eval test x\${$as_var+set} = xset \
- && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
-done
-PS1='$ '
-PS2='> '
-PS4='+ '
-
-# NLS nuisances.
-LC_ALL=C
-export LC_ALL
-LANGUAGE=C
-export LANGUAGE
-
-# CDPATH.
-(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
# as_fn_error STATUS ERROR [LINENO LOG_FD]
@@ -6445,13 +7359,14 @@ as_fn_error ()
as_status=$1; test $as_status -eq 0 && as_status=1
if test "$4"; then
as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
fi
- $as_echo "$as_me: error: $2" >&2
+ printf "%s\n" "$as_me: error: $2" >&2
as_fn_exit $as_status
} # as_fn_error
+
# as_fn_set_status STATUS
# -----------------------
# Set $? to STATUS, without forking.
@@ -6478,18 +7393,20 @@ as_fn_unset ()
{ eval $1=; unset $1;}
}
as_unset=as_fn_unset
+
# as_fn_append VAR VALUE
# ----------------------
# Append the text in VALUE to the end of the definition contained in VAR. Take
# advantage of any shell optimizations that allow amortized linear growth over
# repeated appends, instead of the typical quadratic growth present in naive
# implementations.
-if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null
+then :
eval 'as_fn_append ()
{
eval $1+=\$2
}'
-else
+else $as_nop
as_fn_append ()
{
eval $1=\$$1\$2
@@ -6501,12 +7418,13 @@ fi # as_fn_append
# Perform arithmetic evaluation on the ARGs, and store the result in the
# global $as_val. Take advantage of shells that can avoid forks. The arguments
# must be portable across $(()) and expr.
-if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null
+then :
eval 'as_fn_arith ()
{
as_val=$(( $* ))
}'
-else
+else $as_nop
as_fn_arith ()
{
as_val=`expr "$@" || test $? -eq 1`
@@ -6537,7 +7455,7 @@ as_me=`$as_basename -- "$0" ||
$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
X"$0" : 'X\(//\)$' \| \
X"$0" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X/"$0" |
+printf "%s\n" X/"$0" |
sed '/^.*\/\([^/][^/]*\)\/*$/{
s//\1/
q
@@ -6559,6 +7477,10 @@ as_cr_Letters=$as_cr_letters$as_cr_LETTERS
as_cr_digits='0123456789'
as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# Determine whether it's possible to make 'echo' print without a newline.
+# These variables are no longer used directly by Autoconf, but are AC_SUBSTed
+# for compatibility with existing Makefiles.
ECHO_C= ECHO_N= ECHO_T=
case `echo -n x` in #(((((
-n*)
@@ -6572,6 +7494,12 @@ case `echo -n x` in #(((((
ECHO_N='-n';;
esac
+# For backward compatibility with old third-party macros, we provide
+# the shell variables $as_echo and $as_echo_n. New code should use
+# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively.
+as_echo='printf %s\n'
+as_echo_n='printf %s'
+
rm -f conf$$ conf$$.exe conf$$.file
if test -d conf$$.dir; then
rm -f conf$$.dir/conf$$.file
@@ -6613,7 +7541,7 @@ as_fn_mkdir_p ()
as_dirs=
while :; do
case $as_dir in #(
- *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
*) as_qdir=$as_dir;;
esac
as_dirs="'$as_qdir' $as_dirs"
@@ -6622,7 +7550,7 @@ $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$as_dir" : 'X\(//\)[^/]' \| \
X"$as_dir" : 'X\(//\)$' \| \
X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$as_dir" |
+printf "%s\n" X"$as_dir" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
@@ -6684,8 +7612,8 @@ 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
-generated by GNU Autoconf 2.69. Invocation command line was
+This file was extended by bmake $as_me 20240314, which was
+generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
CONFIG_HEADERS = $CONFIG_HEADERS
@@ -6743,14 +7671,16 @@ $config_headers
Report bugs to <sjg@NetBSD.org>."
_ACEOF
+ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"`
+ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"`
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
-ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\
-bmake config.status 20210201
-configured by $0, generated by GNU Autoconf 2.69,
+bmake config.status 20240314
+configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"
-Copyright (C) 2012 Free Software Foundation, Inc.
+Copyright (C) 2021 Free Software Foundation, Inc.
This config.status script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it."
@@ -6788,15 +7718,15 @@ do
-recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
ac_cs_recheck=: ;;
--version | --versio | --versi | --vers | --ver | --ve | --v | -V )
- $as_echo "$ac_cs_version"; exit ;;
+ printf "%s\n" "$ac_cs_version"; exit ;;
--config | --confi | --conf | --con | --co | --c )
- $as_echo "$ac_cs_config"; exit ;;
+ printf "%s\n" "$ac_cs_config"; exit ;;
--debug | --debu | --deb | --de | --d | -d )
debug=: ;;
--file | --fil | --fi | --f )
$ac_shift
case $ac_optarg in
- *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
'') as_fn_error $? "missing file argument" ;;
esac
as_fn_append CONFIG_FILES " '$ac_optarg'"
@@ -6804,7 +7734,7 @@ do
--header | --heade | --head | --hea )
$ac_shift
case $ac_optarg in
- *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
esac
as_fn_append CONFIG_HEADERS " '$ac_optarg'"
ac_need_defaults=false;;
@@ -6813,7 +7743,7 @@ do
as_fn_error $? "ambiguous option: \`$1'
Try \`$0 --help' for more information.";;
--help | --hel | -h )
- $as_echo "$ac_cs_usage"; exit ;;
+ printf "%s\n" "$ac_cs_usage"; exit ;;
-q | -quiet | --quiet | --quie | --qui | --qu | --q \
| -silent | --silent | --silen | --sile | --sil | --si | --s)
ac_cs_silent=: ;;
@@ -6841,7 +7771,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
if \$ac_cs_recheck; then
set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
shift
- \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+ \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6
CONFIG_SHELL='$SHELL'
export CONFIG_SHELL
exec "\$@"
@@ -6855,7 +7785,7 @@ exec 5>>config.log
sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
## Running $as_me. ##
_ASBOX
- $as_echo "$ac_log"
+ printf "%s\n" "$ac_log"
} >&5
_ACEOF
@@ -6881,8 +7811,8 @@ done
# We use the long form for the default assignment because of an extremely
# bizarre bug on SunOS 4.1.3.
if $ac_need_defaults; then
- test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
- test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+ test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files
+ test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers
fi
# Have a temporary directory for convenience. Make it in the build tree
@@ -7218,7 +8148,7 @@ do
esac ||
as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
esac
- case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+ case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
as_fn_append ac_file_inputs " '$ac_f'"
done
@@ -7226,17 +8156,17 @@ do
# use $as_me), people would be surprised to read:
# /* config.h. Generated by config.status. */
configure_input='Generated from '`
- $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+ printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
`' by configure.'
if test x"$ac_file" != x-; then
configure_input="$ac_file. $configure_input"
- { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
-$as_echo "$as_me: creating $ac_file" >&6;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+printf "%s\n" "$as_me: creating $ac_file" >&6;}
fi
# Neutralize special characters interpreted by sed in replacement strings.
case $configure_input in #(
*\&* | *\|* | *\\* )
- ac_sed_conf_input=`$as_echo "$configure_input" |
+ ac_sed_conf_input=`printf "%s\n" "$configure_input" |
sed 's/[\\\\&|]/\\\\&/g'`;; #(
*) ac_sed_conf_input=$configure_input;;
esac
@@ -7253,7 +8183,7 @@ $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$ac_file" : 'X\(//\)[^/]' \| \
X"$ac_file" : 'X\(//\)$' \| \
X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$ac_file" |
+printf "%s\n" X"$ac_file" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
@@ -7277,9 +8207,9 @@ $as_echo X"$ac_file" |
case "$ac_dir" in
.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
*)
- ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'`
# A ".." for each directory in $ac_dir_suffix.
- ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
case $ac_top_builddir_sub in
"") ac_top_builddir_sub=. ac_top_build_prefix= ;;
*) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
@@ -7336,8 +8266,8 @@ ac_sed_dataroot='
case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
*datarootdir*) ac_datarootdir_seen=yes;;
*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
-$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_datarootdir_hack='
@@ -7380,9 +8310,9 @@ test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
{ ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
{ ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
"$ac_tmp/out"`; test -z "$ac_out"; } &&
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
which seems to be undefined. Please make sure it is defined" >&5
-$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
which seems to be undefined. Please make sure it is defined" >&2;}
rm -f "$ac_tmp/stdin"
@@ -7398,20 +8328,20 @@ which seems to be undefined. Please make sure it is defined" >&2;}
#
if test x"$ac_file" != x-; then
{
- $as_echo "/* $configure_input */" \
+ printf "%s\n" "/* $configure_input */" >&1 \
&& eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
} >"$ac_tmp/config.h" \
|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
-$as_echo "$as_me: $ac_file is unchanged" >&6;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+printf "%s\n" "$as_me: $ac_file is unchanged" >&6;}
else
rm -f "$ac_file"
mv "$ac_tmp/config.h" "$ac_file" \
|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
fi
else
- $as_echo "/* $configure_input */" \
+ printf "%s\n" "/* $configure_input */" >&1 \
&& eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
|| as_fn_error $? "could not create -" "$LINENO" 5
fi
@@ -7452,8 +8382,8 @@ if test "$no_create" != yes; then
$ac_cs_success || as_fn_exit 1
fi
if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
-$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
fi
cat <<EOF
@@ -7465,3 +8395,4 @@ You can now run
to produce a fully functional bmake.
EOF
+
diff --git a/contrib/bmake/configure.in b/contrib/bmake/configure.in
index 877493594a20..0796e9afdc8d 100644
--- a/contrib/bmake/configure.in
+++ b/contrib/bmake/configure.in
@@ -1,42 +1,82 @@
dnl
dnl RCSid:
-dnl $Id: configure.in,v 1.70 2021/02/01 18:29:26 sjg Exp $
+dnl $Id: configure.in,v 1.105 2024/03/19 19:08:20 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_PREREQ([2.71])
+AC_INIT([bmake],[20240314],[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|*/bsh)
+ DEFSHELL_INDEX=DEFSHELL_INDEX_SH;;
+ *=*) # eg. sh=/bin/bsh
+ eval `IFS="="; set -- $1; echo name=$1 defshell_path=$2`
+ case "$name" in
+ csh) DEFSHELL_INDEX=DEFSHELL_INDEX_CSH;;
+ ksh) DEFSHELL_INDEX=DEFSHELL_INDEX_KSH;;
+ sh) DEFSHELL_INDEX=DEFSHELL_INDEX_SH;;
+ *) DEFSHELL_INDEX=DEFSHELL_INDEX_CUSTOM;;
+ esac
+ ;;
+ *) DEFSHELL_INDEX=DEFSHELL_INDEX_CUSTOM
+ defshell_path=$1
+ ;;
+ esac
+ case "$defshell_path,$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],
+[ --with-defshell=[[name=]]SHELL use SHELL by default
+ optional 'name' can be 'sh' to indicate SHELL is sh compatible
+ eg. --with-defshell=sh=/bin/bsh
+ use just '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
+dnl sometimes uname -s output is not useful
+FORCE_MAKE_OS=
+make_os=
case "$OS" in
-CYGWIN*|MINGW*) use_makefile=no;;
+CYGWIN*)
+ use_makefile=no
+ OS=Cygwin
+ FORCE_MAKE_OS=$OS
+ ;;
+Darwin|MINGW*) use_makefile=no;;
*) use_makefile=yes;;
esac
+if test "x$FORCE_MAKE_OS" != x; then
+ force_make_os=FORCE_
+ make_os=${FORCE_MAKE_OS}
+fi
AC_ARG_WITH(makefile,
[ --without-makefile disable use of generated makefile],
[case "${withval}" in
@@ -86,6 +126,13 @@ dev) ;;
*) filemon_h=no;;
esac
])
+dnl some systems have broken/incomplete strftime
+AC_ARG_WITH(bmake_strftime,
+[ --with-bmake-strftime force use of bmake strftime],
+[case "${withval}" in
+yes|no) bmake_strftime=$withval;;
+esac])
+dnl
dnl echo "Note: use_meta=$use_meta use_filemon=$use_filemon filemon_h=$filemon_h" >&6
case "$use_meta" in
yes)
@@ -97,6 +144,47 @@ 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"
+ FORCE_USE_SHELL=1
+ ;;
+esac
+if test "x$FORCE_USE_SHELL" != x; then
+ CPPFLAGS="${CPPFLAGS} -DFORCE_USE_SHELL"
+fi
+dnl
+# Not everyone groks TZ=Europe/Berlin
+# which is used by the localtime tests
+echo $ECHO_N "checking whether system has timezone Europe/Berlin... $ECHO_C" >&6
+eval `TZ=UTC date '+utc_H=%H utc_d=%d' 2> /dev/null`
+eval `TZ=Europe/Berlin date '+utc1_H=%H utc1_d=%d' 2> /dev/null`
+if test ${utc_d-0} -lt ${utc1_d-0} -o ${utc_H-0} -lt ${utc1_H-0}; then
+ echo yes >&6
+ UTC_1=Europe/Berlin
+else
+ eval `TZ=UTC-1 date '+utc1_H=%H utc1_d=%d' 2> /dev/null`
+ if test ${utc_d-0} -lt ${utc1_d-0} -o ${utc_H-0} -lt ${utc1_H-0}; then
+ UTC_1=UTC-1
+ echo no, using UTC-1 >&6
+ fi
+fi
+test "x$UTC_1" = x && echo no >&6
+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
@@ -104,9 +192,11 @@ dnl see _EXTENSIONS_ we use it.
AC_USE_SYSTEM_EXTENSIONS
dnl Checks for programs.
AC_PROG_CC
-AC_PROG_CC_C99
-dnl AC_PROG_GCC_TRADITIONAL
AC_PROG_INSTALL
+# We have to override that on some systems
+case "$OS" in
+IRIX*) ac_INSTALL=$srcdir/install-sh;;
+esac
dnl Executable suffix - normally empty; .exe on os2.
AC_SUBST(ac_exe_suffix)dnl
dnl
@@ -121,12 +211,39 @@ if test $bmake_path_max -gt 1024; then
# this is all we expect
bmake_path_max=1024
fi
+if test ${bmake_strftime:-no} = yes; then
+ CPPFLAGS="${CPPFLAGS} -DFORCE_BMAKE_STRFTIME"
+fi
echo "Using: BMAKE_PATH_MAX=$bmake_path_max" >&6
AC_SUBST(bmake_path_max)dnl
dnl
-dnl AC_C_CROSS
-dnl
-
+# if type does not work which(1) had better!
+# 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
@@ -148,6 +265,7 @@ AC_CHECK_HEADERS( \
paths.h \
poll.h \
ranlib.h \
+ regex.h \
sys/mman.h \
sys/select.h \
sys/socket.h \
@@ -158,38 +276,36 @@ 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
AC_TYPE_SIZE_T
AC_TYPE_UINT32_T
-AC_DECL_SYS_SIGLIST
-AC_HEADER_TIME
+AC_CHECK_DECLS([sys_siglist],[],[],[#include <signal.h>
+/* NetBSD declares sys_siglist in unistd.h. */
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+])
+
+AC_CHECK_HEADERS_ONCE([sys/time.h])
+
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
@@ -197,8 +313,7 @@ AC_DEFINE([sig_atomic_t],[int],)
])
dnl Checks for library functions.
-AC_TYPE_SIGNAL
-AC_FUNC_VFORK
+AC_FUNC_FORK
AC_FUNC_VPRINTF
AC_FUNC_WAIT3
dnl Keep this list sorted
@@ -207,7 +322,6 @@ AC_CHECK_FUNCS( \
errx \
getcwd \
getenv \
- getopt \
getwd \
killpg \
mmap \
@@ -215,15 +329,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 +358,10 @@ AC_CHECK_FUNCS( \
dnl functions which we may need to provide
AC_REPLACE_FUNCS( \
+ getopt \
realpath \
dirname \
+ sigaction \
stresep \
strlcpy \
)
@@ -253,7 +377,6 @@ dnl
dnl Structures
dnl
AC_HEADER_STAT
-AC_STRUCT_ST_RDEV
dnl
echo "checking if compiler supports __func__" >&6
AC_LANG(C)
@@ -261,13 +384,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.
@@ -285,7 +415,7 @@ machine_arch=MACHINE_ARCH
EOF
default_machine=`(eval "$ac_cpp conftest.$ac_ext") 2>&5 |
- egrep machine= | tr -d ' "'`
+ grep machine= | tr -d ' "'`
rm -rf conftest*
if test "$default_machine"; then
eval "$default_machine"
@@ -404,17 +534,38 @@ 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
+dnl Some systems have deprecated egrep in favor of grep -E
+case "`echo bmake | egrep 'a|b' 2>&1`" in
+bmake) egrep=egrep;;
+*) egrep='grep -E';;
+esac
dnl
+AC_SUBST(egrep)
+AC_SUBST(make_os)
+AC_SUBST(force_make_os)
AC_SUBST(machine)
AC_SUBST(force_machine)
AC_SUBST(machine_arch)
@@ -423,16 +574,31 @@ 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
-AC_OUTPUT($bm_outfiles)
+
+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([$bm_outfiles])
+AC_OUTPUT
cat <<EOF
You can now run
diff --git a/contrib/bmake/dir.c b/contrib/bmake/dir.c
index 627e654387f8..5c11d6c794d7 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.286 2023/12/29 18:53:24 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -83,7 +83,7 @@
*
* Dir_End Clean up the module.
*
- * Dir_SetPATH Set ${.PATH} to reflect state of dirSearchPath.
+ * Dir_SetPATH Set ${.PATH} to reflect the state of dirSearchPath.
*
* Dir_HasWildcards
* Returns true if the name given it needs to
@@ -94,13 +94,12 @@
* from the search path.
*
* Dir_FindFile Searches for a file on a given search path.
- * If it exists, the entire path is returned.
- * Otherwise NULL is returned.
+ * If it exists, returns the entire path, otherwise NULL.
*
* Dir_FindHereOrAbove
- * Search for a path in the current directory and
- * then all the directories above it in turn until
- * the path is found or we reach the root ("/").
+ * Search for a path in the current directory and then
+ * all the directories above it in turn, until the path
+ * is found or the root directory ("/") is reached.
*
* Dir_UpdateMTime
* Update the modification time and path of a node with
@@ -114,11 +113,6 @@
* preceded by the command flag and all of them
* separated by a space.
*
- * Dir_Destroy Destroy an element of a search path. Frees up all
- * things that can be freed for the element as long
- * as the element is no longer referenced by any other
- * search path.
- *
* SearchPath_Clear
* Resets a search path to the empty list.
*
@@ -138,7 +132,7 @@
#include "job.h"
/* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */
-MAKE_RCSID("$NetBSD: dir.c,v 1.272 2021/04/04 10:13:09 rillig Exp $");
+MAKE_RCSID("$NetBSD: dir.c,v 1.286 2023/12/29 18:53:24 rillig Exp $");
/*
* A search path is a list of CachedDir structures. A CachedDir has in it the
@@ -152,26 +146,21 @@ MAKE_RCSID("$NetBSD: dir.c,v 1.272 2021/04/04 10:13:09 rillig Exp $");
* All previously-read directories are kept in openDirs, which is checked
* first before a directory is opened.
*
- * The need for the caching of whole directories is brought about by the
- * multi-level transformation code in suff.c, which tends to search for far
- * more files than regular make does. In the initial implementation, the
- * amount of time spent performing "stat" calls was truly astronomical.
- * The problem with caching at the start is, of course, that pmake doesn't
- * then detect changes to these directories during the course of the make.
- * Three possibilities suggest themselves:
+ * This cache is used by the multi-level transformation code in suff.c, which
+ * tends to search for far more files than in regular explicit targets. After
+ * a directory has been cached, any later changes to that directory are not
+ * reflected in the cache. To keep the cache up to date, there are several
+ * ideas:
*
* 1) just use stat to test for a file's existence. As mentioned above,
- * this is very inefficient due to the number of checks engendered by
+ * this is very inefficient due to the number of checks performed by
* the multi-level transformation code.
*
- * 2) use readdir() and company to search the directories, keeping them
- * open between checks. I have tried this and while it didn't slow down
- * the process too much, it could severely affect the amount of
- * parallelism available as each directory open would take another file
- * descriptor out of play for handling I/O for another job. Given that
- * it is only recently (as of 1993 or earlier) that UNIX OS's have taken
- * to allowing more than 20 or 32 file descriptors for a process, this
- * doesn't seem acceptable to me.
+ * 2) use readdir() to search the directories, keeping them open between
+ * checks. Around 1993 or earlier, this didn't slow down the process too
+ * much, but it consumed one file descriptor per open directory, which
+ * was critical on the then-current operating systems, as many limited
+ * the number of open file descriptors to 20 or 32.
*
* 3) record the mtime of the directory in the CachedDir structure and
* verify the directory hasn't changed since the contents were cached.
@@ -184,9 +173,9 @@ MAKE_RCSID("$NetBSD: dir.c,v 1.272 2021/04/04 10:13:09 rillig Exp $");
* number of reloadings and if the number goes over a (small) limit,
* resort to using stat in its place.
*
- * An additional thing to consider is that pmake is used primarily to create
- * C programs and until recently (as of 1993 or earlier) pcc-based compilers
- * refused to allow you to specify where the resulting object file should be
+ * An additional thing to consider is that make is used primarily to create
+ * C programs and until recently (as of 1993 or earlier), pcc-based compilers
+ * didn't have an option to specify where the resulting object file should be
* placed. This forced all objects to be created in the current directory.
* This isn't meant as a full excuse, just an explanation of some of the
* reasons for the caching used here.
@@ -212,7 +201,7 @@ MAKE_RCSID("$NetBSD: dir.c,v 1.272 2021/04/04 10:13:09 rillig Exp $");
/* A cache for the filenames in a directory. */
struct CachedDir {
/*
- * Name of directory, either absolute or relative to the current
+ * Name of the directory, either absolute or relative to the current
* directory. The name is not normalized in any way, that is, "."
* and "./." are different.
*
@@ -238,20 +227,12 @@ struct CachedDir {
typedef List CachedDirList;
typedef ListNode CachedDirListNode;
-typedef ListNode SearchPathNode;
-
/* A list of cached directories, with fast lookup by directory name. */
typedef struct OpenDirs {
CachedDirList list;
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 */
@@ -279,8 +260,7 @@ static CachedDir *dotLast = NULL;
*
* XXX: If this is done way early, there's a chance other rules will have
* already updated the file, in which case we'll update it again. Generally,
- * there won't be two rules to update a single file, so this should be ok,
- * but...
+ * there won't be two rules to update a single file, so this should be ok.
*/
static HashTable mtimes;
@@ -344,7 +324,7 @@ CachedDir_Unref(CachedDir *dir)
free(dir);
}
-/* Update the value of the CachedDir variable, updating the reference counts. */
+/* Update the value of 'var', updating the reference counts. */
static void
CachedDir_Assign(CachedDir **var, CachedDir *dir)
{
@@ -419,9 +399,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 +410,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 +442,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. */
@@ -481,9 +461,7 @@ Dir_Init(void)
CachedDir_Assign(&dotLast, CachedDir_New(".DOTLAST"));
}
-/*
- * Called by Dir_InitDir and whenever .CURDIR is assigned to.
- */
+/* Called by Dir_InitDir and whenever .CURDIR is assigned to. */
void
Dir_InitCur(const char *newCurdir)
{
@@ -493,7 +471,7 @@ Dir_InitCur(const char *newCurdir)
return;
/*
- * Our build directory is not the same as our source directory.
+ * The build directory is not the same as the source directory.
* Keep this one around too.
*/
dir = SearchPath_Add(NULL, newCurdir);
@@ -504,7 +482,7 @@ Dir_InitCur(const char *newCurdir)
}
/*
- * (Re)initialize "dot" (current/object directory) path hash.
+ * (Re)initialize "dot" (the current/object directory).
* Some directories may be cached.
*/
void
@@ -583,6 +561,21 @@ Dir_SetPATH(void)
}
}
+
+void
+Dir_SetSYSPATH(void)
+{
+ CachedDirListNode *ln;
+
+ Var_ReadOnly(".SYSPATH", false);
+ Global_Delete(".SYSPATH");
+ for (ln = sysIncPath->dirs.first; ln != NULL; ln = ln->next) {
+ CachedDir *dir = ln->datum;
+ Global_Append(".SYSPATH", dir->name);
+ }
+ Var_ReadOnly(".SYSPATH", true);
+}
+
/*
* See if the given name has any wildcard characters in it and all braces and
* brackets are properly balanced.
@@ -590,8 +583,6 @@ Dir_SetPATH(void)
* XXX: This code is not 100% correct ([^]] fails etc.). I really don't think
* that make(1) should be expanding patterns, because then you have to set a
* mechanism for escaping the expansion!
- *
- * Return true if the word should be expanded, false otherwise.
*/
bool
Dir_HasWildcards(const char *name)
@@ -628,20 +619,14 @@ Dir_HasWildcards(const char *name)
}
/*
- * See if any files match the pattern and add their names to the 'expansions'
- * list if they do.
+ * See if any files as seen from 'dir' match 'pattern', and add their names
+ * to 'expansions' if they do.
*
- * This is incomplete -- wildcards are only expanded in the final path
- * component, but not in directories like src/lib*c/file*.c, but it
- * will do for now (now being 1993 until at least 2020). To expand these,
+ * Wildcards are only expanded in the final path component, but not in
+ * directories like src/lib*c/file*.c. To expand these wildcards,
* delegate the work to the shell, using the '!=' variable assignment
* operator, the ':sh' variable modifier or the ':!...!' variable modifier,
* such as in ${:!echo src/lib*c/file*.c!}.
- *
- * Input:
- * pattern Pattern to look for
- * dir Directory to search
- * expansion Place to store the results
*/
static void
DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions)
@@ -659,8 +644,10 @@ DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions)
HashIter_InitSet(&hi, &dir->files);
while (HashIter_Next(&hi) != NULL) {
const char *base = hi.entry->key;
+ StrMatchResult res = Str_Match(base, pattern);
+ /* TODO: handle errors from res.error */
- if (!Str_Match(base, pattern))
+ if (!res.matched)
continue;
/*
@@ -678,28 +665,25 @@ DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions)
{
char *fullName = isDot
- ? bmake_strdup(base)
- : str_concat3(dirName, "/", base);
+ ? bmake_strdup(base)
+ : str_concat3(dirName, "/", base);
Lst_Append(expansions, fullName);
}
}
}
-/*
- * Find the next closing brace in the string, taking nested braces into
- * account.
- */
+/* Find the next closing brace in 'p', taking nested braces into account. */
static const char *
closing_brace(const char *p)
{
- int nest = 0;
+ int depth = 0;
while (*p != '\0') {
- if (*p == '}' && nest == 0)
+ if (*p == '}' && depth == 0)
break;
if (*p == '{')
- nest++;
+ depth++;
if (*p == '}')
- nest--;
+ depth--;
p++;
}
return p;
@@ -712,14 +696,14 @@ closing_brace(const char *p)
static const char *
separator_comma(const char *p)
{
- int nest = 0;
+ int depth = 0;
while (*p != '\0') {
- if ((*p == '}' || *p == ',') && nest == 0)
+ if ((*p == '}' || *p == ',') && depth == 0)
break;
if (*p == '{')
- nest++;
+ depth++;
if (*p == '}')
- nest--;
+ depth--;
p++;
}
return p;
@@ -776,7 +760,7 @@ DirExpandCurly(const char *word, const char *brace, SearchPath *path,
const char *prefix, *middle, *piece, *middle_end, *suffix;
size_t prefix_len, suffix_len;
- /* Split the word into prefix '{' middle '}' suffix. */
+ /* Split the word into prefix, '{', middle, '}' and suffix. */
middle = brace + 1;
middle_end = closing_brace(middle);
@@ -798,7 +782,7 @@ DirExpandCurly(const char *word, const char *brace, SearchPath *path,
size_t piece_len = (size_t)(piece_end - piece);
char *file = concat3(prefix, prefix_len, piece, piece_len,
- suffix, suffix_len);
+ suffix, suffix_len);
if (contains_wildcard(file)) {
SearchPath_Expand(path, file, expansions);
@@ -813,14 +797,14 @@ DirExpandCurly(const char *word, const char *brace, SearchPath *path,
}
-/* Expand the word in each of the directories from the path. */
+/* Expand 'pattern' in each of the directories from 'path'. */
static void
-DirExpandPath(const char *word, SearchPath *path, StringList *expansions)
+DirExpandPath(const char *pattern, SearchPath *path, StringList *expansions)
{
- SearchPathNode *ln;
+ CachedDirListNode *ln;
for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum;
- DirMatchFiles(word, dir, expansions);
+ DirMatchFiles(pattern, dir, expansions);
}
}
@@ -850,11 +834,6 @@ SearchPath_ExpandMiddle(SearchPath *path, const char *pattern,
prefix = bmake_strsedup(pattern, wildcardComponent + 1);
/*
- * XXX: Check the "the directory is added to the path" part.
- * It is probably surprising that the directory before a
- * wildcard gets added to the path.
- */
- /*
* XXX: Only the first match of the prefix in the path is
* taken, any others are ignored. The expectation may be
* that the pattern is expanded in the whole path.
@@ -869,7 +848,7 @@ SearchPath_ExpandMiddle(SearchPath *path, const char *pattern,
* path contains ../Etc/Object and we're looking for Etc, it won't
* be found. Ah well. Probably not important.
*
- * XXX: Check whether the above comment is still true.
+ * TODO: Check whether the above comment is still true.
*/
if (dirpath == NULL)
return;
@@ -910,15 +889,9 @@ SearchPath_Expand(SearchPath *path, const char *pattern, StringList *expansions)
goto done;
}
- /* At this point, the pattern does not contain '{'. */
-
slash = strchr(pattern, '/');
if (slash == NULL) {
- /* The pattern has no directory component. */
-
- /* First the files in dot. */
DirMatchFiles(pattern, dot, expansions);
- /* Then the files in every other directory on the path. */
DirExpandPath(pattern, path, expansions);
goto done;
}
@@ -961,13 +934,13 @@ done:
}
/*
- * Find if the file with the given name exists in the given path.
+ * Find if 'base' exists in 'dir'.
* Return the freshly allocated path to the file, or NULL.
*/
static char *
DirLookup(CachedDir *dir, const char *base)
{
- char *file; /* the current filename to check */
+ char *file;
DEBUG1(DIR, " %s ...\n", dir->name);
@@ -983,15 +956,16 @@ DirLookup(CachedDir *dir, const char *base)
/*
- * Find if the file with the given name exists in the given directory.
+ * Find if 'name' exists in 'dir'.
* Return the freshly allocated path to the file, or NULL.
*/
static char *
DirLookupSubdir(CachedDir *dir, const char *name)
{
struct cached_stat cst;
- char *file = dir == dot ? bmake_strdup(name)
- : str_concat3(dir->name, "/", name);
+ char *file = dir == dot
+ ? bmake_strdup(name)
+ : str_concat3(dir->name, "/", name);
DEBUG1(DIR, "checking %s ...\n", file);
@@ -1004,12 +978,12 @@ DirLookupSubdir(CachedDir *dir, const char *name)
}
/*
- * Find if the file with the given name exists in the given path.
- * Return the freshly allocated path to the file, the empty string, or NULL.
- * Returning the empty string means that the search should be terminated.
+ * Find if 'name' (which has basename 'base') exists in 'dir'.
+ * Return the freshly allocated path to the file, an empty string, or NULL.
+ * Returning an empty string means that the search should be terminated.
*/
static char *
-DirLookupAbs(CachedDir *dir, const char *name, const char *cp)
+DirLookupAbs(CachedDir *dir, const char *name, const char *base)
{
const char *dnp; /* pointer into dir->name */
const char *np; /* pointer into name */
@@ -1025,10 +999,10 @@ DirLookupAbs(CachedDir *dir, const char *name, const char *cp)
for (dnp = dir->name, np = name;
*dnp != '\0' && *dnp == *np; dnp++, np++)
continue;
- if (*dnp != '\0' || np != cp - 1)
+ if (*dnp != '\0' || np != base - 1)
return NULL;
- if (!HashSet_Contains(&dir->files, cp)) {
+ if (!HashSet_Contains(&dir->files, base)) {
DEBUG0(DIR, " must be here but isn't -- returning\n");
return bmake_strdup(""); /* to terminate the search */
}
@@ -1040,7 +1014,7 @@ DirLookupAbs(CachedDir *dir, const char *name, const char *cp)
}
/*
- * Find the file given on "." or curdir.
+ * Find the given file in "." or curdir.
* Return the freshly allocated path to the file, or NULL.
*/
static char *
@@ -1068,7 +1042,7 @@ static bool
FindFileRelative(SearchPath *path, bool seenDotLast,
const char *name, char **out_file)
{
- SearchPathNode *ln;
+ CachedDirListNode *ln;
bool checkedDot = false;
char *file;
@@ -1078,11 +1052,11 @@ FindFileRelative(SearchPath *path, bool seenDotLast,
if (dot != NULL) {
checkedDot = true;
if ((file = DirLookupSubdir(dot, name)) != NULL)
- goto found;
+ goto done;
}
if (cur != NULL &&
(file = DirLookupSubdir(cur, name)) != NULL)
- goto found;
+ goto done;
}
for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
@@ -1095,18 +1069,18 @@ FindFileRelative(SearchPath *path, bool seenDotLast,
checkedDot = true;
}
if ((file = DirLookupSubdir(dir, name)) != NULL)
- goto found;
+ goto done;
}
if (seenDotLast) {
if (dot != NULL && !checkedDot) {
checkedDot = true;
if ((file = DirLookupSubdir(dot, name)) != NULL)
- goto found;
+ goto done;
}
if (cur != NULL &&
(file = DirLookupSubdir(cur, name)) != NULL)
- goto found;
+ goto done;
}
if (checkedDot) {
@@ -1116,34 +1090,23 @@ FindFileRelative(SearchPath *path, bool seenDotLast,
*/
DEBUG0(DIR, " Checked . already, returning NULL\n");
file = NULL;
- goto found;
+ goto done;
}
return false;
-found:
+done:
*out_file = file;
return true;
}
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;
+ CachedDirListNode *ln;
- /*
- * For absolute names, compare directory path prefix against
- * the the directory path of each member on the search path
- * for an exact match. If we have an exact match on any member
- * of the search path, use the cached contents of that member
- * to lookup the final file component. If that lookup fails we
- * can safely assume that the file does not exist at all.
- * This is signified by DirLookupAbs() returning an empty
- * string.
- */
DEBUG0(DIR, " Trying exact path matches...\n");
if (!seenDotLast && cur != NULL &&
@@ -1176,13 +1139,6 @@ found:
/*
* Find the file with the given name along the given search path.
*
- * If the file is found in a directory that is not on the path
- * already (either 'name' is absolute or it is a relative path
- * [ dir1/.../dirn/file ] which exists below one of the directories
- * already on the search path), its directory is added to the end
- * of the path, on the assumption that there will be more files in
- * that directory later on. Sometimes this is true. Sometimes not.
- *
* Input:
* name the file to find
* path the directories to search, or NULL
@@ -1195,7 +1151,7 @@ Dir_FindFile(const char *name, SearchPath *path)
{
char *file; /* the current filename to check */
bool seenDotLast = false; /* true if we should search dot last */
- struct cached_stat cst; /* Buffer for stat, if necessary */
+ struct cached_stat cst;
const char *trailing_dot = ".";
const char *base = str_basename(name);
@@ -1222,21 +1178,19 @@ Dir_FindFile(const char *name, SearchPath *path)
* of each of the directories on the search path.
*/
if (base == name || (base - name == 2 && *name == '.')) {
- SearchPathNode *ln;
+ CachedDirListNode *ln;
/*
- * We look through all the directories on the path seeking one
+ * Look through all the directories on the path seeking one
* which contains the final component of the given name. If
- * such a file is found, we concatenate the directory name
- * and the final component and return the resulting string.
- * If we don't find any such thing, we go on to phase two.
+ * such a file is found, return its pathname.
+ * If there is no such file, go on to phase two.
*
- * No matter what, we always look for the file in the current
- * directory before anywhere else (unless we found the magic
- * DOTLAST path, in which case we search it last) and we *do
- * not* add the ./ to it if it exists.
+ * No matter what, always look for the file in the current
+ * directory before anywhere else (unless the path contains
+ * the magic '.DOTLAST', in which case search it last).
* This is so there are no conflicts between what the user
- * specifies (fish.c) and what pmake finds (./fish.c).
+ * specifies (fish.c) and what make finds (./fish.c).
*/
if (!seenDotLast && (file = DirFindDot(name, base)) != NULL)
return file;
@@ -1253,30 +1207,14 @@ Dir_FindFile(const char *name, SearchPath *path)
return file;
}
- /*
- * We didn't find the file on any directory in the search path.
- * If the name doesn't contain a slash, that means it doesn't exist.
- * If it *does* contain a slash, however, there is still hope: it
- * could be in a subdirectory of one of the members of the search
- * path. (eg. /usr/include and sys/types.h. The above search would
- * fail to turn up types.h in /usr/include, but it *is* in
- * /usr/include/sys/types.h).
- * [ This no longer applies: If we find such a file, we assume there
- * will be more (what else can we assume?) and add all but the last
- * component of the resulting name onto the search path (at the
- * end).]
- * This phase is only performed if the file is *not* absolute.
- */
if (base == name) {
DEBUG0(DIR, " failed.\n");
misses++;
return NULL;
}
- if (*base == '\0') {
- /* we were given a trailing "/" */
- base = trailing_dot;
- }
+ if (*base == '\0')
+ base = trailing_dot; /* we were given a trailing "/" */
if (name[0] != '/') {
if (FindFileRelative(path, seenDotLast, name, &file))
@@ -1287,16 +1225,7 @@ Dir_FindFile(const char *name, SearchPath *path)
}
/*
- * Didn't find it that way, either. Sigh. Phase 3. Add its directory
- * onto the search path in any case, just in case, then look for the
- * thing in the hash table. If we find it, grand. We return a new
- * copy of the name. Otherwise we sadly return a NULL pointer. Sigh.
- * Note that if the directory holding the file doesn't exist, this
- * will do an extra search of the final directory on the path. Unless
- * something weird happens, this search won't succeed and life will
- * be groovy.
- *
- * Sigh. We cannot add the directory onto the search path because
+ * We cannot add the directory onto the search path because
* of this amusing case:
* $(INSTALLDIR)/$(FILE): $(FILE)
*
@@ -1304,75 +1233,40 @@ Dir_FindFile(const char *name, SearchPath *path)
* When searching for $(FILE), we will find it in $(INSTALLDIR)
* b/c we added it here. This is not good...
*/
-#if 0
- {
- CachedDir *dir;
- char *prefix;
-
- if (base == trailing_dot) {
- base = strrchr(name, '/');
- base++;
- }
- prefix = bmake_strsedup(name, base - 1);
- (void)SearchPath_Add(path, prefix);
- free(prefix);
-
- bigmisses++;
- if (path->last == NULL)
- return NULL;
- dir = path->last->datum;
- if (HashSet_Contains(&dir->files, base))
- return bmake_strdup(name);
- return NULL;
- }
-#else
DEBUG1(DIR, " Looking for \"%s\" ...\n", name);
bigmisses++;
- if (cached_stat(name, &cst) == 0) {
+ if (cached_stat(name, &cst) == 0)
return bmake_strdup(name);
- }
DEBUG0(DIR, " failed. Returning NULL\n");
return NULL;
-#endif
}
/*
- * Search for a path starting at a given directory and then working our way
- * up towards the root.
- *
- * Input:
- * here starting directory
- * search_path the relative path we are looking for
- *
- * Results:
- * The found path, or NULL.
+ * Search for 'needle' starting at the directory 'here' and then working our
+ * way up towards the root directory. Return the allocated path, or NULL.
*/
char *
-Dir_FindHereOrAbove(const char *here, const char *search_path)
+Dir_FindHereOrAbove(const char *here, const char *needle)
{
struct cached_stat cst;
char *dirbase, *dirbase_end;
char *try, *try_end;
- /* copy out our starting point */
dirbase = bmake_strdup(here);
dirbase_end = dirbase + strlen(dirbase);
- /* loop until we determine a result */
for (;;) {
-
- /* try and stat(2) it ... */
- try = str_concat3(dirbase, "/", search_path);
+ try = str_concat3(dirbase, "/", needle);
if (cached_stat(try, &cst) != -1) {
- /*
- * success! if we found a file, chop off
- * the filename so we return a directory.
- */
if ((cst.cst_mode & S_IFMT) != S_IFDIR) {
+ /*
+ * Chop off the filename, to return a
+ * directory.
+ */
try_end = try + strlen(try);
while (try_end > try && *try_end != '/')
try_end--;
@@ -1385,16 +1279,10 @@ Dir_FindHereOrAbove(const char *here, const char *search_path)
}
free(try);
- /*
- * nope, we didn't find it. if we used up dirbase we've
- * reached the root and failed.
- */
if (dirbase_end == dirbase)
break; /* failed! */
- /*
- * truncate dirbase from the end to move up a dir
- */
+ /* Truncate dirbase from the end to move up a dir. */
while (dirbase_end > dirbase && *dirbase_end != '/')
dirbase_end--;
*dirbase_end = '\0'; /* chop! */
@@ -1431,9 +1319,9 @@ ResolveMovedDepends(GNode *gn)
gn->path = bmake_strdup(fullName);
if (!Job_RunTarget(".STALE", gn->fname))
fprintf(stdout, /* XXX: Why stdout? */
- "%s: %s, %d: ignoring stale %s for %s, found %s\n",
- progname, gn->fname, gn->lineno,
- makeDependfile, gn->name, fullName);
+ "%s: %s, %u: ignoring stale %s for %s, found %s\n",
+ progname, gn->fname, gn->lineno,
+ makeDependfile, gn->name, fullName);
return fullName;
}
@@ -1448,7 +1336,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);
@@ -1465,13 +1353,13 @@ ResolveFullName(GNode *gn)
}
/*
- * Search gn along dirSearchPath and store its modification time in gn->mtime.
- * If no file is found, store 0 instead.
+ * Search 'gn' along 'dirSearchPath' and store its modification time in
+ * 'gn->mtime'. If no file is found, store 0 instead.
*
- * The found file is stored in gn->path, unless the node already had a path.
+ * The found file is stored in 'gn->path', unless the node already had a path.
*/
void
-Dir_UpdateMTime(GNode *gn, bool recheck)
+Dir_UpdateMTime(GNode *gn, bool forceRefresh)
{
char *fullName;
struct cached_stat cst;
@@ -1488,7 +1376,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);
@@ -1551,14 +1439,14 @@ CacheNewDir(const char *name, SearchPath *path)
}
/*
- * Read the list of filenames in the directory and store the result
- * in openDirs.
+ * Read the list of filenames in the directory 'name' and store the result
+ * in 'openDirs'.
*
- * If a path is given, append the directory to that path.
+ * If a search path is given, append the directory to that path.
*
* Input:
* path The path to which the directory should be
- * added, or NULL to only add the directory to openDirs
+ * added, or NULL to only add the directory to openDirs.
* name The name of the directory to add.
* The name is not normalized in any way.
* Output:
@@ -1573,7 +1461,7 @@ SearchPath_Add(SearchPath *path, const char *name)
{
if (path != NULL && strcmp(name, ".DOTLAST") == 0) {
- SearchPathNode *ln;
+ CachedDirListNode *ln;
/* XXX: Linear search gets slow with thousands of entries. */
for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
@@ -1606,7 +1494,7 @@ SearchPath *
Dir_CopyDirSearchPath(void)
{
SearchPath *path = SearchPath_New();
- SearchPathNode *ln;
+ CachedDirListNode *ln;
for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum;
Lst_Append(&path->dirs, CachedDir_Ref(dir));
@@ -1617,22 +1505,14 @@ Dir_CopyDirSearchPath(void)
/*
* Make a string by taking all the directories in the given search path and
* preceding them by the given flag. Used by the suffix module to create
- * variables for compilers based on suffix search paths.
- *
- * Input:
- * flag flag which should precede each directory
- * path list of directories
- *
- * Results:
- * The string mentioned above. Note that there is no space between the
- * given flag and each directory. The empty string is returned if things
- * don't go well.
+ * variables for compilers based on suffix search paths. Note that there is no
+ * space between the given flag and each directory.
*/
char *
SearchPath_ToFlags(SearchPath *path, const char *flag)
{
Buffer buf;
- SearchPathNode *ln;
+ CachedDirListNode *ln;
Buf_Init(&buf);
@@ -1652,7 +1532,7 @@ SearchPath_ToFlags(SearchPath *path, const char *flag)
void
SearchPath_Free(SearchPath *path)
{
- SearchPathNode *ln;
+ CachedDirListNode *ln;
for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum;
@@ -1683,7 +1563,7 @@ SearchPath_Clear(SearchPath *path)
void
SearchPath_AddAll(SearchPath *dst, SearchPath *src)
{
- SearchPathNode *ln;
+ CachedDirListNode *ln;
for (ln = src->dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum;
@@ -1698,7 +1578,6 @@ percentage(int num, int den)
return den != 0 ? num * 100 / den : 0;
}
-/********** DEBUG INFO **********/
void
Dir_PrintDirectories(void)
{
@@ -1721,7 +1600,7 @@ Dir_PrintDirectories(void)
void
SearchPath_Print(const SearchPath *path)
{
- SearchPathNode *ln;
+ CachedDirListNode *ln;
for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
const CachedDir *dir = ln->datum;
diff --git a/contrib/bmake/dir.h b/contrib/bmake/dir.h
index d96393c62ebb..09cbca8ec4c1 100644
--- a/contrib/bmake/dir.h
+++ b/contrib/bmake/dir.h
@@ -1,4 +1,4 @@
-/* $NetBSD: dir.h,v 1.44 2021/04/03 11:08:40 rillig Exp $ */
+/* $NetBSD: dir.h,v 1.47 2023/01/24 00:24:02 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -82,18 +82,19 @@ void Dir_InitCur(const char *);
void Dir_InitDot(void);
void Dir_End(void);
void Dir_SetPATH(void);
-bool Dir_HasWildcards(const char *);
+void Dir_SetSYSPATH(void);
+bool Dir_HasWildcards(const char *) MAKE_ATTR_USE;
void SearchPath_Expand(SearchPath *, const char *, StringList *);
-char *Dir_FindFile(const char *, SearchPath *);
-char *Dir_FindHereOrAbove(const char *, const char *);
+char *Dir_FindFile(const char *, SearchPath *) MAKE_ATTR_USE;
+char *Dir_FindHereOrAbove(const char *, const char *) MAKE_ATTR_USE;
void Dir_UpdateMTime(GNode *, bool);
CachedDir *SearchPath_Add(SearchPath *, const char *);
-char *SearchPath_ToFlags(SearchPath *, const char *);
+char *SearchPath_ToFlags(SearchPath *, const char *) MAKE_ATTR_USE;
void SearchPath_Clear(SearchPath *);
void SearchPath_AddAll(SearchPath *, SearchPath *);
void Dir_PrintDirectories(void);
void SearchPath_Print(const SearchPath *);
-SearchPath *Dir_CopyDirSearchPath(void);
+SearchPath *Dir_CopyDirSearchPath(void) MAKE_ATTR_USE;
/* Stripped-down variant of struct stat. */
struct cached_stat {
@@ -104,4 +105,4 @@ struct cached_stat {
int cached_lstat(const char *, struct cached_stat *);
int cached_stat(const char *, struct cached_stat *);
-#endif /* MAKE_DIR_H */
+#endif
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.h b/contrib/bmake/filemon/filemon.h
index 139d62f1a1f6..d74c99160ba5 100644
--- a/contrib/bmake/filemon/filemon.h
+++ b/contrib/bmake/filemon/filemon.h
@@ -1,4 +1,4 @@
-/* $NetBSD: filemon.h,v 1.5 2021/01/19 20:51:46 rillig Exp $ */
+/* $NetBSD: filemon.h,v 1.6 2021/12/15 12:08:25 rillig Exp $ */
/*
* Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -50,4 +50,4 @@ int filemon_setpid_child(const struct filemon *, pid_t);
int filemon_readfd(const struct filemon *);
int filemon_process(struct filemon *);
-#endif /* MAKE_FILEMON_H */
+#endif
diff --git a/contrib/bmake/filemon/filemon_dev.c b/contrib/bmake/filemon/filemon_dev.c
index 728d84c1f492..741edc7c25ea 100644
--- a/contrib/bmake/filemon/filemon_dev.c
+++ b/contrib/bmake/filemon/filemon_dev.c
@@ -1,4 +1,4 @@
-/* $NetBSD: filemon_dev.c,v 1.8 2021/02/01 21:09:25 rillig Exp $ */
+/* $NetBSD: filemon_dev.c,v 1.9 2022/03/04 23:17:16 sjg Exp $ */
/*
* Copyright (c) 2020 The NetBSD Foundation, Inc.
@@ -46,6 +46,10 @@
#define _PATH_FILEMON "/dev/filemon"
#endif
+#ifndef MAKE_ATTR_UNUSED
+#define MAKE_ATTR_UNUSED __attribute__((__unused__))
+#endif
+
struct filemon {
int fd;
};
@@ -101,7 +105,7 @@ filemon_setfd(struct filemon *F, int fd)
}
void
-filemon_setpid_parent(struct filemon *F, pid_t pid)
+filemon_setpid_parent(struct filemon *F MAKE_ATTR_UNUSED, pid_t pid MAKE_ATTR_UNUSED)
{
/* Nothing to do! */
}
@@ -137,14 +141,14 @@ filemon_close(struct filemon *F)
}
int
-filemon_readfd(const struct filemon *F)
+filemon_readfd(const struct filemon *F MAKE_ATTR_UNUSED)
{
return -1;
}
int
-filemon_process(struct filemon *F)
+filemon_process(struct filemon *F MAKE_ATTR_UNUSED)
{
return 0;
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/find_lib.sh b/contrib/bmake/find_lib.sh
index 3c2e4af2f251..f6419bb85da2 100755
--- a/contrib/bmake/find_lib.sh
+++ b/contrib/bmake/find_lib.sh
@@ -1,6 +1,13 @@
:
re=$1; shift
+# some Linux systems have deprecated egrep in favor of grep -E
+# but not everyone supports that
+case "`echo bmake | egrep 'a|b' 2>&1`" in
+bmake) ;;
+*) egrep() { grep -E "$@"; }
+esac
+
for lib in $*
do
found=`nm $lib | egrep "$re"`
diff --git a/contrib/bmake/for.c b/contrib/bmake/for.c
index 615efb7634c9..91d9b8e13ce6 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.179 2024/04/01 12:33:27 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 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,74 +58,94 @@
#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.179 2024/04/01 12:33:27 rillig Exp $");
-/* One of the variables to the left of the "in" in a .for loop. */
-typedef struct ForVar {
- char *name;
- size_t nameLen;
-} ForVar;
-
typedef struct ForLoop {
+ Vector /* of 'char *' */ vars; /* Iteration variables */
+ SubstringWords items; /* Substitution items */
Buffer body; /* Unexpanded body of the loop */
- Vector /* of ForVar */ vars; /* Iteration variables */
- Words 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 int forLevel = 0; /* Nesting level */
+static ForLoop *accumFor; /* Loop being accumulated */
+/* See LK_FOR_BODY. */
+static void
+skip_whitespace_or_line_continuation(const char **pp)
+{
+ const char *p = *pp;
+ for (;;) {
+ if (ch_isspace(*p))
+ p++;
+ else if (p[0] == '\\' && p[1] == '\n')
+ p += 2;
+ else
+ break;
+ }
+ *pp = p;
+}
+
static ForLoop *
ForLoop_New(void)
{
ForLoop *f = bmake_malloc(sizeof *f);
+ Vector_Init(&f->vars, sizeof(char *));
+ SubstringWords_Init(&f->items);
Buf_Init(&f->body);
- Vector_Init(&f->vars, sizeof(ForVar));
- f->items.words = NULL;
- f->items.freeIt = NULL;
- Buf_Init(&f->curBody);
- f->short_var = false;
- f->sub_next = 0;
+ f->nextItem = 0;
return f;
}
-static void
+void
ForLoop_Free(ForLoop *f)
{
- Buf_Done(&f->body);
-
- while (f->vars.len > 0) {
- ForVar *var = Vector_Pop(&f->vars);
- free(var->name);
- }
+ while (f->vars.len > 0)
+ free(*(char **)Vector_Pop(&f->vars));
Vector_Done(&f->vars);
- Words_Free(f->items);
- Buf_Done(&f->curBody);
+ SubstringWords_Free(f->items);
+ Buf_Done(&f->body);
free(f);
}
-static void
-ForLoop_AddVar(ForLoop *f, const char *name, size_t len)
+char *
+ForLoop_Details(const ForLoop *f)
{
- ForVar *var = Vector_Push(&f->vars);
- var->name = bmake_strldup(name, len);
- var->nameLen = len;
+ size_t i, n;
+ const char **vars;
+ const Substring *items;
+ Buffer buf;
+
+ n = f->vars.len;
+ vars = f->vars.items;
+ assert(f->nextItem >= n);
+ items = f->items.words + f->nextItem - n;
+
+ Buf_Init(&buf);
+ for (i = 0; i < n; i++) {
+ if (i > 0)
+ Buf_AddStr(&buf, ", ");
+ Buf_AddStr(&buf, vars[i]);
+ Buf_AddStr(&buf, " = ");
+ Buf_AddRange(&buf, items[i].start, items[i].end);
+ }
+ return Buf_DoneData(&buf);
}
static bool
+IsValidInVarname(char c)
+{
+ return c != '$' && c != ':' && c != '\\' &&
+ c != '(' && c != '{' && c != ')' && c != '}';
+}
+
+static void
ForLoop_ParseVarnames(ForLoop *f, const char **pp)
{
const char *p = *pp;
@@ -136,34 +156,36 @@ ForLoop_ParseVarnames(ForLoop *f, const char **pp)
cpp_skip_whitespace(&p);
if (*p == '\0') {
Parse_Error(PARSE_FATAL, "missing `in' in for");
- return false;
+ f->vars.len = 0;
+ return;
}
- /*
- * XXX: This allows arbitrary variable names;
- * see directive-for.mk.
- */
- for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++)
- continue;
+ for (len = 0; p[len] != '\0' && !ch_isspace(p[len]); len++) {
+ if (!IsValidInVarname(p[len])) {
+ Parse_Error(PARSE_FATAL,
+ "invalid character '%c' "
+ "in .for loop variable name",
+ p[len]);
+ f->vars.len = 0;
+ return;
+ }
+ }
if (len == 2 && p[0] == 'i' && p[1] == 'n') {
p += 2;
break;
}
- if (len == 1)
- f->short_var = true;
- ForLoop_AddVar(f, p, len);
+ *(char **)Vector_Push(&f->vars) = bmake_strldup(p, len);
p += len;
}
if (f->vars.len == 0) {
Parse_Error(PARSE_FATAL, "no iteration variables in for");
- return false;
+ return;
}
*pp = p;
- return true;
}
static bool
@@ -173,18 +195,16 @@ ForLoop_ParseItems(ForLoop *f, const char *p)
cpp_skip_whitespace(&p);
- if (Var_Subst(p, SCOPE_GLOBAL, VARE_WANTRES, &items) != VPR_OK) {
- Parse_Error(PARSE_FATAL, "Error in .for loop items");
- return false;
- }
+ items = Var_Subst(p, SCOPE_GLOBAL, VARE_WANTRES);
+ /* TODO: handle errors */
- 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')
- f->items.len = 0; /* .for var in ${:U} */
+ 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) {
+ if (f->items.len % f->vars.len != 0) {
Parse_Error(PARSE_FATAL,
"Wrong number of words (%u) in .for "
"substitution list with %u variables",
@@ -212,47 +232,35 @@ IsEndfor(const char *p)
* Evaluate the for loop in the passed line. The line looks like this:
* .for <varname...> in <value...>
*
- * Input:
- * line Line to parse
- *
* Results:
- * 0: Not a .for statement, parse the line
- * 1: We found a for loop
- * -1: A .for statement with a bad syntax error, discard.
+ * 0 not a .for directive
+ * 1 found a .for directive
+ * -1 erroneous .for directive
*/
int
For_Eval(const char *line)
{
- ForLoop *f;
const char *p;
+ ForLoop *f;
p = line + 1; /* skip the '.' */
- cpp_skip_whitespace(&p);
+ skip_whitespace_or_line_continuation(&p);
- if (!IsFor(p)) {
- if (IsEndfor(p)) {
- Parse_Error(PARSE_FATAL, "for-less endfor");
- return -1;
- }
- return 0;
- }
- p += 3;
+ if (IsFor(p)) {
+ p += 3;
- f = ForLoop_New();
+ f = ForLoop_New();
+ ForLoop_ParseVarnames(f, &p);
+ if (f->vars.len > 0 && !ForLoop_ParseItems(f, p))
+ f->items.len = 0; /* don't iterate */
- if (!ForLoop_ParseVarnames(f, &p)) {
- ForLoop_Free(f);
+ accumFor = f;
+ return 1;
+ } else if (IsEndfor(p)) {
+ Parse_Error(PARSE_FATAL, "for-less endfor");
return -1;
- }
-
- if (!ForLoop_ParseItems(f, p)) {
- /* Continue parsing the .for loop, but don't iterate. */
- f->items.len = 0;
- }
-
- accumFor = f;
- forLevel = 1;
- return 1;
+ } else
+ return 0;
}
/*
@@ -260,21 +268,21 @@ For_Eval(const char *line)
* Returns false when the matching .endfor is reached.
*/
bool
-For_Accum(const char *line)
+For_Accum(const char *line, int *forLevel)
{
const char *p = line;
if (*p == '.') {
p++;
- cpp_skip_whitespace(&p);
+ skip_whitespace_or_line_continuation(&p);
if (IsEndfor(p)) {
- DEBUG1(FOR, "For: end for %d\n", forLevel);
- if (--forLevel <= 0)
+ DEBUG1(FOR, "For: end for %d\n", *forLevel);
+ if (--*forLevel == 0)
return false;
} else if (IsFor(p)) {
- forLevel++;
- DEBUG1(FOR, "For: new loop %d\n", forLevel);
+ (*forLevel)++;
+ DEBUG1(FOR, "For: new loop %d\n", *forLevel);
}
}
@@ -283,35 +291,41 @@ For_Accum(const char *line)
return true;
}
-
+/*
+ * When the body of a '.for i' loop is prepared for an iteration, each
+ * occurrence of $i in the body is replaced with ${:U...}, inserting the
+ * value of the item. If this item contains a '$', it may be the start of an
+ * expression. This expression is copied verbatim, its length is
+ * determined here, in a rather naive way, ignoring escape characters and
+ * funny delimiters in modifiers like ':S}from}to}'.
+ */
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,47 +334,56 @@ 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;
}
/*
- * While expanding the body of a .for loop, write the item in the ${:U...}
- * expression, escaping characters as needed.
- *
- * The result is later unescaped by ApplyModifier_Defined.
+ * While expanding the body of a .for loop, write the item as a ${:U...}
+ * expression, escaping characters as needed. The result is later unescaped
+ * by ApplyModifier_Defined.
*/
static void
-Buf_AddEscaped(Buffer *cmds, const char *item, char endc)
+AddEscaped(Buffer *cmds, Substring item, char endc)
{
+ const char *p;
char ch;
if (!NeedsEscapes(item, endc)) {
- Buf_AddStr(cmds, item);
+ Buf_AddRange(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;) {
+ 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 += 1 + 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);
+ p++;
}
}
@@ -369,36 +392,30 @@ Buf_AddEscaped(Buffer *cmds, const char *item, char endc)
* expression like ${i} or ${i:...} or $(i) or $(i:...) with ":Uvalue".
*/
static void
-ForLoop_SubstVarLong(ForLoop *f, const char **pp, const char *bodyEnd,
- char endc, const char **inout_mark)
+ForLoop_SubstVarLong(ForLoop *f, unsigned int firstItem, Buffer *body,
+ const char **pp, char endc, const char **inout_mark)
{
size_t i;
- const char *p = *pp;
+ const char *start = *pp;
+ const char **varnames = Vector_Get(&f->vars, 0);
for (i = 0; i < f->vars.len; i++) {
- ForVar *forVar = Vector_Get(&f->vars, i);
- char *varname = forVar->name;
- size_t varnameLen = forVar->nameLen;
+ const char *p = start;
- if (varnameLen >= (size_t)(bodyEnd - p))
- continue;
- if (memcmp(p, varname, varnameLen) != 0)
+ if (!cpp_skip_string(&p, varnames[i]))
continue;
/* XXX: why test for backslash here? */
- if (p[varnameLen] != ':' && p[varnameLen] != endc &&
- p[varnameLen] != '\\')
+ if (*p != ':' && *p != endc && *p != '\\')
continue;
/*
* Found a variable match. Skip over the variable name and
* instead add ':U<value>' to the current body.
*/
- Buf_AddBytesBetween(&f->curBody, *inout_mark, p);
- Buf_AddStr(&f->curBody, ":U");
- Buf_AddEscaped(&f->curBody,
- f->items.words[f->sub_next + i], endc);
+ Buf_AddRange(body, *inout_mark, start);
+ Buf_AddStr(body, ":U");
+ AddEscaped(body, f->items.words[firstItem + i], endc);
- p += varnameLen;
*inout_mark = p;
*pp = p;
return;
@@ -407,111 +424,117 @@ ForLoop_SubstVarLong(ForLoop *f, const char **pp, const char *bodyEnd,
/*
* While expanding the body of a .for loop, replace single-character
- * variable expressions like $i with their ${:U...} expansion.
+ * expressions like $i with their ${:U...} expansion.
*/
static void
-ForLoop_SubstVarShort(ForLoop *f, const char *p, const char **inout_mark)
+ForLoop_SubstVarShort(ForLoop *f, unsigned int firstItem, Buffer *body,
+ const char *p, const char **inout_mark)
{
- const char ch = *p;
- ForVar *vars;
+ char ch = *p;
+ const char **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);
for (i = 0; i < f->vars.len; i++) {
- const char *varname = vars[i].name;
+ const char *varname = vars[i];
if (varname[0] == ch && varname[1] == '\0')
goto found;
}
return;
found:
+ Buf_AddRange(body, *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_AddByte(&f->curBody, '}');
+ Buf_AddStr(body, "{:U");
+ AddEscaped(body, f->items.words[firstItem + i], '}');
+ Buf_AddByte(body, '}');
}
/*
* Compute the body for the current iteration by copying the unexpanded body,
* replacing the expressions for the iteration variables on the way.
*
- * Using variable expressions ensures that the .for loop can't generate
- * syntax, and that the later parsing will still see a variable.
- * This code assumes that the variable with the empty name will never be
- * defined, see unit-tests/varname-empty.mk for more details.
+ * Using expressions ensures that the .for loop can't generate
+ * syntax, and that the later parsing will still see an expression.
+ * This code assumes that the variable with the empty name is never defined,
+ * see unit-tests/varname-empty.mk.
*
* 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.
+ * See unit-tests/directive-for-escape.mk.
*/
static void
-ForLoop_SubstBody(ForLoop *f)
+ForLoop_SubstBody(ForLoop *f, unsigned int firstItem, Buffer *body)
{
- const char *p, *bodyEnd;
- const char *mark; /* where the last replacement left off */
+ const char *p, *end;
+ const char *mark; /* where the last substitution left off */
- Buf_Empty(&f->curBody);
+ Buf_Clear(body);
mark = f->body.data;
- bodyEnd = f->body.data + f->body.len;
+ end = 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);
- } else if (p[1] != '\0') {
- ForLoop_SubstVarShort(f, p + 1, &mark);
+ ForLoop_SubstVarLong(f, firstItem, body,
+ &p, endc, &mark);
+ } else {
+ ForLoop_SubstVarShort(f, firstItem, body,
+ p + 1, &mark);
p += 2;
- } else
- break;
+ }
}
- Buf_AddBytesBetween(&f->curBody, mark, bodyEnd);
+ Buf_AddRange(body, mark, end);
}
/*
* Compute the body for the current iteration by copying the unexpanded body,
* replacing the expressions for the iteration variables on the way.
*/
-static char *
-ForReadMore(void *v_arg, size_t *out_len)
+bool
+For_NextIteration(ForLoop *f, Buffer *body)
{
- ForLoop *f = v_arg;
+ if (f->nextItem == f->items.len)
+ return false;
- if (f->sub_next == f->items.len) {
- /* No more iterations */
- ForLoop_Free(f);
- return NULL;
+ f->nextItem += (unsigned int)f->vars.len;
+ ForLoop_SubstBody(f, f->nextItem - (unsigned int)f->vars.len, body);
+ if (DEBUG(FOR)) {
+ char *details = ForLoop_Details(f);
+ debug_printf("For: loop body with %s:\n%s",
+ details, body->data);
+ free(details);
}
+ return true;
+}
- ForLoop_SubstBody(f);
- DEBUG1(FOR, "For: loop body:\n%s", f->curBody.data);
- f->sub_next += (unsigned int)f->vars.len;
-
- *out_len = f->curBody.len;
- return f->curBody.data;
+/* Break out of the .for loop. */
+void
+For_Break(ForLoop *f)
+{
+ f->nextItem = (unsigned int)f->items.len;
}
/* Run the .for loop, imitating the actions of an include file. */
void
-For_Run(int lineno)
+For_Run(unsigned headLineno, unsigned bodyReadLines)
{
+ Buffer buf;
ForLoop *f = accumFor;
accumFor = NULL;
- if (f->items.len == 0) {
- /*
- * Nothing to expand - possibly due to an earlier syntax
- * error.
- */
+ if (f->items.len > 0) {
+ Buf_Init(&buf);
+ Parse_PushInput(NULL, headLineno, bodyReadLines, buf, f);
+ } else
ForLoop_Free(f);
- return;
- }
-
- Parse_SetInput(NULL, lineno, -1, ForReadMore, f);
}
diff --git a/contrib/bmake/hash.c b/contrib/bmake/hash.c
index 8b503ac31fb5..88d41c2c0f6f 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.74 2023/12/19 19:33:39 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -69,12 +69,12 @@
* SUCH DAMAGE.
*/
-/* Hash tables with string keys. */
+/* Hash tables with string keys and pointer values. */
#include "make.h"
/* "@(#)hash.c 8.1 (Berkeley) 6/6/93" */
-MAKE_RCSID("$NetBSD: hash.c,v 1.64 2021/04/11 12:46:54 rillig Exp $");
+MAKE_RCSID("$NetBSD: hash.c,v 1.74 2023/12/19 19:33:39 rillig Exp $");
/*
* The ratio of # entries to # buckets at which we rebuild the table to
@@ -84,7 +84,7 @@ MAKE_RCSID("$NetBSD: hash.c,v 1.64 2021/04/11 12:46:54 rillig Exp $");
/* This hash function matches Gosling's Emacs and java.lang.String. */
static unsigned int
-Hash_String(const char *key, size_t *out_keylen)
+Hash_String(const char *key, const char **out_keyEnd)
{
unsigned int h;
const char *p;
@@ -93,8 +93,7 @@ Hash_String(const char *key, size_t *out_keylen)
for (p = key; *p != '\0'; p++)
h = 31 * h + (unsigned char)*p;
- if (out_keylen != NULL)
- *out_keylen = (size_t)(p - key);
+ *out_keyEnd = p;
return h;
}
@@ -112,60 +111,29 @@ Hash_Substring(Substring key)
}
static HashEntry *
-HashTable_Find(HashTable *t, unsigned int h, const char *key)
+HashTable_Find(HashTable *t, Substring key, unsigned int h)
{
- HashEntry *e;
+ HashEntry *he;
unsigned int chainlen = 0;
+ size_t keyLen = Substring_Length(key);
#ifdef DEBUG_HASH_LOOKUP
- DEBUG4(HASH, "%s: %p h=%08x key=%s\n", __func__, t, h, key);
+ DEBUG4(HASH, "HashTable_Find: %p h=%08x key=%.*s\n",
+ t, h, (int)keyLen, key.start);
#endif
- for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
+ for (he = t->buckets[h & t->bucketsMask]; he != NULL; he = he->next) {
chainlen++;
- if (e->key_hash == h && strcmp(e->key, key) == 0)
+ if (he->hash == h &&
+ strncmp(he->key, key.start, keyLen) == 0 &&
+ he->key[keyLen] == '\0')
break;
}
if (chainlen > t->maxchain)
t->maxchain = chainlen;
- return e;
-}
-
-static bool
-HashEntry_KeyEquals(const HashEntry *he, Substring key)
-{
- const char *heKey, *p;
-
- heKey = he->key;
- for (p = key.start; p != key.end; p++, heKey++)
- if (*p != *heKey || *heKey == '\0')
- return false;
- return *heKey == '\0';
-}
-
-static HashEntry *
-HashTable_FindEntryBySubstring(HashTable *t, Substring key, unsigned int h)
-{
- HashEntry *e;
- unsigned int chainlen = 0;
-
-#ifdef DEBUG_HASH_LOOKUP
- DEBUG4(HASH, "%s: %p h=%08x key=%.*s\n", __func__, t, h,
- (int)Substring_Length(key), key.start);
-#endif
-
- for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
- chainlen++;
- if (e->key_hash == h && HashEntry_KeyEquals(e, key))
- break;
- }
-
- if (chainlen > t->maxchain)
- t->maxchain = chainlen;
-
- return e;
+ return he;
}
/* Set up the hash table. */
@@ -213,8 +181,9 @@ HashTable_Done(HashTable *t)
HashEntry *
HashTable_FindEntry(HashTable *t, const char *key)
{
- unsigned int h = Hash_String(key, NULL);
- return HashTable_Find(t, h, key);
+ const char *keyEnd;
+ unsigned int h = Hash_String(key, &keyEnd);
+ return HashTable_Find(t, Substring_Init(key, keyEnd), h);
}
/* Find the value corresponding to the key, or return NULL. */
@@ -232,13 +201,13 @@ HashTable_FindValue(HashTable *t, const char *key)
void *
HashTable_FindValueBySubstringHash(HashTable *t, Substring key, unsigned int h)
{
- HashEntry *he = HashTable_FindEntryBySubstring(t, key, h);
+ HashEntry *he = HashTable_Find(t, key, h);
return he != NULL ? he->value : NULL;
}
/*
* Make the hash table larger. Any bucket numbers from the old table become
- * invalid; the hash codes stay valid though.
+ * invalid; the hash values stay valid though.
*/
static void
HashTable_Enlarge(HashTable *t)
@@ -257,8 +226,8 @@ HashTable_Enlarge(HashTable *t)
HashEntry *he = oldBuckets[i];
while (he != NULL) {
HashEntry *next = he->next;
- he->next = newBuckets[he->key_hash & newMask];
- newBuckets[he->key_hash & newMask] = he;
+ he->next = newBuckets[he->hash & newMask];
+ newBuckets[he->hash & newMask] = he;
he = next;
}
}
@@ -268,8 +237,8 @@ HashTable_Enlarge(HashTable *t)
t->bucketsSize = newSize;
t->bucketsMask = newMask;
t->buckets = newBuckets;
- DEBUG5(HASH, "%s: %p size=%d entries=%d maxchain=%d\n",
- __func__, (void *)t, t->bucketsSize, t->numEntries, t->maxchain);
+ DEBUG4(HASH, "HashTable_Enlarge: %p size=%d entries=%d maxchain=%d\n",
+ (void *)t, t->bucketsSize, t->numEntries, t->maxchain);
t->maxchain = 0;
}
@@ -280,9 +249,9 @@ HashTable_Enlarge(HashTable *t)
HashEntry *
HashTable_CreateEntry(HashTable *t, const char *key, bool *out_isNew)
{
- size_t keylen;
- unsigned int h = Hash_String(key, &keylen);
- HashEntry *he = HashTable_Find(t, h, key);
+ const char *keyEnd;
+ unsigned int h = Hash_String(key, &keyEnd);
+ HashEntry *he = HashTable_Find(t, Substring_Init(key, keyEnd), h);
if (he != NULL) {
if (out_isNew != NULL)
@@ -293,10 +262,10 @@ HashTable_CreateEntry(HashTable *t, const char *key, bool *out_isNew)
if (t->numEntries >= rebuildLimit * t->bucketsSize)
HashTable_Enlarge(t);
- he = bmake_malloc(sizeof *he + keylen);
+ he = bmake_malloc(sizeof *he + (size_t)(keyEnd - key));
he->value = NULL;
- he->key_hash = h;
- memcpy(he->key, key, keylen + 1);
+ he->hash = h;
+ memcpy(he->key, key, (size_t)(keyEnd - key) + 1);
he->next = t->buckets[h & t->bucketsMask];
t->buckets[h & t->bucketsMask] = he;
@@ -307,19 +276,18 @@ HashTable_CreateEntry(HashTable *t, const char *key, bool *out_isNew)
return he;
}
-HashEntry *
+void
HashTable_Set(HashTable *t, const char *key, void *value)
{
HashEntry *he = HashTable_CreateEntry(t, key, NULL);
HashEntry_Set(he, value);
- return he;
}
-/* Delete the entry from the table and free the associated memory. */
+/* Delete the entry from the table, don't free the value of the entry. */
void
HashTable_DeleteEntry(HashTable *t, HashEntry *he)
{
- HashEntry **ref = &t->buckets[he->key_hash & t->bucketsMask];
+ HashEntry **ref = &t->buckets[he->hash & t->bucketsMask];
HashEntry *p;
for (; (p = *ref) != NULL; ref = &p->next) {
@@ -333,15 +301,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.
@@ -370,5 +329,5 @@ void
HashTable_DebugStats(HashTable *t, const char *name)
{
DEBUG4(HASH, "HashTable %s: size=%u numEntries=%u maxchain=%u\n",
- name, t->bucketsSize, t->numEntries, t->maxchain);
+ name, t->bucketsSize, t->numEntries, t->maxchain);
}
diff --git a/contrib/bmake/hash.h b/contrib/bmake/hash.h
index 8e7a567b6dba..2bee685b7ebb 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.48 2023/12/19 19:33:39 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -72,7 +72,7 @@
* from: @(#)hash.h 8.1 (Berkeley) 6/6/93
*/
-/* Hash tables with strings as keys and arbitrary pointers as values. */
+/* Hash tables with string keys and pointer values. */
#ifndef MAKE_HASH_H
#define MAKE_HASH_H
@@ -82,18 +82,17 @@ typedef struct HashEntry {
struct HashEntry *next; /* Used to link together all the entries
* associated with the same bucket. */
void *value;
- unsigned int key_hash; /* hash value of the key */
+ unsigned int hash; /* hash value of the key */
char key[1]; /* key string, variable length */
} HashEntry;
/* The hash table containing the entries. */
typedef struct HashTable {
- HashEntry **buckets; /* Pointers to HashEntry, one
- * for each bucket in the table. */
+ HashEntry **buckets;
unsigned int bucketsSize;
- unsigned int numEntries; /* Number of entries in the table. */
+ unsigned int numEntries;
unsigned int bucketsMask; /* Used to select the bucket for a hash. */
- unsigned int maxchain; /* max length of chain detected */
+ unsigned int maxchain; /* Maximum length of chain seen. */
} HashTable;
/* State of an iteration over all entries in a table. */
@@ -108,30 +107,39 @@ typedef struct HashSet {
HashTable tbl;
} HashSet;
-MAKE_INLINE void *
-HashEntry_Get(HashEntry *h)
+MAKE_INLINE void * MAKE_ATTR_USE
+HashEntry_Get(HashEntry *he)
{
- return h->value;
+ return he->value;
}
MAKE_INLINE void
-HashEntry_Set(HashEntry *h, void *datum)
+HashEntry_Set(HashEntry *he, void *datum)
{
- h->value = datum;
+ he->value = datum;
+}
+
+/* Set things up for iterating over all entries in the hash table. */
+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 *);
-void *HashTable_FindValue(HashTable *, const char *);
-unsigned int Hash_Substring(Substring);
-void *HashTable_FindValueBySubstringHash(HashTable *, Substring, unsigned int);
+HashEntry *HashTable_FindEntry(HashTable *, const char *) MAKE_ATTR_USE;
+void *HashTable_FindValue(HashTable *, const char *) MAKE_ATTR_USE;
+unsigned int Hash_Substring(Substring) MAKE_ATTR_USE;
+void *HashTable_FindValueBySubstringHash(HashTable *, Substring, unsigned int)
+ MAKE_ATTR_USE;
HashEntry *HashTable_CreateEntry(HashTable *, const char *, bool *);
-HashEntry *HashTable_Set(HashTable *, const char *, void *);
+void HashTable_Set(HashTable *, const char *, void *);
void HashTable_DeleteEntry(HashTable *, HashEntry *);
void HashTable_DebugStats(HashTable *, const char *);
-void HashIter_Init(HashIter *, HashTable *);
HashEntry *HashIter_Next(HashIter *);
MAKE_INLINE void
@@ -155,7 +163,7 @@ HashSet_Add(HashSet *set, const char *key)
return isNew;
}
-MAKE_INLINE bool
+MAKE_INLINE bool MAKE_ATTR_USE
HashSet_Contains(HashSet *set, const char *key)
{
return HashTable_FindEntry(&set->tbl, key) != NULL;
@@ -167,4 +175,4 @@ HashIter_InitSet(HashIter *hi, HashSet *set)
HashIter_Init(hi, &set->tbl);
}
-#endif /* MAKE_HASH_H */
+#endif
diff --git a/contrib/bmake/import.sh b/contrib/bmake/import.sh
index b80e120daab1..c409dcfe869e 100755
--- a/contrib/bmake/import.sh
+++ b/contrib/bmake/import.sh
@@ -4,6 +4,7 @@
ECHO=
GIT=${GIT:-git}
+PAGER=${PAGER:-${LESS:-${MORE:-more}}}
# For consistency...
Error() {
@@ -26,6 +27,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 +57,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 +69,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
@@ -78,15 +81,47 @@ grep '^+' $TF.diffs | sed 's,^.,,' | sort > $TF.adds
grep '^-' $TF.diffs | sed 's,^.,,' | sort > $TF.rms
comm -13 $TF.adds $TF.rms > $TF.rm
+post=$SB/tmp/bmake-post.sh
+
+# this is similar to what generates the mail to bmake-announce
+gen_import_F() {
+ echo Import bmake-$VERSION
+ echo
+
+ if [ -s $post ]; then
+ last=`sed -n '/ tag/s,.*/,bmake-,p' $post`
+ else
+ last="last import"
+ fi
+ echo Intersting/relevant changes since $last; echo
+ for C in ChangeLog */ChangeLog
+ do
+ $GIT diff --staged $C |
+ sed -n '/^@@/d;/^\+\+\+/d;/^\+/s,^.,,p' > $TF.C
+ test -s $TF.C || continue
+ echo
+ echo $C since $last
+ echo
+ cat $TF.C
+ done
+}
+
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"
+ gen_import_F > $SB/tmp/bmake-import.F
+ $GIT diff --staged > $SB/tmp/bmake-import.diff
+ $PAGER $SB/tmp/bmake-import.F $SB/tmp/bmake-import.diff
+ { echo "$GIT tag -a -m \"Tag bmake/$VERSION\" vendor/NetBSD/bmake/$VERSION"
+ echo "echo \"When ready do: $GIT push --follow-tags\""
+ } > $post
+ echo "Edit $SB/tmp/bmake-import.F as needed, then:"
+ echo "$GIT commit -F $SB/tmp/bmake-import.F"
+ echo "After you commit, run $post"
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/install-sh b/contrib/bmake/install-sh
index a2473298efcb..aa35fa94c83a 100755
--- a/contrib/bmake/install-sh
+++ b/contrib/bmake/install-sh
@@ -1,4 +1,5 @@
-:
+#!/bin/sh
+
# NAME:
# install.sh - portable version of install(1)
#
@@ -46,16 +47,18 @@
# BUGS:
# The '-i' option is to save your sanity when 'bsd.prog.mk'
# insists on haveing a '-o' "owner" option which is doomed to
-# fail on many systems. We ignore '-b', '-B' and '-c' options.
+# fail on many systems. We ignore '-b' and '-c' options.
#
# AUTHOR:
-# Simon J. Gerraty <sjg@quick.com.au>
+# Simon J. Gerraty <sjg@crufty.net>
#
+# SPDX-License-Identifier: BSD-2-Clause
+#
# RCSid:
-# $Id: install-sh,v 1.18 2001/03/16 17:33:02 sjg Exp $
+# $Id: install-sh,v 1.26 2024/02/17 17:26:57 sjg Exp $
#
-# @(#) Copyright (c) 1993 Simon J. Gerraty
+# @(#) Copyright (c) 1993-2023 Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@@ -65,7 +68,7 @@
# left intact.
#
# Please send copies of changes and bug-fixes to:
-# sjg@quick.com.au
+# sjg@crufty.net
#
set -- `getopt B:bpxCNcsdo:g:m:i:f: $*`
@@ -73,42 +76,46 @@ set -- `getopt B:bpxCNcsdo:g:m:i:f: $*`
Mydir=`dirname $0`
[ -s $Mydir/.installrc ] && . $Mydir/.installrc
+OLD_EXT=.old
owner=:
group=:
mode=:
+MODE=0
strip=:
mkdirs=
compare=:
newer=:
chflags=:
-LS1=
-CP_P=
+LS_1=
+CP_p=
-while [ $# -gt 1 ]
+while :
do
- case $1 in
+ case "$1" in
--) shift; break;;
- -p) CP_P=-p;;
+ -[bc]) ;; # ignore
+ -p) CP_p=-p;;
-x) set -x;;
-B) OLD_EXT=$2; shift;;
-C) compare=Different;;
-N) newer=Newer;
# check if /bin/ls supports -1
- /bin/ls -1 $0 >/dev/null 2>&1 && LS1=1
+ 'ls' -1 $0 > /dev/null 2>&1 && LS_1=1
;;
-o) owner="${CHOWN:-chown} $2 "; shift;;
-g) group="${CHGRP:-chgrp} $2 "; shift;;
- -m) mode="${CHMOD:-chmod} $2 "; shift;;
+ -m) MODE=$2 mode="${CHMOD:-chmod} $2 "; shift;;
-s) strip=${STRIP:-strip};;
-d) mkdirs="mkdir -p";;
-i) ignore_err="$ignore_err$2"; shift;;
-f) chflags="${CHFLAGS:-chflags} $2 "; shift;;
+ *) break;;
esac
shift
done
Newer() {
- n=`/bin/ls -t$LS1 $* 2>/dev/null | head -1`
+ n=`'ls' -t$LS_1 $* 2> /dev/null | head -1`
[ $1 = $n ]
}
@@ -132,7 +139,7 @@ Setem() {
$group $1 || Err g
$owner $1 || Err o
$mode $1 || Err m
- $chflags $1 || Err f
+ $chflags $1 || Err f
return 0
}
@@ -140,19 +147,40 @@ Setem() {
# after any calls to add_path()
args="$*"
-# all this just for chown!
-add_path () { [ -d $1 ] && eval ${2:-PATH}="\$${2:-PATH}:$1"; }
-add_path /etc
-add_path /usr/etc
+add_path () {
+ test -d $1 || return
+ case ":$PATH:" in
+ *:$1:*) return;;
+ esac
+ PATH=$PATH:$1
+}
+
add_path /sbin
add_path /usr/sbin
+case "$owner" in
+:) ;;
+*) # some systems put chown in odd places
+ add_path /etc
+ add_path /usr/etc
+ ;;
+esac
+
# restore saved $*
set -- $args
# make directories if needed
# and ensure mode etc are as desired
if [ "$mkdirs" ]; then
+ case "$MODE" in
+ [1-7]*)
+ # make sure umask is compatible
+ case "$MODE" in
+ ????*) MODE=`echo $MODE | sed 's,.*\(...\)$,\1,'`;;
+ esac
+ umask `expr 0777 - 0$MODE |
+ sed 's,^,000,;s,^.*\(...\)$,\1,'`;;
+ esac
for d in $*
do
[ ! -d $d ] && $mkdirs $d
@@ -162,16 +190,16 @@ if [ "$mkdirs" ]; then
fi
# install files
-if [ $# -gt 2 ]; then
- dest_dir=yes
-elif [ $# -eq 1 ]; then
+if [ $# -eq 1 ]; then
echo "what should I do with $*?" >&2
exit 1
fi
# get list of files
+files=
while [ $# -gt 1 ]
do
+ test "x$files" = x || dest_dir=yes
files="$files $1"
shift
done
@@ -179,7 +207,6 @@ done
dest=$1
shift
-
if [ "$dest_dir" = yes -a ! -d $dest ]; then
echo "no directory $dest" >&2
exit 1
@@ -195,7 +222,7 @@ do
fi
$newer $f $t || continue
$compare $f $t || continue
- [ -f $t ] && { mv -f $t $t.old || exit 1; }
- { cp $CP_P $f $t && Setem $t; } || exit 1
+ [ -f $t ] && { mv -f $t $t$OLD_EXT || exit 1; }
+ { cp $CP_p $f $t && Setem $t; } || exit 1
done
exit 0
diff --git a/contrib/bmake/job.c b/contrib/bmake/job.c
index e4b77477e816..541a61294766 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.471 2024/05/07 18:26:22 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -70,12 +70,11 @@
*/
/*
- * job.c --
- * handle the creation etc. of our child processes.
+ * Create child processes and collect their output.
*
* Interface:
* Job_Init Called to initialize this module. In addition,
- * the .BEGIN target is made including all of its
+ * the .BEGIN target is made, including all of its
* dependencies before this function returns.
* Hence, the makefiles must have been parsed
* before this function is called.
@@ -155,7 +154,7 @@
#include "trace.h"
/* "@(#)job.c 8.2 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: job.c,v 1.435 2021/06/16 09:47:51 rillig Exp $");
+MAKE_RCSID("$NetBSD: job.c,v 1.471 2024/05/07 18:26:22 sjg Exp $");
/*
* A shell defines how the commands are run. All commands for a target are
@@ -214,13 +213,15 @@ typedef struct Shell {
const char *errOff; /* command to turn off error checking */
const char *echoTmpl; /* template to echo a command */
- const char *runIgnTmpl; /* template to run a command
- * without error checking */
- const char *runChkTmpl; /* template to run a command
- * with error checking */
+ const char *runIgnTmpl; /* template to run a command without error
+ * checking */
+ const char *runChkTmpl; /* template to run a command with error
+ * checking */
- /* string literal that results in a newline character when it appears
- * outside of any 'quote' or "quote" characters */
+ /*
+ * A string literal that results in a newline character when it
+ * occurs outside of any 'quote' or "quote" characters.
+ */
const char *newline;
char commentChar; /* character used by shell for comment lines */
@@ -272,9 +273,7 @@ static bool Always_pass_job_queue = true;
#define MAKE_JOB_ERROR_TOKEN "${MAKE_JOB_ERROR_TOKEN:U}"
static bool Job_error_token = true;
-/*
- * error handling variables
- */
+/* error handling variables */
static int job_errors = 0; /* number of errors reported */
static enum { /* Why is the make aborting? */
ABORT_NONE,
@@ -284,9 +283,7 @@ static enum { /* Why is the make aborting? */
} aborting = ABORT_NONE;
#define JOB_TOKENS "+EI+" /* Token to requeue for each abort state */
-/*
- * this tracks the number of tokens currently "out" to build jobs.
- */
+/* Tracks the number of tokens currently "out" to build jobs. */
int jobTokensRunning = 0;
typedef enum JobStartResult {
@@ -315,15 +312,15 @@ typedef enum JobStartResult {
#define DEFSHELL_INDEX_SH 1
#define DEFSHELL_INDEX_KSH 2
#define DEFSHELL_INDEX_CSH 3
-#else /* !DEFSHELL_CUSTOM */
+#else
#define DEFSHELL_INDEX_SH 0
#define DEFSHELL_INDEX_KSH 1
#define DEFSHELL_INDEX_CSH 2
-#endif /* !DEFSHELL_CUSTOM */
+#endif
#ifndef DEFSHELL_INDEX
#define DEFSHELL_INDEX 0 /* DEFSHELL_INDEX_CUSTOM or DEFSHELL_INDEX_SH */
-#endif /* !DEFSHELL_INDEX */
+#endif
static Shell shells[] = {
#ifdef DEFSHELL_CUSTOM
@@ -439,7 +436,7 @@ static char *shell_freeIt = NULL; /* Allocated memory for custom .SHELL */
static Job *job_table; /* The structures that describe them */
static Job *job_table_end; /* job_table + maxJobs */
-static unsigned int wantToken; /* we want a token */
+static unsigned int wantToken;
static bool lurking_children = false;
static bool make_suspended = false; /* Whether we've seen a SIGTSTP (etc) */
@@ -454,7 +451,7 @@ static void watchfd(Job *);
static void clearfd(Job *);
static bool readyfd(Job *);
-static char *targPrefix = NULL; /* To identify a job change in the output. */
+static char *targPrefix = NULL; /* To identify a job change in the output. */
static Job tokenWaitJob; /* token wait pseudo-job */
static Job childExitJob; /* child exit pseudo-job */
@@ -533,13 +530,13 @@ JobDeleteTarget(GNode *gn)
return;
if (gn->type & OP_PHONY)
return;
- if (Targ_Precious(gn))
+ if (GNode_IsPrecious(gn))
return;
if (opts.noExecute)
return;
file = GNode_Path(gn);
- if (eunlink(file) != -1)
+ if (unlink_file(file) == 0)
Error("*** %s removed", file);
}
@@ -574,7 +571,7 @@ JobCreatePipe(Job *job, int minfd)
Punt("Cannot create pipe: %s", strerror(errno));
for (i = 0; i < 2; i++) {
- /* Avoid using low numbered fds */
+ /* Avoid using low-numbered fds */
fd = fcntl(pipe_fds[i], F_DUPFD, minfd);
if (fd != -1) {
close(pipe_fds[i]);
@@ -585,7 +582,6 @@ JobCreatePipe(Job *job, int minfd)
job->inPipe = pipe_fds[0];
job->outPipe = pipe_fds[1];
- /* Set close-on-exec flag for both */
if (fcntl(job->inPipe, F_SETFD, FD_CLOEXEC) == -1)
Punt("Cannot set close-on-exec: %s", strerror(errno));
if (fcntl(job->outPipe, F_SETFD, FD_CLOEXEC) == -1)
@@ -708,10 +704,10 @@ JobPassSig_suspend(int signo)
/*
* We've been continued.
*
- * A whole host of signals continue to happen!
+ * A whole host of signals is going to happen!
* SIGCHLD for any processes that actually suspended themselves.
- * SIGCHLD for any processes that exited while we were alseep.
- * The SIGCONT that actually caused us to wakeup.
+ * SIGCHLD for any processes that exited while we were asleep.
+ * The SIGCONT that actually caused us to wake up.
*
* Since we defer passing the SIGCONT on to our children until
* the main processing loop, we can be sure that all the SIGCHLD
@@ -759,7 +755,8 @@ ParseCommandFlags(char **pp, CommandFlags *out_cmdFlags)
out_cmdFlags->ignerr = true;
else if (*p == '+')
out_cmdFlags->always = true;
- else
+ else if (!ch_isspace(*p))
+ /* Ignore whitespace for compatibility with GNU make */
break;
p++;
}
@@ -794,8 +791,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
@@ -862,7 +859,7 @@ static void
JobWriteSpecialsEchoCtl(Job *job, ShellWriter *wr, CommandFlags *inout_cmdFlags,
const char *escCmd, const char **inout_cmdTemplate)
{
- /* XXX: Why is the job modified at this point? */
+ /* XXX: Why is the whole job modified at this point? */
job->ignerr = true;
if (job->echo && inout_cmdFlags->echo) {
@@ -874,9 +871,6 @@ JobWriteSpecialsEchoCtl(Job *job, ShellWriter *wr, CommandFlags *inout_cmdFlags,
* for toggling the error checking.
*/
inout_cmdFlags->echo = false;
- } else {
- if (inout_cmdFlags->echo)
- ShellWriter_EchoCmd(wr, escCmd);
}
*inout_cmdTemplate = shell->runIgnTmpl;
@@ -892,13 +886,9 @@ static void
JobWriteSpecials(Job *job, ShellWriter *wr, const char *escCmd, bool run,
CommandFlags *inout_cmdFlags, const char **inout_cmdTemplate)
{
- if (!run) {
- /*
- * If there is no command to run, there is no need to switch
- * error checking off and on again for nothing.
- */
+ if (!run)
inout_cmdFlags->ignerr = false;
- } else if (shell->hasErrCtl)
+ else if (shell->hasErrCtl)
ShellWriter_ErrOff(wr, job->echo && inout_cmdFlags->echo);
else if (shell->runIgnTmpl != NULL && shell->runIgnTmpl[0] != '\0') {
JobWriteSpecialsEchoCtl(job, wr, inout_cmdFlags, escCmd,
@@ -940,7 +930,9 @@ JobWriteCommand(Job *job, ShellWriter *wr, StringListNode *ln, const char *ucmd)
run = GNode_ShouldExecute(job->node);
- Var_Subst(ucmd, job->node, VARE_WANTRES, &xcmd);
+ EvalStack_Push(job->node->name, NULL, NULL);
+ xcmd = Var_Subst(ucmd, job->node, VARE_WANTRES);
+ EvalStack_Pop();
/* TODO: handle errors */
xcmdStart = xcmd;
@@ -954,7 +946,7 @@ JobWriteCommand(Job *job, ShellWriter *wr, StringListNode *ln, const char *ucmd)
* We're not actually executing anything...
* but this one needs to be - use compat mode just for it.
*/
- Compat_RunCommand(ucmd, job->node, ln);
+ (void)Compat_RunCommand(ucmd, job->node, ln);
free(xcmdStart);
return;
}
@@ -967,12 +959,10 @@ JobWriteCommand(Job *job, ShellWriter *wr, StringListNode *ln, const char *ucmd)
escCmd = shell->hasErrCtl ? NULL : EscapeShellDblQuot(xcmd);
if (!cmdFlags.echo) {
- if (job->echo && run && shell->hasEchoCtl) {
+ if (job->echo && run && shell->hasEchoCtl)
ShellWriter_EchoOff(wr);
- } else {
- if (shell->hasErrCtl)
- cmdFlags.echo = true;
- }
+ else if (shell->hasErrCtl)
+ cmdFlags.echo = true;
}
if (cmdFlags.ignerr) {
@@ -1069,7 +1059,9 @@ JobSaveCommands(Job *job)
* variables such as .TARGET, .IMPSRC. It is not intended to
* expand the other variables as well; see deptgt-end.mk.
*/
- (void)Var_Subst(cmd, job->node, VARE_WANTRES, &expanded_cmd);
+ EvalStack_Push(job->node->name, NULL, NULL);
+ expanded_cmd = Var_Subst(cmd, job->node, VARE_WANTRES);
+ EvalStack_Pop();
/* TODO: handle errors */
Lst_Append(&Targ_GetEndNode()->commands, expanded_cmd);
}
@@ -1097,10 +1089,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("*** In directory: %s\n", curdir);
+ 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 = Var_Subst(cmd, job->node, VARE_WANTRES);
+ debug_printf("\t=> %s\n", xcmd);
+ free(xcmd);
+ }
+ }
}
static void
@@ -1125,7 +1127,7 @@ JobFinishDoneExitedError(Job *job, WAIT_T *inout_status)
else {
if (deleteOnError)
JobDeleteTarget(job->node);
- PrintOnError(job->node, NULL);
+ PrintOnError(job->node, "\n");
}
}
@@ -1192,7 +1194,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;
@@ -1283,9 +1287,11 @@ TouchRegular(GNode *gn)
return; /* XXX: What about propagating the error? */
}
- /* Last resort: update the file's time stamps in the traditional way.
+ /*
+ * Last resort: update the file's time stamps in the traditional way.
* XXX: This doesn't work for empty files, which are sometimes used
- * as marker files. */
+ * as marker files.
+ */
if (read(fd, &c, 1) == 1) {
(void)lseek(fd, 0, SEEK_SET);
while (write(fd, &c, 1) == -1 && errno == EAGAIN)
@@ -1384,10 +1390,10 @@ 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",
+ "%s: %s, %u: ignoring stale %s for %s\n",
progname, gn->fname, gn->lineno, makeDependfile,
gn->name);
return true;
@@ -1407,7 +1413,7 @@ Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
return false;
}
- abortProc("%s: don't know how to make %s. Stop", progname, gn->name);
+ abortProc("don't know how to make %s. Stop", gn->name);
return false;
}
@@ -1434,7 +1440,7 @@ JobExec(Job *job, char **argv)
}
/*
- * Some jobs produce no output and it's disconcerting to have
+ * Some jobs produce no output, and it's disconcerting to have
* no feedback of their running (since they produce no output, the
* banner with their name in it never appears). This is an attempt to
* provide that feedback, even if nothing follows it.
@@ -1448,7 +1454,7 @@ JobExec(Job *job, char **argv)
/* Pre-emptively mark job running, pid still zero though */
job->status = JOB_ST_RUNNING;
- Var_ReexportVars();
+ Var_ReexportVars(job->node);
cpid = vfork();
if (cpid == -1)
@@ -1459,9 +1465,8 @@ JobExec(Job *job, char **argv)
sigset_t tmask;
#ifdef USE_META
- if (useMeta) {
+ if (useMeta)
meta_job_child(job);
- }
#endif
/*
* Reset all signal handlers; this is necessary because we
@@ -1488,9 +1493,7 @@ JobExec(Job *job, char **argv)
if (Always_pass_job_queue ||
(job->node->type & (OP_MAKE | OP_SUBMAKE))) {
- /*
- * Pass job token pipe to submakes.
- */
+ /* Pass job token pipe to submakes. */
if (fcntl(tokenWaitJob.inPipe, F_SETFD, 0) == -1)
execDie("clear close-on-exec",
"tokenWaitJob.inPipe");
@@ -1544,9 +1547,8 @@ JobExec(Job *job, char **argv)
Trace_Log(JOBSTART, job);
#ifdef USE_META
- if (useMeta) {
+ if (useMeta)
meta_job_parent(job, cpid);
- }
#endif
/*
@@ -1558,7 +1560,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;
}
@@ -1638,7 +1642,7 @@ JobWriteShellCommands(Job *job, GNode *gn, bool *out_run)
#ifdef USE_META
if (useMeta) {
meta_job_start(job, gn);
- if (gn->type & OP_SILENT) /* might have changed */
+ if (gn->type & OP_SILENT) /* might have changed */
job->echo = false;
}
#endif
@@ -1682,7 +1686,7 @@ JobStart(GNode *gn, bool special)
job->special = special || gn->type & OP_SPECIAL;
job->ignerr = opts.ignoreErrors || gn->type & OP_IGNORE;
- job->echo = !(opts.beSilent || gn->type & OP_SILENT);
+ job->echo = !(opts.silent || gn->type & OP_SILENT);
/*
* Check the commands now so any attributes from .DEFAULT have a
@@ -1701,11 +1705,11 @@ JobStart(GNode *gn, bool special)
* also dead...
*/
if (!cmdsOK) {
- PrintOnError(gn, NULL); /* provide some clue */
+ PrintOnError(gn, "\n"); /* provide some clue */
DieHorribly();
}
} else if (((gn->type & OP_MAKE) && !opts.noRecursiveExecute) ||
- (!opts.noExecute && !opts.touchFlag)) {
+ (!opts.noExecute && !opts.touch)) {
/*
* The above condition looks very similar to
* GNode_ShouldExecute but is subtly different. It prevents
@@ -1718,7 +1722,7 @@ JobStart(GNode *gn, bool special)
* also dead...
*/
if (!cmdsOK) {
- PrintOnError(gn, NULL); /* provide some clue */
+ PrintOnError(gn, "\n"); /* provide some clue */
DieHorribly();
}
@@ -1785,12 +1789,12 @@ JobStart(GNode *gn, bool special)
* itself.
*/
static char *
-PrintFilteredOutput(char *cp, char *endp) /* XXX: should all be const */
+PrintFilteredOutput(char *p, const char *endp) /* XXX: p should be const */
{
- char *ecp; /* XXX: should be const */
+ char *ep; /* XXX: should be const */
if (shell->noPrint == NULL || shell->noPrint[0] == '\0')
- return cp;
+ return p;
/*
* XXX: What happens if shell->noPrint occurs on the boundary of
@@ -1798,9 +1802,9 @@ PrintFilteredOutput(char *cp, char *endp) /* XXX: should all be const */
* be a proper stream filter instead of doing string matching on
* selected chunks of the output.
*/
- while ((ecp = strstr(cp, shell->noPrint)) != NULL) {
- if (ecp != cp) {
- *ecp = '\0'; /* XXX: avoid writing to the buffer */
+ while ((ep = strstr(p, shell->noPrint)) != NULL) {
+ if (ep != p) {
+ *ep = '\0'; /* XXX: avoid writing to the buffer */
/*
* The only way there wouldn't be a newline after
* this line is if it were the last in the buffer.
@@ -1808,16 +1812,16 @@ PrintFilteredOutput(char *cp, char *endp) /* XXX: should all be const */
* there must be a newline, so we don't print one.
*/
/* XXX: What about null bytes in the output? */
- (void)fprintf(stdout, "%s", cp);
+ (void)fprintf(stdout, "%s", p);
(void)fflush(stdout);
}
- cp = ecp + shell->noPrintLen;
- if (cp == endp)
+ p = ep + shell->noPrintLen;
+ if (p == endp)
break;
- cp++; /* skip over the (XXX: assumed) newline */
- pp_skip_whitespace(&cp);
+ p++; /* skip over the (XXX: assumed) newline */
+ pp_skip_whitespace(&p);
}
- return cp;
+ return p;
}
/*
@@ -1855,46 +1859,36 @@ again:
if (nRead < 0) {
if (errno == EAGAIN)
return;
- if (DEBUG(JOB)) {
+ if (DEBUG(JOB))
perror("CollectOutput(piperead)");
- }
nr = 0;
- } else {
+ } else
nr = (size_t)nRead;
- }
+
+ if (nr == 0)
+ finish = false; /* stop looping */
/*
* If we hit the end-of-file (the job is dead), we must flush its
* remaining output, so pretend we read a newline if there's any
* output remaining in the buffer.
- * Also clear the 'finish' flag so we stop looping.
*/
if (nr == 0 && job->curPos != 0) {
job->outBuf[job->curPos] = '\n';
nr = 1;
- finish = false;
- } else if (nr == 0) {
- finish = false;
}
- /*
- * Look for the last newline in the bytes we just got. If there is
- * one, break out of the loop with 'i' as its index and gotNL set
- * true.
- */
max = job->curPos + nr;
+ for (i = job->curPos; i < max; i++)
+ if (job->outBuf[i] == '\0')
+ job->outBuf[i] = ' ';
+
+ /* Look for the last newline in the bytes we just got. */
for (i = job->curPos + nr - 1;
i >= job->curPos && i != (size_t)-1; i--) {
if (job->outBuf[i] == '\n') {
gotNL = true;
break;
- } else if (job->outBuf[i] == '\0') {
- /*
- * FIXME: The null characters are only replaced with
- * space _after_ the last '\n'. Everywhere else they
- * hide the rest of the command output.
- */
- job->outBuf[i] = ' ';
}
}
@@ -1922,7 +1916,7 @@ again:
*/
job->outBuf[i] = '\0';
if (i >= job->curPos) {
- char *cp;
+ char *p;
/*
* FIXME: SwitchOutputTo should be here, according to
@@ -1930,23 +1924,23 @@ again:
* do anything in the default shell, this bug has gone
* unnoticed until now.
*/
- cp = PrintFilteredOutput(job->outBuf, &job->outBuf[i]);
+ p = PrintFilteredOutput(job->outBuf, &job->outBuf[i]);
/*
* There's still more in the output buffer. This time,
* though, we know there's no newline at the end, so
* we add one of our own free will.
*/
- if (*cp != '\0') {
- if (!opts.beSilent)
+ if (*p != '\0') {
+ if (!opts.silent)
SwitchOutputTo(job->node);
#ifdef USE_META
if (useMeta) {
- meta_job_output(job, cp,
+ meta_job_output(job, p,
gotNL ? "\n" : "");
}
#endif
- (void)fprintf(stdout, "%s%s", cp,
+ (void)fprintf(stdout, "%s%s", p,
gotNL ? "\n" : "");
(void)fflush(stdout);
}
@@ -2002,7 +1996,7 @@ JobRun(GNode *targ)
Compat_Make(targ, targ);
/* XXX: Replace with GNode_IsError(gn) */
if (targ->made == ERROR) {
- PrintOnError(targ, "\n\nStop.");
+ PrintOnError(targ, "\n\nStop.\n");
exit(1);
}
#endif
@@ -2087,6 +2081,8 @@ JobReapChild(pid_t pid, WAIT_T status, bool isJobs)
job->status = JOB_ST_FINISHED;
job->exit_status = WAIT_STATUS(status);
+ if (WIFEXITED(status))
+ job->node->exit_status = WEXITSTATUS(status);
JobFinish(job, status);
}
@@ -2128,7 +2124,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--;
}
@@ -2150,9 +2146,8 @@ Job_CatchOutput(void)
* than job->inPollfd.
*/
if (useMeta && job->inPollfd != &fds[i]) {
- if (meta_job_event(job) <= 0) {
- fds[i].events = 0; /* never mind */
- }
+ if (meta_job_event(job) <= 0)
+ fds[i].events = 0; /* never mind */
}
#endif
if (--nready == 0)
@@ -2182,8 +2177,11 @@ InitShellNameAndPath(void)
return;
}
#endif
-
+#ifdef DEFSHELL_PATH
+ shellPath = DEFSHELL_PATH;
+#else
shellPath = str_concat3(_PATH_DEFSHELLDIR, "/", shellName);
+#endif
}
void
@@ -2192,7 +2190,8 @@ Shell_Init(void)
if (shellPath == NULL)
InitShellNameAndPath();
- Var_SetWithFlags(SCOPE_CMDLINE, ".SHELL", shellPath, VAR_SET_READONLY);
+ Var_SetWithFlags(SCOPE_CMDLINE, ".SHELL", shellPath,
+ VAR_SET_INTERNAL|VAR_SET_READONLY);
if (shell->errFlag == NULL)
shell->errFlag = "";
if (shell->echoFlag == NULL)
@@ -2203,14 +2202,8 @@ Shell_Init(void)
free(shellErrFlag);
shellErrFlag = NULL;
}
- if (shellErrFlag == NULL) {
- size_t n = strlen(shell->errFlag) + 2;
-
- shellErrFlag = bmake_malloc(n);
- if (shellErrFlag != NULL)
- snprintf(shellErrFlag, n, "-%s",
- shell->errFlag);
- }
+ if (shellErrFlag == NULL)
+ shellErrFlag = str_concat2("-", shell->errFlag);
} else if (shellErrFlag != NULL) {
free(shellErrFlag);
shellErrFlag = NULL;
@@ -2230,14 +2223,13 @@ Shell_GetNewline(void)
void
Job_SetPrefix(void)
{
- if (targPrefix != NULL) {
+ if (targPrefix != NULL)
free(targPrefix);
- } else if (!Var_Exists(SCOPE_GLOBAL, MAKE_JOB_PREFIX)) {
- Global_Set(MAKE_JOB_PREFIX, "---");
- }
+ else if (!Var_Exists(SCOPE_GLOBAL, ".MAKE.JOB.PREFIX"))
+ Global_Set(".MAKE.JOB.PREFIX", "---");
- (void)Var_Subst("${" MAKE_JOB_PREFIX "}",
- SCOPE_GLOBAL, VARE_WANTRES, &targPrefix);
+ targPrefix = Var_Subst("${.MAKE.JOB.PREFIX}",
+ SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
}
@@ -2307,9 +2299,7 @@ Job_Init(void)
watchfd(&childExitJob);
sigemptyset(&caught_signals);
- /*
- * Install a SIGCHLD handler.
- */
+ /* Install a SIGCHLD handler. */
(void)bmake_signal(SIGCHLD, JobChildSig);
sigaddset(&caught_signals, SIGCHLD);
@@ -2335,8 +2325,10 @@ Job_Init(void)
AddSig(SIGCONT, JobContinueSig);
(void)Job_RunTarget(".BEGIN", NULL);
- /* Create the .END node now, even though no code in the unit tests
- * depends on it. See also Targ_GetEndNode in Compat_Run. */
+ /*
+ * Create the .END node now, even though no code in the unit tests
+ * depends on it. See also Targ_GetEndNode in Compat_MakeAll.
+ */
(void)Targ_GetEndNode();
}
@@ -2438,9 +2430,7 @@ Job_ParseShell(char *line)
memset(&newShell, 0, sizeof newShell);
- /*
- * Parse the specification by keyword
- */
+ /* Parse the specification by keyword. */
wordsList = Str_Words(line, true);
words = wordsList.words;
argc = wordsList.len;
@@ -2476,13 +2466,17 @@ Job_ParseShell(char *line)
} else if (strncmp(arg, "newline=", 8) == 0) {
newShell.newline = arg + 8;
} else if (strncmp(arg, "check=", 6) == 0) {
- /* Before 2020-12-10, these two variables
- * had been a single variable. */
+ /*
+ * Before 2020-12-10, these two variables had
+ * been a single variable.
+ */
newShell.errOn = arg + 6;
newShell.echoTmpl = arg + 6;
} else if (strncmp(arg, "ignore=", 7) == 0) {
- /* Before 2020-12-10, these two variables
- * had been a single variable. */
+ /*
+ * Before 2020-12-10, these two variables had
+ * been a single variable.
+ */
newShell.errOff = arg + 7;
newShell.runIgnTmpl = arg + 7;
} else if (strncmp(arg, "errout=", 7) == 0) {
@@ -2531,25 +2525,9 @@ Job_ParseShell(char *line)
}
}
} else {
- /*
- * The user provided a path. If s/he gave nothing else
- * (fullSpec is false), try and find a matching shell in the
- * ones we know of. Else we just take the specification at
- * its word and copy it to a new location. In either case,
- * we need to record the path the user gave for the shell.
- */
shellPath = path;
- path = strrchr(path, '/');
- if (path == NULL) {
- path = UNCONST(shellPath);
- } else {
- path++;
- }
- if (newShell.name != NULL) {
- shellName = newShell.name;
- } else {
- shellName = path;
- }
+ shellName = newShell.name != NULL ? newShell.name
+ : str_basename(path);
if (!fullSpec) {
if ((sh = FindShellByName(shellName)) == NULL) {
Parse_Error(PARSE_WARNING,
@@ -2624,7 +2602,7 @@ JobInterrupt(bool runINTERRUPT, int signo)
JobSigUnlock(&mask);
- if (runINTERRUPT && !opts.touchFlag) {
+ if (runINTERRUPT && !opts.touch) {
interrupt = Targ_FindNode(".INTERRUPT");
if (interrupt != NULL) {
opts.ignoreErrors = false;
@@ -2646,11 +2624,10 @@ Job_Finish(void)
GNode *endNode = Targ_GetEndNode();
if (!Lst_IsEmpty(&endNode->commands) ||
!Lst_IsEmpty(&endNode->children)) {
- if (job_errors != 0) {
+ if (job_errors != 0)
Error("Errors reported so .END ignored");
- } else {
+ else
JobRun(endNode);
- }
}
return job_errors;
}
@@ -2790,9 +2767,7 @@ clearfd(Job *job)
fdsLen--;
}
#endif
- /*
- * Move last job in table into hole made by dead job.
- */
+ /* Move last job in table into hole made by dead job. */
if (fdsLen != i) {
fds[i] = fds[fdsLen];
jobByFdIndex[i] = jobByFdIndex[fdsLen];
@@ -2850,7 +2825,7 @@ Job_TempFile(const char *pattern, char *tfile, size_t tfile_sz)
JobSigLock(&mask);
fd = mkTempFile(pattern, tfile, tfile_sz);
if (tfile != NULL && !DEBUG(SCRIPT))
- unlink(tfile);
+ unlink(tfile);
JobSigUnlock(&mask);
return fd;
@@ -2929,9 +2904,8 @@ Job_TokenWithdraw(void)
if (count == 0)
Fatal("eof on job pipe!");
if (count < 0 && jobTokensRunning != 0) {
- if (errno != EAGAIN) {
+ if (errno != EAGAIN)
Fatal("job pipe read: %s", strerror(errno));
- }
DEBUG1(JOB, "(%d) blocked for token\n", getpid());
wantToken = 1;
return false;
@@ -2982,7 +2956,7 @@ Job_RunTarget(const char *target, const char *fname)
JobRun(gn);
/* XXX: Replace with GNode_IsError(gn) */
if (gn->made == ERROR) {
- PrintOnError(gn, "\n\nStop.");
+ PrintOnError(gn, "\n\nStop.\n");
exit(1);
}
return true;
@@ -3047,4 +3021,4 @@ emul_poll(struct pollfd *fd, int nfd, int timeout)
return npoll;
}
-#endif /* USE_SELECT */
+#endif /* USE_SELECT */
diff --git a/contrib/bmake/job.h b/contrib/bmake/job.h
index ef66602518d7..26185ba84a7d 100644
--- a/contrib/bmake/job.h
+++ b/contrib/bmake/job.h
@@ -1,4 +1,4 @@
-/* $NetBSD: job.h,v 1.73 2021/04/03 11:08:40 rillig Exp $ */
+/* $NetBSD: job.h,v 1.78 2023/12/19 19:33:39 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -73,14 +73,12 @@
* from: @(#)job.h 8.1 (Berkeley) 6/6/93
*/
-/*
- * Running of jobs in parallel mode.
- */
+/* Run jobs in parallel mode. */
#ifndef MAKE_JOB_H
#define MAKE_JOB_H
-#define TMPPAT "makeXXXXXX" /* relative to tmpdir */
+#define TMPPAT "makeXXXXXX" /* relative to tmpdir */
#ifdef USE_SELECT
/*
@@ -92,16 +90,15 @@
#define pollfd emul_pollfd
struct emul_pollfd {
- int fd;
- short events;
- short revents;
+ int fd;
+ short events;
+ short revents;
};
#define POLLIN 0x0001
#define POLLOUT 0x0004
-int
-emul_poll(struct pollfd *fd, int nfd, int timeout);
+int emul_poll(struct pollfd *, int, int);
#endif
/*
@@ -145,9 +142,11 @@ typedef struct Job {
/* The target the child is making */
GNode *node;
- /* If one of the shell commands is "...", all following commands are
- * delayed until the .END node is made. This list node points to the
- * first of these commands, if any. */
+ /*
+ * If one of the shell commands is "...", all following commands are
+ * delayed until the .END node is made. This list node points to the
+ * first of these commands, if any.
+ */
StringListNode *tailCmds;
/* This is where the shell commands go. */
@@ -187,24 +186,25 @@ extern char *shellErrFlag;
extern int jobTokensRunning; /* tokens currently "out" */
void Shell_Init(void);
-const char *Shell_GetNewline(void);
+const char *Shell_GetNewline(void) MAKE_ATTR_USE;
void Job_Touch(GNode *, bool);
-bool Job_CheckCommands(GNode *, void (*abortProc)(const char *, ...));
+bool Job_CheckCommands(GNode *, void (*abortProc)(const char *, ...))
+ MAKE_ATTR_USE;
void Job_CatchChildren(void);
void Job_CatchOutput(void);
void Job_Make(GNode *);
void Job_Init(void);
-bool Job_ParseShell(char *);
+bool Job_ParseShell(char *) MAKE_ATTR_USE;
int Job_Finish(void);
void Job_End(void);
void Job_Wait(void);
void Job_AbortAll(void);
void Job_TokenReturn(void);
-bool Job_TokenWithdraw(void);
+bool Job_TokenWithdraw(void) MAKE_ATTR_USE;
void Job_ServerStart(int, int, int);
void Job_SetPrefix(void);
bool Job_RunTarget(const char *, const char *);
void Job_FlagsToString(const Job *, char *, size_t);
-int Job_TempFile(const char *, char *, size_t);
+int Job_TempFile(const char *, char *, size_t) MAKE_ATTR_USE;
-#endif /* MAKE_JOB_H */
+#endif
diff --git a/contrib/bmake/lst.c b/contrib/bmake/lst.c
index 372973112783..ae57ba101845 100644
--- a/contrib/bmake/lst.c
+++ b/contrib/bmake/lst.c
@@ -1,4 +1,4 @@
-/* $NetBSD: lst.c,v 1.105 2021/03/15 16:45:30 rillig Exp $ */
+/* $NetBSD: lst.c,v 1.108 2024/04/27 17:33:46 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -34,7 +34,7 @@
#include "make.h"
-MAKE_RCSID("$NetBSD: lst.c,v 1.105 2021/03/15 16:45:30 rillig Exp $");
+MAKE_RCSID("$NetBSD: lst.c,v 1.108 2024/04/27 17:33:46 rillig Exp $");
static ListNode *
LstNodeNew(ListNode *prev, ListNode *next, void *datum)
@@ -48,15 +48,6 @@ LstNodeNew(ListNode *prev, ListNode *next, void *datum)
return ln;
}
-/* Create and initialize a new, empty list. */
-List *
-Lst_New(void)
-{
- List *list = bmake_malloc(sizeof *list);
- Lst_Init(list);
- return list;
-}
-
void
Lst_Done(List *list)
{
@@ -69,26 +60,17 @@ Lst_Done(List *list)
}
void
-Lst_DoneCall(List *list, LstFreeProc freeProc)
+Lst_DoneFree(List *list)
{
ListNode *ln, *next;
for (ln = list->first; ln != NULL; ln = next) {
next = ln->next;
- freeProc(ln->datum);
+ free(ln->datum);
free(ln);
}
}
-/* Free a list and all its nodes. The node data are not freed though. */
-void
-Lst_Free(List *list)
-{
-
- Lst_Done(list);
- free(list);
-}
-
/* Insert a new node with the datum before the given node. */
void
Lst_InsertBefore(List *list, ListNode *ln, void *datum)
@@ -163,6 +145,8 @@ Lst_Remove(List *list, ListNode *ln)
list->first = ln->next;
if (list->last == ln)
list->last = ln->prev;
+
+ free(ln);
}
/* Replace the datum in the given node with the new datum. */
diff --git a/contrib/bmake/lst.h b/contrib/bmake/lst.h
index cddd6439f611..7640366d8261 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.105 2024/04/27 17:33:46 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -103,19 +103,10 @@ struct List {
ListNode *last;
};
-/* Free the datum of a node, called before freeing the node itself. */
-typedef void LstFreeProc(void *);
-
-/* Create or destroy a list */
-
-/* Create a new list. */
-List *Lst_New(void);
-/* Free the list nodes, but not the list itself. */
+/* Free the list nodes. */
void Lst_Done(List *);
-/* Free the list nodes, freeing the node data using the given function. */
-void Lst_DoneCall(List *, LstFreeProc);
-/* Free the list, leaving the node data unmodified. */
-void Lst_Free(List *);
+/* Free the list nodes, as well as each node's datum. */
+void Lst_DoneFree(List *);
#define LST_INIT { NULL, NULL }
@@ -129,22 +120,22 @@ Lst_Init(List *list)
/* Get information about a list */
-MAKE_INLINE bool
+MAKE_INLINE bool MAKE_ATTR_USE
Lst_IsEmpty(List *list)
{
return list->first == NULL;
}
/* Find the first node that contains the given datum, or NULL. */
-ListNode *Lst_FindDatum(List *, const void *);
+ListNode *Lst_FindDatum(List *, const void *) MAKE_ATTR_USE;
/* Modify a list */
/* 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 head of the list. */
void Lst_Prepend(List *, void *);
-/* Place a datum at the end of the list. */
+/* Add a datum at the tail of the list. */
void Lst_Append(List *, void *);
/* Remove the node from the list. */
void Lst_Remove(List *, ListNode *);
@@ -163,12 +154,13 @@ void LstNode_SetNull(ListNode *);
/* Add a datum at the tail of the queue. */
MAKE_INLINE void
-Lst_Enqueue(List *list, void *datum) {
+Lst_Enqueue(List *list, void *datum)
+{
Lst_Append(list, datum);
}
/* Remove the head node of the queue and return its datum. */
-void *Lst_Dequeue(List *);
+void *Lst_Dequeue(List *) MAKE_ATTR_USE;
/*
* A vector is an ordered collection of items, allowing for fast indexed
@@ -187,7 +179,7 @@ void Vector_Init(Vector *, size_t);
* Return the pointer to the given item in the vector.
* The returned data is valid until the next modifying operation.
*/
-MAKE_INLINE void *
+MAKE_INLINE void * MAKE_ATTR_USE
Vector_Get(Vector *v, size_t i)
{
unsigned char *items = v->items;
@@ -198,8 +190,9 @@ void *Vector_Push(Vector *);
void *Vector_Pop(Vector *);
MAKE_INLINE void
-Vector_Done(Vector *v) {
+Vector_Done(Vector *v)
+{
free(v->items);
}
-#endif /* MAKE_LST_H */
+#endif
diff --git a/contrib/bmake/machine.sh b/contrib/bmake/machine.sh
index 0cf9daeeef3a..14fc5269ad40 100755
--- a/contrib/bmake/machine.sh
+++ b/contrib/bmake/machine.sh
@@ -1,10 +1,12 @@
:
-# derrived from /etc/rc_d/os.sh
+# This is mostly redundant.
+# These days I use the pseudo machine "host" when building for host
+# and $TARGET_HOST for its objdir
# RCSid:
-# $Id: machine.sh,v 1.18 2017/08/13 19:11:28 sjg Exp $
+# $Id: machine.sh,v 1.19 2023/01/17 18:30:21 sjg Exp $
#
-# @(#) Copyright (c) 1994-2002 Simon J. Gerraty
+# @(#) Copyright (c) 1994-2023 Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@@ -17,82 +19,23 @@
# sjg@crufty.net
#
-OS=`uname`
-OSREL=`uname -r`
-OSMAJOR=`IFS=.; set $OSREL; echo $1`
-machine=`uname -p 2>/dev/null || uname -m`
-MACHINE=
-
-# there is at least one case of `uname -p` outputting
-# a bunch of usless drivel
-case "$machine" in
-unknown|*[!A-Za-z0-9_-]*)
- machine=`uname -m`
- ;;
-esac
-
-# Great! Solaris keeps moving arch(1)
-# we need this here, and it is not always available...
-Which() {
- # some shells cannot correctly handle `IFS`
- # in conjunction with the for loop.
- _dirs=`IFS=:; echo ${2:-$PATH}`
- for d in $_dirs
- do
- test -x $d/$1 && { echo $d/$1; break; }
- done
-}
+# leverage os.sh
+Mydir=`dirname $0`
+. $Mydir/os.sh
+# some further overrides - mostly for MACHINE_ACH
case $OS in
AIX) # from http://gnats.netbsd.org/29386
- OSMAJOR=`uname -v`
- OSMINOR=`uname -r`
- MACHINE=$OS$OSMAJOR.$OSMINOR
MACHINE_ARCH=`bootinfo -T`
;;
-OpenBSD)
- MACHINE=$OS$OSMAJOR.$machine
- arch=`Which arch /usr/bin:/usr/ucb:$PATH`
- MACHINE_ARCH=`$arch -s`;
- ;;
Bitrig)
- MACHINE=$OS$OSMAJOR.$machine
- MACHINE_ARCH=`uname -m`;
- ;;
-*BSD)
- MACHINE=$OS$OSMAJOR.$machine
- ;;
-SunOS)
- arch=`Which arch /usr/bin:/usr/ucb:$PATH`
- test "$arch" && machine_arch=`$arch`
-
- case "$OSREL" in
- 4.0*) MACHINE_ARCH=$machine_arch MACHINE=$machine_arch;;
- 4*) MACHINE_ARCH=$machine_arch;;
- esac
+ MACHINE_ARCH=$MACHINE;
;;
HP-UX)
- MACHINE_ARCH=`IFS="/-."; set $machine; echo $1`
- ;;
-Interix)
- MACHINE=i386
- MACHINE_ARCH=i386
+ MACHINE_ARCH=`IFS="/-."; set $MACHINE; echo $1`
;;
-UnixWare)
- OSREL=`uname -v`
- OSMAJOR=`IFS=.; set $OSREL; echo $1`
- MACHINE_ARCH=`uname -m`
- ;;
-Linux)
- case "$machine" in
- i?86) MACHINE_ARCH=i386;;# does anyone really care about 686 vs 586?
- esac
- ;;
esac
-MACHINE=${MACHINE:-$OS$OSMAJOR}
-MACHINE_ARCH=${MACHINE_ARCH:-$machine}
-
(
case "$0" in
arch*) echo $MACHINE_ARCH;;
@@ -103,4 +46,4 @@ arch*) echo $MACHINE_ARCH;;
esac
;;
esac
-) | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz
+) | toLower
diff --git a/contrib/bmake/main.c b/contrib/bmake/main.c
index 85a8a1cce7a1..fff294291ccf 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.615 2024/05/07 18:26:22 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -111,8 +111,8 @@
#include "trace.h"
/* "@(#)main.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: main.c,v 1.540 2021/06/18 12:54:17 rillig Exp $");
-#if defined(MAKE_NATIVE) && !defined(lint)
+MAKE_RCSID("$NetBSD: main.c,v 1.615 2024/05/07 18:26:22 sjg Exp $");
+#if defined(MAKE_NATIVE)
__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 "
"The Regents of the University of California. "
"All rights reserved.");
@@ -125,20 +125,20 @@ __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 "
CmdOpts opts;
time_t now; /* Time at start of make */
GNode *defaultNode; /* .DEFAULT node */
-bool allPrecious; /* .PRECIOUS given on line by itself */
+bool allPrecious; /* .PRECIOUS given on a line by itself */
bool deleteOnError; /* .DELETE_ON_ERROR: set */
static int maxJobTokens; /* -j argument */
-bool enterFlagObj; /* -w and objdir != srcdir */
+static bool enterFlagObj; /* -w and objdir != srcdir */
static int jp_0 = -1, jp_1 = -1; /* ends of parent job pipe */
bool doing_depend; /* Set while reading .depend */
static bool jobsRunning; /* true if the jobs might be running */
static const char *tracefile;
-static int ReadMakefile(const char *);
+static bool ReadMakefile(const char *);
static void purge_relative_cached_realpaths(void);
-static bool ignorePWD; /* if we use -C, PWD is meaningless */
+static bool ignorePWD; /* if we use -C, PWD is meaningless */
static char objdir[MAXPATHLEN + 1]; /* where we chdir'ed to */
char curdir[MAXPATHLEN + 1]; /* Startup directory */
const char *progname;
@@ -152,40 +152,31 @@ static HashTable cached_realpaths;
/*
* For compatibility with the POSIX version of MAKEFLAGS that includes
- * all the options without '-', convert 'flags' to '-f -l -a -g -s'.
+ * all the options without '-', convert 'flags' to '-f -l -a -g -s '.
*/
static char *
explode(const char *flags)
{
- size_t len;
- char *nf, *st;
- const char *f;
+ char *exploded, *ep;
+ const char *p;
if (flags == NULL)
return NULL;
- for (f = flags; *f != '\0'; f++)
- if (!ch_isalpha(*f))
- break;
-
- if (*f != '\0')
- return bmake_strdup(flags);
+ for (p = flags; *p != '\0'; p++)
+ if (!ch_isalpha(*p))
+ return bmake_strdup(flags);
- len = strlen(flags);
- st = nf = bmake_malloc(len * 3 + 1);
- while (*flags != '\0') {
- *nf++ = '-';
- *nf++ = *flags++;
- *nf++ = ' ';
+ exploded = bmake_malloc((size_t)(p - flags) * 3 + 1);
+ for (p = flags, ep = exploded; *p != '\0'; p++) {
+ *ep++ = '-';
+ *ep++ = *p;
+ *ep++ = ' ';
}
- *nf = '\0';
- return st;
+ *ep = '\0';
+ return exploded;
}
-/*
- * usage --
- * exit with usage message
- */
MAKE_ATTR_DEAD static void
usage(void)
{
@@ -229,15 +220,14 @@ MainParseArgDebugFile(const char *arg)
fname = bmake_malloc(len + 20);
memcpy(fname, arg, len + 1);
- /* Let the filename be modified by the pid */
- if (strcmp(fname + len - 3, ".%d") == 0)
+ /* Replace the trailing '%d' after '.%d' with the pid. */
+ if (len >= 3 && memcmp(fname + len - 3, ".%d", 3) == 0)
snprintf(fname + len - 2, 20, "%d", getpid());
opts.debug_file = fopen(fname, mode);
if (opts.debug_file == NULL) {
- fprintf(stderr, "Cannot open debug file %s\n",
- fname);
- usage();
+ fprintf(stderr, "Cannot open debug file \"%s\"\n", fname);
+ exit(2);
}
free(fname);
}
@@ -251,83 +241,83 @@ MainParseArgDebug(const char *argvalue)
for (modules = argvalue; *modules != '\0'; modules++) {
switch (*modules) {
case '0': /* undocumented, only intended for tests */
- debug = DEBUG_NONE;
+ memset(&debug, 0, sizeof(debug));
break;
case 'A':
- debug = DEBUG_ALL;
+ memset(&debug, ~0, sizeof(debug));
break;
case 'a':
- debug |= DEBUG_ARCH;
+ debug.DEBUG_ARCH = true;
break;
case 'C':
- debug |= DEBUG_CWD;
+ debug.DEBUG_CWD = true;
break;
case 'c':
- debug |= DEBUG_COND;
+ debug.DEBUG_COND = true;
break;
case 'd':
- debug |= DEBUG_DIR;
+ debug.DEBUG_DIR = true;
break;
case 'e':
- debug |= DEBUG_ERROR;
+ debug.DEBUG_ERROR = true;
break;
case 'f':
- debug |= DEBUG_FOR;
+ debug.DEBUG_FOR = true;
break;
case 'g':
if (modules[1] == '1') {
- debug |= DEBUG_GRAPH1;
+ debug.DEBUG_GRAPH1 = true;
modules++;
} else if (modules[1] == '2') {
- debug |= DEBUG_GRAPH2;
+ debug.DEBUG_GRAPH2 = true;
modules++;
} else if (modules[1] == '3') {
- debug |= DEBUG_GRAPH3;
+ debug.DEBUG_GRAPH3 = true;
modules++;
}
break;
case 'h':
- debug |= DEBUG_HASH;
+ debug.DEBUG_HASH = true;
break;
case 'j':
- debug |= DEBUG_JOB;
+ debug.DEBUG_JOB = true;
break;
case 'L':
opts.strict = true;
break;
case 'l':
- debug |= DEBUG_LOUD;
+ debug.DEBUG_LOUD = true;
break;
case 'M':
- debug |= DEBUG_META;
+ debug.DEBUG_META = true;
break;
case 'm':
- debug |= DEBUG_MAKE;
+ debug.DEBUG_MAKE = true;
break;
case 'n':
- debug |= DEBUG_SCRIPT;
+ debug.DEBUG_SCRIPT = true;
break;
case 'p':
- debug |= DEBUG_PARSE;
+ debug.DEBUG_PARSE = true;
break;
case 's':
- debug |= DEBUG_SUFF;
+ debug.DEBUG_SUFF = true;
break;
case 't':
- debug |= DEBUG_TARG;
+ debug.DEBUG_TARG = true;
break;
case 'V':
opts.debugVflag = true;
break;
case 'v':
- debug |= DEBUG_VAR;
+ debug.DEBUG_VAR = true;
break;
case 'x':
- debug |= DEBUG_SHELL;
+ debug.DEBUG_SHELL = true;
break;
case 'F':
MainParseArgDebugFile(modules + 1);
- goto debug_setbuf;
+ goto finish;
default:
(void)fprintf(stderr,
"%s: illegal argument to d option -- %c\n",
@@ -336,20 +326,15 @@ MainParseArgDebug(const char *argvalue)
}
}
-debug_setbuf:
+finish:
opts.debug = debug;
- /*
- * Make the debug_file unbuffered, and make
- * stdout line buffered (unless debugfile == stdout).
- */
setvbuf(opts.debug_file, NULL, _IONBF, 0);
- if (opts.debug_file != stdout) {
+ if (opts.debug_file != stdout)
setvbuf(stdout, NULL, _IOLBF, 0);
- }
}
-/* Is path relative, or does it contain any relative component "." or ".."? */
+/* Is path relative or does it contain any relative component "." or ".."? */
static bool
IsRelativePath(const char *path)
{
@@ -403,12 +388,6 @@ MainParseArgJobsInternal(const char *argvalue)
}
if ((fcntl(jp_0, F_GETFD, 0) < 0) ||
(fcntl(jp_1, F_GETFD, 0) < 0)) {
-#if 0
- (void)fprintf(stderr,
- "%s: ###### warning -- J descriptors were closed!\n",
- progname);
- exit(2);
-#endif
jp_0 = -1;
jp_1 = -1;
opts.compatMake = true;
@@ -419,29 +398,51 @@ MainParseArgJobsInternal(const char *argvalue)
}
static void
-MainParseArgJobs(const char *argvalue)
+MainParseArgJobs(const char *arg)
{
- char *p;
+ const char *p;
+ char *end;
+ char v[12];
forceJobs = true;
- opts.maxJobs = (int)strtol(argvalue, &p, 0);
+ opts.maxJobs = (int)strtol(arg, &end, 0);
+ p = end;
+#ifdef _SC_NPROCESSORS_ONLN
+ if (*p != '\0') {
+ double d;
+
+ if (*p == 'C')
+ d = (opts.maxJobs > 0) ? opts.maxJobs : 1;
+ else if (*p == '.') {
+ d = strtod(arg, &end);
+ p = end;
+ } else
+ d = 0.0;
+ if (d > 0.0) {
+ p = "";
+ opts.maxJobs = (int)sysconf(_SC_NPROCESSORS_ONLN);
+ opts.maxJobs = (int)(d * (double)opts.maxJobs);
+ }
+ }
+#endif
if (*p != '\0' || opts.maxJobs < 1) {
(void)fprintf(stderr,
- "%s: illegal argument to -j -- must be positive integer!\n",
- progname);
+ "%s: argument '%s' to option '-j' "
+ "must be a positive number\n",
+ progname, arg);
exit(2); /* Not 1 so -q can distinguish error */
}
+ snprintf(v, sizeof(v), "%d", opts.maxJobs);
Global_Append(MAKEFLAGS, "-j");
- Global_Append(MAKEFLAGS, argvalue);
- Global_Set(".MAKE.JOBS", argvalue);
+ Global_Append(MAKEFLAGS, v);
+ Global_Set(".MAKE.JOBS", v);
maxJobTokens = opts.maxJobs;
}
static void
MainParseArgSysInc(const char *argvalue)
{
- /* look for magic parent directory search string */
- if (strncmp(".../", argvalue, 4) == 0) {
+ if (strncmp(argvalue, ".../", 4) == 0) {
char *found_path = Dir_FindHereOrAbove(curdir, argvalue + 4);
if (found_path == NULL)
return;
@@ -452,10 +453,11 @@ MainParseArgSysInc(const char *argvalue)
}
Global_Append(MAKEFLAGS, "-m");
Global_Append(MAKEFLAGS, argvalue);
+ Dir_SetSYSPATH();
}
static bool
-MainParseArg(char c, const char *argvalue)
+MainParseOption(char c, const char *argvalue)
{
switch (c) {
case '\0':
@@ -463,19 +465,20 @@ MainParseArg(char c, const char *argvalue)
case 'B':
opts.compatMake = true;
Global_Append(MAKEFLAGS, "-B");
- Global_Set(MAKE_MODE, "compat");
+ Global_Set(".MAKE.MODE", "compat");
break;
case 'C':
MainParseArgChdir(argvalue);
break;
case 'D':
- if (argvalue[0] == '\0') return false;
- Global_SetExpand(argvalue, "1");
+ if (argvalue[0] == '\0')
+ return false;
+ Var_SetExpand(SCOPE_GLOBAL, argvalue, "1");
Global_Append(MAKEFLAGS, "-D");
Global_Append(MAKEFLAGS, argvalue);
break;
case 'I':
- Parse_AddIncludeDir(argvalue);
+ SearchPath_Add(parseIncPath, argvalue);
Global_Append(MAKEFLAGS, "-I");
Global_Append(MAKEFLAGS, argvalue);
break;
@@ -506,7 +509,7 @@ MainParseArg(char c, const char *argvalue)
break;
case 'W':
opts.parseWarnFatal = true;
- /* XXX: why no Var_Append? */
+ /* XXX: why no Global_Append? */
break;
case 'X':
opts.varNoExportEnv = true;
@@ -549,7 +552,7 @@ MainParseArg(char c, const char *argvalue)
Global_Append(MAKEFLAGS, "-n");
break;
case 'q':
- opts.queryFlag = true;
+ opts.query = true;
/* Kind of nonsensical, wot? */
Global_Append(MAKEFLAGS, "-q");
break;
@@ -558,11 +561,11 @@ MainParseArg(char c, const char *argvalue)
Global_Append(MAKEFLAGS, "-r");
break;
case 's':
- opts.beSilent = true;
+ opts.silent = true;
Global_Append(MAKEFLAGS, "-s");
break;
case 't':
- opts.touchFlag = true;
+ opts.touch = true;
Global_Append(MAKEFLAGS, "-t");
break;
case 'w':
@@ -570,7 +573,6 @@ MainParseArg(char c, const char *argvalue)
Global_Append(MAKEFLAGS, "-w");
break;
default:
- case '?':
usage();
}
return true;
@@ -622,7 +624,10 @@ rearg:
/* '-' found at some earlier point */
optspec = strchr(optspecs, c);
if (c != '\0' && optspec != NULL && optspec[1] == ':') {
- /* -<something> found, and <something> should have an arg */
+ /*
+ * -<something> found, and <something> should have an
+ * argument
+ */
inOption = false;
arginc = 1;
argvalue = optscan;
@@ -644,7 +649,7 @@ rearg:
dashDash = true;
break;
default:
- if (!MainParseArg(c, argvalue))
+ if (!MainParseOption(c, argvalue))
goto noarg;
}
argv += arginc;
@@ -657,10 +662,7 @@ rearg:
* on the end of the "create" list.
*/
for (; argc > 1; argv++, argc--) {
- VarAssign var;
- if (Parse_IsVar(argv[1], &var)) {
- Parse_Var(&var, SCOPE_CMDLINE);
- } else {
+ if (!Parse_VarAssign(argv[1], false, SCOPE_CMDLINE)) {
if (argv[1][0] == '\0')
Punt("illegal (null) argument.");
if (argv[1][0] == '-' && !dashDash)
@@ -687,32 +689,18 @@ Main_ParseArgLine(const char *line)
{
Words words;
char *buf;
+ const char *p;
if (line == NULL)
return;
- /* XXX: don't use line as an iterator variable */
- for (; *line == ' '; line++)
+ for (p = line; *p == ' '; p++)
continue;
- if (line[0] == '\0')
+ if (p[0] == '\0')
return;
-#ifndef POSIX
- {
- /*
- * $MAKE may simply be naming the make(1) binary
- */
- char *cp;
-
- if (!(cp = strrchr(line, '/')))
- cp = line;
- if ((cp = strstr(cp, "make")) &&
- strcmp(cp, "make") == 0)
- return;
- }
-#endif
{
FStr argv0 = Var_Value(SCOPE_GLOBAL, ".MAKE");
- buf = str_concat3(argv0.str, " ", line);
+ buf = str_concat3(argv0.str, " ", p);
FStr_Done(&argv0);
}
@@ -735,7 +723,6 @@ Main_SetObjdir(bool writable, const char *fmt, ...)
char *path;
char buf[MAXPATHLEN + 1];
char buf2[MAXPATHLEN + 1];
- bool rc = false;
va_list ap;
va_start(ap, fmt);
@@ -743,75 +730,62 @@ Main_SetObjdir(bool writable, const char *fmt, ...)
va_end(ap);
if (path[0] != '/') {
- snprintf(buf2, MAXPATHLEN, "%s/%s", curdir, path);
- path = buf2;
+ if (snprintf(buf2, MAXPATHLEN, "%s/%s", curdir, path) <= MAXPATHLEN)
+ path = buf2;
+ else
+ return false;
}
/* look for the directory and try to chdir there */
- if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
- if ((writable && access(path, W_OK) != 0) ||
- (chdir(path) != 0)) {
- (void)fprintf(stderr, "%s warning: %s: %s.\n",
- progname, path, strerror(errno));
- } else {
- snprintf(objdir, sizeof objdir, "%s", path);
- Global_Set(".OBJDIR", objdir);
- setenv("PWD", objdir, 1);
- Dir_InitDot();
- purge_relative_cached_realpaths();
- rc = true;
- if (opts.enterFlag && strcmp(objdir, curdir) != 0)
- enterFlagObj = true;
- }
+ if (stat(path, &sb) != 0 || !S_ISDIR(sb.st_mode))
+ return false;
+
+ if ((writable && access(path, W_OK) != 0) || chdir(path) != 0) {
+ (void)fprintf(stderr, "%s: warning: %s: %s.\n",
+ progname, path, strerror(errno));
+ return false;
}
- return rc;
+ snprintf(objdir, sizeof objdir, "%s", path);
+ Global_Set(".OBJDIR", objdir);
+ setenv("PWD", objdir, 1);
+ Dir_InitDot();
+ purge_relative_cached_realpaths();
+ if (opts.enterFlag && strcmp(objdir, curdir) != 0)
+ enterFlagObj = true;
+ return true;
}
static bool
SetVarObjdir(bool writable, const char *var, const char *suffix)
{
FStr path = Var_Value(SCOPE_CMDLINE, var);
- FStr xpath;
if (path.str == NULL || path.str[0] == '\0') {
FStr_Done(&path);
return false;
}
- /* expand variable substitutions */
- xpath = FStr_InitRefer(path.str);
- if (strchr(path.str, '$') != 0) {
- char *expanded;
- (void)Var_Subst(path.str, SCOPE_GLOBAL, VARE_WANTRES, &expanded);
- /* TODO: handle errors */
- xpath = FStr_InitOwn(expanded);
- }
+ Var_Expand(&path, SCOPE_GLOBAL, VARE_WANTRES);
- (void)Main_SetObjdir(writable, "%s%s", xpath.str, suffix);
+ (void)Main_SetObjdir(writable, "%s%s", path.str, suffix);
- FStr_Done(&xpath);
FStr_Done(&path);
return true;
}
/*
- * Splits str into words, adding them to the list.
+ * Splits str into words (in-place, modifying it), adding them to the list.
* The string must be kept alive as long as the list.
*/
-int
-str2Lst_Append(StringList *lp, char *str)
+void
+AppendWords(StringList *lp, char *str)
{
- char *cp;
- int n;
-
+ char *p;
const char *sep = " \t";
- for (n = 0, cp = strtok(str, sep); cp != NULL; cp = strtok(NULL, sep)) {
- Lst_Append(lp, cp);
- n++;
- }
- return n;
+ for (p = strtok(str, sep); p != NULL; p = strtok(NULL, sep))
+ Lst_Append(lp, p);
}
#ifdef SIGINFO
@@ -834,9 +808,8 @@ siginfo(int signo MAKE_ATTR_UNUSED)
static void
MakeMode(void)
{
- char *mode;
-
- (void)Var_Subst("${" MAKE_MODE ":tl}", SCOPE_GLOBAL, VARE_WANTRES, &mode);
+ char *mode = Var_Subst("${.MAKE.MODE:tl}",
+ SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
if (mode[0] != '\0') {
@@ -848,6 +821,8 @@ MakeMode(void)
if (strstr(mode, "meta") != NULL)
meta_mode_init(mode);
#endif
+ if (strstr(mode, "randomize-targets") != NULL)
+ opts.randomizeTargets = true;
}
free(mode);
@@ -857,20 +832,18 @@ static void
PrintVar(const char *varname, bool expandVars)
{
if (strchr(varname, '$') != NULL) {
- char *evalue;
- (void)Var_Subst(varname, SCOPE_GLOBAL, VARE_WANTRES, &evalue);
+ char *evalue = Var_Subst(varname, SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
printf("%s\n", evalue);
- bmake_free(evalue);
+ free(evalue);
} else if (expandVars) {
char *expr = str_concat3("${", varname, "}");
- char *evalue;
- (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &evalue);
+ char *evalue = Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
free(expr);
printf("%s\n", evalue);
- bmake_free(evalue);
+ free(evalue);
} else {
FStr value = Var_Value(SCOPE_GLOBAL, varname);
@@ -892,7 +865,7 @@ GetBooleanExpr(const char *expr, bool fallback)
char *value;
bool res;
- (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &value);
+ value = Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
res = ParseBoolean(value, fallback);
free(value);
@@ -923,7 +896,7 @@ static bool
runTargets(void)
{
GNodeList targs = LST_INIT; /* target nodes to create */
- bool outOfDate; /* false if all targets up to date */
+ bool outOfDate; /* false if all targets up to date */
/*
* Have now read the entire graph and need to make a list of
@@ -944,7 +917,7 @@ runTargets(void)
* (to prevent the .BEGIN from being executed should
* it exist).
*/
- if (!opts.queryFlag) {
+ if (!opts.query) {
Job_Init();
jobsRunning = true;
}
@@ -952,11 +925,7 @@ runTargets(void)
/* Traverse the graph, checking on all the targets */
outOfDate = Make_Run(&targs);
} else {
- /*
- * Compat_Init will take care of creating all the
- * targets as well as initializing the module.
- */
- Compat_Run(&targs);
+ Compat_MakeAll(&targs);
outOfDate = false;
}
Lst_Done(&targs); /* Don't free the targets themselves. */
@@ -964,9 +933,9 @@ runTargets(void)
}
/*
- * Set up the .TARGETS variable to contain the list of targets to be
- * created. If none specified, make the variable empty -- the parser
- * will fill the thing in with the default or .MAIN target.
+ * Set up the .TARGETS variable to contain the list of targets to be created.
+ * If none specified, make the variable empty for now, the parser will fill
+ * in the default or .MAIN target later.
*/
static void
InitVarTargets(void)
@@ -1052,36 +1021,27 @@ InitVarMachineArch(void)
#ifndef NO_PWD_OVERRIDE
/*
- * All this code is so that we know where we are when we start up
- * on a different machine with pmake.
- *
- * XXX: Make no longer has "local" and "remote" mode. Is this code still
- * necessary?
- *
* Overriding getcwd() with $PWD totally breaks MAKEOBJDIRPREFIX
* since the value of curdir can vary depending on how we got
- * here. Ie sitting at a shell prompt (shell that provides $PWD)
- * or via subdir.mk in which case its likely a shell which does
+ * here. That is, sitting at a shell prompt (shell that provides $PWD)
+ * or via subdir.mk, in which case it's likely a shell which does
* not provide it.
*
* So, to stop it breaking this case only, we ignore PWD if
- * MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains a variable expression.
+ * MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains an expression.
*/
static void
HandlePWD(const struct stat *curdir_st)
{
char *pwd;
- FStr prefix, makeobjdir;
+ FStr makeobjdir;
struct stat pwd_st;
if (ignorePWD || (pwd = getenv("PWD")) == NULL)
return;
- prefix = Var_Value(SCOPE_CMDLINE, "MAKEOBJDIRPREFIX");
- if (prefix.str != NULL) {
- FStr_Done(&prefix);
+ if (Var_Exists(SCOPE_CMDLINE, "MAKEOBJDIRPREFIX"))
return;
- }
makeobjdir = Var_Value(SCOPE_CMDLINE, "MAKEOBJDIR");
if (makeobjdir.str != NULL && strchr(makeobjdir.str, '$') != NULL)
@@ -1098,13 +1058,13 @@ ignore_pwd:
#endif
/*
- * Find the .OBJDIR. If MAKEOBJDIRPREFIX, or failing that,
- * MAKEOBJDIR is set in the environment, try only that value
- * and fall back to .CURDIR if it does not exist.
+ * Find the .OBJDIR. If MAKEOBJDIRPREFIX, or failing that, MAKEOBJDIR is set
+ * in the environment, try only that value and fall back to .CURDIR if it
+ * does not exist.
*
* Otherwise, try _PATH_OBJDIR.MACHINE-MACHINE_ARCH, _PATH_OBJDIR.MACHINE,
- * and * finally _PATH_OBJDIRPREFIX`pwd`, in that order. If none
- * of these paths exist, just use .CURDIR.
+ * and finally _PATH_OBJDIRPREFIX`pwd`, in that order. If none of these
+ * paths exist, just use .CURDIR.
*/
static void
InitObjdir(const char *machine, const char *machine_arch)
@@ -1127,10 +1087,15 @@ 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) {
+#ifdef BMAKE_NOFILE_MAX
+ if (BMAKE_NOFILE_MAX < rl.rlim_max)
+ rl.rlim_cur = BMAKE_NOFILE_MAX;
+ else
+#endif
rl.rlim_cur = rl.rlim_max;
(void)setrlimit(RLIMIT_NOFILE, &rl);
}
@@ -1141,7 +1106,7 @@ static void
CmdOpts_Init(void)
{
opts.compatMake = false;
- opts.debug = DEBUG_NONE;
+ memset(&opts.debug, 0, sizeof(opts.debug));
/* opts.debug_file has already been initialized earlier */
opts.strict = false;
opts.debugVflag = false;
@@ -1152,10 +1117,10 @@ CmdOpts_Init(void)
opts.keepgoing = false; /* Stop on error */
opts.noRecursiveExecute = false; /* Execute all .MAKE targets */
opts.noExecute = false; /* Execute all commands */
- opts.queryFlag = false;
+ opts.query = false;
opts.noBuiltins = false; /* Read the built-in rules */
- opts.beSilent = false; /* Print commands as executed */
- opts.touchFlag = false;
+ opts.silent = false; /* Print commands as executed */
+ opts.touch = false;
opts.printVars = PVM_NONE;
Lst_Init(&opts.variables);
opts.parseWarnFatal = false;
@@ -1197,7 +1162,7 @@ static void
InitDefSysIncPath(char *syspath)
{
static char defsyspath[] = _PATH_DEFSYSPATH;
- char *start, *cp;
+ char *start, *p;
/*
* If no user-supplied system path was given (through the -m option)
@@ -1209,11 +1174,13 @@ InitDefSysIncPath(char *syspath)
else
syspath = bmake_strdup(syspath);
- for (start = syspath; *start != '\0'; start = cp) {
- for (cp = start; *cp != '\0' && *cp != ':'; cp++)
+ /* do NOT search .CURDIR first for .include <makefile> */
+ SearchPath_Add(defSysIncPath, ".DOTLAST");
+ for (start = syspath; *start != '\0'; start = p) {
+ for (p = start; *p != '\0' && *p != ':'; p++)
continue;
- if (*cp == ':')
- *cp++ = '\0';
+ if (*p == ':')
+ *p++ = '\0';
/* look for magic parent directory search string */
if (strncmp(start, ".../", 4) == 0) {
@@ -1245,16 +1212,14 @@ ReadBuiltinRules(void)
Fatal("%s: no system rules (%s).", progname, _PATH_DEFSYSMK);
for (ln = sysMkFiles.first; ln != NULL; ln = ln->next)
- if (ReadMakefile(ln->datum) == 0)
+ if (ReadMakefile(ln->datum))
break;
if (ln == NULL)
Fatal("%s: cannot open %s.",
progname, (const char *)sysMkFiles.first->datum);
- /* Free the list nodes but not the actual filenames since these may
- * still be used in GNodes. */
- Lst_Done(&sysMkFiles);
+ Lst_DoneFree(&sysMkFiles);
}
static void
@@ -1267,7 +1232,7 @@ InitMaxJobs(void)
!Var_Exists(SCOPE_GLOBAL, ".MAKE.JOBS"))
return;
- (void)Var_Subst("${.MAKE.JOBS}", SCOPE_GLOBAL, VARE_WANTRES, &value);
+ value = Var_Subst("${.MAKE.JOBS}", SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
n = (int)strtol(value, NULL, 0);
if (n < 1) {
@@ -1302,33 +1267,33 @@ InitVpath(void)
if (!Var_Exists(SCOPE_CMDLINE, "VPATH"))
return;
- (void)Var_Subst("${VPATH}", SCOPE_CMDLINE, VARE_WANTRES, &vpath);
+ vpath = Var_Subst("${VPATH}", SCOPE_CMDLINE, VARE_WANTRES);
/* TODO: handle errors */
path = vpath;
do {
- char *cp;
+ char *p;
/* skip to end of directory */
- for (cp = path; *cp != ':' && *cp != '\0'; cp++)
+ for (p = path; *p != ':' && *p != '\0'; p++)
continue;
/* Save terminator character so know when to stop */
- savec = *cp;
- *cp = '\0';
+ savec = *p;
+ *p = '\0';
/* Add directory to search path */
(void)SearchPath_Add(&dirSearchPath, path);
- *cp = savec;
- path = cp + 1;
+ *p = savec;
+ path = p + 1;
} while (savec == ':');
free(vpath);
}
static void
-ReadAllMakefiles(StringList *makefiles)
+ReadAllMakefiles(const StringList *makefiles)
{
StringListNode *ln;
for (ln = makefiles->first; ln != NULL; ln = ln->next) {
const char *fname = ln->datum;
- if (ReadMakefile(fname) != 0)
+ if (!ReadMakefile(fname))
Fatal("%s: cannot open %s.", progname, fname);
}
}
@@ -1336,23 +1301,19 @@ ReadAllMakefiles(StringList *makefiles)
static void
ReadFirstDefaultMakefile(void)
{
+ StringList makefiles = LST_INIT;
StringListNode *ln;
- char *prefs;
-
- (void)Var_Subst("${" MAKE_MAKEFILE_PREFERENCE "}",
- SCOPE_CMDLINE, VARE_WANTRES, &prefs);
+ char *prefs = Var_Subst("${.MAKE.MAKEFILE_PREFERENCE}",
+ SCOPE_CMDLINE, VARE_WANTRES);
/* TODO: handle errors */
- /* XXX: This should use a local list instead of opts.makefiles
- * since these makefiles do not come from the command line. They
- * also have different semantics in that only the first file that
- * is found is processed. See ReadAllMakefiles. */
- (void)str2Lst_Append(&opts.makefiles, prefs);
+ AppendWords(&makefiles, prefs);
- for (ln = opts.makefiles.first; ln != NULL; ln = ln->next)
- if (ReadMakefile(ln->datum) == 0)
+ for (ln = makefiles.first; ln != NULL; ln = ln->next)
+ if (ReadMakefile(ln->datum))
break;
+ Lst_Done(&makefiles);
free(prefs);
}
@@ -1373,6 +1334,7 @@ main_Init(int argc, char **argv)
/* default to writing debug to stderr */
opts.debug_file = stderr;
+ Str_Intern_Init();
HashTable_Init(&cached_realpaths);
#ifdef SIGINFO
@@ -1391,39 +1353,37 @@ main_Init(int argc, char **argv)
exit(2);
}
- /*
- * Get the name of this type of MACHINE from utsname
- * so we can share an executable for similar machines.
- * (i.e. m68k: amiga hp300, mac68k, sun3, ...)
- *
- * Note that both MACHINE and MACHINE_ARCH are decided at
- * run-time.
- */
machine = InitVarMachine(&utsname);
machine_arch = InitVarMachineArch();
myPid = getpid(); /* remember this for vFork() */
- /*
- * Just in case MAKEOBJDIR wants us to do something tricky.
- */
+ /* Just in case MAKEOBJDIR wants us to do something tricky. */
Targ_Init();
Var_Init();
- Global_Set(".MAKE.OS", utsname.sysname);
+#ifdef FORCE_MAKE_OS
+ Global_Set_ReadOnly(".MAKE.OS", FORCE_MAKE_OS);
+#else
+ Global_Set_ReadOnly(".MAKE.OS", utsname.sysname);
+#endif
Global_Set("MACHINE", machine);
Global_Set("MACHINE_ARCH", machine_arch);
#ifdef MAKE_VERSION
Global_Set("MAKE_VERSION", MAKE_VERSION);
#endif
- Global_Set(".newline", "\n"); /* handy for :@ loops */
- /*
- * This is the traditional preference for makefiles.
- */
+ Global_Set_ReadOnly(".newline", "\n");
#ifndef MAKEFILE_PREFERENCE_LIST
+ /* This is the traditional preference for makefiles. */
# define MAKEFILE_PREFERENCE_LIST "makefile Makefile"
#endif
- Global_Set(MAKE_MAKEFILE_PREFERENCE, MAKEFILE_PREFERENCE_LIST);
- Global_Set(MAKE_DEPENDFILE, ".depend");
+ Global_Set(".MAKE.MAKEFILE_PREFERENCE", MAKEFILE_PREFERENCE_LIST);
+ Global_Set(".MAKE.DEPENDFILE", ".depend");
+ /* Tell makefiles like jobs.mk whether we support -jC */
+#ifdef _SC_NPROCESSORS_ONLN
+ Global_Set_ReadOnly(".MAKE.JOBS.C", "yes");
+#else
+ Global_Set_ReadOnly(".MAKE.JOBS.C", "no");
+#endif
CmdOpts_Init();
allPrecious = false; /* Remove targets when interrupted */
@@ -1448,29 +1408,28 @@ main_Init(int argc, char **argv)
Parse_Init();
InitVarMake(argv[0]);
Global_Set(MAKEFLAGS, "");
- Global_Set(MAKEOVERRIDES, "");
+ Global_Set(".MAKEOVERRIDES", "");
Global_Set("MFLAGS", "");
Global_Set(".ALLTARGETS", "");
- /* some makefiles need to know this */
- Var_Set(SCOPE_CMDLINE, MAKE_LEVEL ".ENV", MAKE_LEVEL_ENV);
+ Global_Set_ReadOnly(".MAKE.LEVEL.ENV", MAKE_LEVEL_ENV);
/* Set some other useful variables. */
{
- char tmp[64], *ep = getenv(MAKE_LEVEL_ENV);
+ char buf[64], *ep = getenv(MAKE_LEVEL_ENV);
makelevel = ep != NULL && ep[0] != '\0' ? atoi(ep) : 0;
if (makelevel < 0)
makelevel = 0;
- snprintf(tmp, sizeof tmp, "%d", makelevel);
- Global_Set(MAKE_LEVEL, tmp);
- snprintf(tmp, sizeof tmp, "%u", myPid);
- Global_Set(".MAKE.PID", tmp);
- snprintf(tmp, sizeof tmp, "%u", getppid());
- Global_Set(".MAKE.PPID", tmp);
- snprintf(tmp, sizeof tmp, "%u", getuid());
- Global_Set(".MAKE.UID", tmp);
- snprintf(tmp, sizeof tmp, "%u", getgid());
- Global_Set(".MAKE.GID", tmp);
+ snprintf(buf, sizeof buf, "%d", makelevel);
+ Global_Set(".MAKE.LEVEL", buf);
+ snprintf(buf, sizeof buf, "%u", myPid);
+ Global_Set_ReadOnly(".MAKE.PID", buf);
+ snprintf(buf, sizeof buf, "%u", getppid());
+ Global_Set_ReadOnly(".MAKE.PPID", buf);
+ snprintf(buf, sizeof buf, "%u", getuid());
+ Global_Set_ReadOnly(".MAKE.UID", buf);
+ snprintf(buf, sizeof buf, "%u", getgid());
+ Global_Set_ReadOnly(".MAKE.GID", buf);
}
if (makelevel > 0) {
char pn[1024];
@@ -1483,25 +1442,12 @@ main_Init(int argc, char **argv)
#endif
Dir_Init();
- /*
- * First snag any flags out of the MAKE environment variable.
- * (Note this is *not* MAKEFLAGS since /bin/make uses that and it's
- * in a different format).
- */
-#ifdef POSIX
{
- char *p1 = explode(getenv("MAKEFLAGS"));
- Main_ParseArgLine(p1);
- free(p1);
+ char *makeflags = explode(getenv("MAKEFLAGS"));
+ Main_ParseArgLine(makeflags);
+ free(makeflags);
}
-#else
- Main_ParseArgLine(getenv("MAKE"));
-#endif
- /*
- * Find where we are (now).
- * We take care of PWD for the automounter below...
- */
if (getcwd(curdir, MAXPATHLEN) == NULL) {
(void)fprintf(stderr, "%s: getcwd: %s.\n",
progname, strerror(errno));
@@ -1513,9 +1459,6 @@ main_Init(int argc, char **argv)
if (opts.enterFlag)
printf("%s: Entering directory `%s'\n", progname, curdir);
- /*
- * Verify that cwd is sane.
- */
if (stat(curdir, &sa) == -1) {
(void)fprintf(stderr, "%s: %s: %s.\n",
progname, curdir, strerror(errno));
@@ -1529,10 +1472,6 @@ main_Init(int argc, char **argv)
InitObjdir(machine, machine_arch);
- /*
- * Initialize archive, target and suffix modules in preparation for
- * parsing the makefile(s)
- */
Arch_Init();
Suff_Init();
Trace_Init(tracefile);
@@ -1555,9 +1494,14 @@ static void
main_ReadFiles(void)
{
+ if (Lst_IsEmpty(&sysIncPath->dirs))
+ SearchPath_AddAll(sysIncPath, defSysIncPath);
+
+ Dir_SetSYSPATH();
if (!opts.noBuiltins)
ReadBuiltinRules();
+ posix_state = PS_MAYBE_NEXT_LINE;
if (!Lst_IsEmpty(&opts.makefiles))
ReadAllMakefiles(&opts.makefiles);
else
@@ -1570,8 +1514,8 @@ main_PrepareMaking(void)
{
/* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */
if (!opts.noBuiltins || opts.printVars == PVM_NONE) {
- (void)Var_Subst("${.MAKE.DEPENDFILE}",
- SCOPE_CMDLINE, VARE_WANTRES, &makeDependfile);
+ makeDependfile = Var_Subst("${.MAKE.DEPENDFILE}",
+ SCOPE_CMDLINE, VARE_WANTRES);
if (makeDependfile[0] != '\0') {
/* TODO: handle errors */
doing_depend = true;
@@ -1593,10 +1537,6 @@ main_PrepareMaking(void)
InitMaxJobs();
- /*
- * Be compatible if the user did not specify -j and did not explicitly
- * turn compatibility on.
- */
if (!opts.compatMake && !forceJobs)
opts.compatMake = true;
@@ -1648,16 +1588,11 @@ static void
main_CleanUp(void)
{
#ifdef CLEANUP
- Lst_DoneCall(&opts.variables, free);
- /*
- * Don't free the actual strings from opts.makefiles, they may be
- * used in GNodes.
- */
- Lst_Done(&opts.makefiles);
- Lst_DoneCall(&opts.create, free);
+ Lst_DoneFree(&opts.variables);
+ Lst_DoneFree(&opts.makefiles);
+ Lst_DoneFree(&opts.create);
#endif
- /* print the graph now it's been processed if the user requested it */
if (DEBUG(GRAPH2))
Targ_PrintGraph(2);
@@ -1679,13 +1614,14 @@ main_CleanUp(void)
Dir_End();
Job_End();
Trace_End();
+ Str_Intern_End();
}
/* Determine the exit code. */
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;
}
@@ -1705,18 +1641,16 @@ main(int argc, char **argv)
/*
* Open and parse the given makefile, with all its side effects.
- *
- * Results:
- * 0 if ok. -1 if couldn't open file.
+ * Return false if the file could not be opened.
*/
-static int
+static bool
ReadMakefile(const char *fname)
{
int fd;
char *name, *path = NULL;
if (strcmp(fname, "-") == 0) {
- Parse_File(NULL /*stdin*/, -1);
+ Parse_File("(stdin)", -1);
Var_Set(SCOPE_INTERNAL, "MAKEFILE", "");
} else {
/* if we've chdir'd, rebuild the path name */
@@ -1745,13 +1679,13 @@ ReadMakefile(const char *fname)
name = Dir_FindFile(fname, parseIncPath);
if (name == NULL) {
SearchPath *sysInc = Lst_IsEmpty(&sysIncPath->dirs)
- ? defSysIncPath : sysIncPath;
+ ? defSysIncPath : sysIncPath;
name = Dir_FindFile(fname, sysInc);
}
if (name == NULL || (fd = open(name, O_RDONLY)) == -1) {
free(name);
free(path);
- return -1;
+ return false;
}
fname = name;
/*
@@ -1765,154 +1699,153 @@ found:
Parse_File(fname, fd);
}
free(path);
- return 0;
+ return true;
}
/*
- * Cmd_Exec --
- * Execute the command in cmd, and return the output of that command
- * in a string. In the output, newlines are replaced with spaces.
- *
- * Results:
- * A string containing the output of the command, or the empty string.
- * *errfmt returns a format string describing the command failure,
- * if any, using a single %s conversion specification.
- *
- * Side Effects:
- * The string must be freed by the caller.
+ * Execute the command in cmd, and return its output (only stdout, not
+ * stderr, possibly empty). In the output, replace newlines with spaces.
*/
char *
-Cmd_Exec(const char *cmd, const char **errfmt)
+Cmd_Exec(const char *cmd, char **error)
{
- const char *args[4]; /* Args for invoking the shell */
+ const char *args[4]; /* Arguments for invoking the shell */
int pipefds[2];
int cpid; /* Child PID */
int pid; /* PID from wait() */
int status; /* command exit status */
Buffer buf; /* buffer to store the result */
ssize_t bytes_read;
- char *res; /* result */
- size_t res_len;
- char *cp;
- int savederr; /* saved errno */
-
- *errfmt = NULL;
+ char *output;
+ char *p;
+ int saved_errno;
+ char cmd_file[MAXPATHLEN];
+ size_t cmd_len;
+ int cmd_fd = -1;
- if (shellName == NULL)
+ if (shellPath == NULL)
Shell_Init();
- /*
- * Set up arguments for shell
- */
+
+ cmd_len = strlen(cmd);
+ if (cmd_len > 1000) {
+ cmd_fd = mkTempFile(NULL, cmd_file, sizeof(cmd_file));
+ if (cmd_fd >= 0) {
+ ssize_t n;
+
+ n = write(cmd_fd, cmd, cmd_len);
+ close(cmd_fd);
+ if (n < (ssize_t)cmd_len) {
+ unlink(cmd_file);
+ cmd_fd = -1;
+ }
+ }
+ }
+
args[0] = shellName;
- args[1] = "-c";
- args[2] = cmd;
- args[3] = NULL;
+ if (cmd_fd >= 0) {
+ args[1] = cmd_file;
+ args[2] = NULL;
+ } else {
+ cmd_file[0] = '\0';
+ args[1] = "-c";
+ args[2] = cmd;
+ args[3] = NULL;
+ }
+ DEBUG1(VAR, "Capturing the output of command \"%s\"\n", cmd);
- /*
- * Open a pipe for fetching its output
- */
if (pipe(pipefds) == -1) {
- *errfmt = "Couldn't create pipe for \"%s\"";
- goto bad;
+ *error = str_concat3(
+ "Couldn't create pipe for \"", cmd, "\"");
+ return bmake_strdup("");
}
- Var_ReexportVars();
+ Var_ReexportVars(SCOPE_GLOBAL);
- /*
- * Fork
- */
switch (cpid = vfork()) {
case 0:
- (void)close(pipefds[0]); /* Close input side of pipe */
-
- /*
- * Duplicate the output stream to the shell's output, then
- * shut the extra thing down. Note we don't fetch the error
- * stream...why not? Why?
- */
- (void)dup2(pipefds[1], 1);
+ (void)close(pipefds[0]);
+ (void)dup2(pipefds[1], STDOUT_FILENO);
(void)close(pipefds[1]);
(void)execv(shellPath, UNCONST(args));
_exit(1);
- /*NOTREACHED*/
+ /* NOTREACHED */
case -1:
- *errfmt = "Couldn't exec \"%s\"";
- goto bad;
-
- default:
- (void)close(pipefds[1]); /* No need for the writing half */
-
- savederr = 0;
- Buf_Init(&buf);
-
- do {
- char result[BUFSIZ];
- bytes_read = read(pipefds[0], result, sizeof result);
- if (bytes_read > 0)
- Buf_AddBytes(&buf, result, (size_t)bytes_read);
- } while (bytes_read > 0 ||
- (bytes_read == -1 && errno == EINTR));
- if (bytes_read == -1)
- savederr = errno;
-
- (void)close(pipefds[0]); /* Close the input side of the pipe. */
-
- /* Wait for the process to exit. */
- while ((pid = waitpid(cpid, &status, 0)) != cpid && pid >= 0)
- JobReapChild(pid, status, false);
-
- res_len = buf.len;
- res = Buf_DoneData(&buf);
-
- if (savederr != 0)
- *errfmt = "Couldn't read shell's output for \"%s\"";
-
- if (WIFSIGNALED(status))
- *errfmt = "\"%s\" exited on a signal";
- else if (WEXITSTATUS(status) != 0)
- *errfmt = "\"%s\" returned non-zero status";
-
- /* Convert newlines to spaces. A final newline is just stripped */
- if (res_len > 0 && res[res_len - 1] == '\n')
- res[res_len - 1] = '\0';
- for (cp = res; *cp != '\0'; cp++)
- if (*cp == '\n')
- *cp = ' ';
- break;
+ *error = str_concat3("Couldn't exec \"", cmd, "\"");
+ return bmake_strdup("");
}
- return res;
-bad:
- return bmake_strdup("");
+
+ (void)close(pipefds[1]); /* No need for the writing half */
+
+ saved_errno = 0;
+ Buf_Init(&buf);
+
+ do {
+ char result[BUFSIZ];
+ bytes_read = read(pipefds[0], result, sizeof result);
+ if (bytes_read > 0)
+ Buf_AddBytes(&buf, result, (size_t)bytes_read);
+ } while (bytes_read > 0 || (bytes_read == -1 && errno == EINTR));
+ if (bytes_read == -1)
+ saved_errno = errno;
+
+ (void)close(pipefds[0]); /* Close the input side of the pipe. */
+
+ while ((pid = waitpid(cpid, &status, 0)) != cpid && pid >= 0)
+ JobReapChild(pid, status, false);
+
+ if (Buf_EndsWith(&buf, '\n'))
+ buf.data[buf.len - 1] = '\0';
+
+ output = Buf_DoneData(&buf);
+ for (p = output; *p != '\0'; p++)
+ if (*p == '\n')
+ *p = ' ';
+
+ if (WIFSIGNALED(status))
+ *error = str_concat3("\"", cmd, "\" exited on a signal");
+ else if (WEXITSTATUS(status) != 0)
+ *error = str_concat3(
+ "\"", cmd, "\" returned non-zero status");
+ else if (saved_errno != 0)
+ *error = str_concat3(
+ "Couldn't read shell's output for \"", cmd, "\"");
+ else
+ *error = NULL;
+ if (cmd_file[0] != '\0')
+ unlink(cmd_file);
+ return output;
}
/*
* Print a printf-style error message.
*
- * In default mode, this error message has no consequences, in particular it
- * does not affect the exit status. Only in lint mode (-dL) it does.
+ * In default mode, this error message has no consequences, for compatibility
+ * reasons, in particular it does not affect the exit status. Only in lint
+ * mode (-dL) it does.
*/
void
Error(const char *fmt, ...)
{
va_list ap;
- FILE *err_file;
+ FILE *f;
- err_file = opts.debug_file;
- if (err_file == stdout)
- err_file = stderr;
+ f = opts.debug_file;
+ if (f == stdout)
+ f = stderr;
(void)fflush(stdout);
+
for (;;) {
+ fprintf(f, "%s: ", progname);
va_start(ap, fmt);
- fprintf(err_file, "%s: ", progname);
- (void)vfprintf(err_file, fmt, ap);
+ (void)vfprintf(f, fmt, ap);
va_end(ap);
- (void)fprintf(err_file, "\n");
- (void)fflush(err_file);
- if (err_file == stderr)
+ (void)fprintf(f, "\n");
+ (void)fflush(f);
+ if (f == stderr)
break;
- err_file = stderr;
+ f = stderr;
}
main_errors++;
}
@@ -1933,13 +1866,15 @@ Fatal(const char *fmt, ...)
Job_Wait();
(void)fflush(stdout);
+ fprintf(stderr, "%s: ", progname);
va_start(ap, fmt);
(void)vfprintf(stderr, fmt, ap);
va_end(ap);
(void)fprintf(stderr, "\n");
(void)fflush(stderr);
+ PrintStackTrace(true);
- PrintOnError(NULL, NULL);
+ PrintOnError(NULL, "\n");
if (DEBUG(GRAPH2) || DEBUG(GRAPH3))
Targ_PrintGraph(2);
@@ -1956,15 +1891,15 @@ Punt(const char *fmt, ...)
{
va_list ap;
- va_start(ap, fmt);
(void)fflush(stdout);
(void)fprintf(stderr, "%s: ", progname);
+ va_start(ap, fmt);
(void)vfprintf(stderr, fmt, ap);
va_end(ap);
(void)fprintf(stderr, "\n");
(void)fflush(stderr);
- PrintOnError(NULL, NULL);
+ PrintOnError(NULL, "\n");
DieHorribly();
}
@@ -1994,12 +1929,8 @@ Finish(int errs)
Fatal("%d error%s", errs, errs == 1 ? "" : "s");
}
-/*
- * eunlink --
- * Remove a file carefully, avoiding directories.
- */
int
-eunlink(const char *file)
+unlink_file(const char *file)
{
struct stat st;
@@ -2007,6 +1938,10 @@ eunlink(const char *file)
return -1;
if (S_ISDIR(st.st_mode)) {
+ /*
+ * POSIX says for unlink: "The path argument shall not name
+ * a directory unless [...]".
+ */
errno = EISDIR;
return -1;
}
@@ -2020,6 +1955,7 @@ write_all(int fd, const void *data, size_t n)
while (n > 0) {
ssize_t written = write(fd, mem, n);
+ /* XXX: Should this EAGAIN be EINTR? */
if (written == -1 && errno == EAGAIN)
continue;
if (written == -1)
@@ -2029,10 +1965,7 @@ write_all(int fd, const void *data, size_t n)
}
}
-/*
- * execDie --
- * Print why exec failed, avoiding stdio.
- */
+/* Print why exec failed, avoiding stdio. */
void MAKE_ATTR_DEAD
execDie(const char *af, const char *av)
{
@@ -2054,28 +1987,29 @@ execDie(const char *af, const char *av)
_exit(1);
}
-/* purge any relative paths */
static void
purge_relative_cached_realpaths(void)
{
- HashEntry *he, *nhe;
+ HashEntry *he, *next;
HashIter hi;
HashIter_Init(&hi, &cached_realpaths);
he = HashIter_Next(&hi);
while (he != NULL) {
- nhe = HashIter_Next(&hi);
+ next = HashIter_Next(&hi);
if (he->key[0] != '/') {
DEBUG1(DIR, "cached_realpath: purging %s\n", he->key);
HashTable_DeleteEntry(&cached_realpaths, he);
- /* XXX: What about the allocated he->value? Either
- * free them or document why they cannot be freed. */
+ /*
+ * XXX: What about the allocated he->value? Either
+ * free them or document why they cannot be freed.
+ */
}
- he = nhe;
+ he = next;
}
}
-char *
+const char *
cached_realpath(const char *pathname, char *resolved)
{
const char *rp;
@@ -2127,10 +2061,13 @@ static void
SetErrorVars(GNode *gn)
{
StringListNode *ln;
+ char sts[16];
/*
* We can print this even if there is no .ERROR target.
*/
+ snprintf(sts, sizeof(sts), "%d", gn->exit_status);
+ Global_Set(".ERROR_EXIT", sts);
Global_Set(".ERROR_TARGET", gn->name);
Global_Delete(".ERROR_CMD");
@@ -2160,9 +2097,7 @@ PrintOnError(GNode *gn, const char *msg)
if (errorNode != NULL)
return; /* we've been here! */
- if (msg != NULL)
- printf("%s", msg);
- printf("\n%s: stopped in %s\n", progname, curdir);
+ printf("%s%s: stopped in %s\n", msg, progname, curdir);
/* we generally want to keep quiet if a sub-make died */
if (shouldDieQuietly(gn, -1))
@@ -2172,9 +2107,9 @@ PrintOnError(GNode *gn, const char *msg)
SetErrorVars(gn);
{
- char *errorVarsValues;
- (void)Var_Subst("${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}",
- SCOPE_GLOBAL, VARE_WANTRES, &errorVarsValues);
+ char *errorVarsValues = Var_Subst(
+ "${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}",
+ SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
printf("%s", errorVarsValues);
free(errorVarsValues);
@@ -2196,23 +2131,18 @@ void
Main_ExportMAKEFLAGS(bool first)
{
static bool once = true;
- const char *expr;
- char *s;
+ char *flags;
if (once != first)
return;
once = false;
- expr = "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}";
- (void)Var_Subst(expr, SCOPE_CMDLINE, VARE_WANTRES, &s);
+ flags = Var_Subst(
+ "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}",
+ SCOPE_CMDLINE, VARE_WANTRES);
/* TODO: handle errors */
- if (s[0] != '\0') {
-#ifdef POSIX
- setenv("MAKEFLAGS", s, 1);
-#else
- setenv("MAKE", s, 1);
-#endif
- }
+ if (flags[0] != '\0')
+ setenv("MAKEFLAGS", flags, 1);
}
char *
@@ -2224,9 +2154,9 @@ getTmpdir(void)
if (tmpdir != NULL)
return tmpdir;
- /* Honor $TMPDIR but only if it is valid. Ensure it ends with '/'. */
- (void)Var_Subst("${TMPDIR:tA:U" _PATH_TMP ":S,/$,,W}/",
- SCOPE_GLOBAL, VARE_WANTRES, &tmpdir);
+ /* Honor $TMPDIR if it is valid, strip a trailing '/'. */
+ tmpdir = Var_Subst("${TMPDIR:tA:U" _PATH_TMP ":S,/$,,W}/",
+ SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
if (stat(tmpdir, &st) < 0 || !S_ISDIR(st.st_mode)) {
@@ -2238,7 +2168,7 @@ getTmpdir(void)
/*
* Create and open a temp file using "pattern".
- * If out_fname is provided, set it to a copy of the filename created.
+ * If tfile is provided, set it to a copy of the filename created.
* Otherwise unlink the file once open.
*/
int
@@ -2253,20 +2183,21 @@ mkTempFile(const char *pattern, char *tfile, size_t tfile_sz)
if (tmpdir == NULL)
tmpdir = getTmpdir();
if (tfile == NULL) {
- tfile = tbuf;
- tfile_sz = sizeof tbuf;
+ tfile = tbuf;
+ tfile_sz = sizeof tbuf;
}
- if (pattern[0] == '/') {
+
+ if (pattern[0] == '/')
snprintf(tfile, tfile_sz, "%s", pattern);
- } else {
+ else
snprintf(tfile, tfile_sz, "%s%s", tmpdir, pattern);
- }
+
if ((fd = mkstemp(tfile)) < 0)
Punt("Could not create temporary file %s: %s", tfile,
strerror(errno));
- if (tfile == tbuf) {
+ if (tfile == tbuf)
unlink(tfile); /* we just want the descriptor */
- }
+
return fd;
}
diff --git a/contrib/bmake/make-bootstrap.sh.in b/contrib/bmake/make-bootstrap.sh.in
index 0ecce455da74..d4f3c0a39c89 100755
--- a/contrib/bmake/make-bootstrap.sh.in
+++ b/contrib/bmake/make-bootstrap.sh.in
@@ -2,6 +2,7 @@
set -e
+prefix=@prefix@
srcdir=@srcdir@
DEFAULT_SYS_PATH="@default_sys_path@"
@@ -18,6 +19,7 @@ MAKE_VERSION=@_MAKE_VERSION@
MDEFS="-DMAKE_VERSION=\"$MAKE_VERSION\" \
-D@force_machine@MACHINE=\"@machine@\" \
-D@force_machine_arch@MACHINE_ARCH=\"@machine_arch@\" \
+-D@force_make_os@MAKE_OS=\"@make_os@\" \
-D_PATH_DEFSYSPATH=\"${DEFAULT_SYS_PATH}\""
@@ -59,7 +61,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-conf.h b/contrib/bmake/make-conf.h
index dcf5b3162dea..4bba07a405e4 100644
--- a/contrib/bmake/make-conf.h
+++ b/contrib/bmake/make-conf.h
@@ -1,4 +1,4 @@
-/* $NetBSD: config.h,v 1.28 2020/12/11 22:53:08 rillig Exp $ */
+/* $NetBSD: config.h,v 1.29 2024/02/07 06:43:02 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -73,20 +73,6 @@
*/
/*
- * INCLUDES
- * LIBRARIES
- * These control the handling of the .INCLUDES and .LIBS variables.
- *
- * If INCLUDES is defined, the .INCLUDES variable will be filled
- * from the search paths of those suffixes which are marked by
- * .INCLUDES dependency lines. Similarly for LIBRARIES and .LIBS.
- *
- * See varname-dot-include.mk and varname-dot-libs.mk for more details.
- */
-#define INCLUDES
-#define LIBRARIES
-
-/*
* LIBSUFF
* Is the suffix used to denote libraries and is used by the Suff module
* to find the search path on which to seek any -l<xx> targets.
@@ -108,40 +94,6 @@
*/
#define RECHECK
-/*
- * POSIX
- * Adhere to the POSIX 1003.2 draft for the make(1) program.
- * - Use MAKEFLAGS instead of MAKE to pick arguments from the
- * environment.
- */
-#define POSIX
-
-/*
- * SYSVINCLUDE
- * Recognize system V like include directives [include "filename"]
- * (required by POSIX 2018)
- * SYSVVARSUB
- * Recognize system V like ${VAR:x=y} variable substitutions
- * (required by POSIX 2018)
- */
-#define SYSVINCLUDE
-#define SYSVVARSUB
-
-/*
- * GMAKEEXPORT
- * Recognize gmake like variable export directives [export <VAR>=<VALUE>]
- */
-#define GMAKEEXPORT
-
-/*
- * SUNSHCMD
- * Recognize SunOS and Solaris:
- * VAR :sh= CMD # Assign VAR to the command substitution of CMD
- * ${VAR:sh} # Return the command substitution of the value
- * # of ${VAR}
- */
-#define SUNSHCMD
-
#if defined(MAKE_NATIVE) && !defined(__ELF__)
# ifndef RANLIBMAG
# define RANLIBMAG "__.SYMDEF"
diff --git a/contrib/bmake/make.1 b/contrib/bmake/make.1
index b50a3b4a74b9..069c115ecb8b 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.375 2024/03/10 02:53:37 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 March 9, 2024
.Dt MAKE 1
.Os
.Sh NAME
@@ -49,8 +49,8 @@
.Op Fl T Ar file
.Op Fl V Ar variable
.Op Fl v Ar variable
-.Op Ar variable=value
-.Op Ar target ...
+.Op Ar variable\| Ns Cm \&= Ns Ar value
+.Op Ar target No ...
.Sh DESCRIPTION
.Nm
is a program designed to simplify the maintenance of other programs.
@@ -58,34 +58,35 @@ Its input is a list of specifications as to the files upon which programs
and other files depend.
If no
.Fl f Ar makefile
-makefile option is given,
+option is given,
.Nm
-will try to open
-.Ql Pa makefile
+tries to open
+.Sq Pa makefile
then
-.Ql Pa Makefile
+.Sq Pa Makefile
in order to find the specifications.
If the file
-.Ql Pa .depend
-exists, it is read (see
-.Xr mkdep 1 ) .
+.Sq Pa .depend
+exists, it is read, see
+.Xr mkdep 1 .
.Pp
This manual page is intended as a reference document only.
For a more thorough description of
.Nm
and makefiles, please refer to
-.%T "PMake \- A Tutorial" .
+.%T "PMake \- A Tutorial"
+(from 1993).
.Pp
.Nm
-will prepend the contents of the
-.Va MAKEFLAGS
+prepends the contents of the
+.Ev MAKEFLAGS
environment variable to the command line arguments before parsing them.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl B
Try to be backwards compatible by executing a single shell per command and
-by executing the commands to make the sources of a dependency line in sequence.
+by making the sources of a dependency line in sequence.
.It Fl C Ar directory
Change to
.Ar directory
@@ -100,92 +101,97 @@ is equivalent to
Define
.Ar variable
to be 1, in the global scope.
-.It Fl d Ar [-]flags
+.It Fl d Oo Cm \- Oc Ns Ar flags
Turn on debugging, and specify which portions of
.Nm
are to print debugging information.
Unless the flags are preceded by
-.Ql \-
+.Ql \- ,
they are added to the
-.Va MAKEFLAGS
-environment variable and will be processed by any child make processes.
+.Ev MAKEFLAGS
+environment variable and are passed on to any child make processes.
By default, debugging information is printed to standard error,
but this can be changed using the
-.Ar F
+.Cm F
debugging flag.
The debugging output is always unbuffered; in addition, if debugging
is enabled but debugging output is not directed to standard output,
-then the standard output is line buffered.
-.Ar Flags
-is one or more of the following:
+the standard output is line buffered.
+The available
+.Ar flags
+are:
.Bl -tag -width Ds
-.It Ar A
+.It Cm A
Print all possible debugging information;
equivalent to specifying all of the debugging flags.
-.It Ar a
+.It Cm a
Print debugging information about archive searching and caching.
-.It Ar C
-Print debugging information about current working directory.
-.It Ar c
+.It Cm C
+Print debugging information about the current working directory.
+.It Cm c
Print debugging information about conditional evaluation.
-.It Ar d
+.It Cm d
Print debugging information about directory searching and caching.
-.It Ar e
+.It Cm e
Print debugging information about failed commands and targets.
-.It Ar F Ns Oo Sy \&+ Oc Ns Ar filename
+.It Cm F Ns Oo Cm \&+ Oc Ns Ar filename
Specify where debugging output is written.
This must be the last flag, because it consumes the remainder of
the argument.
If the character immediately after the
-.Ql F
+.Cm F
flag is
.Ql \&+ ,
-then the file will be opened in append mode;
-otherwise the file will be overwritten.
+the file is opened in append mode;
+otherwise the file is overwritten.
If the file name is
.Ql stdout
or
-.Ql stderr
-then debugging output will be written to the
-standard output or standard error output file descriptors respectively
-(and the
+.Ql stderr ,
+debugging output is written to the standard output or standard error output
+respectively (and the
.Ql \&+
option has no effect).
-Otherwise, the output will be written to the named file.
-If the file name ends
-.Ql .%d
-then the
+Otherwise, the output is written to the named file.
+If the file name ends with
+.Ql .%d ,
+the
.Ql %d
is replaced by the pid.
-.It Ar f
+.It Cm f
Print debugging information about loop evaluation.
-.It Ar "g1"
+.It Cm g1
Print the input graph before making anything.
-.It Ar "g2"
+.It Cm g2
Print the input graph after making everything, or before exiting
on error.
-.It Ar "g3"
+.It Cm g3
Print the input graph before exiting on error.
-.It Ar h
+.It Cm h
Print debugging information about hash table operations.
-.It Ar j
+.It Cm j
Print debugging information about running multiple shells.
-.It Ar L
+.It Cm L
Turn on lint checks.
-This will throw errors for variable assignments that do not parse
-correctly, at the time of assignment so the file and line number
-are available.
-.It Ar l
+This throws errors for variable assignments that do not parse correctly,
+at the time of assignment, so the file and line number are available.
+.It Cm l
Print commands in Makefiles regardless of whether or not they are prefixed by
.Ql @
-or other "quiet" flags.
-Also known as "loud" behavior.
-.It Ar M
-Print debugging information about "meta" mode decisions about targets.
-.It Ar m
+or other
+.Dq quiet
+flags.
+Also known as
+.Dq loud
+behavior.
+.It Cm M
+Print debugging information about
+.Dq meta
+mode decisions about targets.
+.It Cm m
Print debugging information about making targets, including modification
dates.
-.It Ar n
+.It Cm n
Don't delete the temporary command scripts created when running commands.
These temporary scripts are created in the directory
referred to by the
@@ -205,35 +211,36 @@ This can create many files in
or
.Pa /tmp ,
so use with care.
-.It Ar p
+.It Cm p
Print debugging information about makefile parsing.
-.It Ar s
+.It Cm s
Print debugging information about suffix-transformation rules.
-.It Ar t
+.It Cm t
Print debugging information about target list maintenance.
-.It Ar V
+.It Cm V
Force the
.Fl V
-option to print raw values of variables, overriding the default behavior
-set via
+option to print raw values of variables,
+overriding the default behavior set via
.Va .MAKE.EXPAND_VARIABLES .
-.It Ar v
-Print debugging information about variable assignment.
-.It Ar x
+.It Cm v
+Print debugging information about variable assignment and expansion.
+.It Cm x
Run shell commands with
.Fl x
so the actual commands are printed as they are executed.
.El
.It Fl e
-Specify that environment variables override macro assignments within
-makefiles.
+Let environment variables override global variables within makefiles.
.It Fl f Ar makefile
Specify a makefile to read instead of the default
-.Ql Pa makefile .
+.Pa makefile
+or
+.Pa Makefile .
If
.Ar makefile
is
-.Ql Fl ,
+.Ql \&- ,
standard input is read.
Multiple makefiles may be specified, and are read in the order specified.
.It Fl I Ar directory
@@ -244,7 +251,7 @@ option) is automatically included as part of this list.
.It Fl i
Ignore non-zero exit of shell commands in the makefile.
Equivalent to specifying
-.Ql Fl
+.Ql \&-
before each command line in the makefile.
.It Fl J Ar private
This option should
@@ -252,7 +259,7 @@ This option should
be specified by the user.
.Pp
When the
-.Ar j
+.Fl j
option is in use in a recursive build, this option is passed by a make
to child makes to allow all the make processes in the build to
cooperate to avoid overloading the system.
@@ -260,11 +267,19 @@ cooperate to avoid overloading the system.
Specify the maximum number of jobs that
.Nm
may have running at any one time.
-The value is saved in
+If
+.Ar max_jobs
+is a floating point number, or ends with
+.Ql C ,
+then the value is multiplied by the number of CPUs reported online by
+.Xr sysconf 3 .
+The value of
+.Ar max_jobs
+is saved in
.Va .MAKE.JOBS .
Turns compatibility mode off, unless the
-.Ar B
-flag is also specified.
+.Fl B
+option is also specified.
When compatibility mode is off, all commands associated with a
target are executed in a single shell invocation as opposed to the
traditional one shell invocation per line.
@@ -273,60 +288,72 @@ command invocation and then expect to start with a fresh environment
on the next line.
It is more efficient to correct the scripts rather than turn backwards
compatibility on.
+.Pp
+A job token pool with
+.Ar max_jobs
+tokens is used to control the total number of jobs running.
+Each instance of
+.Nm
+will wait for a token from the pool before running a new job.
.It Fl k
Continue processing after errors are encountered, but only on those targets
that do not depend on the target whose creation caused the error.
.It Fl m Ar directory
-Specify a directory in which to search for sys.mk and makefiles included
-via the
+Specify a directory in which to search for
+.Pa sys.mk
+and makefiles included via the
.Li \&< Ns Ar file Ns Li \&> Ns -style
include statement.
The
.Fl m
option can be used multiple times to form a search path.
-This path will override the default system include path: /usr/share/mk.
-Furthermore the system include path will be appended to the search path used
-for
+This path overrides the default system include path
+.Pa /usr/share/mk .
+Furthermore, the system include path is appended to the search path used for
.Li \*q Ns Ar file Ns Li \*q Ns -style
include statements (see the
.Fl I
option).
+The system include path can be referenced via the read-only variable
+.Va .SYSPATH .
.Pp
-If a file or directory name in the
+If a directory name in the
.Fl m
argument (or the
.Ev MAKESYSPATH
environment variable) starts with the string
-.Qq \&.../
-then
+.Ql \&.../ ,
.Nm
-will search for the specified file or directory named in the remaining part
+searches for the specified file or directory named in the remaining part
of the argument string.
-The search starts with the current directory of
-the Makefile and then works upward towards the root of the file system.
-If the search is successful, then the resulting directory replaces the
-.Qq \&.../
+The search starts with the current directory
+and then works upward towards the root of the file system.
+If the search is successful, the resulting directory replaces the
+.Ql \&.../
specification in the
.Fl m
argument.
-If used, this feature allows
+This feature allows
.Nm
-to easily search in the current source tree for customized sys.mk files
-(e.g., by using
-.Qq \&.../mk/sys.mk
+to easily search in the current source tree for customized
+.Pa sys.mk
+files (e.g., by using
+.Ql \&.../mk/sys.mk
as an argument).
.It Fl n
Display the commands that would have been executed, but do not
-actually execute them unless the target depends on the .MAKE special
-source (see below) or the command is prefixed with
-.Ql Ic + .
+actually execute them unless the target depends on the
+.Va .MAKE
+special source (see below) or the command is prefixed with
+.Sq Cm + .
.It Fl N
-Display the commands which would have been executed, but do not
-actually execute any of them; useful for debugging top-level makefiles
+Display the commands that would have been executed,
+but do not actually execute any of them;
+useful for debugging top-level makefiles
without descending into subdirectories.
.It Fl q
-Do not execute any commands, but exit 0 if the specified targets are
-up-to-date and 1, otherwise.
+Do not execute any commands,
+instead exit 0 if the specified targets are up to date, and 1 otherwise.
.It Fl r
Do not use the built-in rules specified in the system makefile.
.It Fl S
@@ -336,7 +363,7 @@ This is the default behavior and the opposite of
.It Fl s
Do not echo any commands as they are executed.
Equivalent to specifying
-.Ql Ic @
+.Sq Ic @
before each command line in the makefile.
.It Fl T Ar tracefile
When used with the
@@ -353,24 +380,25 @@ Print the value of
.Ar variable .
Do not build any targets.
Multiple instances of this option may be specified;
-the variables will be printed one per line,
+the variables are printed one per line,
with a blank line for each null or undefined variable.
The value printed is extracted from the global scope after all
makefiles have been read.
+.Pp
By default, the raw variable contents (which may
include additional unexpanded variable references) are shown.
If
.Ar variable
contains a
-.Ql \&$
-then the value will be recursively expanded to its complete resultant
-text before printing.
-The expanded value will also be printed if
+.Ql \&$ ,
+it is not interpreted as a variable name but rather as an expression.
+Its value is expanded before printing.
+The value is also expanded before printing if
.Va .MAKE.EXPAND_VARIABLES
-is set to true and
-the
+is set to true and the
.Fl dV
option has not been used to override it.
+.Pp
Note that loop-local and target-local variables, as well as values
taken temporarily by global variables during makefile processing, are
not accessible via this option.
@@ -380,8 +408,13 @@ debug mode can be used to see these at the cost of generating
substantial extraneous output.
.It Fl v Ar variable
Like
+.Fl V ,
+but all printed variables are always expanded to their complete value.
+The last occurrence of
.Fl V
-but the variable is always expanded to its complete value.
+or
+.Fl v
+decides whether all variables are expanded or not.
.It Fl W
Treat any warnings during makefile parsing as errors.
.It Fl w
@@ -389,13 +422,12 @@ Print entering and leaving directory messages, pre and post processing.
.It Fl X
Don't export variables passed on the command line to the environment
individually.
-Variables passed on the command line are still exported
-via the
-.Va MAKEFLAGS
+Variables passed on the command line are still exported via the
+.Ev MAKEFLAGS
environment variable.
This option may be useful on systems which have a small limit on the
size of command arguments.
-.It Ar variable=value
+.It Ar variable\| Ns Cm \&= Ns Ar value
Set the value of the variable
.Ar variable
to
@@ -409,12 +441,12 @@ Variable assignments should follow options for POSIX compatibility
but no ordering is enforced.
.El
.Pp
-There are seven different types of lines in a makefile: file dependency
+There are several different types of lines in a makefile: dependency
specifications, shell commands, variable assignments, include statements,
-conditional directives, for loops, and comments.
+conditional directives, for loops, other directives, and comments.
.Pp
-In general, lines may be continued from one line to the next by ending
-them with a backslash
+Lines may be continued from one line to the next
+by ending them with a backslash
.Pq Ql \e .
The trailing newline character and initial whitespace on the following
line are compressed into a single space.
@@ -423,11 +455,10 @@ Dependency lines consist of one or more targets, an operator, and zero
or more sources.
This creates a relationship where the targets
.Dq depend
-on the sources
-and are customarily created from them.
-A target is considered out-of-date if it does not exist, or if its
-modification time is less than that of any of its sources.
-An out-of-date target will be re-created, but not until all sources
+on the sources and are customarily created from them.
+A target is considered out of date if it does not exist,
+or if its modification time is less than that of any of its sources.
+An out-of-date target is re-created, but not until all sources
have been examined and themselves re-created as needed.
Three operators may be used:
.Bl -tag -width flag
@@ -451,15 +482,16 @@ shell commands are run if the target is out of date with respect to
Thus, different groups of the attached shell commands may be run
depending on the circumstances.
Furthermore, unlike
-.Ic \&:,
+.Ic \&: ,
for dependency lines with no sources, the attached shell
commands are always run.
Also unlike
-.Ic \&:,
-the target will not be removed if
+.Ic \&: ,
+the target is not removed if
.Nm
is interrupted.
.El
+.Pp
All dependency lines mentioning a particular target must use the same
operator.
.Pp
@@ -474,206 +506,229 @@ The values
.Ql * ,
and
.Ql []
-may only be used as part of the final
-component of the target or source, and must be used to describe existing
-files.
+may only be used as part of the final component of the target or source,
+and only match existing files.
The value
.Ql {}
need not necessarily be used to describe existing files.
Expansion is in directory order, not alphabetically as done in the shell.
.Sh SHELL COMMANDS
-Each target may have associated with it one or more lines of shell
-commands, normally
-used to create the target.
+Each target may have associated with it one or more lines of shell commands,
+normally used to create the target.
Each of the lines in this script
.Em must
be preceded by a tab.
(For historical reasons, spaces are not accepted.)
-While targets can appear in many dependency lines if desired, by
-default only one of these rules may be followed by a creation
-script.
+While targets can occur in many dependency lines if desired,
+by default only one of these rules may be followed by a creation script.
If the
-.Ql Ic \&::
-operator is used, however, all rules may include scripts and the
-scripts are executed in the order found.
+.Sq Ic \&::
+operator is used, however, all rules may include scripts,
+and the respective scripts are executed in the order found.
.Pp
-Each line is treated as a separate shell command, unless the end of
-line is escaped with a backslash
-.Pq Ql \e
+Each line is treated as a separate shell command,
+unless the end of line is escaped with a backslash
+.Ql \e ,
in which case that line and the next are combined.
-.\" The escaped newline is retained and passed to the shell, which
-.\" normally ignores it.
-.\" However, the tab at the beginning of the following line is removed.
If the first characters of the command are any combination of
-.Ql Ic @ ,
-.Ql Ic + ,
+.Sq Ic @ ,
+.Sq Ic + ,
or
-.Ql Ic \- ,
+.Sq Ic \- ,
the command is treated specially.
-A
-.Ql Ic @
+.Bl -tag -offset indent -width indent
+.It Ic @
causes the command not to be echoed before it is executed.
-A
-.Ql Ic +
+.It Ic +
causes the command to be executed even when
.Fl n
is given.
-This is similar to the effect of the .MAKE special source,
+This is similar to the effect of the
+.Va .MAKE
+special source,
except that the effect can be limited to a single line of a script.
-A
-.Ql Ic \-
+.It Ic \-
in compatibility mode
causes any non-zero exit status of the command line to be ignored.
+.El
.Pp
When
.Nm
is run in jobs mode with
.Fl j Ar max_jobs ,
-the entire script for the target is fed to a
-single instance of the shell.
+the entire script for the target is fed to a single instance of the shell.
In compatibility (non-jobs) mode, each command is run in a separate process.
If the command contains any shell meta characters
-.Pq Ql #=|^(){};&<>*?[]:$`\e\en
-it will be passed to the shell; otherwise
+.Pq Ql #=|^(){};&<>*?[]:$`\e\en ,
+it is passed to the shell; otherwise
.Nm
-will attempt direct execution.
+attempts direct execution.
If a line starts with
-.Ql Ic \-
-and the shell has ErrCtl enabled then failure of the command line
-will be ignored as in compatibility mode.
+.Sq Ic \-
+and the shell has ErrCtl enabled,
+failure of the command line is ignored as in compatibility mode.
Otherwise
-.Ql Ic \-
+.Sq Ic \-
affects the entire job;
-the script will stop at the first command line that fails,
-but the target will not be deemed to have failed.
+the script stops at the first command line that fails,
+but the target is not deemed to have failed.
.Pp
Makefiles should be written so that the mode of
.Nm
operation does not change their behavior.
-For example, any command which needs to use
+For example, any command which uses
.Dq cd
or
.Dq chdir
-without potentially changing the directory for subsequent commands
+without the intention of changing the directory for subsequent commands
should be put in parentheses so it executes in a subshell.
-To force the use of one shell, escape the line breaks so as to make
+To force the use of a single shell, escape the line breaks so as to make
the whole script one command.
For example:
.Bd -literal -offset indent
avoid-chdir-side-effects:
- @echo Building $@ in `pwd`
+ @echo "Building $@ in $$(pwd)"
@(cd ${.CURDIR} && ${MAKE} $@)
- @echo Back in `pwd`
+ @echo "Back in $$(pwd)"
ensure-one-shell-regardless-of-mode:
- @echo Building $@ in `pwd`; \e
+ @echo "Building $@ in $$(pwd)"; \e
(cd ${.CURDIR} && ${MAKE} $@); \e
- echo Back in `pwd`
+ echo "Back in $$(pwd)"
.Ed
.Pp
Since
.Nm
-will
-.Xr chdir 2
-to
-.Ql Va .OBJDIR
-before executing any targets, each child process
-starts with that as its current working directory.
+changes the current working directory to
+.Sq Va .OBJDIR
+before executing any targets,
+each child process starts with that as its current working directory.
.Sh VARIABLE ASSIGNMENTS
-Variables in make are much like variables in the shell, and, by tradition,
-consist of all upper-case letters.
-.Ss Variable assignment modifiers
-The five operators that can be used to assign values to variables are as
-follows:
+Variables in make behave much like macros in the C preprocessor.
+.Pp
+Variable assignments have the form
+.Sq Ar NAME Ar op Ar value ,
+where:
+.Bl -tag -offset Ds -width Ds
+.It Ar NAME
+is a single-word variable name,
+consisting, by tradition, of all upper-case letters,
+.It Ar op
+is one of the variable assignment operators described below, and
+.It Ar value
+is interpreted according to the variable assignment operator.
+.El
+.Pp
+Whitespace around
+.Ar NAME ,
+.Ar op
+and
+.Ar value
+is discarded.
+.Ss Variable assignment operators
+The five operators that assign values to variables are:
.Bl -tag -width Ds
.It Ic \&=
Assign the value to the variable.
-Any previous value is overridden.
+Any previous value is overwritten.
.It Ic \&+=
-Append the value to the current value of the variable.
+Append the value to the current value of the variable,
+separating them by a single space.
.It Ic \&?=
Assign the value to the variable if it is not already defined.
.It Ic \&:=
-Assign with expansion, i.e. expand the value before assigning it
-to the variable.
-Normally, expansion is not done until the variable is referenced.
+Expand the value, then assign it to the variable.
+.Pp
.Em NOTE :
References to undefined variables are
.Em not
expanded.
This can cause problems when variable modifiers are used.
+.\" See var-op-expand.mk, the section with LATER and INDIRECT.
.It Ic \&!=
-Expand the value and pass it to the shell for execution and assign
-the result to the variable.
+Expand the value and pass it to the shell for execution,
+then assign the output from the child's standard output to the variable.
Any newlines in the result are replaced with spaces.
.El
+.Ss Expansion of variables
+In most contexts where variables are expanded,
+.Ql \&$$
+expands to a single dollar sign.
+In other contexts (most variable modifiers, string literals in conditions),
+.Ql \&\e$
+expands to a single dollar sign.
.Pp
-Any white-space before the assigned
-.Ar value
-is removed; if the value is being appended, a single space is inserted
-between the previous contents of the variable and the appended value.
-.Pp
-Variables are expanded by surrounding the variable name with either
-curly braces
-.Pq Ql {}
-or parentheses
-.Pq Ql ()
-and preceding it with
-a dollar sign
-.Pq Ql \&$ .
-If the variable name contains only a single letter, the surrounding
-braces or parentheses are not required.
+References to variables have the form
+.Cm \&${ Ns Ar name Ns Oo Ns Cm \&: Ns Ar modifiers Oc Ns Cm \&}
+or
+.Cm \&$( Ns Ar name Ns Oo Ns Cm \&: Ns Ar modifiers Oc Ns Cm \&) .
+If the variable name consists of only a single character
+and the expression contains no modifiers,
+the surrounding curly braces or parentheses are not required.
This shorter form is not recommended.
.Pp
-If the variable name contains a dollar, then the name itself is expanded first.
+If the variable name contains a dollar, the name itself is expanded first.
This allows almost arbitrary variable names, however names containing dollar,
-braces, parentheses, or whitespace are really best avoided!
+braces, parentheses or whitespace are really best avoided.
.Pp
-If the result of expanding a variable contains a dollar sign
-.Pq Ql \&$
-the string is expanded again.
+If the result of expanding a nested variable expression contains a dollar sign
+.Pq Ql \&$ ,
+the result is subject to further expansion.
.Pp
-Variable substitution occurs at three distinct times, depending on where
+Variable substitution occurs at four distinct times, depending on where
the variable is being used.
.Bl -enum
.It
Variables in dependency lines are expanded as the line is read.
.It
+Variables in conditionals are expanded individually,
+but only as far as necessary to determine the result of the conditional.
+.It
Variables in shell commands are expanded when the shell command is
executed.
.It
-.Dq .for
+.Ic .for
loop index variables are expanded on each loop iteration.
-Note that other variables are not expanded inside loops so
-the following example code:
+Note that other variables are not expanded when composing the body of a loop,
+so the following example code:
.Bd -literal -offset indent
-
-.Dv .for i in 1 2 3
+\&.for i in 1 2 3
a+= ${i}
j= ${i}
b+= ${j}
-.Dv .endfor
+\&.endfor
all:
@echo ${a}
@echo ${b}
-
.Ed
-will print:
+.Pp
+prints:
.Bd -literal -offset indent
1 2 3
3 3 3
-
.Ed
-Because while ${a} contains
-.Dq 1 2 3
-after the loop is executed, ${b}
+.Pp
+After the loop is executed:
+.Bl -tag -offset indent -width indent
+.It Va a
+contains
+.Ql ${:U1} ${:U2} ${:U3} ,
+which expands to
+.Ql 1 2 3 .
+.It Va j
+contains
+.Ql ${:U3} ,
+which expands to
+.Ql 3 .
+.It Va b
contains
-.Dq ${j} ${j} ${j}
+.Ql ${j} ${j} ${j} ,
which expands to
-.Dq 3 3 3
-since after the loop completes ${j} contains
-.Dq 3 .
+.Ql ${:U3} ${:U3} ${:U3}
+and further to
+.Ql 3 3 3 .
+.El
.El
.Ss Variable classes
The four different classes of variables (in order of increasing precedence)
@@ -691,55 +746,85 @@ Variables defined as part of the command line.
Variables that are defined specific to a certain target.
.El
.Pp
-Local variables are all built in and their values vary magically from
-target to target.
-It is not currently possible to define new local variables.
-The seven local variables are as follows:
-.Bl -tag -width ".ARCHIVE" -offset indent
+Local variables can be set on a dependency line, unless
+.Va .MAKE.TARGET_LOCAL_VARIABLES
+is set to
+.Ql false .
+The rest of the line
+(which already has had global variables expanded)
+is the variable value.
+For example:
+.Bd -literal -offset indent
+COMPILER_WRAPPERS= ccache distcc icecc
+
+${OBJS}: .MAKE.META.CMP_FILTER=${COMPILER_WRAPPERS:S,^,N,}
+.Ed
+.Pp
+Only the targets
+.Ql ${OBJS}
+are impacted by that filter (in
+.Dq meta
+mode) and
+simply enabling/disabling any of the compiler wrappers does not render all
+of those targets out-of-date.
+.Pp
+.Em NOTE :
+target-local variable assignments behave differently in that;
+.Bl -tag -width Ds -offset indent
+.It Ic \&+=
+Only appends to a previous local assignment
+for the same target and variable.
+.It Ic \&:=
+Is redundant with respect to global variables,
+which have already been expanded.
+.El
+.Pp
+The seven built-in local variables are:
+.Bl -tag -width ".Va .ARCHIVE" -offset indent
.It Va .ALLSRC
The list of all sources for this target; also known as
-.Ql Va \&> .
+.Sq Va \&> .
.It Va .ARCHIVE
The name of the archive file; also known as
-.Ql Va \&! .
+.Sq Va \&! .
.It Va .IMPSRC
In suffix-transformation rules, the name/path of the source from which the
target is to be transformed (the
.Dq implied
source); also known as
-.Ql Va \&< .
+.Sq Va \&< .
It is not defined in explicit rules.
.It Va .MEMBER
The name of the archive member; also known as
-.Ql Va % .
+.Sq Va % .
.It Va .OODATE
The list of sources for this target that were deemed out-of-date; also
known as
-.Ql Va \&? .
+.Sq Va \&? .
.It Va .PREFIX
-The file prefix of the target, containing only the file portion, no suffix
-or preceding directory components; also known as
-.Ql Va * .
-The suffix must be one of the known suffixes declared with
-.Ic .SUFFIXES
-or it will not be recognized.
+The name of the target with suffix (if declared in
+.Ic .SUFFIXES )
+removed; also known as
+.Sq Va * .
.It Va .TARGET
The name of the target; also known as
-.Ql Va @ .
+.Sq Va @ .
For compatibility with other makes this is an alias for
-.Ic .ARCHIVE
+.Va .ARCHIVE
in archive member rules.
.El
.Pp
The shorter forms
-.Ql ( Va > ,
-.Ql Va \&! ,
-.Ql Va < ,
-.Ql Va % ,
-.Ql Va \&? ,
-.Ql Va * ,
+.Po
+.Sq Va \&> ,
+.Sq Va \&! ,
+.Sq Va \&< ,
+.Sq Va \&% ,
+.Sq Va \&? ,
+.Sq Va \&* ,
and
-.Ql Va @ )
+.Sq Va \&@
+.Pc
are permitted for backward
compatibility with historical makefiles and legacy POSIX make and are
not recommended.
@@ -748,8 +833,8 @@ Variants of these variables with the punctuation followed immediately by
.Ql D
or
.Ql F ,
-e.g.
-.Ql Va $(@D) ,
+e.g.\&
+.Ql $(@D) ,
are legacy forms equivalent to using the
.Ql :H
and
@@ -762,52 +847,73 @@ makefiles and POSIX but are not recommended.
Four of the local variables may be used in sources on dependency lines
because they expand to the proper value for each target on the line.
These variables are
-.Ql Va .TARGET ,
-.Ql Va .PREFIX ,
-.Ql Va .ARCHIVE ,
+.Sq Va .TARGET ,
+.Sq Va .PREFIX ,
+.Sq Va .ARCHIVE ,
and
-.Ql Va .MEMBER .
+.Sq Va .MEMBER .
.Ss Additional built-in variables
In addition,
.Nm
sets or knows about the following variables:
-.Bl -tag -width .MAKEOVERRIDES
-.It Va \&$
-A single dollar sign
-.Ql \&$ ,
-i.e.
-.Ql \&$$
-expands to a single dollar
-sign.
+.Bl -tag
+.\" NB: This list is sorted case-insensitive, ignoring punctuation.
+.\" NB: To find all built-in variables in make's source code,
+.\" NB: search for Var_*, Global_*, SetVarObjdir, GetBooleanExpr,
+.\" NB: and the implementation of Var_SetWithFlags.
+.\" NB: Last synced on 2023-01-01.
.It Va .ALLTARGETS
-The list of all targets encountered in the Makefile.
-If evaluated during
-Makefile parsing, lists only those targets encountered thus far.
+The list of all targets encountered in the makefiles.
+If evaluated during makefile parsing,
+lists only those targets encountered thus far.
.It Va .CURDIR
A path to the directory where
.Nm
was executed.
Refer to the description of
-.Ql Ev PWD
+.Sq Va PWD
for more details.
+.It Va .ERROR_CMD
+Is used in error handling, see
+.Va MAKE_PRINT_VAR_ON_ERROR .
+.It Va .ERROR_CWD
+Is used in error handling, see
+.Va MAKE_PRINT_VAR_ON_ERROR .
+.It Va .ERROR_EXIT
+Is used in error handling, see
+.Va MAKE_PRINT_VAR_ON_ERROR .
+.It Va .ERROR_META_FILE
+Is used in error handling in
+.Dq meta
+mode, see
+.Va MAKE_PRINT_VAR_ON_ERROR .
+.It Va .ERROR_TARGET
+Is used in error handling, see
+.Va MAKE_PRINT_VAR_ON_ERROR .
.It Va .INCLUDEDFROMDIR
-The directory of the file this Makefile was included from.
+The directory of the file this makefile was included from.
.It Va .INCLUDEDFROMFILE
-The filename of the file this Makefile was included from.
-.It Ev MAKE
+The filename of the file this makefile was included from.
+.\" .INCLUDES is intentionally undocumented, as it is obsolete.
+.\" .LIBS is intentionally undocumented, as it is obsolete.
+.It Va MACHINE
+The machine hardware name, see
+.Xr uname 1 .
+.It Va MACHINE_ARCH
+The machine processor architecture name, see
+.Xr uname 1 .
+.It Va MAKE
The name that
.Nm
was executed with
.Pq Va argv[0] .
-For compatibility
-.Nm
-also sets
-.Va .MAKE
-with the same value.
+.It Va .MAKE
+The same as
+.Va MAKE ,
+for compatibility.
The preferred variable to use is the environment variable
.Ev MAKE
-because it is more compatible with other versions of
-.Nm
+because it is more compatible with other make variants
and cannot be confused with the special target with the same name.
.It Va .MAKE.ALWAYS_PASS_JOB_QUEUE
Tells
@@ -820,10 +926,16 @@ The default is
for backwards compatability with
.Fx 9.0
and earlier.
+.\" '.MAKE.cmd_filtered' is intentionally undocumented,
+.\" as it is an internal implementation detail.
.It Va .MAKE.DEPENDFILE
Names the makefile (default
-.Ql Pa .depend )
+.Sq Pa .depend )
from which generated dependencies are read.
+.It Va .MAKE.DIE_QUIETLY
+If set to
+.Ql true ,
+do not print error information at the end.
.It Va .MAKE.EXPAND_VARIABLES
A boolean that controls the default behavior of the
.Fl V
@@ -835,174 +947,236 @@ include additional unexpanded variable references) are shown.
.It Va .MAKE.EXPORTED
The list of variables exported by
.Nm .
-.It Va .MAKE.JOBS
-The argument to the
-.Fl j
-option.
+.It Va MAKEFILE
+The top-level makefile that is currently read,
+as given in the command line.
+.It Va .MAKEFLAGS
+The environment variable
+.Sq Ev MAKEFLAGS
+may contain anything that
+may be specified on
+.Nm Ns 's
+command line.
+Anything specified on
+.Nm Ns 's
+command line is appended to the
+.Va .MAKEFLAGS
+variable, which is then added to the environment for all programs that
+.Nm
+executes.
+.It Va .MAKE.GID
+The numeric group ID of the user running
+.Nm .
+It is read-only.
.It Va .MAKE.JOB.PREFIX
If
.Nm
is run with
-.Ar j
-then output for each target is prefixed with a token
-.Ql --- target ---
+.Fl j ,
+the output for each target is prefixed with a token
+.Dl --- Ar target Li ---
the first part of which can be controlled via
.Va .MAKE.JOB.PREFIX .
If
.Va .MAKE.JOB.PREFIX
is empty, no token is printed.
-.br
-For example:
-.Li .MAKE.JOB.PREFIX=${.newline}---${.MAKE:T}[${.MAKE.PID}]
+For example, setting
+.Va .MAKE.JOB.PREFIX
+to
+.Ql ${.newline}---${.MAKE:T}[${.MAKE.PID}]
would produce tokens like
-.Ql ---make[1234] target ---
+.Dl ---make[1234] Ar target Li ---
making it easier to track the degree of parallelism being achieved.
-.It Ev MAKEFLAGS
-The environment variable
-.Ql Ev MAKEFLAGS
-may contain anything that
-may be specified on
-.Nm Ns 's
-command line.
-Anything specified on
-.Nm Ns 's
-command line is appended to the
-.Ql Ev MAKEFLAGS
-variable which is then
-entered into the environment for all programs which
-.Nm
-executes.
+.It Va .MAKE.JOBS
+The argument to the
+.Fl j
+option.
+.It Va .MAKE.JOBS.C
+A read-only boolean that indicates whether the
+.Fl j
+option supports use of
+.Ql C .
.It Va .MAKE.LEVEL
The recursion depth of
.Nm .
-The initial instance of
+The top-level instance of
.Nm
-will be 0, and an incremented value is put into the environment
-to be seen by the next generation.
+has level 0, and each child make has its parent level plus 1.
This allows tests like:
.Li .if ${.MAKE.LEVEL} == 0
-to protect things which should only be evaluated in the initial instance of
+to protect things which should only be evaluated in the top-level instance of
+.Nm .
+.It Va .MAKE.LEVEL.ENV
+The name of the environment variable that stores the level of nested calls to
.Nm .
.It Va .MAKE.MAKEFILE_PREFERENCE
The ordered list of makefile names
(default
-.Ql Pa makefile ,
-.Ql Pa Makefile )
+.Sq Pa makefile ,
+.Sq Pa Makefile )
that
.Nm
-will look for.
+looks for.
.It Va .MAKE.MAKEFILES
The list of makefiles read by
.Nm ,
which is useful for tracking dependencies.
Each makefile is recorded only once, regardless of the number of times read.
+.It Va .MAKE.META.BAILIWICK
+In
+.Dq meta
+mode, provides a list of prefixes which
+match the directories controlled by
+.Nm .
+If a file that was generated outside of
+.Va .OBJDIR
+but within said bailiwick is missing,
+the current target is considered out-of-date.
+.It Va .MAKE.META.CMP_FILTER
+In
+.Dq meta
+mode, it can (very rarely!) be useful to filter command
+lines before comparison.
+This variable can be set to a set of modifiers that are applied to
+each line of the old and new command that differ, if the filtered
+commands still differ, the target is considered out-of-date.
+.It Va .MAKE.META.CREATED
+In
+.Dq meta
+mode, this variable contains a list of all the meta files
+updated.
+If not empty, it can be used to trigger processing of
+.Va .MAKE.META.FILES .
+.It Va .MAKE.META.FILES
+In
+.Dq meta
+mode, this variable contains a list of all the meta files
+used (updated or not).
+This list can be used to process the meta files to extract dependency
+information.
+.It Va .MAKE.META.IGNORE_FILTER
+Provides a list of variable modifiers to apply to each pathname.
+Ignore if the expansion is an empty string.
+.It Va .MAKE.META.IGNORE_PATHS
+Provides a list of path prefixes that should be ignored;
+because the contents are expected to change over time.
+The default list includes:
+.Sq Pa /dev /etc /proc /tmp /var/run /var/tmp
+.It Va .MAKE.META.IGNORE_PATTERNS
+Provides a list of patterns to match against pathnames.
+Ignore any that match.
+.It Va .MAKE.META.PREFIX
+Defines the message printed for each meta file updated in
+.Dq meta verbose
+mode.
+The default value is:
+.Dl Building ${.TARGET:H:tA}/${.TARGET:T}
.It Va .MAKE.MODE
Processed after reading all makefiles.
-Can affect the mode that
+Affects the mode that
.Nm
runs in.
-It can contain a number of keywords:
-.Bl -hang -width missing-filemon=bf.
-.It Pa compat
+It can contain these keywords:
+.Bl -tag -width indent
+.It Cm compat
Like
.Fl B ,
puts
.Nm
-into "compat" mode.
-.It Pa meta
+into
+.Dq compat
+mode.
+.It Cm meta
Puts
.Nm
-into "meta" mode, where meta files are created for each target
-to capture the command run, the output generated and if
+into
+.Dq meta
+mode, where meta files are created for each target
+to capture the command run, the output generated, and if
.Xr filemon 4
is available, the system calls which are of interest to
.Nm .
-The captured output can be very useful when diagnosing errors.
-.It Pa curdirOk= Ar bf
-Normally
+The captured output can be useful when diagnosing errors.
+.It Cm curdirOk= Ns Ar bf
+By default,
.Nm
-will not create .meta files in
-.Ql Va .CURDIR .
+does not create
+.Pa .meta
+files in
+.Sq Va .CURDIR .
This can be overridden by setting
-.Va bf
-to a value which represents True.
-.It Pa missing-meta= Ar bf
+.Ar bf
+to a value which represents true.
+.It Cm missing-meta= Ns Ar bf
If
-.Va bf
-is True, then a missing .meta file makes the target out-of-date.
-.It Pa missing-filemon= Ar bf
+.Ar bf
+is true, a missing
+.Pa .meta
+file makes the target out-of-date.
+.It Cm missing-filemon= Ns Ar bf
If
-.Va bf
-is True, then missing filemon data makes the target out-of-date.
-.It Pa nofilemon
+.Ar bf
+is true, missing filemon data makes the target out-of-date.
+.It Cm nofilemon
Do not use
.Xr filemon 4 .
-.It Pa env
+.It Cm env
For debugging, it can be useful to include the environment
-in the .meta file.
-.It Pa verbose
-If in "meta" mode, print a clue about the target being built.
+in the
+.Pa .meta
+file.
+.It Cm verbose
+If in
+.Dq meta
+mode, print a clue about the target being built.
This is useful if the build is otherwise running silently.
-The message printed the value of:
+The message printed is the expanded value of
.Va .MAKE.META.PREFIX .
-.It Pa ignore-cmd
+.It Cm ignore-cmd
Some makefiles have commands which are simply not stable.
This keyword causes them to be ignored for
-determining whether a target is out of date in "meta" mode.
+determining whether a target is out of date in
+.Dq meta
+mode.
See also
.Ic .NOMETA_CMP .
-.It Pa silent= Ar bf
+.It Cm silent= Ns Ar bf
If
-.Va bf
-is True, when a .meta file is created, mark the target
+.Ar bf
+is true, when a .meta file is created, mark the target
.Ic .SILENT .
+.It Cm randomize-targets
+In both compat and parallel mode, do not make the targets in the usual order,
+but instead randomize their order.
+This mode can be used to detect undeclared dependencies between files.
.El
-.It Va .MAKE.META.BAILIWICK
-In "meta" mode, provides a list of prefixes which
-match the directories controlled by
-.Nm .
-If a file that was generated outside of
-.Va .OBJDIR
-but within said bailiwick is missing,
-the current target is considered out-of-date.
-.It Va .MAKE.META.CREATED
-In "meta" mode, this variable contains a list of all the meta files
-updated.
-If not empty, it can be used to trigger processing of
-.Va .MAKE.META.FILES .
-.It Va .MAKE.META.FILES
-In "meta" mode, this variable contains a list of all the meta files
-used (updated or not).
-This list can be used to process the meta files to extract dependency
-information.
-.It Va .MAKE.META.IGNORE_PATHS
-Provides a list of path prefixes that should be ignored;
-because the contents are expected to change over time.
-The default list includes:
-.Ql Pa /dev /etc /proc /tmp /var/run /var/tmp
-.It Va .MAKE.META.IGNORE_PATTERNS
-Provides a list of patterns to match against pathnames.
-Ignore any that match.
-.It Va .MAKE.META.IGNORE_FILTER
-Provides a list of variable modifiers to apply to each pathname.
-Ignore if the expansion is an empty string.
-.It Va .MAKE.META.PREFIX
-Defines the message printed for each meta file updated in "meta verbose" mode.
-The default value is:
-.Dl Building ${.TARGET:H:tA}/${.TARGET:T}
+.It Va MAKEOBJDIR
+Used to create files in a separate directory, see
+.Va .OBJDIR .
+.It Va MAKE_OBJDIR_CHECK_WRITABLE
+Used to force a separate directory for the created files,
+even if that directory is not writable, see
+.Va .OBJDIR .
+.It Va MAKEOBJDIRPREFIX
+Used to create files in a separate directory, see
+.Va .OBJDIR .
+.It Va .MAKE.OS
+The name of the operating system, see
+.Xr uname 1 .
+It is read-only.
.It Va .MAKEOVERRIDES
This variable is used to record the names of variables assigned to
on the command line, so that they may be exported as part of
-.Ql Ev MAKEFLAGS .
+.Sq Ev MAKEFLAGS .
This behavior can be disabled by assigning an empty value to
-.Ql Va .MAKEOVERRIDES
+.Sq Va .MAKEOVERRIDES
within a makefile.
Extra variables can be exported from a makefile
by appending their names to
-.Ql Va .MAKEOVERRIDES .
-.Ql Ev MAKEFLAGS
+.Sq Va .MAKEOVERRIDES .
+.Sq Ev MAKEFLAGS
is re-exported whenever
-.Ql Va .MAKEOVERRIDES
+.Sq Va .MAKEOVERRIDES
is modified.
.It Va .MAKE.PATH_FILEMON
If
@@ -1012,13 +1186,38 @@ was built with
support, this is set to the path of the device node.
This allows makefiles to test for this support.
.It Va .MAKE.PID
-The process-id of
+The process ID of
.Nm .
+It is read-only.
.It Va .MAKE.PPID
-The parent process-id of
+The parent process ID of
.Nm .
+It is read-only.
+.It Va MAKE_PRINT_VAR_ON_ERROR
+When
+.Nm
+stops due to an error, it sets
+.Sq Va .ERROR_TARGET
+to the name of the target that failed,
+.Sq Va .ERROR_EXIT
+to the exit status of the failed target,
+.Sq Va .ERROR_CMD
+to the commands of the failed target,
+and in
+.Dq meta
+mode, it also sets
+.Sq Va .ERROR_CWD
+to the
+.Xr getcwd 3 ,
+and
+.Sq Va .ERROR_META_FILE
+to the path of the meta file (if any) describing the failed target.
+It then prints its name and the value of
+.Sq Va .CURDIR
+as well as the value of any variables named in
+.Sq Va MAKE_PRINT_VAR_ON_ERROR .
.It Va .MAKE.SAVE_DOLLARS
-value should be a boolean that controls whether
+If true,
.Ql $$
are preserved when doing
.Ql :=
@@ -1030,40 +1229,35 @@ If set to false,
becomes
.Ql $
per normal evaluation rules.
+.It Va .MAKE.TARGET_LOCAL_VARIABLES
+If set to
+.Ql false ,
+apparent variable assignments in dependency lines are
+treated as normal sources.
.It Va .MAKE.UID
-The user-id running
-.Nm .
-.It Va .MAKE.GID
-The group-id running
+The numeric ID of the user running
.Nm .
-.It Va MAKE_PRINT_VAR_ON_ERROR
-When
-.Nm
-stops due to an error, it sets
-.Ql Va .ERROR_TARGET
-to the name of the target that failed,
-.Ql Va .ERROR_CMD
-to the commands of the failed target,
-and in "meta" mode, it also sets
-.Ql Va .ERROR_CWD
-to the
-.Xr getcwd 3 ,
-and
-.Ql Va .ERROR_META_FILE
-to the path of the meta file (if any) describing the failed target.
-It then prints its name and the value of
-.Ql Va .CURDIR
-as well as the value of any variables named in
-.Ql Va MAKE_PRINT_VAR_ON_ERROR .
+It is read-only.
+.\" 'MAKE_VERSION' is intentionally undocumented
+.\" since it is only defined in the bmake distribution,
+.\" but not in NetBSD's native make.
+.\" '.meta.%d.lcwd' is intentionally undocumented
+.\" since it is an internal implementation detail.
+.\" '.meta.%d.ldir' is intentionally undocumented
+.\" since it is an internal implementation detail.
+.\" 'MFLAGS' is intentionally undocumented
+.\" since it is obsolete.
.It Va .newline
This variable is simply assigned a newline character as its value.
+It is read-only.
This allows expansions using the
.Cm \&:@
modifier to put a newline between
iterations of the loop rather than a space.
-For example, the printing of
-.Ql Va MAKE_PRINT_VAR_ON_ERROR
-could be done as ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}.
+For example, in case of an error,
+.Nm
+prints the variable names and their values using:
+.Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}
.It Va .OBJDIR
A path to the directory where the targets are built.
Its value is determined by trying to
@@ -1071,186 +1265,222 @@ Its value is determined by trying to
to the following directories in order and using the first match:
.Bl -enum
.It
-.Ev ${MAKEOBJDIRPREFIX}${.CURDIR}
+.Cm ${MAKEOBJDIRPREFIX} Ns Cm ${.CURDIR}
.Pp
(Only if
-.Ql Ev MAKEOBJDIRPREFIX
+.Sq Ev MAKEOBJDIRPREFIX
is set in the environment or on the command line.)
.It
-.Ev ${MAKEOBJDIR}
+.Cm ${MAKEOBJDIR}
.Pp
(Only if
-.Ql Ev MAKEOBJDIR
+.Sq Ev MAKEOBJDIR
is set in the environment or on the command line.)
.It
-.Ev ${.CURDIR} Ns Pa /obj. Ns Ev ${MACHINE}
+.Cm ${.CURDIR} Ns Pa /obj. Ns Cm ${MACHINE}
.It
-.Ev ${.CURDIR} Ns Pa /obj
+.Cm ${.CURDIR} Ns Pa /obj
.It
-.Pa /usr/obj/ Ns Ev ${.CURDIR}
+.Pa /usr/obj/ Ns Cm ${.CURDIR}
.It
-.Ev ${.CURDIR}
+.Cm ${.CURDIR}
.El
.Pp
-Variable expansion is performed on the value before it's used,
+Variable expansion is performed on the value before it is used,
so expressions such as
-.Dl ${.CURDIR:S,^/usr/src,/var/obj,}
+.Cm ${.CURDIR:S,^/usr/src,/var/obj,}
may be used.
This is especially useful with
-.Ql Ev MAKEOBJDIR .
+.Sq Ev MAKEOBJDIR .
.Pp
-.Ql Va .OBJDIR
+.Sq Va .OBJDIR
may be modified in the makefile via the special target
-.Ql Ic .OBJDIR .
+.Sq Ic .OBJDIR .
In all cases,
.Nm
-will
-.Xr chdir 2
-to the specified directory if it exists, and set
-.Ql Va .OBJDIR
+changes to the specified directory if it exists, and sets
+.Sq Va .OBJDIR
and
-.Ql Ev PWD
+.Sq Va PWD
to that directory before executing any targets.
.Pp
Except in the case of an explicit
-.Ql Ic .OBJDIR
+.Sq Ic .OBJDIR
target,
.Nm
-will check that the specified directory is writable and ignore it if not.
+checks that the specified directory is writable and ignores it if not.
This check can be skipped by setting the environment variable
-.Ql Ev MAKE_OBJDIR_CHECK_WRITABLE
-to "no".
-.
+.Sq Ev MAKE_OBJDIR_CHECK_WRITABLE
+to
+.Dq no .
.It Va .PARSEDIR
-A path to the directory of the current
-.Ql Pa Makefile
-being parsed.
+The directory name of the current makefile being parsed.
.It Va .PARSEFILE
-The basename of the current
-.Ql Pa Makefile
-being parsed.
+The basename of the current makefile being parsed.
This variable and
-.Ql Va .PARSEDIR
-are both set only while the
-.Ql Pa Makefiles
-are being parsed.
-If you want to retain their current values, assign them to a variable
-using assignment with expansion:
-.Pq Ql Cm \&:= .
+.Sq Va .PARSEDIR
+are both set only while the makefiles are being parsed.
+To retain their current values,
+assign them to a variable using assignment with expansion
+.Sq Cm \&:= .
.It Va .PATH
-A variable that represents the list of directories that
+The space-separated list of directories that
.Nm
-will search for files.
-The search list should be updated using the target
-.Ql Va .PATH
-rather than the variable.
-.It Ev PWD
+searches for files.
+To update this search list, use the special target
+.Sq Ic .PATH
+rather than modifying the variable directly.
+.It Va %POSIX
+Is set in POSIX mode, see the special
+.Ql Va .POSIX
+target.
+.\" XXX: There is no make variable named 'PWD',
+.\" XXX: make only reads and writes the environment variable 'PWD'.
+.It Va PWD
Alternate path to the current directory.
.Nm
normally sets
-.Ql Va .CURDIR
+.Sq Va .CURDIR
to the canonical path given by
.Xr getcwd 3 .
However, if the environment variable
-.Ql Ev PWD
-is set and gives a path to the current directory, then
+.Sq Ev PWD
+is set and gives a path to the current directory,
.Nm
sets
-.Ql Va .CURDIR
+.Sq Va .CURDIR
to the value of
-.Ql Ev PWD
+.Sq Ev PWD
instead.
This behavior is disabled if
-.Ql Ev MAKEOBJDIRPREFIX
+.Sq Ev MAKEOBJDIRPREFIX
is set or
-.Ql Ev MAKEOBJDIR
+.Sq Ev MAKEOBJDIR
contains a variable transform.
-.Ql Ev PWD
+.Sq Va PWD
is set to the value of
-.Ql Va .OBJDIR
+.Sq Va .OBJDIR
for all programs which
.Nm
executes.
-.It Ev .SHELL
+.It Va .SHELL
The pathname of the shell used to run target scripts.
It is read-only.
-.It Ev .TARGETS
+.It Va .SUFFIXES
+The list of known suffixes.
+It is read-only.
+.It Va .SYSPATH
+The space-separated list of directories that
+.Nm
+searches for makefiles, referred to as the system include path.
+To update this search list, use the special target
+.Sq Ic .SYSPATH
+rather than modifying the variable which is read-only.
+.It Va .TARGETS
The list of targets explicitly specified on the command line, if any.
-.It Ev VPATH
-Colon-separated
+.It Va VPATH
+The colon-separated
.Pq Dq \&:
-lists of directories that
+list of directories that
.Nm
-will search for files.
-The variable is supported for compatibility with old make programs only,
-use
-.Ql Va .PATH
+searches for files.
+This variable is supported for compatibility with old make programs only, use
+.Sq Va .PATH
instead.
.El
.Ss Variable modifiers
-Variable expansion may be modified to select or modify each word of the
-variable (where a
-.Dq word
-is white-space delimited sequence of characters).
-The general format of a variable expansion is as follows:
+The general format of a variable expansion is:
.Pp
-.Dl ${variable[:modifier[:...]]}
+.Sm off
+.D1 Ic \&${ Ar variable\| Oo Ic \&: Ar modifier\| Oo Ic \&: No ... Oc Oc Ic \&}
+.Sm on
.Pp
-Each modifier begins with a colon,
-which may be escaped with a backslash
-.Pq Ql \e .
+Each modifier begins with a colon.
+To escape a colon, precede it with a backslash
+.Ql \e .
.Pp
-A set of modifiers can be specified via a variable, as follows:
+A list of indirect modifiers can be specified via a variable, as follows:
.Pp
-.Dl modifier_variable=modifier[:...]
-.Dl ${variable:${modifier_variable}[:...]}
+.Bd -literal -offset indent
+.Ar modifier_variable\^ Li \&= Ar modifier Ns Oo Ic \&: Ns No ... Oc
+
+.Sm off
+.Ic \&${ Ar variable Ic \&:${ Ar modifier_variable Ic \&} Oo Ic \&: No ... Oc Ic \&}
+.Sm on
+.Ed
.Pp
-In this case the first modifier in the modifier_variable does not
-start with a colon, since that must appear in the referencing
-variable.
-If any of the modifiers in the modifier_variable contain a dollar sign
+In this case, the first modifier in the
+.Ar modifier_variable
+does not start with a colon,
+since that colon already occurs in the referencing variable.
+If any of the modifiers in the
+.Ar modifier_variable
+contains a dollar sign
.Pq Ql $ ,
these must be doubled to avoid early expansion.
.Pp
+Some modifiers interpret the expression value as a single string,
+others treat the expression value as a whitespace-separated list of words.
+When splitting a string into words,
+whitespace can be escaped using double quotes, single quotes and backslashes,
+like in the shell.
+The quotes and backslashes are retained in the words.
+.Pp
The supported modifiers are:
.Bl -tag -width EEE
.It Cm \&:E
-Replaces each word in the variable with its suffix.
+Replaces each word with its suffix.
.It Cm \&:H
-Replaces each word in the variable with everything but the last component.
-.It Cm \&:M Ns Ar pattern
+Replaces each word with its dirname.
+.It Cm \&:M\| Ns Ar pattern
Selects only those words that match
.Ar pattern .
The standard shell wildcard characters
.Pf ( Ql * ,
.Ql \&? ,
and
-.Ql Oo Oc )
+.Ql \&[] )
may
be used.
The wildcard characters may be escaped with a backslash
.Pq Ql \e .
As a consequence of the way values are split into words, matched,
-and then joined, a construct like
-.Dl ${VAR:M*}
-will normalize the inter-word spacing, removing all leading and
-trailing space, and converting multiple consecutive spaces
-to single spaces.
-.
-.It Cm \&:N Ns Ar pattern
-This is identical to
-.Ql Cm \&:M ,
-but selects all words which do not match
+and then joined, the construct
+.Ql ${VAR:M*}
+removes all leading and trailing whitespace
+and normalizes the inter-word spacing to a single space.
+.It Cm \&:N\| Ns Ar pattern
+This is the opposite of
+.Sq Cm \&:M ,
+selecting all words which do
+.Em not
+match
.Ar pattern .
.It Cm \&:O
-Orders every word in variable alphabetically.
+Orders the words lexicographically.
+.It Cm \&:On
+Orders the words numerically.
+A number followed by one of
+.Ql k ,
+.Ql M
+or
+.Ql G
+is multiplied by the appropriate factor, which is 1024 for
+.Ql k ,
+1048576 for
+.Ql M ,
+or 1073741824 for
+.Ql G .
+Both upper- and lower-case letters are accepted.
.It Cm \&:Or
-Orders every word in variable in reverse alphabetical order.
+Orders the words in reverse lexicographical order.
+.It Cm \&:Orn
+Orders the words in reverse numerical order.
.It Cm \&:Ox
-Shuffles the words in variable.
-The results will be different each time you are referring to the
+Shuffles the words.
+The results are different each time you are referring to the
modified variable; use the assignment with expansion
-.Pq Ql Cm \&:=
+.Sq Cm \&:=
to prevent such behavior.
For example,
.Bd -literal -offset indent
@@ -1272,86 +1502,110 @@ due uno quattro tre
due uno quattro tre
.Ed
.It Cm \&:Q
-Quotes every shell meta-character in the variable, so that it can be passed
+Quotes every shell meta-character in the value, so that it can be passed
safely to the shell.
.It Cm \&:q
-Quotes every shell meta-character in the variable, and also doubles
+Quotes every shell meta-character in the value, and also doubles
.Sq $
characters so that it can be passed
safely through recursive invocations of
.Nm .
-This is equivalent to:
-.Sq \&:S/\e\&$/&&/g:Q .
+This is equivalent to
+.Sq Cm \&:S/\e\&$/&&/g:Q .
.It Cm \&:R
-Replaces each word in the variable with everything but its suffix.
-.It Cm \&:range[=count]
+Replaces each word with everything but its suffix.
+.It Cm \&:range Ns Oo Cm = Ns Ar count Oc
The value is an integer sequence representing the words of the original
value, or the supplied
-.Va count .
-.It Cm \&:gmtime[=utc]
-The value is a format string for
+.Ar count .
+.It Cm \&:gmtime Ns Oo Cm = Ns Ar timestamp Oc
+The value is interpreted as a format string for
.Xr strftime 3 ,
using
-.Xr gmtime 3 .
+.Xr gmtime 3 ,
+producing the formatted timestamp.
+Note: the
+.Ql %s
+format should only be used with
+.Sq Cm \&:localtime .
If a
-.Va utc
+.Ar timestamp
value is not provided or is 0, the current time is used.
.It Cm \&:hash
-Computes a 32-bit hash of the value and encode it as hex digits.
-.It Cm \&:localtime[=utc]
-The value is a format string for
+Computes a 32-bit hash of the value and encodes it as 8 hex digits.
+.It Cm \&:localtime Ns Oo Cm = Ns Ar timestamp Oc
+The value is interpreted as a format string for
.Xr strftime 3 ,
using
-.Xr localtime 3 .
+.Xr localtime 3 ,
+producing the formatted timestamp.
If a
-.Va utc
+.Ar timestamp
value is not provided or is 0, the current time is used.
+.It Cm \&:mtime Ns Oo Cm = Ns Ar timestamp Oc
+Call
+.Xr stat 2
+with each word as pathname;
+use
+.Ql st_mtime
+as the new value.
+If
+.Xr stat 2
+fails; use
+.Ar timestamp
+or current time.
+If
+.Ar timestamp
+is set to
+.Ql error ,
+then
+.Xr stat 2
+failure will cause an error.
.It Cm \&:tA
-Attempts to convert variable to an absolute path using
-.Xr realpath 3 ,
-if that fails, the value is unchanged.
+Attempts to convert the value to an absolute path using
+.Xr realpath 3 .
+If that fails, the value is unchanged.
.It Cm \&:tl
-Converts variable to lower-case letters.
+Converts the value to lower-case letters.
.It Cm \&:ts Ns Ar c
-Words in the variable are normally separated by a space on expansion.
-This modifier sets the separator to the character
+When joining the words after a modifier that treats the value as words,
+the words are normally separated by a space.
+This modifier changes the separator to the character
.Ar c .
If
.Ar c
-is omitted, then no separator is used.
+is omitted, no separator is used.
The common escapes (including octal numeric codes) work as expected.
.It Cm \&:tu
-Converts variable to upper-case letters.
+Converts the value to upper-case letters.
.It Cm \&:tW
-Causes the value to be treated as a single word
-(possibly containing embedded white space).
+Causes subsequent modifiers to treat the value as a single word
+(possibly containing embedded whitespace).
See also
-.Ql Cm \&:[*] .
+.Sq Cm \&:[*] .
.It Cm \&:tw
-Causes the value to be treated as a sequence of
-words delimited by white space.
+Causes the value to be treated as a list of words.
See also
-.Ql Cm \&:[@] .
+.Sq Cm \&:[@] .
.Sm off
-.It Cm \&:S No \&/ Ar old_string No \&/ Ar new_string No \&/ Op Cm 1gW
+.It Cm \&:S\| No \&/ Ar old_string\| No \&/ Ar new_string\| No \&/ Op Cm 1gW
.Sm on
Modifies the first occurrence of
.Ar old_string
-in each word of the variable's value, replacing it with
+in each word of the value, replacing it with
.Ar new_string .
If a
.Ql g
-is appended to the last delimiter of the pattern, all occurrences
-in each word are replaced.
+is appended to the last delimiter of the pattern,
+all occurrences in each word are replaced.
If a
.Ql 1
-is appended to the last delimiter of the pattern, only the first occurrence
-is affected.
+is appended to the last delimiter of the pattern,
+only the first occurrence is affected.
If a
.Ql W
is appended to the last delimiter of the pattern,
-then the value is treated as a single word
-(possibly containing embedded white space).
+the value is treated as a single word.
If
.Ar old_string
begins with a caret
@@ -1369,39 +1623,37 @@ an ampersand
.Pq Ql &
is replaced by
.Ar old_string
-(without any
+(without the anchoring
.Ql ^
or
.Ql \&$ ) .
-Any character may be used as a delimiter for the parts of the modifier
+Any character may be used as the delimiter for the parts of the modifier
string.
-The anchoring, ampersand and delimiter characters may be escaped with a
+The anchoring, ampersand and delimiter characters can be escaped with a
backslash
.Pq Ql \e .
.Pp
-Variable expansion occurs in the normal fashion inside both
+Both
.Ar old_string
and
.Ar new_string
-with the single exception that a backslash is used to prevent the expansion
-of a dollar sign
-.Pq Ql \&$ ,
-not a preceding dollar sign as is usual.
+may contain nested expressions.
+To prevent a dollar sign from starting a nested expression,
+escape it with a backslash.
.Sm off
-.It Cm \&:C No \&/ Ar pattern No \&/ Ar replacement No \&/ Op Cm 1gW
+.It Cm \&:C\| No \&/ Ar pattern\| No \&/ Ar replacement\| No \&/ Op Cm 1gW
.Sm on
The
.Cm \&:C
-modifier is just like the
+modifier works like the
.Cm \&:S
modifier except that the old and new strings, instead of being
-simple strings, are an extended regular expression (see
-.Xr regex 3 )
-string
+simple strings, are an extended regular expression
.Ar pattern
+(see
+.Xr regex 3 )
and an
.Xr ed 1 Ns \-style
-string
.Ar replacement .
Normally, the first occurrence of the pattern
.Ar pattern
@@ -1417,7 +1669,7 @@ search pattern
as occur in the word or words it is found in; the
.Ql W
modifier causes the value to be treated as a single word
-(possibly containing embedded white space).
+(possibly containing embedded whitespace).
.Pp
As for the
.Cm \&:S
@@ -1428,103 +1680,99 @@ and
are subjected to variable expansion before being parsed as
regular expressions.
.It Cm \&:T
-Replaces each word in the variable with its last path component.
+Replaces each word with its last path component (basename).
.It Cm \&:u
Removes adjacent duplicate words (like
.Xr uniq 1 ) .
.Sm off
-.It Cm \&:\&? Ar true_string Cm \&: Ar false_string
+.It Cm \&:\&?\| Ar true_string\| Cm \&: Ar false_string
.Sm on
-If the variable name (not its value), when parsed as a .if conditional
-expression, evaluates to true, return as its value the
+If the variable name (not its value), when parsed as a
+.Cm .if
+conditional expression, evaluates to true, return as its value the
.Ar true_string ,
otherwise return the
.Ar false_string .
-Since the variable name is used as the expression, \&:\&? must be the
-first modifier after the variable name itself - which will, of course,
-usually contain variable expansions.
+Since the variable name is used as the expression,
+\&:\&? must be the first modifier after the variable name
+.No itself Ns \^\(em\^ Ns
+which, of course, usually contains variable expansions.
A common error is trying to use expressions like
.Dl ${NUMBERS:M42:?match:no}
-which actually tests defined(NUMBERS),
-to determine if any words match "42" you need to use something like:
+which actually tests defined(NUMBERS).
+To determine if any words match
+.Dq 42 ,
+you need to use something like:
.Dl ${"${NUMBERS:M42}" != \&"\&":?match:no} .
-.It Ar :old_string=new_string
+.It Cm :\| Ns Ar old_string\| Ns Cm = Ns Ar new_string
This is the
.At V
-style variable substitution.
-It must be the last modifier specified.
-If
+style substitution.
+It can only be the last modifier specified,
+as a
+.Ql \&:
+in either
.Ar old_string
or
.Ar new_string
-do not contain the pattern matching character
-.Ar %
-then it is assumed that they are
-anchored at the end of each word, so only suffixes or entire
-words may be replaced.
-Otherwise
-.Ar %
-is the substring of
-.Ar old_string
-to be replaced in
-.Ar new_string .
-If only
+is treated as a regular character, not as the end of the modifier.
+.Pp
+If
.Ar old_string
-contains the pattern matching character
-.Ar % ,
-and
+does not contain the pattern matching character
+.Ql % ,
+and the word ends with
.Ar old_string
-matches, then the result is the
+or equals it,
+that suffix is replaced with
.Ar new_string .
-If only the
-.Ar new_string
-contains the pattern matching character
-.Ar % ,
-then it is not treated specially and it is printed as a literal
-.Ar %
-on match.
-If there is more than one pattern matching character
-.Ar ( % )
-in either the
+.Pp
+Otherwise, the first
+.Ql %
+in
+.Ar old_string
+matches a possibly empty substring of arbitrary characters,
+and if the whole pattern is found in the word,
+the matching part is replaced with
+.Ar new_string ,
+and the first occurrence of
+.Ql %
+in
.Ar new_string
-or
-.Ar old_string ,
-only the first instance is treated specially (as the pattern character);
-all subsequent instances are treated as regular characters.
+(if any) is replaced with the substring matched by the
+.Ql % .
.Pp
-Variable expansion occurs in the normal fashion inside both
+Both
.Ar old_string
and
.Ar new_string
-with the single exception that a backslash is used to prevent the
-expansion of a dollar sign
-.Pq Ql \&$ ,
-not a preceding dollar sign as is usual.
+may contain nested expressions.
+To prevent a dollar sign from starting a nested expression,
+escape it with a backslash.
.Sm off
-.It Cm \&:@ Ar temp Cm @ Ar string Cm @
+.It Cm \&:@ Ar varname\| Cm @ Ar string\| Cm @
.Sm on
This is the loop expansion mechanism from the OSF Development
Environment (ODE) make.
Unlike
.Cm \&.for
loops, expansion occurs at the time of reference.
-Assigns
-.Ar temp
-to each word in the variable and evaluates
+For each word in the value, assign the word to the variable named
+.Ar varname
+and evaluate
.Ar string .
The ODE convention is that
-.Ar temp
-should start and end with a period.
-For example.
+.Ar varname
+should start and end with a period, for example:
.Dl ${LINKS:@.LINK.@${LN} ${TARGET} ${.LINK.}@}
.Pp
-However a single character variable is often more readable:
+However, a single-letter variable is often more readable:
.Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}
-.It Cm \&:_[=var]
+.It Cm \&:_ Ns Oo Cm = Ns Ar var Oc
Saves the current variable value in
.Ql $_
or the named
-.Va var
+.Ar var
for later reference.
Example usage:
.Bd -literal -offset indent
@@ -1541,59 +1789,53 @@ is used to save the result of the
.Ql :S
modifier which is later referenced using the index values from
.Ql :range .
-.It Cm \&:U Ns Ar newval
+.It Cm \&:U\| Ns Ar newval
If the variable is undefined,
+the optional
.Ar newval
-is the value.
+(which may be empty) is the value.
If the variable is defined, the existing value is returned.
This is another ODE make feature.
It is handy for setting per-target CFLAGS for instance:
.Dl ${_${.TARGET:T}_CFLAGS:U${DEF_CFLAGS}}
If a value is only required if the variable is undefined, use:
.Dl ${VAR:D:Unewval}
-.It Cm \&:D Ns Ar newval
+.It Cm \&:D\| Ns Ar newval
If the variable is defined,
.Ar newval
-is the value.
+(which may be empty) is the value.
.It Cm \&:L
The name of the variable is the value.
.It Cm \&:P
-The path of the node which has the same name as the variable
-is the value.
-If no such node exists or its path is null, then the
-name of the variable is used.
+The path of the node which has the same name as the variable is the value.
+If no such node exists or its path is null, the name of the variable is used.
In order for this modifier to work, the name (node) must at least have
-appeared on the rhs of a dependency.
+appeared on the right-hand side of a dependency.
.Sm off
-.It Cm \&:\&! Ar cmd Cm \&!
+.It Cm \&:\&! Ar cmd\| Cm \&!
.Sm on
The output of running
.Ar cmd
is the value.
.It Cm \&:sh
-If the variable is non-empty it is run as a command and the output
-becomes the new value.
+The value is run as a command, and the output becomes the new value.
.It Cm \&::= Ns Ar str
The variable is assigned the value
.Ar str
after substitution.
-This modifier and its variations are useful in
-obscure situations such as wanting to set a variable when shell commands
-are being parsed.
-These assignment modifiers always expand to
-nothing, so if appearing in a rule line by themselves should be
-preceded with something to keep
-.Nm
-happy.
+This modifier and its variations are useful in obscure situations
+such as wanting to set a variable
+at a point where a target's shell commands are being parsed.
+These assignment modifiers always expand to nothing.
.Pp
The
-.Ql Cm \&::
+.Sq Cm \&::
helps avoid false matches with the
.At V
style
-.Cm \&:=
-modifier and since substitution always occurs the
-.Cm \&::=
+.Ql \&:=
+modifier and since substitution always occurs, the
+.Ql \&::=
form is vaguely appropriate.
.It Cm \&::?= Ns Ar str
As for
@@ -1610,17 +1852,12 @@ to the variable.
.It Cm \&:\&[ Ns Ar range Ns Cm \&]
Selects one or more words from the value,
or performs other operations related to the way in which the
-value is divided into words.
+value is split into words.
.Pp
-Ordinarily, a value is treated as a sequence of words
-delimited by white space.
-Some modifiers suppress this behavior,
-causing a value to be treated as a single word
-(possibly containing embedded white space).
An empty value, or a value that consists entirely of white-space,
is treated as a single word.
For the purposes of the
-.Ql Cm \&:[]
+.Sq Cm \&:[]
modifier, the words are indexed both forwards using positive integers
(where index 1 represents the first word),
and backwards using negative integers
@@ -1642,52 +1879,55 @@ to
.Ar end ,
inclusive.
For example,
-.Ql Cm \&:[2..-1]
+.Sq Cm \&:[2..-1]
selects all words from the second word to the last word.
If
.Ar start
is greater than
.Ar end ,
-then the words are output in reverse order.
+the words are output in reverse order.
For example,
-.Ql Cm \&:[-1..1]
+.Sq Cm \&:[-1..1]
selects all the words from last to first.
-If the list is already ordered, then this effectively reverses
-the list, but it is more efficient to use
-.Ql Cm \&:Or
+If the list is already ordered,
+this effectively reverses the list,
+but it is more efficient to use
+.Sq Cm \&:Or
instead of
-.Ql Cm \&:O:[-1..1] .
+.Sq Cm \&:O:[-1..1] .
.\" :[*]
.It Cm \&*
Causes subsequent modifiers to treat the value as a single word
-(possibly containing embedded white space).
+(possibly containing embedded whitespace).
Analogous to the effect of
-\&"$*\&"
+.Li \&$*
in Bourne shell.
.\" :[0]
.It 0
Means the same as
-.Ql Cm \&:[*] .
+.Sq Cm \&:[*] .
.\" :[*]
.It Cm \&@
Causes subsequent modifiers to treat the value as a sequence of words
-delimited by white space.
+delimited by whitespace.
Analogous to the effect of
-\&"$@\&"
+.Li \&$@
in Bourne shell.
.\" :[#]
.It Cm \&#
Returns the number of words in the value.
.El \" :[range]
.El
-.Sh INCLUDE STATEMENTS, CONDITIONALS AND FOR LOOPS
-Makefile inclusion, conditional structures and for loops reminiscent
-of the C programming language are provided in
-.Nm .
-All such structures are identified by a line beginning with a single
-dot
+.Sh DIRECTIVES
+.Nm
+offers directives for including makefiles, conditionals and for loops.
+All these directives are identified by a line beginning with a single dot
.Pq Ql \&.
-character.
+character, followed by the keyword of the directive, such as
+.Cm include
+or
+.Cm if .
+.Ss File inclusion
Files are included with either
.Cm \&.include \&< Ns Ar file Ns Cm \&>
or
@@ -1699,36 +1939,28 @@ the system makefile directory.
If double quotes are used, the including makefile's directory and any
directories specified using the
.Fl I
-option are searched before the system
-makefile directory.
-For compatibility with other versions of
-.Nm
-.Ql include file ...
+option are searched before the system makefile directory.
+.Pp
+For compatibility with other make variants,
+.Sq Cm include Ar file No ...
+(without leading dot)
is also accepted.
.Pp
If the include statement is written as
.Cm .-include
or as
-.Cm .sinclude
-then errors locating and/or opening include files are ignored.
+.Cm .sinclude ,
+errors locating and/or opening include files are ignored.
.Pp
If the include statement is written as
-.Cm .dinclude
+.Cm .dinclude ,
not only are errors locating and/or opening include files ignored,
-but stale dependencies within the included file will be ignored
-just like
+but stale dependencies within the included file are ignored just like in
.Va .MAKE.DEPENDFILE .
-.Pp
-Conditional expressions are also preceded by a single dot as the first
-character of a line.
-The possible conditionals are as follows:
+.Ss Exporting variables
+The directives for exporting and unexporting variables are:
.Bl -tag -width Ds
-.It Ic .error Ar message
-The message is printed along with the name of the makefile and line number,
-then
-.Nm
-will exit immediately.
-.It Ic .export Ar variable ...
+.It Ic .export Ar variable No ...
Export the specified global variable.
If no variable list is provided, all globals are exported
except for internal variables (those that start with
@@ -1736,16 +1968,14 @@ except for internal variables (those that start with
This is not affected by the
.Fl X
flag, so should be used with caution.
-For compatibility with other
-.Nm
-programs
-.Ql export variable=value
-is also accepted.
+For compatibility with other make programs,
+.Cm export Ar variable\| Ns Cm \&= Ns Ar value
+(without leading dot) is also accepted.
.Pp
Appending a variable name to
.Va .MAKE.EXPORTED
is equivalent to exporting a variable.
-.It Ic .export-env Ar variable ...
+.It Ic .export-env Ar variable No ...
The same as
.Ql .export ,
except that the variable is not appended to
@@ -1754,21 +1984,16 @@ This allows exporting a value to the environment which is different from that
used by
.Nm
internally.
-.It Ic .export-literal Ar variable ...
+.It Ic .export-literal Ar variable No ...
The same as
.Ql .export-env ,
except that variables in the value are not expanded.
-.It Ic .info Ar message
-The message is printed along with the name of the makefile and line number.
-.It Ic .undef Ar variable ...
-Un-define the specified global variables.
-Only global variables can be un-defined.
-.It Ic .unexport Ar variable ...
+.It Ic .unexport Ar variable No ...
The opposite of
.Ql .export .
The specified global
-.Va variable
-will be removed from
+.Ar variable
+is removed from
.Va .MAKE.EXPORTED .
If no variable list is provided, all globals are unexported,
and
@@ -1777,11 +2002,11 @@ deleted.
.It Ic .unexport-env
Unexport all globals previously exported and
clear the environment inherited from the parent.
-This operation will cause a memory leak of the original environment,
+This operation causes a memory leak of the original environment,
so should be used sparingly.
Testing for
.Va .MAKE.LEVEL
-being 0, would make sense.
+being 0 would make sense.
Also note that any variables which originated in the parent environment
should be explicitly preserved if desired.
For example:
@@ -1794,52 +2019,68 @@ PATH := ${PATH}
.Pp
.Ed
Would result in an environment containing only
-.Ql Ev PATH ,
+.Sq Ev PATH ,
which is the minimal useful environment.
+.\" TODO: Check the below sentence, environment variables don't start with '.'.
Actually
-.Ql Ev .MAKE.LEVEL
-will also be pushed into the new environment.
+.Sq Va .MAKE.LEVEL
+is also pushed into the new environment.
+.El
+.Ss Messages
+The directives for printing messages to the output are:
+.Bl -tag -width Ds
+.It Ic .info Ar message
+The message is printed along with the name of the makefile and line number.
.It Ic .warning Ar message
The message prefixed by
-.Ql Pa warning:
+.Sq Li warning:
is printed along with the name of the makefile and line number.
-.It Ic \&.if Oo \&! Oc Ns Ar expression Op Ar operator expression ...
+.It Ic .error Ar message
+The message is printed along with the name of the makefile and line number,
+.Nm
+exits immediately.
+.El
+.Ss Conditionals
+The directives for conditionals are:
+.ds maybenot Oo Ic \&! Oc Ns
+.Bl -tag
+.It Ic .if \*[maybenot] Ar expression Op Ar operator expression No ...
Test the value of an expression.
-.It Ic .ifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ...
-Test the value of a variable.
-.It Ic .ifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ...
-Test the value of a variable.
-.It Ic .ifmake Oo \&! Oc Ns Ar target Op Ar operator target ...
-Test the target being built.
-.It Ic .ifnmake Oo \&! Ns Oc Ar target Op Ar operator target ...
-Test the target being built.
+.It Ic .ifdef \*[maybenot] Ar variable Op Ar operator variable No ...
+Test whether a variable is defined.
+.It Ic .ifndef \*[maybenot] Ar variable Op Ar operator variable No ...
+Test whether a variable is not defined.
+.It Ic .ifmake \*[maybenot] Ar target Op Ar operator target No ...
+Test the target being requested.
+.It Ic .ifnmake \*[maybenot] Ar target Op Ar operator target No ...
+Test the target being requested.
.It Ic .else
Reverse the sense of the last conditional.
-.It Ic .elif Oo \&! Ns Oc Ar expression Op Ar operator expression ...
+.It Ic .elif \*[maybenot] Ar expression Op Ar operator expression No ...
A combination of
-.Ql Ic .else
+.Sq Ic .else
followed by
-.Ql Ic .if .
-.It Ic .elifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ...
+.Sq Ic .if .
+.It Ic .elifdef \*[maybenot] Ar variable Op Ar operator variable No ...
A combination of
-.Ql Ic .else
+.Sq Ic .else
followed by
-.Ql Ic .ifdef .
-.It Ic .elifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ...
+.Sq Ic .ifdef .
+.It Ic .elifndef \*[maybenot] Ar variable Op Ar operator variable No ...
A combination of
-.Ql Ic .else
+.Sq Ic .else
followed by
-.Ql Ic .ifndef .
-.It Ic .elifmake Oo \&! Oc Ns Ar target Op Ar operator target ...
+.Sq Ic .ifndef .
+.It Ic .elifmake \*[maybenot] Ar target Op Ar operator target No ...
A combination of
-.Ql Ic .else
+.Sq Ic .else
followed by
-.Ql Ic .ifmake .
-.It Ic .elifnmake Oo \&! Oc Ns Ar target Op Ar operator target ...
+.Sq Ic .ifmake .
+.It Ic .elifnmake \*[maybenot] Ar target Op Ar operator target No ...
A combination of
-.Ql Ic .else
+.Sq Ic .else
followed by
-.Ql Ic .ifnmake .
+.Sq Ic .ifnmake .
.It Ic .endif
End the body of the conditional.
.El
@@ -1847,133 +2088,158 @@ End the body of the conditional.
The
.Ar operator
may be any one of the following:
-.Bl -tag -width "Cm XX"
-.It Cm \&|\&|
+.Bl -tag
+.It Ic \&|\&|
Logical OR.
-.It Cm \&&&
-Logical
-.Tn AND ;
-of higher precedence than
-.Dq \&|\&| .
+.It Ic \&&&
+Logical AND; of higher precedence than
+.Sq Ic \&|\&| .
.El
.Pp
-As in C,
.Nm
-will only evaluate a conditional as far as is necessary to determine
-its value.
-Parentheses may be used to change the order of evaluation.
+only evaluates a conditional as far as is necessary to determine its value.
+Parentheses can be used to override the operator precedence.
The boolean operator
-.Ql Ic \&!
-may be used to logically negate an entire
-conditional.
+.Sq Ic \&!
+may be used to logically negate an expression, typically a function call.
It is of higher precedence than
-.Ql Ic \&&& .
+.Sq Ic \&&& .
.Pp
The value of
.Ar expression
-may be any of the following:
-.Bl -tag -width defined
-.It Ic defined
-Takes a variable name as an argument and evaluates to true if the variable
+may be any of the following function call expressions:
+.Bl -tag
+.Sm off
+.It Ic defined Li \&( Ar varname Li \&)
+.Sm on
+Evaluates to true if the variable
+.Ar varname
has been defined.
-.It Ic make
-Takes a target name as an argument and evaluates to true if the target
-was specified as part of
+.Sm off
+.It Ic make Li \&( Ar target Li \&)
+.Sm on
+Evaluates to true if the target was specified as part of
.Nm Ns 's
command line or was declared the default target (either implicitly or
explicitly, see
.Va .MAIN )
before the line containing the conditional.
-.It Ic empty
-Takes a variable, with possible modifiers, and evaluates to true if
-the expansion of the variable would result in an empty string.
-.It Ic exists
-Takes a file name as an argument and evaluates to true if the file exists.
-The file is searched for on the system search path (see
+.Sm off
+.It Ic empty Li \&( Ar varname Oo Li : Ar modifiers Oc Li \&)
+.Sm on
+Evaluates to true if the expansion of the variable,
+after applying the modifiers, results in an empty string.
+.Sm off
+.It Ic exists Li \&( Ar pathname Li \&)
+.Sm on
+Evaluates to true if the given pathname exists.
+If relative, the pathname is searched for on the system search path (see
.Va .PATH ) .
-.It Ic target
-Takes a target name as an argument and evaluates to true if the target
-has been defined.
-.It Ic commands
-Takes a target name as an argument and evaluates to true if the target
-has been defined and has commands associated with it.
+.Sm off
+.It Ic target Li \&( Ar target Li \&)
+.Sm on
+Evaluates to true if the target has been defined.
+.Sm off
+.It Ic commands Li \&( Ar target Li \&)
+.Sm on
+Evaluates to true if the target has been defined
+and has commands associated with it.
.El
.Pp
.Ar Expression
may also be an arithmetic or string comparison.
-Variable expansion is
-performed on both sides of the comparison, after which the numerical
-values are compared.
-A value is interpreted as hexadecimal if it is
-preceded by 0x, otherwise it is decimal; octal numbers are not supported.
-The standard C relational operators are all supported.
-If after
-variable expansion, either the left or right hand side of a
-.Ql Ic ==
-or
-.Ql Ic "!="
-operator is not a numerical value, then
-string comparison is performed between the expanded
-variables.
-If no relational operator is given, it is assumed that the expanded
-variable is being compared against 0, or an empty string in the case
-of a string comparison.
+Variable expansion is performed on both sides of the comparison.
+If both sides are numeric and neither is enclosed in quotes,
+the comparison is done numerically, otherwise lexicographically.
+A string is interpreted as a hexadecimal integer if it is preceded by
+.Li 0x ,
+otherwise it is interpreted as a decimal floating-point number;
+octal numbers are not supported.
+.Pp
+All comparisons may use the operators
+.Sq Ic \&==
+and
+.Sq Ic \&!= .
+Numeric comparisons may also use the operators
+.Sq Ic \&< ,
+.Sq Ic \&<= ,
+.Sq Ic \&>
+and
+.Sq Ic \&>= .
+.Pp
+If the comparison has neither a comparison operator nor a right side,
+the expression evaluates to true if it is nonempty
+and its numeric value (if any) is not zero.
.Pp
When
.Nm
is evaluating one of these conditional expressions, and it encounters
-a (white-space separated) word it doesn't recognize, either the
+a (whitespace-separated) word it doesn't recognize, either the
.Dq make
or
.Dq defined
-expression is applied to it, depending on the form of the conditional.
+function is applied to it, depending on the form of the conditional.
If the form is
-.Ql Ic .ifdef ,
-.Ql Ic .ifndef ,
+.Sq Ic .ifdef ,
+.Sq Ic .ifndef
or
-.Ql Ic .if
+.Sq Ic .if ,
the
.Dq defined
-expression is applied.
+function is applied.
Similarly, if the form is
-.Ql Ic .ifmake
+.Sq Ic .ifmake
or
-.Ql Ic .ifnmake ,
+.Sq Ic .ifnmake ,
the
.Dq make
-expression is applied.
+function is applied.
.Pp
-If the conditional evaluates to true the parsing of the makefile continues
-as before.
-If it evaluates to false, the following lines are skipped.
-In both cases this continues until a
-.Ql Ic .else
+If the conditional evaluates to true,
+parsing of the makefile continues as before.
+If it evaluates to false, the following lines until the corresponding
+.Sq Ic .elif
+variant,
+.Sq Ic .else
or
-.Ql Ic .endif
-is found.
-.Pp
+.Sq Ic .endif
+are skipped.
+.Ss For loops
For loops are typically used to apply a set of rules to a list of files.
The syntax of a for loop is:
.Pp
.Bl -tag -compact -width Ds
-.It Ic \&.for Ar variable Oo Ar variable ... Oc Ic in Ar expression
-.It Aq make-lines
+.It Ic \&.for Ar variable Oo Ar variable No ... Oc Ic in Ar expression
+.It Aq Ar make-lines
.It Ic \&.endfor
.El
.Pp
-After the for
-.Ic expression
-is evaluated, it is split into words.
+The
+.Ar expression
+is expanded and then split into words.
On each iteration of the loop, one word is taken and assigned to each
-.Ic variable ,
+.Ar variable ,
in order, and these
-.Ic variables
+.Ar variables
are substituted into the
-.Ic make-lines
+.Ar make-lines
inside the body of the for loop.
The number of words must come out even; that is, if there are three
iteration variables, the number of words provided must be a multiple
of three.
+.Pp
+If
+.Sq Ic .break
+is encountered within a
+.Cm \&.for
+loop, it causes early termination of the loop, otherwise a parse error.
+.\" TODO: Describe limitations with defined/empty.
+.Ss Other directives
+.Bl -tag -width Ds
+.It Ic .undef Ar variable No ...
+Un-define the specified global variables.
+Only global variables can be un-defined.
+.El
.Sh COMMENTS
Comments begin with a hash
.Pq Ql \&#
@@ -1992,7 +2258,7 @@ as if they all were preceded by a dash
.\" .It Ic .JOIN
.\" XXX
.It Ic .MADE
-Mark all sources of this target as being up-to-date.
+Mark all sources of this target as being up to date.
.It Ic .MAKE
Execute the commands associated with this target even if the
.Fl n
@@ -2010,7 +2276,9 @@ or
Usage in conjunction with
.Ic .MAKE
is the most likely case.
-In "meta" mode, the target is out-of-date if the meta file is missing.
+In
+.Dq meta
+mode, the target is out-of-date if the meta file is missing.
.It Ic .NOMETA
Do not create a meta file for the target.
Meta files are also not created for
@@ -2022,16 +2290,17 @@ targets.
.It Ic .NOMETA_CMP
Ignore differences in commands when deciding if target is out of date.
This is useful if the command contains a value which always changes.
-If the number of commands change, though, the target will still be out of date.
+If the number of commands change, though,
+the target is still considered out of date.
The same effect applies to any command line that uses the variable
.Va .OODATE ,
which can be used for that purpose even when not otherwise needed or desired:
.Bd -literal -offset indent
skip-compare-for-some:
- @echo this will be compared
- @echo this will not ${.OODATE:M.NOMETA_CMP}
- @echo this will also be compared
+ @echo this is compared
+ @echo this is not ${.OODATE:M.NOMETA_CMP}
+ @echo this is also compared
.Ed
The
@@ -2039,7 +2308,7 @@ The
pattern suppresses any expansion of the unwanted variable.
.It Ic .NOPATH
Do not search for the target in the directories specified by
-.Ic .PATH .
+.Va .PATH .
.It Ic .NOTMAIN
Normally
.Nm
@@ -2049,12 +2318,12 @@ This source prevents this target from being selected.
.It Ic .OPTIONAL
If a target is marked with this attribute and
.Nm
-can't figure out how to create it, it will ignore this fact and assume
+can't figure out how to create it, it ignores this fact and assumes
the file isn't needed or already exists.
.It Ic .PHONY
-The target does not
-correspond to an actual file; it is always considered to be out of date,
-and will not be created with the
+The target does not correspond to an actual file;
+it is always considered to be out of date,
+and is not created with the
.Fl t
option.
Suffix-transformation rules are not applied to
@@ -2086,9 +2355,9 @@ If the target already has commands, the
target's commands are appended
to them.
.It Ic .USEBEFORE
-Exactly like
+Like
.Ic .USE ,
-but prepend the
+but instead of appending, prepend the
.Ic .USEBEFORE
target commands to the target.
.It Ic .WAIT
@@ -2116,7 +2385,7 @@ the output is always
.Ql b1 ,
.Ql b ,
.Ql x .
-.br
+.Pp
The ordering imposed by
.Ic .WAIT
is only relevant for parallel makes.
@@ -2131,17 +2400,15 @@ else is done.
.It Ic .DEFAULT
This is sort of a
.Ic .USE
-rule for any target (that was used only as a
-source) that
+rule for any target (that was used only as a source) that
.Nm
can't figure out any other way to create.
Only the shell script is used.
The
-.Ic .IMPSRC
+.Va .IMPSRC
variable of a target that inherits
.Ic .DEFAULT Ns 's
-commands is set
-to the target's own name.
+commands is set to the target's own name.
.It Ic .DELETE_ON_ERROR
If this target is present in the makefile, it globally causes make to
delete targets whose commands fail.
@@ -2152,14 +2419,12 @@ This setting can be used to help prevent half-finished or malformed
targets from being left around and corrupting future rebuilds.
.It Ic .END
Any command lines attached to this target are executed after everything
-else is done.
+else is done successfully.
.It Ic .ERROR
Any command lines attached to this target are executed when another target fails.
-The
-.Ic .ERROR_TARGET
-variable is set to the target that failed.
-See also
-.Ic MAKE_PRINT_VAR_ON_ERROR .
+See
+.Va MAKE_PRINT_VAR_ON_ERROR
+for the variables that will be set.
.It Ic .IGNORE
Mark each of the sources with the
.Ic .IGNORE
@@ -2170,24 +2435,24 @@ option.
.It Ic .INTERRUPT
If
.Nm
-is interrupted, the commands for this target will be executed.
+is interrupted, the commands for this target are executed.
.It Ic .MAIN
If no target is specified when
.Nm
-is invoked, this target will be built.
+is invoked, this target is built.
.It Ic .MAKEFLAGS
This target provides a way to specify flags for
.Nm
-when the makefile is used.
+at the time when the makefiles are read.
The flags are as if typed to the shell, though the
.Fl f
-option will have
+option has
no effect.
.\" XXX: NOT YET!!!!
.\" .It Ic .NOTPARALLEL
.\" The named targets are executed in non parallel mode.
.\" If no targets are
-.\" specified, then all targets are executed in non parallel mode.
+.\" specified, all targets are executed in non parallel mode.
.It Ic .NOPATH
Apply the
.Ic .NOPATH
@@ -2198,18 +2463,19 @@ Disable parallel mode.
Synonym for
.Ic .NOTPARALLEL ,
for compatibility with other pmake variants.
+.It Ic .NOREADONLY
+clear the read-only attribute from the global variables specified as sources.
.It Ic .OBJDIR
The source is a new value for
-.Ql Va .OBJDIR .
+.Sq Va .OBJDIR .
If it exists,
.Nm
-will
-.Xr chdir 2
-to it and update the value of
-.Ql Va .OBJDIR .
+changes the current working directory to it and updates the value of
+.Sq Va .OBJDIR .
.It Ic .ORDER
-The named targets are made in sequence.
+In parallel mode, the named targets are made in sequence.
This ordering does not add targets to the list of targets to be made.
+.Pp
Since the dependents of a target do not get built until the target itself
could be built, unless
.Ql a
@@ -2220,24 +2486,20 @@ the following is a dependency loop:
b: a
.Ed
.Pp
-The ordering imposed by
-.Ic .ORDER
-is only relevant for parallel makes.
.\" XXX: NOT YET!!!!
.\" .It Ic .PARALLEL
.\" The named targets are executed in parallel mode.
.\" If no targets are
-.\" specified, then all targets are executed in parallel mode.
+.\" specified, all targets are executed in parallel mode.
.It Ic .PATH
The sources are directories which are to be searched for files not
found in the current directory.
-If no sources are specified, any previously specified directories are
-deleted.
+If no sources are specified,
+any previously specified directories are removed from the search path.
If the source is the special
.Ic .DOTLAST
-target, then the current working
-directory is searched last.
-.It Ic .PATH. Ns Va suffix
+target, the current working directory is searched last.
+.It Ic .PATH. Ns Ar suffix
Like
.Ic .PATH
but applies only to files with a particular suffix.
@@ -2247,52 +2509,70 @@ The suffix must have been previously declared with
Apply the
.Ic .PHONY
attribute to any specified sources.
+.It Ic .POSIX
+If this is the first non-comment line in the main makefile,
+the variable
+.Va %POSIX
+is set to the value
+.Ql 1003.2
+and the makefile
+.Ql <posix.mk>
+is included if it exists,
+to provide POSIX-compatible default rules.
+If
+.Nm
+is run with the
+.Fl r
+flag, only
+.Ql posix.mk
+contributes to the default rules.
.It Ic .PRECIOUS
Apply the
.Ic .PRECIOUS
attribute to any specified sources.
If no sources are specified, the
.Ic .PRECIOUS
-attribute is applied to every
-target in the file.
+attribute is applied to every target in the file.
+.It Ic .READONLY
+set the read-only attribute on the global variables specified as sources.
.It Ic .SHELL
Sets the shell that
.Nm
-will use to execute commands.
+uses to execute commands.
The sources are a set of
-.Ar field=value
+.Ar field\| Ns Cm \&= Ns Ar value
pairs.
-.Bl -tag -width hasErrCtls
-.It Ar name
+.Bl -tag -width ".Li hasErrCtls"
+.It Li name
This is the minimal specification, used to select one of the built-in
shell specs;
-.Ar sh ,
-.Ar ksh ,
+.Li sh ,
+.Li ksh ,
and
-.Ar csh .
-.It Ar path
-Specifies the path to the shell.
-.It Ar hasErrCtl
+.Li csh .
+.It Li path
+Specifies the absolute path to the shell.
+.It Li hasErrCtl
Indicates whether the shell supports exit on error.
-.It Ar check
+.It Li check
The command to turn on error checking.
-.It Ar ignore
+.It Li ignore
The command to disable error checking.
-.It Ar echo
+.It Li echo
The command to turn on echoing of commands executed.
-.It Ar quiet
+.It Li quiet
The command to turn off echoing of commands executed.
-.It Ar filter
+.It Li filter
The output to filter after issuing the
-.Ar quiet
+.Li quiet
command.
It is typically identical to
-.Ar quiet .
-.It Ar errFlag
+.Li quiet .
+.It Li errFlag
The flag to pass the shell to enable error checking.
-.It Ar echoFlag
+.It Li echoFlag
The flag to pass the shell to enable command echoing.
-.It Ar newline
+.It Li newline
The string literal to pass the shell that results in a single newline
character when used outside of any quoting characters.
.El
@@ -2323,10 +2603,18 @@ It allows the creation of suffix-transformation rules.
.Pp
Example:
.Bd -literal
-\&.SUFFIXES: .o
+\&.SUFFIXES: .c .o
\&.c.o:
cc \-o ${.TARGET} \-c ${.IMPSRC}
.Ed
+.It Ic .SYSPATH
+The sources are directories which are to be added to the system
+include path which
+.Nm
+searches for makefiles.
+If no sources are specified,
+any previously specified directories are removed from the system
+include path.
.El
.Sh ENVIRONMENT
.Nm
@@ -2349,23 +2637,23 @@ may only be set in the environment or on the command line to
.Nm
and not as makefile variables;
see the description of
-.Ql Va .OBJDIR
+.Sq Va .OBJDIR
for more details.
.Sh FILES
.Bl -tag -width /usr/share/mk -compact
.It .depend
list of dependencies
-.It Makefile
-list of dependencies
.It makefile
-list of dependencies
+first default makefile if no makefile is specified on the command line
+.It Makefile
+second default makefile if no makefile is specified on the command line
.It sys.mk
system makefile
.It /usr/share/mk
system makefile directory
.El
.Sh COMPATIBILITY
-The basic make syntax is compatible between different versions of make;
+The basic make syntax is compatible between different make variants;
however the special variables, variable modifiers and conditionals are not.
.Ss Older versions
An incomplete list of changes in older versions of
@@ -2394,13 +2682,15 @@ The
and
.Ic .ORDER
declarations and most functionality pertaining to parallelization.
-(GNU make supports parallelization but lacks these features needed to
+(GNU make supports parallelization but lacks the features needed to
control it effectively.)
.It
Directives, including for loops and conditionals and most of the
forms of include files.
(GNU make has its own incompatible and less powerful syntax for
conditionals.)
+.\" The "less powerful" above means that GNU make does not have the
+.\" make(target), target(target) and commands(target) functions.
.It
All built-in variables that begin with a dot.
.It
@@ -2412,7 +2702,7 @@ and
.Ic .SUFFIXES .
.It
Variable modifiers, except for the
-.Dl :old=new
+.Ql :old=new
string substitution, which does not portably support globbing with
.Ql %
and historically only works on declared suffixes.
@@ -2429,7 +2719,7 @@ Some features are somewhat more portable, such as assignment with
and
.Ic != .
The
-.Ic .PATH
+.Va .PATH
functionality is based on an older feature
.Ic VPATH
found in GNU make and many versions of SVR4 make; however,
@@ -2456,22 +2746,22 @@ command appeared in
.At v7 .
This
.Nm
-implementation is based on Adam De Boor's pmake program which was written
-for Sprite at Berkeley.
+implementation is based on Adam de Boor's pmake program,
+which was written for Sprite at Berkeley.
It was designed to be a parallel distributed make running jobs on different
machines using a daemon called
.Dq customs .
.Pp
Historically the target/dependency
-.Dq FRC
+.Ic FRC
has been used to FoRCe rebuilding (since the target/dependency
-does not exist... unless someone creates an
-.Dq FRC
+does not exist ... unless someone creates an
+.Pa FRC
file).
.Sh BUGS
The
.Nm
-syntax is difficult to parse without actually acting on the data.
+syntax is difficult to parse.
For instance, finding the end of a variable's use should involve scanning
each of the modifiers, using the correct terminator for each field.
In many places
@@ -2479,3 +2769,13 @@ In many places
just counts {} and () in order to find the end of a variable expansion.
.Pp
There is no way of escaping a space character in a filename.
+.Pp
+In jobs mode, when a target fails;
+.Nm
+will put an error token into the job token pool.
+This will cause all other instances of
+.Nm
+using that token pool to abort the build and exit with error code 6.
+Sometimes the attempt to suppress a cascade of unnecessary errors,
+can result in a seemingly unexplained
+.Ql *** Error code 6
diff --git a/contrib/bmake/make.c b/contrib/bmake/make.c
index a85a497be32d..53f7b35754ba 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.262 2024/01/05 23:22:06 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -104,7 +104,7 @@
#include "job.h"
/* "@(#)make.c 8.1 (Berkeley) 6/6/93" */
-MAKE_RCSID("$NetBSD: make.c,v 1.244 2021/04/04 10:05:08 rillig Exp $");
+MAKE_RCSID("$NetBSD: make.c,v 1.262 2024/01/05 23:22:06 rillig Exp $");
/* Sequence # to detect recursion. */
static unsigned int checked_seqno = 1;
@@ -120,53 +120,88 @@ static GNodeList toBeMade = LST_INIT;
void
debug_printf(const char *fmt, ...)
{
- va_list args;
+ va_list ap;
- va_start(args, fmt);
- vfprintf(opts.debug_file, fmt, args);
- va_end(args);
+ va_start(ap, fmt);
+ vfprintf(opts.debug_file, fmt, ap);
+ va_end(ap);
}
-MAKE_ATTR_DEAD static void
-make_abort(GNode *gn, int lineno)
+static const char *
+GNodeType_ToString(GNodeType type, void **freeIt)
{
-
- debug_printf("make_abort from line %d\n", lineno);
- Targ_PrintNode(gn, 2);
- Targ_PrintNodes(&toBeMade, 2);
- Targ_PrintGraph(3);
- abort();
+ Buffer buf;
+
+ Buf_Init(&buf);
+#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));
}
-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 *
+GNodeFlags_ToString(GNodeFlags flags, void **freeIt)
+{
+ Buffer buf;
+
+ Buf_Init(&buf);
+ Buf_AddFlag(&buf, flags.remake, "REMAKE");
+ Buf_AddFlag(&buf, flags.childMade, "CHILDMADE");
+ Buf_AddFlag(&buf, flags.force, "FORCE");
+ Buf_AddFlag(&buf, flags.doneWait, "DONE_WAIT");
+ Buf_AddFlag(&buf, flags.doneOrder, "DONE_ORDER");
+ Buf_AddFlag(&buf, flags.fromDepend, "FROM_DEPEND");
+ Buf_AddFlag(&buf, flags.doneAllsrc, "DONE_ALLSRC");
+ Buf_AddFlag(&buf, flags.cycle, "CYCLE");
+ Buf_AddFlag(&buf, flags.doneCycle, "DONECYCLE");
+ 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 +321,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
@@ -295,13 +330,12 @@ GNode_IsOODate(GNode *gn)
* out-of-date.
*/
if (DEBUG(MAKE)) {
- if (gn->type & OP_FORCE) {
+ if (gn->type & OP_FORCE)
debug_printf("! operator...");
- } else if (gn->type & OP_PHONY) {
+ else if (gn->type & OP_PHONY)
debug_printf(".PHONY node...");
- } else {
+ else
debug_printf(".EXEC node...");
- }
}
oodate = true;
} else if (IsOODateRegular(gn)) {
@@ -315,16 +349,15 @@ 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
- if (useMeta) {
+ if (useMeta)
oodate = meta_oodate(gn, oodate);
- }
#endif
/*
@@ -406,12 +439,11 @@ Make_HandleUse(GNode *cgn, GNode *pgn)
* We don't need to do this for commands.
* They get expanded properly when we execute.
*/
- if (gn->uname == NULL) {
+ if (gn->uname == NULL)
gn->uname = gn->name;
- } else {
+ else
free(gn->name);
- }
- (void)Var_Subst(gn->uname, pgn, VARE_WANTRES, &gn->name);
+ gn->name = Var_Subst(gn->uname, pgn, VARE_WANTRES);
/* TODO: handle errors */
if (gn->uname != NULL && strcmp(gn->name, gn->uname) != 0) {
/* See if we have a target for this node. */
@@ -426,7 +458,7 @@ Make_HandleUse(GNode *cgn, GNode *pgn)
}
pgn->type |=
- cgn->type & ~(OP_OPMASK | OP_USE | OP_USEBEFORE | OP_TRANSFORM);
+ cgn->type & (unsigned)~(OP_OPMASK | OP_USE | OP_USEBEFORE | OP_TRANSFORM);
}
/*
@@ -512,9 +544,8 @@ Make_Recheck(GNode *gn)
* depend on FRC to be made, so we have to check for gn->children
* being empty as well.
*/
- if (!Lst_IsEmpty(gn->commands) || Lst_IsEmpty(gn->children)) {
+ if (!Lst_IsEmpty(gn->commands) || Lst_IsEmpty(gn->children))
gn->mtime = now;
- }
#else
/*
* This is what Make does and it's actually a good thing, as it
@@ -551,8 +582,9 @@ Make_Recheck(GNode *gn)
}
#endif
- /* XXX: The returned mtime may differ from gn->mtime.
- * Intentionally? */
+ /*
+ * XXX: The returned mtime may differ from gn->mtime. Intentionally?
+ */
return mtime;
}
@@ -568,7 +600,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 +617,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,
@@ -596,7 +628,7 @@ IsWaitingForOrder(GNode *gn)
return false;
}
-static void MakeBuildParent(GNode *, GNodeListNode *);
+static bool MakeBuildChild(GNode *, GNodeListNode *);
static void
ScheduleOrderSuccessors(GNode *gn)
@@ -604,8 +636,13 @@ ScheduleOrderSuccessors(GNode *gn)
GNodeListNode *toBeMadeNext = toBeMade.first;
GNodeListNode *ln;
- for (ln = gn->order_succ.first; ln != NULL; ln = ln->next)
- MakeBuildParent(ln->datum, toBeMadeNext);
+ for (ln = gn->order_succ.first; ln != NULL; ln = ln->next) {
+ GNode *succ = ln->datum;
+
+ if (succ->made == DEFERRED &&
+ !MakeBuildChild(succ, toBeMadeNext))
+ succ->flags.doneOrder = true;
+ }
}
/*
@@ -649,9 +686,8 @@ Make_Update(GNode *cgn)
* now -- some rules won't actually update the file. If the file
* still doesn't exist, make its mtime now.
*/
- if (cgn->made != UPTODATE) {
+ if (cgn->made != UPTODATE)
mtime = Make_Recheck(cgn);
- }
/*
* If this is a `::' node, we must consult its first instance
@@ -684,13 +720,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 +743,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 +776,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;
}
@@ -778,7 +814,7 @@ UnmarkChildren(GNode *gn)
for (ln = gn->children.first; ln != NULL; ln = ln->next) {
GNode *child = ln->datum;
- child->type &= ~OP_MARK;
+ child->type &= (unsigned)~OP_MARK;
}
}
@@ -873,7 +909,7 @@ GNode_SetLocalVars(GNode *gn)
{
GNodeListNode *ln;
- if (gn->flags & DONE_ALLSRC)
+ if (gn->flags.doneAllsrc)
return;
UnmarkChildren(gn);
@@ -887,7 +923,29 @@ 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 void
+ScheduleRandomly(GNode *gn)
+{
+ GNodeListNode *ln;
+ size_t i, n;
+
+ n = 0;
+ for (ln = toBeMade.first; ln != NULL; ln = ln->next)
+ n++;
+ i = n > 0 ? (size_t)random() % (n + 1) : 0;
+
+ if (i == 0) {
+ Lst_Append(&toBeMade, gn);
+ return;
+ }
+ i--;
+
+ for (ln = toBeMade.first; i > 0; ln = ln->next)
+ i--;
+ Lst_InsertBefore(&toBeMade, ln, gn);
}
static bool
@@ -904,7 +962,9 @@ MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext)
/* If this node is on the RHS of a .ORDER, check LHSs. */
if (IsWaitingForOrder(cn)) {
- /* Can't build this (or anything else in this child list) yet */
+ /*
+ * Can't build this (or anything else in this child list) yet
+ */
cn->made = DEFERRED;
return false; /* but keep looking */
}
@@ -913,7 +973,9 @@ MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext)
cn->name, cn->cohort_num);
cn->made = REQUESTED;
- if (toBeMadeNext == NULL)
+ if (opts.randomizeTargets && !(cn->type & OP_WAIT))
+ ScheduleRandomly(cn);
+ else if (toBeMadeNext == NULL)
Lst_Append(&toBeMade, cn);
else
Lst_InsertBefore(&toBeMade, toBeMadeNext, cn);
@@ -933,19 +995,6 @@ MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext)
return cn->type & OP_WAIT && cn->unmade > 0;
}
-/* When a .ORDER LHS node completes, we do this on each RHS. */
-static void
-MakeBuildParent(GNode *pn, GNodeListNode *toBeMadeNext)
-{
- if (pn->made != DEFERRED)
- return;
-
- if (!MakeBuildChild(pn, toBeMadeNext)) {
- /* When this node is built, reschedule its parents. */
- pn->flags |= DONE_ORDER;
- }
-}
-
static void
MakeChildren(GNode *gn)
{
@@ -983,13 +1032,12 @@ MakeStartJobs(void)
DEBUG2(MAKE, "Examining %s%s...\n", gn->name, gn->cohort_num);
if (gn->made != REQUESTED) {
- /*
- * XXX: Replace %d with string representation;
- * see made_name.
- */
- DEBUG1(MAKE, "state %d\n", gn->made);
-
- make_abort(gn, __LINE__);
+ debug_printf("internal error: made = %s\n",
+ GNodeMade_Name(gn->made));
+ Targ_PrintNode(gn, 2);
+ Targ_PrintNodes(&toBeMade, 2);
+ Targ_PrintGraph(3);
+ abort();
}
if (gn->checked_seqno == checked_seqno) {
@@ -1022,8 +1070,8 @@ MakeStartJobs(void)
gn->made = BEINGMADE;
if (GNode_IsOODate(gn)) {
DEBUG0(MAKE, "out-of-date\n");
- if (opts.queryFlag)
- return true;
+ if (opts.query)
+ return strcmp(gn->name, ".MAIN") != 0;
GNode_SetLocalVars(gn);
Job_Make(gn);
have_token = false;
@@ -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 */
@@ -1172,12 +1220,12 @@ MakePrintStatusList(GNodeList *gnodes, int *errors)
static void
ExamineLater(GNodeList *examine, GNodeList *toBeExamined)
{
- ListNode *ln;
+ GNodeListNode *ln;
for (ln = toBeExamined->first; ln != NULL; ln = ln->next) {
GNode *gn = ln->datum;
- 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);
@@ -1279,7 +1327,9 @@ add_wait_dependency(GNodeListNode *owln, GNode *wn)
DEBUG3(MAKE, ".WAIT: add dependency %s%s -> %s\n",
cn->name, cn->cohort_num, wn->name);
- /* XXX: This pattern should be factored out, it repeats often */
+ /*
+ * XXX: This pattern should be factored out, it repeats often
+ */
Lst_Append(&wn->children, cn);
wn->unmade++;
Lst_Append(&cn->parents, wn);
@@ -1301,7 +1351,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 +1379,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)
@@ -1388,7 +1438,7 @@ Make_Run(GNodeList *targs)
Targ_PrintGraph(1);
}
- if (opts.queryFlag) {
+ if (opts.query) {
/*
* We wouldn't do any work unless we could start some jobs
* in the next loop... (we won't actually start any, of
diff --git a/contrib/bmake/make.h b/contrib/bmake/make.h
index a074923c643e..b7fcc77b91b3 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.333 2024/05/07 18:26:22 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -74,7 +74,7 @@
/*
* make.h --
- * The global definitions for pmake
+ * The global definitions for make
*/
#ifndef MAKE_MAKE_H
@@ -110,9 +110,9 @@
#define MAKE_GNUC_PREREQ(x, y) \
((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || \
(__GNUC__ > (x)))
-#else /* defined(__GNUC__) */
+#else
#define MAKE_GNUC_PREREQ(x, y) 0
-#endif /* defined(__GNUC__) */
+#endif
#if MAKE_GNUC_PREREQ(2, 7)
#define MAKE_ATTR_UNUSED __attribute__((__unused__))
@@ -135,26 +135,55 @@
#define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) /* delete */
#endif
+#if MAKE_GNUC_PREREQ(4, 0)
+#define MAKE_ATTR_USE __attribute__((__warn_unused_result__))
+#else
+#define MAKE_ATTR_USE /* delete */
+#endif
+
+#if MAKE_GNUC_PREREQ(8, 0)
+#define MAKE_ATTR_NOINLINE __attribute__((__noinline__))
+#else
+#define MAKE_ATTR_NOINLINE /* delete */
+#endif
+
+#if __STDC_VERSION__ >= 199901L || defined(lint)
#define MAKE_INLINE static inline MAKE_ATTR_UNUSED
+#else
+#define MAKE_INLINE static MAKE_ATTR_UNUSED
+#endif
+
+/* 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"
@@ -173,6 +202,13 @@ typedef unsigned int Boolean;
#endif
/*
+ * IRIX defines OP_NONE in sys/fcntl.h
+ */
+#if defined(OP_NONE)
+# undef OP_NONE
+#endif
+
+/*
* The typical flow of states is:
*
* The direct successful path:
@@ -193,26 +229,34 @@ typedef unsigned int Boolean;
typedef enum GNodeMade {
/* Not examined yet. */
UNMADE,
- /* The node has been examined but is not yet ready since its
- * dependencies have to be made first. */
+ /*
+ * The node has been examined but is not yet ready since its
+ * dependencies have to be made first.
+ */
DEFERRED,
/* The node is on the toBeMade list. */
REQUESTED,
- /* The node is already being made. Trying to build a node in this
- * state indicates a cycle in the graph. */
+ /*
+ * The node is already being made. Trying to build a node in this
+ * state indicates a cycle in the graph.
+ */
BEINGMADE,
/* Was out-of-date and has been made. */
MADE,
/* Was already up-to-date, does not need to be made. */
UPTODATE,
- /* An error occurred while it was being made.
- * Used only in compat mode. */
+ /*
+ * An error occurred while it was being made. Used only in compat
+ * mode.
+ */
ERROR,
- /* The target was aborted due to an error making a dependency.
- * Used only in compat mode. */
+ /*
+ * The target was aborted due to an error making a dependency. Used
+ * only in compat mode.
+ */
ABORTED
} GNodeMade;
@@ -228,16 +272,22 @@ typedef enum GNodeMade {
typedef enum GNodeType {
OP_NONE = 0,
- /* The dependency operator ':' is the most common one. The commands
- * of this node are executed if any child is out-of-date. */
+ /*
+ * The dependency operator ':' is the most common one. The commands
+ * of this node are executed if any child is out-of-date.
+ */
OP_DEPENDS = 1 << 0,
- /* The dependency operator '!' always executes its commands, even if
- * its children are up-to-date. */
+ /*
+ * The dependency operator '!' always executes its commands, even if
+ * its children are up-to-date.
+ */
OP_FORCE = 1 << 1,
- /* The dependency operator '::' behaves like ':', except that it
+ /*
+ * The dependency operator '::' behaves like ':', except that it
* allows multiple dependency groups to be defined. Each of these
- * groups is executed on its own, independently from the others.
- * Each individual dependency group is called a cohort. */
+ * groups is executed on its own, independently from the others. Each
+ * individual dependency group is called a cohort.
+ */
OP_DOUBLEDEP = 1 << 2,
/* Matches the dependency operators ':', '!' and '::'. */
@@ -247,21 +297,29 @@ typedef enum GNodeType {
OP_OPTIONAL = 1 << 3,
/* Use associated commands for parents. */
OP_USE = 1 << 4,
- /* Target is never out of date, but always execute commands anyway.
- * Its time doesn't matter, so it has none...sort of. */
+ /*
+ * Target is never out of date, but always execute commands anyway.
+ * Its time doesn't matter, so it has none...sort of.
+ */
OP_EXEC = 1 << 5,
- /* Ignore non-zero exit status from shell commands when creating the
- * node. */
+ /*
+ * Ignore non-zero exit status from shell commands when creating the
+ * node.
+ */
OP_IGNORE = 1 << 6,
/* Don't remove the target when interrupted. */
OP_PRECIOUS = 1 << 7,
/* Don't echo commands when executed. */
OP_SILENT = 1 << 8,
- /* Target is a recursive make so its commands should always be
- * executed when it is out of date, regardless of the state of the
- * -n or -t flags. */
+ /*
+ * Target is a recursive make so its commands should always be
+ * executed when it is out of date, regardless of the state of the -n
+ * or -t flags.
+ */
OP_MAKE = 1 << 9,
- /* Target is out-of-date only if any of its children was out-of-date. */
+ /*
+ * Target is out-of-date only if any of its children was out-of-date.
+ */
OP_JOIN = 1 << 10,
/* Assume the children of the node have been already made. */
OP_MADE = 1 << 11,
@@ -269,20 +327,26 @@ typedef enum GNodeType {
OP_SPECIAL = 1 << 12,
/* Like .USE, only prepend commands. */
OP_USEBEFORE = 1 << 13,
- /* The node is invisible to its parents. I.e. it doesn't show up in
- * the parents' local variables (.IMPSRC, .ALLSRC). */
+ /*
+ * The node is invisible to its parents. I.e. it doesn't show up in
+ * the parents' local variables (.IMPSRC, .ALLSRC).
+ */
OP_INVISIBLE = 1 << 14,
- /* The node does not become the main target, even if it is the first
- * target in the first makefile. */
+ /*
+ * The node does not become the main target, even if it is the first
+ * target in the first makefile.
+ */
OP_NOTMAIN = 1 << 15,
/* Not a file target; run always. */
OP_PHONY = 1 << 16,
/* Don't search for the file in the path. */
OP_NOPATH = 1 << 17,
- /* In a dependency line "target: source1 .WAIT source2", source1 is
+ /*
+ * In a dependency line "target: source1 .WAIT source2", source1 is
* made first, including its children. Once that is finished,
* source2 is made, including its children. The .WAIT keyword may
- * appear more than once in a single dependency declaration. */
+ * appear more than once in a single dependency declaration.
+ */
OP_WAIT = 1 << 18,
/* .NOMETA do not create a .meta file */
OP_NOMETA = 1 << 19,
@@ -300,50 +364,56 @@ typedef enum GNodeType {
/* Target is a member of an archive */
/* XXX: How does this differ from OP_ARCHV? */
OP_MEMBER = 1 << 29,
- /* The node is a library,
- * its name has the form "-l<libname>" */
+ /*
+ * The node is a library, its name has the form "-l<libname>".
+ */
OP_LIB = 1 << 28,
- /* The node is an archive member,
- * its name has the form "archive(member)" */
+ /*
+ * The node is an archive member, its name has the form
+ * "archive(member)".
+ */
/* XXX: How does this differ from OP_MEMBER? */
OP_ARCHV = 1 << 27,
- /* Target has all the commands it should. Used when parsing to catch
+ /*
+ * Target has all the commands it should. Used when parsing to catch
* multiple command groups for a target. Only applies to the
- * dependency operators ':' and '!', but not to '::'. */
+ * dependency operators ':' and '!', but not to '::'.
+ */
OP_HAS_COMMANDS = 1 << 26,
- /* The special command "..." has been seen. All further commands from
- * this node will be saved on the .END node instead, to be executed at
- * the very end. */
+ /*
+ * The special command "..." has been seen. All further commands from
+ * this node will be saved on the .END node instead, to be executed
+ * at the very end.
+ */
OP_SAVE_CMDS = 1 << 25,
- /* Already processed by Suff_FindDeps, to find dependencies from
- * suffix transformation rules. */
+ /*
+ * Already processed by Suff_FindDeps, to find dependencies from
+ * suffix transformation rules.
+ */
OP_DEPS_FOUND = 1 << 24,
/* Node found while expanding .ALLSRC */
- OP_MARK = 1 << 23,
-
- OP_NOTARGET = OP_NOTMAIN | OP_USE | OP_EXEC | OP_TRANSFORM
+ OP_MARK = 1 << 23
} 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;
@@ -358,21 +428,27 @@ typedef struct SearchPath {
/*
* A graph node represents a target that can possibly be made, including its
- * relation to other targets and a lot of other details.
+ * relation to other targets.
*/
typedef struct GNode {
/* The target's name, such as "clean" or "make.c" */
char *name;
/* The unexpanded name of a .USE node */
char *uname;
- /* The full pathname of the file belonging to the target.
+ /*
+ * The full pathname of the file belonging to the target.
+ *
* XXX: What about .PHONY targets? These don't have an associated
- * path. */
+ * path.
+ */
char *path;
- /* The type of operator used to define the sources (see the OP flags
+ /*
+ * The type of operator used to define the sources (see the OP flags
* below).
- * XXX: This looks like a wild mixture of type and flags. */
+ *
+ * XXX: This looks like a wild mixture of type and flags.
+ */
GNodeType type;
GNodeFlags flags;
@@ -381,29 +457,39 @@ typedef struct GNode {
/* The number of unmade children */
int unmade;
- /* The modification time; 0 means the node does not have a
- * corresponding file; see GNode_IsOODate. */
+ /*
+ * The modification time; 0 means the node does not have a
+ * corresponding file; see GNode_IsOODate.
+ */
time_t mtime;
struct GNode *youngestChild;
- /* The GNodes for which this node is an implied source. May be empty.
- * For example, when there is an inference rule for .c.o, the node for
- * file.c has the node for file.o in this list. */
+ /*
+ * The GNodes for which this node is an implied source. May be empty.
+ * For example, when there is an inference rule for .c.o, the node
+ * for file.c has the node for file.o in this list.
+ */
GNodeList implicitParents;
- /* The nodes that depend on this one, or in other words, the nodes for
- * which this is a source. */
+ /*
+ * The nodes that depend on this one, or in other words, the nodes
+ * for which this is a source.
+ */
GNodeList parents;
/* The nodes on which this one depends. */
GNodeList children;
- /* .ORDER nodes we need made. The nodes that must be made (if they're
+ /*
+ * .ORDER nodes we need made. The nodes that must be made (if they're
* made) before this node can be made, but that do not enter into the
- * datedness of this node. */
+ * datedness of this node.
+ */
GNodeList order_pred;
- /* .ORDER nodes who need us. The nodes that must be made (if they're
+ /*
+ * .ORDER nodes who need us. The nodes that must be made (if they're
* made at all) after this node is made, but that do not depend on
- * this node, in the normal sense. */
+ * this node, in the normal sense.
+ */
GNodeList order_succ;
/*
@@ -415,8 +501,10 @@ typedef struct GNode {
char cohort_num[8];
/* The number of unmade instances on the cohorts list */
int unmade_cohorts;
- /* Pointer to the first instance of a '::' node; only set when on a
- * cohorts list */
+ /*
+ * Pointer to the first instance of a '::' node; only set when on a
+ * cohorts list
+ */
struct GNode *centurion;
/* Last time (sequence number) we tried to make this node */
@@ -435,21 +523,36 @@ typedef struct GNode {
/* The commands to be given to a shell to create this target. */
StringList commands;
- /* Suffix for the node (determined by Suff_FindDeps and opaque to
- * everyone but the Suff module) */
+ /*
+ * Suffix for the node (determined by Suff_FindDeps and opaque to
+ * everyone but the Suff module)
+ */
struct Suffix *suffix;
- /* Filename where the GNode got defined */
- /* XXX: What is the lifetime of this string? */
+ /* Filename where the GNode got defined, unlimited lifetime */
const char *fname;
- /* Line number where the GNode got defined */
- int lineno;
+ /* Line number where the GNode got defined, 1-based */
+ unsigned lineno;
+ int exit_status;
} GNode;
+/*
+ * Keep track of whether to include <posix.mk> when parsing the line
+ * '.POSIX:'.
+ */
+extern enum PosixState {
+ PS_NOT_YET,
+ PS_MAYBE_NEXT_LINE,
+ PS_NOW_OR_NEVER,
+ PS_TOO_LATE
+} posix_state;
+
/* Error levels for diagnostics during parsing. */
typedef enum ParseErrorLevel {
- /* Exit when the current top-level makefile has been parsed
- * completely. */
+ /*
+ * Exit when the current top-level makefile has been parsed
+ * completely.
+ */
PARSE_FATAL = 1,
/* Print "warning"; may be upgraded to fatal by the -w option. */
PARSE_WARNING,
@@ -460,20 +563,28 @@ typedef enum ParseErrorLevel {
/*
* Values returned by Cond_EvalLine and Cond_EvalCondition.
*/
-typedef enum CondEvalResult {
- COND_PARSE, /* Parse the next lines */
- COND_SKIP, /* Skip the next lines */
- COND_INVALID /* Not a conditional statement */
-} CondEvalResult;
+typedef enum CondResult {
+ CR_TRUE, /* Parse the next lines */
+ CR_FALSE, /* Skip the next lines */
+ CR_ERROR /* Unknown directive or parse error */
+} CondResult;
+
+typedef struct {
+ enum GuardKind {
+ GK_VARIABLE,
+ GK_TARGET
+ } kind;
+ char *name;
+} Guard;
/* Names of the variables that are "local" to a specific target. */
-#define TARGET "@" /* Target of dependency */
-#define OODATE "?" /* All out-of-date sources */
-#define ALLSRC ">" /* All sources */
-#define IMPSRC "<" /* Source implied by transformation */
-#define PREFIX "*" /* Common prefix */
-#define ARCHIVE "!" /* Archive in "archive(member)" syntax */
-#define MEMBER "%" /* Member in "archive(member)" syntax */
+#define TARGET "@" /* Target of dependency */
+#define OODATE "?" /* All out-of-date sources */
+#define ALLSRC ">" /* All sources */
+#define IMPSRC "<" /* Source implied by transformation */
+#define PREFIX "*" /* Common prefix */
+#define ARCHIVE "!" /* Archive in "archive(member)" syntax */
+#define MEMBER "%" /* Member in "archive(member)" syntax */
/*
* Global Variables
@@ -499,8 +610,8 @@ extern GNode *SCOPE_GLOBAL;
extern GNode *SCOPE_CMDLINE;
/*
- * Value returned by Var_Parse when an error is encountered. It actually
- * points to an empty string, so naive callers needn't worry about it.
+ * Value returned by Var_Parse when an error is encountered. It points to an
+ * empty string, so naive callers needn't worry about it.
*/
extern char var_Error[];
@@ -531,51 +642,41 @@ extern int makelevel;
extern char *makeDependfile;
/* If we replaced environ, this will be non-NULL. */
extern char **savedEnv;
+extern GNode *mainNode;
extern pid_t myPid;
#define MAKEFLAGS ".MAKEFLAGS"
-#define MAKEOVERRIDES ".MAKEOVERRIDES"
-/* prefix when printing the target of a job */
-#define MAKE_JOB_PREFIX ".MAKE.JOB.PREFIX"
-#define MAKE_EXPORTED ".MAKE.EXPORTED" /* exported variables */
-#define MAKE_MAKEFILES ".MAKE.MAKEFILES" /* all loaded makefiles */
-#define MAKE_LEVEL ".MAKE.LEVEL" /* recursion level */
-#define MAKE_MAKEFILE_PREFERENCE ".MAKE.MAKEFILE_PREFERENCE"
-#define MAKE_DEPENDFILE ".MAKE.DEPENDFILE" /* .depend */
-#define MAKE_MODE ".MAKE.MODE"
#ifndef MAKE_LEVEL_ENV
# define MAKE_LEVEL_ENV "MAKELEVEL"
#endif
-typedef enum DebugFlags {
- DEBUG_NONE = 0,
- DEBUG_ARCH = 1 << 0,
- DEBUG_COND = 1 << 1,
- DEBUG_CWD = 1 << 2,
- DEBUG_DIR = 1 << 3,
- DEBUG_ERROR = 1 << 4,
- DEBUG_FOR = 1 << 5,
- DEBUG_GRAPH1 = 1 << 6,
- DEBUG_GRAPH2 = 1 << 7,
- DEBUG_GRAPH3 = 1 << 8,
- DEBUG_HASH = 1 << 9,
- DEBUG_JOB = 1 << 10,
- DEBUG_LOUD = 1 << 11,
- DEBUG_MAKE = 1 << 12,
- DEBUG_META = 1 << 13,
- DEBUG_PARSE = 1 << 14,
- DEBUG_SCRIPT = 1 << 15,
- DEBUG_SHELL = 1 << 16,
- DEBUG_SUFF = 1 << 17,
- DEBUG_TARG = 1 << 18,
- DEBUG_VAR = 1 << 19,
- DEBUG_ALL = (1 << 20) - 1
+typedef struct DebugFlags {
+ bool DEBUG_ARCH:1;
+ bool DEBUG_COND:1;
+ bool DEBUG_CWD:1;
+ bool DEBUG_DIR:1;
+ bool DEBUG_ERROR:1;
+ bool DEBUG_FOR:1;
+ bool DEBUG_GRAPH1:1;
+ bool DEBUG_GRAPH2:1;
+ bool DEBUG_GRAPH3:1;
+ bool DEBUG_HASH:1;
+ bool DEBUG_JOB:1;
+ bool DEBUG_LOUD:1;
+ bool DEBUG_MAKE:1;
+ bool DEBUG_META:1;
+ bool DEBUG_PARSE:1;
+ bool DEBUG_SCRIPT:1;
+ bool DEBUG_SHELL:1;
+ bool DEBUG_SUFF:1;
+ bool DEBUG_TARG:1;
+ bool DEBUG_VAR:1;
} DebugFlags;
#define CONCAT(a, b) a##b
-#define DEBUG(module) ((opts.debug & CONCAT(DEBUG_, module)) != 0)
+#define DEBUG(module) (opts.debug.CONCAT(DEBUG_, module))
void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
@@ -583,10 +684,10 @@ 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))
+#define DEBUG0(module, fmt) \
+ DEBUG_IMPL(module, (fmt))
#define DEBUG1(module, fmt, arg1) \
DEBUG_IMPL(module, (fmt, arg1))
#define DEBUG2(module, fmt, arg1, arg2) \
@@ -606,20 +707,24 @@ typedef enum PrintVarsMode {
/* Command line options */
typedef struct CmdOpts {
- /* -B: whether we are make compatible */
+ /* -B: whether to be compatible to traditional make */
bool compatMake;
- /* -d: debug control: There is one bit per module. It is up to the
- * module what debug information to print. */
+ /*
+ * -d: debug control: There is one flag per module. It is up to the
+ * module what debug information to print.
+ */
DebugFlags debug;
/* -df: debug output is written here - default stderr */
FILE *debug_file;
- /* -dL: lint mode
+ /*
+ * -dL: lint mode
*
* Runs make in strict mode, with additional checks and better error
- * handling. */
+ * handling.
+ */
bool strict;
/* -dV: for the -V option, print unexpanded variable values */
@@ -634,12 +739,16 @@ typedef struct CmdOpts {
/* -i: if true, ignore all errors from shell commands */
bool ignoreErrors;
- /* -j: the maximum number of jobs that can run in parallel;
- * this is coordinated with the submakes */
+ /*
+ * -j: the maximum number of jobs that can run in parallel; this is
+ * coordinated with the submakes
+ */
int maxJobs;
- /* -k: if true and an error occurs while making a node, continue
- * making nodes that do not depend on the erroneous node */
+ /*
+ * -k: if true and an error occurs while making a node, continue
+ * making nodes that do not depend on the erroneous node
+ */
bool keepgoing;
/* -N: execute no commands from the targets */
@@ -652,17 +761,19 @@ typedef struct CmdOpts {
* -q: if true, do not really make anything, just see if the targets
* are out-of-date
*/
- bool queryFlag;
+ bool query;
/* -r: raw mode, do not load the builtin rules. */
bool noBuiltins;
/* -s: don't echo the shell commands before executing them */
- bool beSilent;
+ bool silent;
- /* -t: touch the targets if they are out-of-date, but don't actually
- * make them */
- bool touchFlag;
+ /*
+ * -t: touch the targets if they are out-of-date, but don't actually
+ * make them
+ */
+ bool touch;
/* -[Vv]: print expanded or unexpanded selected variables */
PrintVarsMode printVars;
@@ -675,99 +786,377 @@ typedef struct CmdOpts {
/* -w: print 'Entering' and 'Leaving' for submakes */
bool enterFlag;
- /* -X: if true, do not export variables set on the command line to the
- * environment. */
+ /*
+ * -X: if true, do not export variables set on the command line to
+ * the environment.
+ */
bool varNoExportEnv;
- /* The target names specified on the command line.
- * Used to resolve .if make(...) statements. */
+ /*
+ * The target names specified on the command line. Used to resolve
+ * .if make(...) statements.
+ */
StringList create;
+ /*
+ * Randomize the order in which the targets from toBeMade are made,
+ * to catch undeclared dependencies.
+ */
+ bool randomizeTargets;
} CmdOpts;
extern CmdOpts opts;
+extern bool forceJobs;
+extern char **environ;
+
+/* arch.c */
+void Arch_Init(void);
+void Arch_End(void);
+
+bool Arch_ParseArchive(char **, GNodeList *, GNode *);
+void Arch_Touch(GNode *);
+void Arch_TouchLib(GNode *);
+void Arch_UpdateMTime(GNode *);
+void Arch_UpdateMemberMTime(GNode *);
+void Arch_FindLib(GNode *, SearchPath *);
+bool Arch_LibOODate(GNode *) MAKE_ATTR_USE;
+bool Arch_IsLib(GNode *) MAKE_ATTR_USE;
+
+/* compat.c */
+bool Compat_RunCommand(const char *, GNode *, StringListNode *);
+void Compat_MakeAll(GNodeList *);
+void Compat_Make(GNode *, GNode *);
+
+/* cond.c */
+extern unsigned int cond_depth;
+CondResult Cond_EvalCondition(const char *) MAKE_ATTR_USE;
+CondResult Cond_EvalLine(const char *) MAKE_ATTR_USE;
+Guard *Cond_ExtractGuard(const char *) MAKE_ATTR_USE;
+void Cond_EndFile(void);
+
+/* dir.c; see also dir.h */
+
+MAKE_INLINE const char * MAKE_ATTR_USE
+str_basename(const char *pathname)
+{
+ const char *lastSlash = strrchr(pathname, '/');
+ return lastSlash != NULL ? lastSlash + 1 : pathname;
+}
+
+MAKE_INLINE SearchPath * MAKE_ATTR_USE
+SearchPath_New(void)
+{
+ SearchPath *path = bmake_malloc(sizeof *path);
+ Lst_Init(&path->dirs);
+ return path;
+}
+
+void SearchPath_Free(SearchPath *);
+
+/* for.c */
+struct ForLoop;
+int For_Eval(const char *) MAKE_ATTR_USE;
+bool For_Accum(const char *, int *) MAKE_ATTR_USE;
+void For_Run(unsigned, unsigned);
+bool For_NextIteration(struct ForLoop *, Buffer *);
+char *ForLoop_Details(const struct ForLoop *);
+void ForLoop_Free(struct ForLoop *);
+void For_Break(struct ForLoop *);
+
+/* job.c */
+void JobReapChild(pid_t, int, bool);
+
+/* main.c */
+void Main_ParseArgLine(const char *);
+char *Cmd_Exec(const char *, char **) MAKE_ATTR_USE;
+void Error(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
+void Fatal(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD;
+void Punt(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD;
+void DieHorribly(void) MAKE_ATTR_DEAD;
+void Finish(int) MAKE_ATTR_DEAD;
+int unlink_file(const char *) MAKE_ATTR_USE;
+void execDie(const char *, const char *);
+char *getTmpdir(void) MAKE_ATTR_USE;
+bool ParseBoolean(const char *, bool) MAKE_ATTR_USE;
+const char *cached_realpath(const char *, char *);
+bool GetBooleanExpr(const char *, bool);
+
+/* parse.c */
+void Parse_Init(void);
+void Parse_End(void);
+
+void PrintLocation(FILE *, bool, const GNode *);
+void PrintStackTrace(bool);
+void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
+bool Parse_VarAssign(const char *, bool, GNode *) MAKE_ATTR_USE;
+void Parse_File(const char *, int);
+void Parse_PushInput(const char *, unsigned, unsigned, Buffer,
+ struct ForLoop *);
+void Parse_MainName(GNodeList *);
+int Parse_NumErrors(void) MAKE_ATTR_USE;
+unsigned int CurFile_CondMinDepth(void) MAKE_ATTR_USE;
+void Parse_GuardElse(void);
+void Parse_GuardEndif(void);
+
+
+/* suff.c */
+void Suff_Init(void);
+void Suff_End(void);
+
+void Suff_ClearSuffixes(void);
+bool Suff_IsTransform(const char *) MAKE_ATTR_USE;
+GNode *Suff_AddTransform(const char *);
+void Suff_EndTransform(GNode *);
+void Suff_AddSuffix(const char *);
+SearchPath *Suff_GetPath(const char *) MAKE_ATTR_USE;
+void Suff_ExtendPaths(void);
+void Suff_AddInclude(const char *);
+void Suff_AddLib(const char *);
+void Suff_FindDeps(GNode *);
+SearchPath *Suff_FindPath(GNode *) MAKE_ATTR_USE;
+void Suff_SetNull(const char *);
+void Suff_PrintAll(void);
+char *Suff_NamesStr(void) MAKE_ATTR_USE;
+
+/* targ.c */
+void Targ_Init(void);
+void Targ_End(void);
+
+void Targ_Stats(void);
+GNodeList *Targ_List(void) MAKE_ATTR_USE;
+GNode *GNode_New(const char *) MAKE_ATTR_USE;
+GNode *Targ_FindNode(const char *) MAKE_ATTR_USE;
+GNode *Targ_GetNode(const char *) MAKE_ATTR_USE;
+GNode *Targ_NewInternalNode(const char *) MAKE_ATTR_USE;
+GNode *Targ_GetEndNode(void);
+void Targ_FindList(GNodeList *, StringList *);
+void Targ_PrintCmds(GNode *);
+void Targ_PrintNode(GNode *, int);
+void Targ_PrintNodes(GNodeList *, int);
+const char *Targ_FmtTime(time_t) MAKE_ATTR_USE;
+void Targ_PrintType(GNodeType);
+void Targ_PrintGraph(int);
+void Targ_Propagate(void);
+const char *GNodeMade_Name(GNodeMade) MAKE_ATTR_USE;
+
+/* var.c */
+void Var_Init(void);
+void Var_End(void);
+
+typedef enum VarEvalMode {
+
+ /*
+ * Only parse the expression but don't evaluate any part of it.
+ *
+ * TODO: Document what Var_Parse and Var_Subst return in this mode.
+ * As of 2021-03-15, they return unspecified, inconsistent results.
+ */
+ VARE_PARSE_ONLY,
+
+ /*
+ * Parse text in which '${...}' and '$(...)' are not parsed as
+ * subexpressions (with all their individual escaping rules) but
+ * instead simply as text with balanced '${}' or '$()'. Other '$'
+ * are copied verbatim.
+ */
+ VARE_PARSE_BALANCED,
+
+ /* Parse and evaluate the expression. */
+ VARE_WANTRES,
+
+ /*
+ * Parse and evaluate the expression. It is an error if a
+ * subexpression evaluates to undefined.
+ */
+ VARE_UNDEFERR,
-#include "nonints.h"
+ /*
+ * Parse and evaluate the expression. Keep '$$' as '$$' instead of
+ * reducing it to a single '$'. Subexpressions that evaluate to
+ * undefined expand to an empty string.
+ *
+ * Used in variable assignments using the ':=' operator. It allows
+ * multiple such assignments to be chained without accidentally
+ * expanding '$$file' to '$file' in the first assignment and
+ * interpreting it as '${f}' followed by 'ile' in the next assignment.
+ */
+ VARE_EVAL_KEEP_DOLLAR,
+
+ /*
+ * Parse and evaluate the expression. Keep undefined variables as-is
+ * instead of expanding them to an empty string.
+ *
+ * Example for a ':=' assignment:
+ * CFLAGS = $(.INCLUDES)
+ * CFLAGS := -I.. $(CFLAGS)
+ * # If .INCLUDES (an undocumented special variable, by the
+ * # way) is still undefined, the updated CFLAGS becomes
+ * # "-I.. $(.INCLUDES)".
+ */
+ VARE_EVAL_KEEP_UNDEF,
+
+ /*
+ * Parse and evaluate the expression. Keep '$$' as '$$' and preserve
+ * undefined subexpressions.
+ */
+ VARE_KEEP_DOLLAR_UNDEF
+} VarEvalMode;
+
+typedef enum VarSetFlags {
+ VAR_SET_NONE = 0,
+ /* do not export */
+ VAR_SET_NO_EXPORT = 1 << 0,
+
+ /*
+ * Make the variable read-only. No further modification is possible,
+ * except for another call to Var_Set with the same flag. See the
+ * special targets '.NOREADONLY' and '.READONLY'.
+ */
+ VAR_SET_READONLY = 1 << 1,
+ VAR_SET_INTERNAL = 1 << 2
+} VarSetFlags;
+
+typedef enum VarExportMode {
+ /* .export-env */
+ VEM_ENV,
+ /* .export: Initial export or update an already exported variable. */
+ VEM_PLAIN,
+ /* .export-literal: Do not expand the variable value. */
+ VEM_LITERAL
+} VarExportMode;
+
+void Var_Delete(GNode *, const char *);
+void Var_Undef(const char *);
+void Var_Set(GNode *, const char *, const char *);
+void Var_SetExpand(GNode *, const char *, const char *);
+void Var_SetWithFlags(GNode *, const char *, const char *, VarSetFlags);
+void Var_Append(GNode *, const char *, const char *);
+void Var_AppendExpand(GNode *, const char *, const char *);
+bool Var_Exists(GNode *, const char *) MAKE_ATTR_USE;
+bool Var_ExistsExpand(GNode *, const char *) MAKE_ATTR_USE;
+FStr Var_Value(GNode *, const char *) MAKE_ATTR_USE;
+const char *GNode_ValueDirect(GNode *, const char *) MAKE_ATTR_USE;
+FStr Var_Parse(const char **, GNode *, VarEvalMode);
+char *Var_Subst(const char *, GNode *, VarEvalMode);
+void Var_Expand(FStr *, GNode *, VarEvalMode);
+void Var_Stats(void);
+void Var_Dump(GNode *);
+void Var_ReexportVars(GNode *);
+void Var_Export(VarExportMode, const char *);
+void Var_ExportVars(const char *);
+void Var_UnExport(bool, const char *);
+void Var_ReadOnly(const char *, bool);
+
+void Global_Set(const char *, const char *);
+void Global_Append(const char *, const char *);
+void Global_Delete(const char *);
+void Global_Set_ReadOnly(const char *, const char *);
+
+void EvalStack_Push(const char *, const char *, const char *);
+void EvalStack_Pop(void);
+const char *EvalStack_Details(void);
+
+/* util.c */
+typedef void (*SignalProc)(int);
+SignalProc bmake_signal(int, SignalProc);
+
+/* make.c */
void GNode_UpdateYoungestChild(GNode *, GNode *);
-bool GNode_IsOODate(GNode *);
+bool GNode_IsOODate(GNode *) MAKE_ATTR_USE;
void Make_ExpandUse(GNodeList *);
-time_t Make_Recheck(GNode *);
+time_t Make_Recheck(GNode *) MAKE_ATTR_USE;
void Make_HandleUse(GNode *, GNode *);
void Make_Update(GNode *);
void GNode_SetLocalVars(GNode *);
bool Make_Run(GNodeList *);
-bool shouldDieQuietly(GNode *, int);
+bool shouldDieQuietly(GNode *, int) MAKE_ATTR_USE;
void PrintOnError(GNode *, const char *);
void Main_ExportMAKEFLAGS(bool);
bool Main_SetObjdir(bool, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
-int mkTempFile(const char *, char *, size_t);
-int str2Lst_Append(StringList *, char *);
+int mkTempFile(const char *, char *, size_t) MAKE_ATTR_USE;
+void AppendWords(StringList *, char *);
void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *);
-bool GNode_ShouldExecute(GNode *gn);
+bool GNode_ShouldExecute(GNode *gn) MAKE_ATTR_USE;
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *, const char *, size_t);
+#endif
/* See if the node was seen on the left-hand side of a dependency operator. */
-MAKE_INLINE bool
+MAKE_INLINE bool MAKE_ATTR_USE
GNode_IsTarget(const GNode *gn)
{
- return (gn->type & OP_OPMASK) != 0;
+ return (gn->type & OP_OPMASK) != OP_NONE;
}
-MAKE_INLINE const char *
+MAKE_INLINE const char * MAKE_ATTR_USE
GNode_Path(const GNode *gn)
{
return gn->path != NULL ? gn->path : gn->name;
}
-MAKE_INLINE bool
+MAKE_INLINE bool MAKE_ATTR_USE
GNode_IsWaitingFor(const GNode *gn)
{
- return (gn->flags & REMAKE) && gn->made <= REQUESTED;
+ return gn->flags.remake && gn->made <= REQUESTED;
}
-MAKE_INLINE bool
+MAKE_INLINE bool MAKE_ATTR_USE
GNode_IsReady(const GNode *gn)
{
return gn->made > DEFERRED;
}
-MAKE_INLINE bool
+MAKE_INLINE bool MAKE_ATTR_USE
GNode_IsDone(const GNode *gn)
{
return gn->made >= MADE;
}
-MAKE_INLINE bool
+MAKE_INLINE bool MAKE_ATTR_USE
GNode_IsError(const GNode *gn)
{
return gn->made == ERROR || gn->made == ABORTED;
}
-MAKE_INLINE const char *
+MAKE_INLINE bool MAKE_ATTR_USE
+GNode_IsMainCandidate(const GNode *gn)
+{
+ return (gn->type & (OP_NOTMAIN | OP_USE | OP_USEBEFORE |
+ OP_EXEC | OP_TRANSFORM)) == 0;
+}
+
+/* Return whether the target file should be preserved on interrupt. */
+MAKE_INLINE bool MAKE_ATTR_USE
+GNode_IsPrecious(const GNode *gn)
+{
+ /* XXX: Why are '::' targets precious? */
+ return allPrecious || gn->type & (OP_PRECIOUS | OP_DOUBLEDEP);
+}
+
+MAKE_INLINE const char * MAKE_ATTR_USE
GNode_VarTarget(GNode *gn) { return GNode_ValueDirect(gn, TARGET); }
-MAKE_INLINE const char *
+MAKE_INLINE const char * MAKE_ATTR_USE
GNode_VarOodate(GNode *gn) { return GNode_ValueDirect(gn, OODATE); }
-MAKE_INLINE const char *
+MAKE_INLINE const char * MAKE_ATTR_USE
GNode_VarAllsrc(GNode *gn) { return GNode_ValueDirect(gn, ALLSRC); }
-MAKE_INLINE const char *
+MAKE_INLINE const char * MAKE_ATTR_USE
GNode_VarImpsrc(GNode *gn) { return GNode_ValueDirect(gn, IMPSRC); }
-MAKE_INLINE const char *
+MAKE_INLINE const char * MAKE_ATTR_USE
GNode_VarPrefix(GNode *gn) { return GNode_ValueDirect(gn, PREFIX); }
-MAKE_INLINE const char *
+MAKE_INLINE const char * MAKE_ATTR_USE
GNode_VarArchive(GNode *gn) { return GNode_ValueDirect(gn, ARCHIVE); }
-MAKE_INLINE const char *
+MAKE_INLINE const char * MAKE_ATTR_USE
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 * MAKE_ATTR_USE
+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
@@ -786,19 +1175,21 @@ GNode_VarMember(GNode *gn) { return GNode_ValueDirect(gn, MEMBER); }
#define KILLPG(pid, sig) killpg((pid), (sig))
#endif
-MAKE_INLINE bool
+MAKE_INLINE bool MAKE_ATTR_USE
ch_isalnum(char ch) { return isalnum((unsigned char)ch) != 0; }
-MAKE_INLINE bool
+MAKE_INLINE bool MAKE_ATTR_USE
ch_isalpha(char ch) { return isalpha((unsigned char)ch) != 0; }
-MAKE_INLINE bool
+MAKE_INLINE bool MAKE_ATTR_USE
ch_isdigit(char ch) { return isdigit((unsigned char)ch) != 0; }
-MAKE_INLINE bool
+MAKE_INLINE bool MAKE_ATTR_USE
+ch_islower(char ch) { return islower((unsigned char)ch) != 0; }
+MAKE_INLINE bool MAKE_ATTR_USE
ch_isspace(char ch) { return isspace((unsigned char)ch) != 0; }
-MAKE_INLINE bool
+MAKE_INLINE bool MAKE_ATTR_USE
ch_isupper(char ch) { return isupper((unsigned char)ch) != 0; }
-MAKE_INLINE char
+MAKE_INLINE char MAKE_ATTR_USE
ch_tolower(char ch) { return (char)tolower((unsigned char)ch); }
-MAKE_INLINE char
+MAKE_INLINE char MAKE_ATTR_USE
ch_toupper(char ch) { return (char)toupper((unsigned char)ch); }
MAKE_INLINE void
@@ -815,6 +1206,17 @@ cpp_skip_hspace(const char **pp)
(*pp)++;
}
+MAKE_INLINE bool
+cpp_skip_string(const char **pp, const char *s)
+{
+ const char *p = *pp;
+ while (*p == *s && *s != '\0')
+ p++, s++;
+ if (*s == '\0')
+ *pp = p;
+ return *s == '\0';
+}
+
MAKE_INLINE void
pp_skip_whitespace(char **pp)
{
@@ -830,18 +1232,29 @@ pp_skip_hspace(char **pp)
}
#if defined(lint)
-# define MAKE_RCSID(id) extern void do_not_define_rcsid(void)
+void do_not_define_rcsid(void); /* for lint */
+# define MAKE_RCSID(id) 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)
# define MAKE_RCSID(id) static volatile char \
MAKE_RCSID_CONCAT(rcsid_, __COUNTER__)[] = id
#elif defined(MAKE_ALL_IN_ONE)
-# define MAKE_RCSID(id) extern void do_not_define_rcsid(void)
+# define MAKE_RCSID(id) void do_not_define_rcsid(void)
#else
# define MAKE_RCSID(id) static volatile char rcsid[] = id
#endif
-#endif /* MAKE_MAKE_H */
+#endif
diff --git a/contrib/bmake/make_malloc.c b/contrib/bmake/make_malloc.c
index 42a69f43704f..ea347e0ec2ca 100644
--- a/contrib/bmake/make_malloc.c
+++ b/contrib/bmake/make_malloc.c
@@ -1,4 +1,4 @@
-/* $NetBSD: make_malloc.c,v 1.25 2021/01/19 20:51:46 rillig Exp $ */
+/* $NetBSD: make_malloc.c,v 1.26 2022/01/07 08:30:04 rillig Exp $ */
/*
* Copyright (c) 2009 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
#include "make.h"
-MAKE_RCSID("$NetBSD: make_malloc.c,v 1.25 2021/01/19 20:51:46 rillig Exp $");
+MAKE_RCSID("$NetBSD: make_malloc.c,v 1.26 2022/01/07 08:30:04 rillig Exp $");
#ifndef USE_EMALLOC
@@ -57,12 +57,12 @@ bmake_malloc(size_t len)
char *
bmake_strdup(const char *str)
{
- size_t len;
+ size_t size;
char *p;
- len = strlen(str) + 1;
- p = bmake_malloc(len);
- return memcpy(p, str, len);
+ size = strlen(str) + 1;
+ p = bmake_malloc(size);
+ return memcpy(p, str, size);
}
/* Allocate a string starting from str with exactly len characters. */
diff --git a/contrib/bmake/make_malloc.h b/contrib/bmake/make_malloc.h
index 72f6c11fd882..b1cfe8487837 100644
--- a/contrib/bmake/make_malloc.h
+++ b/contrib/bmake/make_malloc.h
@@ -1,4 +1,4 @@
-/* $NetBSD: make_malloc.h,v 1.16 2021/01/19 20:51:46 rillig Exp $ */
+/* $NetBSD: make_malloc.h,v 1.18 2021/12/15 11:01:39 rillig Exp $ */
/*
* Copyright (c) 2009 The NetBSD Foundation, Inc.
@@ -27,10 +27,10 @@
*/
#ifndef USE_EMALLOC
-void *bmake_malloc(size_t);
-void *bmake_realloc(void *, size_t);
-char *bmake_strdup(const char *);
-char *bmake_strldup(const char *, size_t);
+void *bmake_malloc(size_t) MAKE_ATTR_USE;
+void *bmake_realloc(void *, size_t) MAKE_ATTR_USE;
+char *bmake_strdup(const char *) MAKE_ATTR_USE;
+char *bmake_strldup(const char *, size_t) MAKE_ATTR_USE;
#else
#include <util.h>
#define bmake_malloc(n) emalloc(n)
@@ -39,18 +39,4 @@ char *bmake_strldup(const char *, size_t);
#define bmake_strldup(s, n) estrndup(s, n)
#endif
-char *bmake_strsedup(const char *, const char *);
-
-/*
- * Thin wrapper around free(3) to avoid the extra function call in case
- * p is NULL, to save a few machine instructions.
- *
- * The case of a NULL pointer happens especially often after Var_Value,
- * since only environment variables need to be freed, but not others.
- */
-MAKE_INLINE void
-bmake_free(void *p)
-{
- if (p != NULL)
- free(p);
-}
+char *bmake_strsedup(const char *, const char *) MAKE_ATTR_USE;
diff --git a/contrib/bmake/meta.c b/contrib/bmake/meta.c
index c1f78136fb7c..dea3ed12d965 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.208 2024/04/27 17:33:46 rillig Exp $ */
/*
* Implement 'meta' mode.
@@ -69,6 +69,9 @@ static char *metaIgnorePathsStr; /* string storage for the list */
#ifndef MAKE_META_IGNORE_FILTER
#define MAKE_META_IGNORE_FILTER ".MAKE.META.IGNORE_FILTER"
#endif
+#ifndef MAKE_META_CMP_FILTER
+#define MAKE_META_CMP_FILTER ".MAKE.META.CMP_FILTER"
+#endif
bool useMeta = false;
static bool useFilemon = false;
@@ -80,11 +83,10 @@ static bool metaVerbose = false;
static bool metaIgnoreCMDs = false; /* ignore CMDs in .meta files */
static bool metaIgnorePatterns = false; /* do we need to do pattern matches */
static bool metaIgnoreFilter = false; /* do we have more complex filtering? */
+static bool metaCmpFilter = false; /* do we have CMP_FILTER ? */
static bool metaCurdirOk = false; /* write .meta in .CURDIR Ok? */
static bool metaSilent = false; /* if we have a .meta be SILENT */
-extern bool forceJobs;
-extern char **environ;
#define MAKE_META_PREFIX ".MAKE.META.PREFIX"
@@ -98,6 +100,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 +154,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 +196,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;
@@ -203,42 +210,25 @@ filemon_read(FILE *mfp, int fd)
* we use this, to clean up ./ and ../
*/
static void
-eat_dots(char *buf, size_t bufsz, int dots)
+eat_dots(char *buf)
{
- char *cp;
- char *cp2;
- const char *eat;
- size_t eatlen;
-
- switch (dots) {
- case 1:
- eat = "/./";
- eatlen = 2;
- break;
- case 2:
- eat = "/../";
- eatlen = 3;
- break;
- default:
- return;
- }
+ char *p;
- do {
- cp = strstr(buf, eat);
- if (cp != NULL) {
- cp2 = cp + eatlen;
- if (dots == 2 && cp > buf) {
- do {
- cp--;
- } while (cp > buf && *cp != '/');
- }
- if (*cp == '/') {
- strlcpy(cp, cp2, bufsz - (size_t)(cp - buf));
- } else {
- return; /* can't happen? */
- }
+ while ((p = strstr(buf, "/./")) != NULL)
+ memmove(p, p + 2, strlen(p + 2) + 1);
+
+ while ((p = strstr(buf, "/../")) != NULL) {
+ char *p2 = p + 3;
+ if (p > buf) {
+ do {
+ p--;
+ } while (p > buf && *p != '/');
}
- } while (cp != NULL);
+ if (*p == '/')
+ memmove(p, p2, strlen(p2) + 1);
+ else
+ return; /* can't happen? */
+ }
}
static char *
@@ -248,8 +238,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 +251,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 {
@@ -282,23 +272,26 @@ meta_name(char *mname, size_t mnamelen,
} else {
snprintf(buf, sizeof buf, "%s/%s", cwd, tname);
}
- eat_dots(buf, sizeof buf, 1); /* ./ */
- eat_dots(buf, sizeof buf, 2); /* ../ */
+ eat_dots(buf);
tname = buf;
}
}
/* on some systems dirname may modify its arg */
tp = bmake_strdup(tname);
dtp = dirname(tp);
- if (strcmp(dname, dtp) == 0)
- snprintf(mname, mnamelen, "%s.meta", tname);
- else {
+ if (strcmp(dname, dtp) == 0) {
+ if (snprintf(mname, mnamelen, "%s.meta", tname) >= (int)mnamelen)
+ mname[mnamelen - 1] = '\0';
+ } else {
+ int x;
+
ldname = strlen(dname);
if (strncmp(dname, dtp, ldname) == 0 && dtp[ldname] == '/')
- snprintf(mname, mnamelen, "%s/%s.meta", dname, &tname[ldname+1]);
+ x = snprintf(mname, mnamelen, "%s/%s.meta", dname, &tname[ldname+1]);
else
- snprintf(mname, mnamelen, "%s/%s.meta", dname, tname);
-
+ x = snprintf(mname, mnamelen, "%s/%s.meta", dname, tname);
+ if (x >= (int)mnamelen)
+ mname[mnamelen - 1] = '\0';
/*
* Replace path separators in the file name after the
* current object directory path.
@@ -325,17 +318,15 @@ 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 *cp2;
bool rc = false;
if (p_make == NULL) {
p_make = Var_Value(gn, ".MAKE").str;
p_len = strlen(p_make);
}
- cp = strchr(cmd, '$');
- if (cp != NULL) {
- (void)Var_Subst(cmd, gn, VARE_WANTRES, &mp);
+ if (strchr(cmd, '$') != NULL) {
+ mp = Var_Subst(cmd, gn, VARE_WANTRES);
/* TODO: handle errors */
cmd = mp;
}
@@ -381,13 +372,7 @@ printCMD(const char *ucmd, FILE *fp, GNode *gn)
{
FStr xcmd = FStr_InitRefer(ucmd);
- if (strchr(ucmd, '$') != NULL) {
- char *expanded;
- (void)Var_Subst(ucmd, gn, VARE_WANTRES, &expanded);
- /* TODO: handle errors */
- xcmd = FStr_InitOwn(expanded);
- }
-
+ Var_Expand(&xcmd, gn, VARE_WANTRES);
fprintf(fp, "CMD %s\n", xcmd.str);
FStr_Done(&xcmd);
}
@@ -404,15 +389,13 @@ printCMDs(GNode *gn, FILE *fp)
/*
* Certain node types never get a .meta file
*/
-#define SKIP_META_TYPE(_type) do { \
- if ((gn->type & __CONCAT(OP_, _type))) { \
- if (verbose) { \
- debug_printf("Skipping meta for %s: .%s\n", \
- gn->name, __STRING(_type)); \
- } \
+#define SKIP_META_TYPE(flag, str) do { \
+ if ((gn->type & (flag))) { \
+ if (verbose) \
+ debug_printf("Skipping meta for %s: .%s\n", gn->name, str); \
return false; \
} \
-} while (/*CONSTCOND*/false)
+} while (false)
/*
@@ -430,12 +413,12 @@ meta_needed(GNode *gn, const char *dname,
/* This may be a phony node which we don't want meta data for... */
/* Skip .meta for .BEGIN, .END, .ERROR etc as well. */
/* Or it may be explicitly flagged as .NOMETA */
- SKIP_META_TYPE(NOMETA);
+ SKIP_META_TYPE(OP_NOMETA, "NOMETA");
/* Unless it is explicitly flagged as .META */
if (!(gn->type & OP_META)) {
- SKIP_META_TYPE(PHONY);
- SKIP_META_TYPE(SPECIAL);
- SKIP_META_TYPE(MAKE);
+ SKIP_META_TYPE(OP_PHONY, "PHONY");
+ SKIP_META_TYPE(OP_SPECIAL, "SPECIAL");
+ SKIP_META_TYPE(OP_MAKE, "MAKE");
}
/* Check if there are no commands to execute. */
@@ -497,10 +480,8 @@ meta_create(BuildMon *pbm, GNode *gn)
dname.str = objdir_realpath;
if (metaVerbose) {
- char *mp;
-
/* Describe the target we are building */
- (void)Var_Subst("${" MAKE_META_PREFIX "}", gn, VARE_WANTRES, &mp);
+ char *mp = Var_Subst("${" MAKE_META_PREFIX "}", gn, VARE_WANTRES);
/* TODO: handle errors */
if (mp[0] != '\0')
fprintf(stdout, "%s\n", mp);
@@ -523,7 +504,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 +522,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 +540,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,8 +577,7 @@ void
meta_mode_init(const char *make_mode)
{
static bool once = false;
- char *cp;
- FStr value;
+ const char *cp;
useMeta = true;
useFilemon = true;
@@ -635,33 +617,33 @@ meta_mode_init(const char *make_mode)
/*
* We consider ourselves master of all within ${.MAKE.META.BAILIWICK}
*/
- (void)Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}",
- SCOPE_GLOBAL, VARE_WANTRES, &metaBailiwickStr);
+ metaBailiwickStr = Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}",
+ SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
- str2Lst_Append(&metaBailiwick, metaBailiwickStr);
+ AppendWords(&metaBailiwick, metaBailiwickStr);
/*
* We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS}
*/
Global_Append(MAKE_META_IGNORE_PATHS,
"/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}");
- (void)Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}",
- SCOPE_GLOBAL, VARE_WANTRES, &metaIgnorePathsStr);
+ metaIgnorePathsStr = Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}",
+ SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
- str2Lst_Append(&metaIgnorePaths, metaIgnorePathsStr);
+ AppendWords(&metaIgnorePaths, metaIgnorePathsStr);
/*
* We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS}
*/
- value = Var_Value(SCOPE_GLOBAL, MAKE_META_IGNORE_PATTERNS);
- if (value.str != NULL) {
- metaIgnorePatterns = true;
- FStr_Done(&value);
- }
- value = Var_Value(SCOPE_GLOBAL, MAKE_META_IGNORE_FILTER);
- if (value.str != NULL) {
- metaIgnoreFilter = true;
- FStr_Done(&value);
- }
+ metaIgnorePatterns = Var_Exists(SCOPE_GLOBAL, MAKE_META_IGNORE_PATTERNS);
+ metaIgnoreFilter = Var_Exists(SCOPE_GLOBAL, MAKE_META_IGNORE_FILTER);
+ metaCmpFilter = Var_Exists(SCOPE_GLOBAL, MAKE_META_CMP_FILTER);
+}
+
+MAKE_INLINE BuildMon *
+BM(Job *job)
+{
+
+ return ((job != NULL) ? &job->bm : &Mybm);
}
/*
@@ -672,11 +654,7 @@ meta_job_start(Job *job, GNode *gn)
{
BuildMon *pbm;
- if (job != NULL) {
- pbm = &job->bm;
- } else {
- pbm = &Mybm;
- }
+ pbm = BM(job);
pbm->mfp = meta_create(pbm, gn);
#ifdef USE_FILEMON_ONCE
/* compat mode we open the filemon dev once per command */
@@ -698,16 +676,12 @@ meta_job_start(Job *job, GNode *gn)
* It does not disturb our state.
*/
void
-meta_job_child(Job *job)
+meta_job_child(Job *job MAKE_ATTR_UNUSED)
{
#ifdef USE_FILEMON
BuildMon *pbm;
- if (job != NULL) {
- pbm = &job->bm;
- } else {
- pbm = &Mybm;
- }
+ pbm = BM(job);
if (pbm->mfp != NULL) {
close(fileno(pbm->mfp));
if (useFilemon && pbm->filemon != NULL) {
@@ -715,7 +689,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));
}
}
}
@@ -723,16 +697,12 @@ meta_job_child(Job *job)
}
void
-meta_job_parent(Job *job, pid_t pid)
+meta_job_parent(Job *job MAKE_ATTR_UNUSED, pid_t pid MAKE_ATTR_UNUSED)
{
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
BuildMon *pbm;
- if (job != NULL) {
- pbm = &job->bm;
- } else {
- pbm = &Mybm;
- }
+ pbm = BM(job);
if (useFilemon && pbm->filemon != NULL) {
filemon_setpid_parent(pbm->filemon, pid);
}
@@ -740,16 +710,12 @@ meta_job_parent(Job *job, pid_t pid)
}
int
-meta_job_fd(Job *job)
+meta_job_fd(Job *job MAKE_ATTR_UNUSED)
{
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
BuildMon *pbm;
- if (job != NULL) {
- pbm = &job->bm;
- } else {
- pbm = &Mybm;
- }
+ pbm = BM(job);
if (useFilemon && pbm->filemon != NULL) {
return filemon_readfd(pbm->filemon);
}
@@ -758,16 +724,12 @@ meta_job_fd(Job *job)
}
int
-meta_job_event(Job *job)
+meta_job_event(Job *job MAKE_ATTR_UNUSED)
{
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
BuildMon *pbm;
- if (job != NULL) {
- pbm = &job->bm;
- } else {
- pbm = &Mybm;
- }
+ pbm = BM(job);
if (useFilemon && pbm->filemon != NULL) {
return filemon_process(pbm->filemon);
}
@@ -781,20 +743,18 @@ meta_job_error(Job *job, GNode *gn, bool ignerr, int status)
char cwd[MAXPATHLEN];
BuildMon *pbm;
- if (job != NULL) {
- pbm = &job->bm;
- if (gn == NULL)
+ pbm = BM(job);
+ if (job != NULL && gn == NULL)
gn = job->node;
- } else {
- pbm = &Mybm;
- }
if (pbm->mfp != NULL) {
fprintf(pbm->mfp, "\n*** Error code %d%s\n",
status, ignerr ? "(ignored)" : "");
}
if (gn != NULL)
Global_Set(".ERROR_TARGET", GNode_Path(gn));
- getcwd(cwd, sizeof cwd);
+ if (getcwd(cwd, sizeof cwd) == NULL)
+ Punt("Cannot get cwd: %s", strerror(errno));
+
Global_Set(".ERROR_CWD", cwd);
if (pbm->meta_fname[0] != '\0') {
Global_Set(".ERROR_META_FILE", pbm->meta_fname);
@@ -807,11 +767,7 @@ meta_job_output(Job *job, char *cp, const char *nl)
{
BuildMon *pbm;
- if (job != NULL) {
- pbm = &job->bm;
- } else {
- pbm = &Mybm;
- }
+ pbm = BM(job);
if (pbm->mfp != NULL) {
if (metaVerbose) {
static char *meta_prefix = NULL;
@@ -820,8 +776,8 @@ meta_job_output(Job *job, char *cp, const char *nl)
if (meta_prefix == NULL) {
char *cp2;
- (void)Var_Subst("${" MAKE_META_PREFIX "}",
- SCOPE_GLOBAL, VARE_WANTRES, &meta_prefix);
+ meta_prefix = Var_Subst("${" MAKE_META_PREFIX "}",
+ SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
if ((cp2 = strchr(meta_prefix, '$')) != NULL)
meta_prefix_len = (size_t)(cp2 - meta_prefix);
@@ -855,8 +811,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;
@@ -877,11 +835,7 @@ meta_job_finish(Job *job)
int error = 0;
int x;
- if (job != NULL) {
- pbm = &job->bm;
- } else {
- pbm = &Mybm;
- }
+ pbm = BM(job);
if (pbm->mfp != NULL) {
error = meta_cmd_finish(pbm);
x = fclose(pbm->mfp);
@@ -985,6 +939,13 @@ meta_ignore(GNode *gn, const char *p)
return true;
if (*p == '/') {
+ /* first try the raw path "as is" */
+ if (has_any_prefix(p, &metaIgnorePaths)) {
+#ifdef DEBUG_META_MODE
+ DEBUG1(META, "meta_oodate: ignoring path: %s\n", p);
+#endif
+ return true;
+ }
cached_realpath(p, fname); /* clean it up */
if (has_any_prefix(fname, &metaIgnorePaths)) {
#ifdef DEBUG_META_MODE
@@ -1006,7 +967,7 @@ meta_ignore(GNode *gn, const char *p)
*/
Var_Set(gn, ".p.", p);
expr = "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}";
- (void)Var_Subst(expr, gn, VARE_WANTRES, &pm);
+ pm = Var_Subst(expr, gn, VARE_WANTRES);
/* TODO: handle errors */
if (pm[0] != '\0') {
#ifdef DEBUG_META_MODE
@@ -1025,7 +986,7 @@ meta_ignore(GNode *gn, const char *p)
snprintf(fname, sizeof fname,
"${%s:L:${%s:ts:}}",
p, MAKE_META_IGNORE_FILTER);
- (void)Var_Subst(fname, gn, VARE_WANTRES, &fm);
+ fm = Var_Subst(fname, gn, VARE_WANTRES);
/* TODO: handle errors */
if (*fm == '\0') {
#ifdef DEBUG_META_MODE
@@ -1053,7 +1014,7 @@ meta_ignore(GNode *gn, const char *p)
* Setting oodate true will have that effect.
*/
#define CHECK_VALID_META(p) if (!(p != NULL && *p != '\0')) { \
- warnx("%s: %d: malformed", fname, lineno); \
+ warnx("%s: %u: malformed", fname, lineno); \
oodate = true; \
continue; \
}
@@ -1076,6 +1037,36 @@ append_if_new(StringList *list, const char *str)
Lst_Append(list, bmake_strdup(str));
}
+/* A "reserved" variable to store the command to be filtered */
+#define META_CMD_FILTER_VAR ".MAKE.cmd_filtered"
+
+static char *
+meta_filter_cmd(GNode *gn, char *s)
+{
+ Var_Set(gn, META_CMD_FILTER_VAR, s);
+ s = Var_Subst(
+ "${" META_CMD_FILTER_VAR ":${" MAKE_META_CMP_FILTER ":ts:}}",
+ gn, VARE_WANTRES);
+ return s;
+}
+
+static int
+meta_cmd_cmp(GNode *gn, char *a, char *b, bool filter)
+{
+ int rc;
+
+ rc = strcmp(a, b);
+ if (rc == 0 || !filter)
+ return rc;
+ a = meta_filter_cmd(gn, a);
+ b = meta_filter_cmd(gn, b);
+ rc = strcmp(a, b);
+ free(a);
+ free(b);
+ Var_Delete(gn, META_CMD_FILTER_VAR);
+ return rc;
+}
+
bool
meta_oodate(GNode *gn, bool oodate)
{
@@ -1100,6 +1091,7 @@ meta_oodate(GNode *gn, bool oodate)
bool needOODATE = false;
StringList missingFiles;
bool have_filemon = false;
+ bool cmp_filter;
if (oodate)
return oodate; /* we're done */
@@ -1131,7 +1123,7 @@ meta_oodate(GNode *gn, bool oodate)
if ((fp = fopen(fname, "r")) != NULL) {
static char *buf = NULL;
static size_t bufsz;
- int lineno = 0;
+ unsigned lineno = 0;
int lastpid = 0;
int pid;
int x;
@@ -1159,13 +1151,15 @@ meta_oodate(GNode *gn, bool oodate)
/* we want to track all the .meta we read */
Global_Append(".MAKE.META.FILES", fname);
+ cmp_filter = metaCmpFilter || Var_Exists(gn, MAKE_META_CMP_FILTER);
+
cmdNode = gn->commands.first;
while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) {
lineno++;
if (buf[x - 1] == '\n')
buf[x - 1] = '\0';
else {
- warnx("%s: %d: line truncated at %u", fname, lineno, x);
+ warnx("%s: %u: line truncated at %u", fname, lineno, x);
oodate = true;
break;
}
@@ -1186,7 +1180,7 @@ meta_oodate(GNode *gn, bool oodate)
/* Delimit the record type. */
p = buf;
#ifdef DEBUG_META_MODE
- DEBUG3(META, "%s: %d: %s\n", fname, lineno, buf);
+ DEBUG3(META, "%s: %u: %s\n", fname, lineno, buf);
#endif
strsep(&p, " ");
if (have_filemon) {
@@ -1232,8 +1226,8 @@ meta_oodate(GNode *gn, bool oodate)
if (lastpid > 0) {
/* We need to remember these. */
- Global_SetExpand(lcwd_vname, lcwd);
- Global_SetExpand(ldir_vname, latestdir);
+ Global_Set(lcwd_vname, lcwd);
+ Global_Set(ldir_vname, latestdir);
}
snprintf(lcwd_vname, sizeof lcwd_vname, LCWD_VNAME_FMT, pid);
snprintf(ldir_vname, sizeof ldir_vname, LDIR_VNAME_FMT, pid);
@@ -1254,7 +1248,7 @@ meta_oodate(GNode *gn, bool oodate)
continue;
#ifdef DEBUG_META_MODE
if (DEBUG(META))
- debug_printf("%s: %d: %d: %c: cwd=%s lcwd=%s ldir=%s\n",
+ debug_printf("%s: %u: %d: %c: cwd=%s lcwd=%s ldir=%s\n",
fname, lineno,
pid, buf[0], cwd, lcwd, latestdir);
#endif
@@ -1266,8 +1260,8 @@ meta_oodate(GNode *gn, bool oodate)
/* Process according to record type. */
switch (buf[0]) {
case 'X': /* eXit */
- Var_DeleteExpand(SCOPE_GLOBAL, lcwd_vname);
- Var_DeleteExpand(SCOPE_GLOBAL, ldir_vname);
+ Var_Delete(SCOPE_GLOBAL, lcwd_vname);
+ Var_Delete(SCOPE_GLOBAL, ldir_vname);
lastpid = 0; /* no need to save ldir_vname */
break;
@@ -1279,13 +1273,13 @@ meta_oodate(GNode *gn, bool oodate)
child = atoi(p);
if (child > 0) {
snprintf(cldir, sizeof cldir, LCWD_VNAME_FMT, child);
- Global_SetExpand(cldir, lcwd);
+ Global_Set(cldir, lcwd);
snprintf(cldir, sizeof cldir, LDIR_VNAME_FMT, child);
- Global_SetExpand(cldir, latestdir);
+ Global_Set(cldir, latestdir);
#ifdef DEBUG_META_MODE
if (DEBUG(META))
debug_printf(
- "%s: %d: %d: cwd=%s lcwd=%s ldir=%s\n",
+ "%s: %u: %d: cwd=%s lcwd=%s ldir=%s\n",
fname, lineno,
child, cwd, lcwd, latestdir);
#endif
@@ -1297,10 +1291,10 @@ meta_oodate(GNode *gn, bool oodate)
/* Update lcwd and latest directory. */
strlcpy(latestdir, p, sizeof latestdir);
strlcpy(lcwd, p, sizeof lcwd);
- Global_SetExpand(lcwd_vname, lcwd);
- Global_SetExpand(ldir_vname, lcwd);
+ Global_Set(lcwd_vname, lcwd);
+ Global_Set(ldir_vname, lcwd);
#ifdef DEBUG_META_MODE
- DEBUG4(META, "%s: %d: cwd=%s ldir=%s\n",
+ DEBUG4(META, "%s: %u: cwd=%s ldir=%s\n",
fname, lineno, cwd, lcwd);
#endif
break;
@@ -1435,25 +1429,25 @@ meta_oodate(GNode *gn, bool oodate)
continue; /* no point */
/* Check vs latestdir */
- snprintf(fname1, sizeof fname1, "%s/%s", latestdir, p);
- sdirs[sdx++] = fname1;
+ if (snprintf(fname1, sizeof fname1, "%s/%s", latestdir, p) < (int)(sizeof fname1))
+ sdirs[sdx++] = fname1;
if (strcmp(latestdir, lcwd) != 0) {
/* Check vs lcwd */
- snprintf(fname2, sizeof fname2, "%s/%s", lcwd, p);
- sdirs[sdx++] = fname2;
+ if (snprintf(fname2, sizeof fname2, "%s/%s", lcwd, p) < (int)(sizeof fname2))
+ sdirs[sdx++] = fname2;
}
if (strcmp(lcwd, cwd) != 0) {
/* Check vs cwd */
- snprintf(fname3, sizeof fname3, "%s/%s", cwd, p);
- sdirs[sdx++] = fname3;
+ if (snprintf(fname3, sizeof fname3, "%s/%s", cwd, p) < (int)(sizeof fname3))
+ sdirs[sdx++] = fname3;
}
}
sdirs[sdx++] = NULL;
for (sdp = sdirs; *sdp != NULL && !found; sdp++) {
#ifdef DEBUG_META_MODE
- DEBUG3(META, "%s: %d: looking for: %s\n",
+ DEBUG3(META, "%s: %u: looking for: %s\n",
fname, lineno, *sdp);
#endif
if (cached_stat(*sdp, &cst) == 0) {
@@ -1463,12 +1457,12 @@ meta_oodate(GNode *gn, bool oodate)
}
if (found) {
#ifdef DEBUG_META_MODE
- DEBUG3(META, "%s: %d: found: %s\n",
+ DEBUG3(META, "%s: %u: found: %s\n",
fname, lineno, p);
#endif
if (!S_ISDIR(cst.cst_mode) &&
cst.cst_mtime > gn->mtime) {
- DEBUG3(META, "%s: %d: file '%s' is newer than the target...\n",
+ DEBUG3(META, "%s: %u: file '%s' is newer than the target...\n",
fname, lineno, p);
oodate = true;
} else if (S_ISDIR(cst.cst_mode)) {
@@ -1500,7 +1494,7 @@ meta_oodate(GNode *gn, bool oodate)
* meta data file.
*/
if (cmdNode == NULL) {
- DEBUG2(META, "%s: %d: there were more build commands in the meta data file than there are now...\n",
+ DEBUG2(META, "%s: %u: there were more build commands in the meta data file than there are now...\n",
fname, lineno);
oodate = true;
} else {
@@ -1517,10 +1511,10 @@ meta_oodate(GNode *gn, bool oodate)
}
if (hasOODATE) {
needOODATE = true;
- DEBUG2(META, "%s: %d: cannot compare command using .OODATE\n",
+ DEBUG2(META, "%s: %u: cannot compare command using .OODATE\n",
fname, lineno);
}
- (void)Var_Subst(cmd, gn, VARE_UNDEFERR, &cmd);
+ cmd = Var_Subst(cmd, gn, VARE_UNDEFERR);
/* TODO: handle errors */
if ((cp = strchr(cmd, '\n')) != NULL) {
@@ -1540,7 +1534,7 @@ meta_oodate(GNode *gn, bool oodate)
x = n;
lineno++;
if (buf[x - 1] != '\n') {
- warnx("%s: %d: line truncated at %u", fname, lineno, x);
+ warnx("%s: %u: line truncated at %u", fname, lineno, x);
break;
}
cp = strchr(cp + 1, '\n');
@@ -1551,8 +1545,8 @@ meta_oodate(GNode *gn, bool oodate)
if (p != NULL &&
!hasOODATE &&
!(gn->type & OP_NOMETA_CMP) &&
- (strcmp(p, cmd) != 0)) {
- DEBUG4(META, "%s: %d: a build command has changed\n%s\nvs\n%s\n",
+ (meta_cmd_cmp(gn, p, cmd, cmp_filter) != 0)) {
+ DEBUG4(META, "%s: %u: a build command has changed\n%s\nvs\n%s\n",
fname, lineno, p, cmd);
if (!metaIgnoreCMDs)
oodate = true;
@@ -1566,13 +1560,13 @@ meta_oodate(GNode *gn, bool oodate)
* that weren't in the meta data file.
*/
if (!oodate && cmdNode != NULL) {
- DEBUG2(META, "%s: %d: there are extra build commands now that weren't in the meta data file\n",
+ DEBUG2(META, "%s: %u: there are extra build commands now that weren't in the meta data file\n",
fname, lineno);
oodate = true;
}
CHECK_VALID_META(p);
if (strcmp(p, cwd) != 0) {
- DEBUG4(META, "%s: %d: the current working directory has changed from '%s' to '%s'\n",
+ DEBUG4(META, "%s: %u: the current working directory has changed from '%s' to '%s'\n",
fname, lineno, p, curdir);
oodate = true;
}
@@ -1608,7 +1602,7 @@ meta_oodate(GNode *gn, bool oodate)
}
}
- Lst_DoneCall(&missingFiles, free);
+ Lst_DoneFree(&missingFiles);
if (oodate && needOODATE) {
/*
@@ -1710,7 +1704,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/meta.h b/contrib/bmake/meta.h
index d4f02da4e78b..5d247422ef8c 100644
--- a/contrib/bmake/meta.h
+++ b/contrib/bmake/meta.h
@@ -1,4 +1,4 @@
-/* $NetBSD: meta.h,v 1.10 2021/04/03 11:08:40 rillig Exp $ */
+/* $NetBSD: meta.h,v 1.11 2021/12/15 09:53:41 rillig Exp $ */
/*
* Things needed for 'meta' mode.
@@ -46,13 +46,13 @@ void meta_mode_init(const char *);
void meta_job_start(struct Job *, GNode *);
void meta_job_child(struct Job *);
void meta_job_parent(struct Job *, pid_t);
-int meta_job_fd(struct Job *);
-int meta_job_event(struct Job *);
+int meta_job_fd(struct Job *) MAKE_ATTR_USE;
+int meta_job_event(struct Job *) MAKE_ATTR_USE;
void meta_job_error(struct Job *, GNode *, bool, int);
void meta_job_output(struct Job *, char *, const char *);
int meta_cmd_finish(void *);
int meta_job_finish(struct Job *);
-bool meta_oodate(GNode *, bool);
+bool meta_oodate(GNode *, bool) MAKE_ATTR_USE;
void meta_compat_start(void);
void meta_compat_child(void);
void meta_compat_parent(pid_t);
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..11711e876017 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.20 2022/01/08 11:04:13 rillig Exp $ */
/*
* Copyright (c) 2015 The NetBSD Foundation, Inc.
@@ -33,16 +33,20 @@
#include "make.h"
-extern unsigned char _metachar[];
+extern const unsigned char _metachar[];
-#define is_shell_metachar(c) (_metachar[(c) & 0x7f] != 0)
+MAKE_INLINE bool MAKE_ATTR_USE
+ch_is_shell_meta(char c)
+{
+ return _metachar[c & 0x7f] != 0;
+}
-MAKE_INLINE bool
+MAKE_INLINE bool MAKE_ATTR_USE
needshell(const char *cmd)
{
- while (!is_shell_metachar(*cmd) && *cmd != ':' && *cmd != '=')
+ while (!ch_is_shell_meta(*cmd) && *cmd != ':' && *cmd != '=')
cmd++;
return *cmd != '\0';
}
-#endif /* MAKE_METACHAR_H */
+#endif
diff --git a/contrib/bmake/mk/ChangeLog b/contrib/bmake/mk/ChangeLog
index f73c4fb68c6b..4c3f4f4572c9 100644
--- a/contrib/bmake/mk/ChangeLog
+++ b/contrib/bmake/mk/ChangeLog
@@ -1,3 +1,609 @@
+2024-05-04 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20240504
+
+ * dirdeps.mk: allow BUILD_DIRDEPS_OVERRIDES to pass overrides to
+ sub-make building DIRDEPS_CACHE.
+
+2024-04-24 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * meta.autodep.mk: do not override start_utc
+
+2024-04-18 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * sys.dirdeps.mk: set defaults for DEP_* at level 0 too.
+ These help when first include of Makefile.depend happens in a leaf
+ dir.
+
+ * install-mk (MK_VERSION): 20240414
+
+2024-04-09 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20240408
+
+ * init.mk: allow for _ as well as . to join V
+ and Q from QUALIFIED_VAR_LIST and VAR_QUALIFIER_LIST.
+
+ * progs.mk: avoid overlap between PROG_VARS and
+ init.mk's QUALIFIED_VAR_LIST since PROG would also
+ match its VAR_QUALIFIER_LIST,
+ libs.mk does not have the same issue.
+
+ * subdir.mk: _SUBDIRUSE for realinstall should run install
+ remove include of ${.CURDIR}/Makefile.inc that can be done via
+ local.subdir.mk where needed
+
+ * own.mk: do not conflict with man.mk
+
+2024-03-19 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20240314
+
+ * add sys/Cygwin.mk from Christian Franke
+
+2024-03-09 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20240309
+
+ * meta.sys.mk: _metaError: if .ERROR_EXIT == 6, we do not
+ want to save the .ERROR_META_FILE
+
+2024-02-20 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20240220
+
+ * sys.dirdeps.mk, dirdeps-targets.mk, init.mk:
+ do not set .MAIN: dirdeps in sys.dirdeps.mk
+ dirdeps-targets.mk will do that for top-level builds
+ and init.mk will do it for others.
+ This allows a Makefile which has no need of 'dirdeps' to
+ set .MAIN for itself and "just work".
+
+2024-02-18 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * bsd.*.mk: for makefiles that get a bsd. symlink,
+ use _this in multiple inclusion tags since .PARSEFILE will not
+ DTRT when such a makefile is included directly by Makefile and
+ automatically (without bsd. prefix).
+ Since we cannot guarantee that our sys.mk will be used, we provide
+ a default _this in each makefile that gets a bsd. prefix such that
+ the value is the same regardless of bsd. prefix.
+
+ * subdir.mk: drop the !target guard on $SUBDIR_TARGETS
+
+2024-02-12 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20240212
+
+ * SPDX-License-Identifier: BSD-2-Clause
+ Add SPDX-License-Identifier to inidicate that I consider
+ my copyright on any of these makefiles equivalent to BSD-2-Clause
+
+ * autoconf.mk: allow for configure.ac as currently recommended
+
+ * subdir.mk: support @auto
+ which is replaced with each subdir that
+ has a [Mm]akefile.
+
+ * subdir.mk: include local.subdir.mk if it exists.
+
+ * subdir.mk: rework to handle .WAIT
+
+2024-02-11 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * subdir.mk: _SUBDIRUSE report the target we are entering subdirs for.
+
+2024-02-10 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * prog.mk: treat empty SRCS the same as undefined
+
+2024-02-02 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * Avoid undefined errors in lint (-dL) mode
+
+ * man.mk (CMT2DOC_FLAGS): note that -mm does mdoc(7)
+
+2024-01-28 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20240128
+
+ * FILES: add ccm.dep.mk for C++ modules
+ add suffixes.mk for common location for generic SUFFIX rules.
+
+ * auto.dep.mk autodep.mk meta.autodep.mk: include ccm.dep.mk
+ replace OBJ_EXTENSIONS with OBJ_SUFFIXES
+
+ * autodep.mk: leverage CXX_SUFFIXES for __depsrcs
+ and update style (spaces around = etc)
+
+ * init.mk: add OBJS_SRCS_FILTER to filter SRCS when
+ setting OBJS
+
+ * meta2deps.py: handle multiple ./ embedded in path better.
+
+2024-01-05 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20240105
+ * dirdeps.mk: for MAKE_VERSION 20240105 we do not have the same
+ limits on command line length, so skip export of lists to env.
+
+2023-12-24 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * man.mk: add logic for staging man pages
+
+2023-11-28 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * jobs.mk: avoid C suffix in JOB_MAX_C if factor is floating
+ point. This keeps JOB_MAX numeric incase another makefile does
+ comparisons.
+
+2023-11-04 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * dpadd.mk: add support for DPLIBS_QUALIFIER_LIST
+
+ * gendirdeps.mk: if META_XTRAS is passed to us, add to META_FILES
+
+2023-10-03 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * compiler.mk (COMPILER_VERSION): clang at least is into
+ double digit major versions.
+
+2023-10-02 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20231001
+
+ * set _CCLINK in init.mk so lib.mk can use it for default SHLIB_LD
+
+ * lib.mk (cleanlib): use LD_solink so we remove all the right files.
+ Use -Wl for -soname since we now default to linking with CC
+ We should not need SHLIB_LDSTARTFILE or SHLIB_LDENDFILE when linking
+ with CC.
+
+2023-09-24 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * init.mk (QUALIFIED_VAR_LIST): Add SRCS
+
+2023-09-09 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * jobs.mk (JOB_MAX): use -jC if we can
+ we actually use JOB_MAX_C which defaults to 1.33C
+
+2023-08-18 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * now_utc: %s only works with :localtime
+
+2023-07-14 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-sh: ignore -c as claimed and only insist on
+ a directory for destination when more than one file to copy.
+
+ * sys.mk: when looking for SYS_OS_MK try ${.MAKE.OS} and
+ ${.MAKE.OS:S,64,,} early (so we find sys/IRIX.mk for IRIX64)
+
+2023-07-13 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20230711
+
+ * sys.mk: set SYS_MK and INSTALL_SH for systems with incompatible
+ install(1)
+
+ * sys/IRIX.mk: when setting ROOT_GROUP only match the first :0:
+ set INSTALL to install-sh rather than pathname that may not exist
+ (yet).
+
+2023-07-07 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * dirdeps.mk: pass DIRDEP_TARGETS to DIRDEP_MAKE
+ normally this is empty - for the default target, but there are
+ use-cases where we might set it to something else.
+
+2023-07-04 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20230704
+
+ * dirdeps.mk: apply DEBUG_DIRDEPS_LIST_FILTER to lists we output
+ when DEBUG_DIRDEPS is in effect.
+ Eg. DEBUG_DIRDEPS_LIST_FILTER=ts\n
+ can greatly improve readability.
+
+2023-05-25 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * meta.autodep.mk (beforegendirdeps): allow tasks to be done
+ at END but before gendirdeps
+
+2023-05-22 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20230522
+
+ * host-target.mk: deal with garbage from uname -m on
+ Darwin ppc, also NetBSD appears to use x86_64 for MACHINE_ARCH
+ these days so just leave it be.
+ For Darwin arm and i386 use _HOST_MACHINE for _HOST_ARCH so we get
+ arm64 and x86_64 in HOST_TARGET.
+
+2023-05-15 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * sys.vars.mk: M_mtime use :mtime or 'stat -f %m' for older
+ versions of bmake.
+
+ * dirdeps.mk (TARGET_SPEC_VARS.host):
+ While *most* projects need only DEP_MACHINE for host,
+ there is always an exception. So we allow for
+ TARGET_SPEC_VARS.host to be a subset of TARGET_SPEC_VARS.
+ The default will *just work* for most projects.
+ We set DEP_TARGET_SPEC_VARS and hence DEP_TARGET_SPEC
+ based on DEP_MACHINE.
+ Allow for M_dep_qual_fixes.host to be different too
+ and take care to apply the right set.
+
+2023-05-14 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * sys.dirdeps.mk: we *do* want to override OBJTOP
+ and if MAKEOBJDIR was not in env as we want it;
+ put it there - carefully.
+ Ensure OBJROOT ends in / or - (/ preferred)
+ Add more comments to explain what/why.
+
+2023-05-13 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20230512
+
+ * dirdeps.mk: take care not to qualify "host" dirdeps
+
+ * sys.dirdeps.mk (OBJTOP): must use ?=
+
+2023-05-10 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * meta.autodep.mk: if LOCAL_DEPENDS_GUARD is "no"
+ suppress processing of .depend
+
+2023-05-09 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * dirdeps.mk: do not add _CURDIR to DIRDEPS for SRCTOP
+
+ * meta.sys.mk sys.dirdeps.mk:
+ originally DIRDEPS_BUILD and META_MODE were the same thing,
+ but META_MODE is useful by itself.
+ Move things from meta.sys.mk which actually pertain to
+ DIRDEPS_BUILD to sys.dirdeps.mk
+
+2023-05-04 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20230504 May the Forth be with you
+
+ * dirdeps.mk: as with meta.sys.mk we treat "host" as special.
+ DEP_TARGET_SPEC is just ${DEP_MACHINE}
+
+ * meta.sys.mk: ensure DEP_* for TARGET_SPEC_VARS are set at
+ level > 0 since these are often refered to in Makefile.depend*
+
+2023-04-26 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * jobs.mk: report ${.TARGET} ${JOB_ARGS} ${JOB_LOG} and
+ anything in ${JOB_LOG_START}
+
+ * jobs.mk: look for newlog.sh in ${.SYSPATH:U${.PARSEDIR}}
+ or a scripts subdir before searching $PATH.
+
+ * FILES: include newlog.sh for jobs.mk
+
+2023-04-20 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20230420
+
+ * lib.mk: include LDFLAGS and LDADD when linking shared libs
+
+ * gendirdeps.mk: document setting GENDIRDEPS_FILTER_VARS etc
+ via local.meta.sys.mk rather than local.gendirdeps.mk
+ so DEP_* variables can be set at level 1+ to avoid syntax errors
+ when used in conditionals in manually maintained Makefile.depend
+ files.
+
+ * dirdeps.mk: ensure M_dep_qual_fixes is applied to all _machines
+
+2023-04-18 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * dirdeps.mk: check we were not included by
+ Makefile.depend.options as the result is bad.
+
+2023-04-14 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20230414
+
+ * meta.sys.mk: since we have :range we can put the logic for
+ processing TARGET_SPEC from env here.
+
+ * dirdeps.mk: reset DIRDEPS and DEP_RELDIR before including
+ local.dirdeps-missing.mk, also improve debug output.
+
+ * dirdeps.mk: to allow make -f dirdeps.mk include.$TARGET_SPEC
+ we need to use :M*[/.]* same as for when actually setting DIRDEPS
+ from the targets on command line.
+
+2023-04-12 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * Add jobs.mk
+
+2023-03-21 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20230321
+
+ * meta.stage.mk: allow STAGE_SHLIB_LINKS_FILTER to filter
+ STAGE_LIBS for SHLIB_LINKS.
+
+ * autoconf.mk: add .WAIT after config.status
+
+2023-02-17 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * sys.vars.mk: add M_Index to report the index of a word in a list.
+
+2023-02-15 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20230215
+
+ * warnings.mk: allow better control of -Werror
+ allow -Wno-error or similar to be added if
+ WARNINGS_SET < WERROR_SET
+ account for COMPILER_TYPE
+
+2023-01-29 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * autoconf.mk: hook config.status to beforebuild.
+
+ * whats.mk: what*.c is NOTMAIN
+
+2023-01-27 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20230127
+ control umask so directories are created with suitable mode.
+
+2023-01-20 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20230120
+
+ * sys.vars.mk: add M_On and M_Onr also cleanup to be more
+ consistent wrt testing MAKE_VERSION
+
+2023-01-12 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20230112
+
+ * meta2deps.{py,sh}: assert if filemon data is truncated
+ we should see the '# Bye bye' record - assert if we do not.
+
+2022-09-09 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20220909
+
+ * sys/Linux.mk set EGREP to grep -E to avoid deprecation warnings
+
+2022-09-06 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * dirdeps-options.mk: explain the need to use
+ ${DEP_${TARGET_SPEC_VAR}:U${TARGET_SPEC_VAR}} when refering to
+ ${TARGET_SPEC_VAR}
+
+2022-09-03 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20220903
+
+ * M_cmpv handle more than 3 dots and clear leading 0's
+
+2022-07-20 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20220720
+
+ * prog.mk: handle PROG_CXX for more than just NetBSD
+
+2022-06-20 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20220620
+
+ * yacc.mk: when we have *.y in SRCS used explicit rules and .ORDER
+ rather than just suffix rules
+
+2022-04-23 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20220422
+
+ * gendirdeps.mk: If LOCAL_DEPENDS_GUARD is set to "no"
+ do not capture any local depends in Makefile.depend
+
+2022-03-25 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20220323
+ * posix.mk: default rules for .POSIX:
+
+2022-03-17 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * sys/*.mk: remove l from ARFLAGS
+
+2022-03-14 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20220314
+
+ * dirdeps-options.mk: allow options to be per RELDIR
+ try DIRDEPS_OPTIONS_QUALIFIER_LIST first prefixed
+ with ${DEP_RELDIR}.
+
+2022-02-14 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20220214
+
+ * cc-wrap.mk: fix :@ modifier
+
+2022-02-06 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20220206
+
+ * cc-wrap.mk: docuement how CCACHE etc might be set for
+ maximum flexibility
+
+2022-02-05 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * sys.vars.mk: use JOT_CMD (jot or seq) if available for M_JOT
+
+2022-02-04 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20220204
+
+ * host-target.mk: use .MAKE.OS if available
+
+2022-02-02 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20220202
+
+ * cc-wrap.mk: allow other entries in CC_WRAP_FILTER
+ We add our filter on extensions last, so prior filters
+ can apply to the whole value of .IMPSRC
+
+2022-02-01 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * cc-wrap.mk: take advantage of target local variables to
+ wrap compilers like CC CXX with wrappers like ccache distcc etc
+
+2022-01-28 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * meta2deps: we do not expect any trace data for setid apps
+
+2022-01-26 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * dirdeps.mk: ensure TARGET_SPEC and TARGET_SPEC_VARS are passed
+ to sub-make using DIRDEPS_CACHE
+
+2022-01-07 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * dirdeps.mk: use _cache_script to minimize the number of shells
+ forked when generating dirdeps.cache
+
+2022-01-02 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20220101
+
+ * dirdeps.mk: initialize DEP_* and _debug_reldir earlier.
+ If initial DIRDEPS are from command line, create the target
+ _dirdeps_cmdline as an indication.
+
+2022-01-01 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * init.mk (_SKIP_BUILD): when doing DIRDEPS_BUILD
+ at top-level only some targets are allowed at level 0,
+ for leaf makefiles only the default (all) target is restricted
+
+2021-12-28 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20211228
+
+ * meta2deps.py: filemon on Linux is not as reliable as we might
+ like, we do not want to update DIRDEPS if filemon output is
+ incomplete. Track pids that we 'E'xec and make sure we see an
+ e'X'it for each one. Throw an error if we are missing any 'X'
+ records.
+
+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..39ac101d36f4 100644
--- a/contrib/bmake/mk/FILES
+++ b/contrib/bmake/mk/FILES
@@ -6,6 +6,8 @@ auto.obj.mk
autoconf.mk
autodep.mk
auto.dep.mk
+cc-wrap.mk
+ccm.dep.mk
compiler.mk
cython.mk
dep.mk
@@ -18,7 +20,9 @@ host.libnames.mk
inc.mk
init.mk
install-mk
+install-sh
java.mk
+jobs.mk
ldorder.mk
lib.mk
libnames.mk
@@ -28,10 +32,12 @@ man.mk
manifest.mk
mk-files.txt
mkopt.sh
+newlog.sh
nls.mk
obj.mk
options.mk
own.mk
+posix.mk
prlist.mk
prog.mk
progs.mk
@@ -40,12 +46,15 @@ scripts.mk
srctop.mk
stage-install.sh
subdir.mk
+suffixes.mk
sys.mk
sys.clean-env.mk
sys.debug.mk
sys.dependfile.mk
+sys.dirdeps.mk
sys.vars.mk
sys/AIX.mk
+sys/Cygwin.mk
sys/Darwin.mk
sys/Generic.mk
sys/HP-UX.mk
@@ -54,6 +63,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..400789a37366 100644
--- a/contrib/bmake/mk/auto.dep.mk
+++ b/contrib/bmake/mk/auto.dep.mk
@@ -1,8 +1,9 @@
+# SPDX-License-Identifier: BSD-2-Clause
#
# RCSid:
-# $Id: auto.dep.mk,v 1.6 2020/08/19 17:51:53 sjg Exp $
+# $Id: auto.dep.mk,v 1.12 2024/02/17 17:26:57 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 +31,62 @@
# 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
+
+.-include <ccm.dep.mk>
+
.endif
diff --git a/contrib/bmake/mk/auto.obj.mk b/contrib/bmake/mk/auto.obj.mk
index 0405b5e6441d..cfe60ef9c4d6 100644
--- a/contrib/bmake/mk/auto.obj.mk
+++ b/contrib/bmake/mk/auto.obj.mk
@@ -1,4 +1,6 @@
-# $Id: auto.obj.mk,v 1.16 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: auto.obj.mk,v 1.17 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2004, Simon J. Gerraty
#
diff --git a/contrib/bmake/mk/autoconf.mk b/contrib/bmake/mk/autoconf.mk
index 61e6978043a8..24532c4e9eab 100644
--- a/contrib/bmake/mk/autoconf.mk
+++ b/contrib/bmake/mk/autoconf.mk
@@ -1,6 +1,8 @@
-# $Id: autoconf.mk,v 1.10 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
#
-# @(#) Copyright (c) 1996-2009, Simon J. Gerraty
+# $Id: autoconf.mk,v 1.20 2024/02/17 17:26:57 sjg Exp $
+#
+# @(#) Copyright (c) 1996-2024, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@@ -13,39 +15,46 @@
# 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}
+beforebuild: .NOTMAIN config.status .WAIT
+
+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...
# this is not quite per the autoconf manual,
# and is extremely convoluted - but all utterly necessary!
-.if make(autoconf-in) || make(configure) || make(config.h.in) || ${AUTO_AUTOCONF:Uno:tl} == "yes"
+.if make(autoconf-input) || make(configure) || make(config.h.in) || ${MK_AUTO_AUTOCONF:Uno} == "yes"
AUTOCONF ?= autoconf
AUTOHEADER ?= autoheader
@@ -66,15 +75,24 @@ ACLOCAL += aclocal.m4
.if exists(${.CURDIR}/acconfig.h)
ACCONFIG += acconfig.h
.endif
+.if exists(${.CURDIR}/configure.ac)
+CONFIGURE_SRC = ${.CURDIR}/configure.ac
+.else
+CONFIGURE_SRC ?= ${.CURDIR}/configure.in
+.endif
-config.h.in: ${.CURDIR}/configure.in ${ACCONFIG}
+config.h.in: .NOTMAIN ${CONFIGURE_SRC} ${ACCONFIG}
(cd ${.CURDIR} && ${AUTOHEADER})
-configure: ${.CURDIR}/configure.in ${ACLOCAL}
+configure: .NOTMAIN ${CONFIGURE_SRC} ${ACLOCAL}
(cd ${.CURDIR} && ${AUTOCONF})
AUTOCONF_INPUTS += configure
-autoconf-input: ${AUTOCONF_INPUTS}
+.if exists(${.CURDIR}/config.h.in)
+AUTOCONF_INPUTS += config.h.in
+.endif
+
+autoconf-input: .NOTMAIN ${AUTOCONF_INPUTS}
.endif
.endif
diff --git a/contrib/bmake/mk/autodep.mk b/contrib/bmake/mk/autodep.mk
index a7bb942278c9..88a2643bccff 100644
--- a/contrib/bmake/mk/autodep.mk
+++ b/contrib/bmake/mk/autodep.mk
@@ -1,8 +1,9 @@
+# SPDX-License-Identifier: BSD-2-Clause
#
# RCSid:
-# $Id: autodep.mk,v 1.38 2020/08/19 17:51:53 sjg Exp $
+# $Id: autodep.mk,v 1.43 2024/02/17 17:26:57 sjg Exp $
#
-# @(#) Copyright (c) 1999-2010, Simon J. Gerraty
+# @(#) Copyright (c) 1999-2024, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@@ -20,7 +21,7 @@
# dependencies are normally updated as part of compilation.
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+__${.PARSEFILE}__: .NOTMAIN
DEPENDFILE?= .depend
.for d in ${DEPENDFILE:N.depend}
@@ -32,56 +33,58 @@ DEPENDFILE?= .depend
.endif
.endfor
+# should have been set by sys.mk
+CXX_SUFFIXES ?= .cc .ccm .cpp .cxx .C
+
# it does nothing if SRCS is not defined or is empty
.if defined(SRCS) && !empty(SRCS)
-DEPSRCS?=${SRCS}
-__depsrcs=${DEPSRCS:M*.c}
-__depsrcs+=${DEPSRCS:M*.y}
-__depsrcs+=${DEPSRCS:M*.l}
-__depsrcs+=${DEPSRCS:M*.s}
-__depsrcs+=${DEPSRCS:M*.S}
-__depsrcs+=${DEPSRCS:M*.cc}
-__depsrcs+=${DEPSRCS:M*.cpp}
-__depsrcs+=${DEPSRCS:M*.C}
-__depsrcs+=${DEPSRCS:M*.cxx}
-__depsrcs+=${DEPSRCS:M*.pc}
+DEPSRCS ?= ${SRCS}
+__depsrcs = ${DEPSRCS:M*.c}
+__depsrcs += ${DEPSRCS:M*.y}
+__depsrcs += ${DEPSRCS:M*.l}
+__depsrcs += ${DEPSRCS:M*.s}
+__depsrcs += ${DEPSRCS:M*.S}
+__depsrcs += ${DEPSRCS:M*.pc}
+.for s in ${CXX_SUFFIXES}
+__depsrcs += ${DEPSRCS:M*$s}
+.endfor
.for s in ${__depsrcs}
${s:T:R}.d: $s
.endfor
-__depsrcs:=${__depsrcs:T:R:S/$/.d/g}
+__depsrcs := ${__depsrcs:T:R:S/$/.d/g}
# we also need to handle makefiles where the .d's from __depsrcs
# don't match those from OBJS
# we avoid using := here, since the modifier applied to OBJS
# can cause trouble if there are any undefined vars in OBJS.
-__dependsrcsx?= ${__depsrcs} ${OBJS:S/.o/.d/}
-__dependsrcs= ${__dependsrcsx:O:u}
+__dependsrcsx ?= ${__depsrcs} ${OBJS:S/.o/.d/}
+__dependsrcs = ${__dependsrcsx:O:u}
# clean up any .c files we may have generated
-#__gensrcs:= ${DEPSRCS:M*.y} ${DEPSRCS:M*.l}
-#CLEANFILES+= ${__gensrcs:T:R:S/$/.c/g}
+#__gensrcs := ${DEPSRCS:M*.y} ${DEPSRCS:M*.l}
+#CLEANFILES += ${__gensrcs:T:R:S/$/.c/g}
# set this to -MMD to ignore /usr/include
# actually it ignores <> so may not be a great idea
-CFLAGS_MD?=-MD
+CFLAGS_MD ?= -MD
# -MF etc not available on all gcc versions.
# we "fix" the .o later
-CFLAGS_MF?=-MF ${.TARGET:T:R}.d -MT ${.TARGET:T:R}.o
-CFLAGS+= ${CFLAGS_MD} ${CFLAGS_MF}
+.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
-MAKE_SHELL?= sh
+MAKE_SHELL ?= sh
# watch out for people who don't use CPPFLAGS
-CPPFLAGS_MD=${CFLAGS:M-[IUD]*} ${CPPFLAGS}
-CXXFLAGS_MD=${CXXFLAGS:M-[IUD]*} ${CPPFLAGS}
+CPPFLAGS_MD = ${CFLAGS:M-[IUD]*} ${CPPFLAGS}
+CXXFLAGS_MD = ${CXXFLAGS:M-[IUD]*} ${CPPFLAGS}
# just in case these need to be different
-CC_MD?=${CC}
-CXX_MD?=${CXX}
-
-# should have been set by sys.mk
-CXX_SUFFIXES?= .cc .cpp .cxx .C
+CC_MD ?= ${CC}
+CXX_MD ?= ${CXX}
# so we can do an explicit make depend, but not otherwise
.if make(depend)
@@ -111,7 +114,9 @@ CXX_SUFFIXES?= .cc .cpp .cxx .C
${CXX_SUFFIXES:%=%.d}:
@echo updating dependencies for $<
@${MAKE_SHELL} -ec "${CXX_MD} -M ${CXXFLAGS_MD} $< | sed '/:/s/^/$@ /' > $@" || { ${RM} -f $@; false; }
+
.else
+
.y.d:
${YACC} ${YFLAGS} $<
${CC_MD} ${CFLAGS_MD:S/D//} ${CPPFLAGS_MD} y.tab.c > $@ || { ${RM} -f y.tab.c $@; false; }
@@ -130,6 +135,7 @@ ${CXX_SUFFIXES:%=%.d}:
${CXX_SUFFIXES:%=%.d}:
${CXX_MD} ${CFLAGS_MD:S/D//} ${CXXFLAGS_MD} $< > $@ || { ${RM} -f $@; false; }
+
.endif
.if !target(depend)
@@ -149,47 +155,47 @@ ${PROG} ${_LIBS}: ${DEPENDFILE}
.ORDER: beforedepend ${DEPENDFILE} afterdepend
.if ${.OBJDIR} != ${.CURDIR}
-__depfiles= *.d
+__depfiles = *.d
.else
-__depfiles= ${__dependsrcs}
+__depfiles = ${__dependsrcs}
.endif
-DEPCLEANFILES= ${DEPENDFILE} ${__depfiles} y.tab.d *.tmp.d
+DEPCLEANFILES = ${DEPENDFILE} ${__depfiles} y.tab.d *.tmp.d
cleandir: cleanautodepend
cleanautodepend:
${RM} -f ${DEPCLEANFILES}
-CLEANFILES+= ${DEPCLEANFILES}
+CLEANFILES += ${DEPCLEANFILES}
.if defined(__dependsrcs) && !empty(__dependsrcs)
.if make(depend) || !(make(clean*) || make(destroy*) || make(obj) || make(*install) || make(install-*))
# this ensures we do the right thing if only building a shared or
# profiled lib
-OBJ_EXTENSIONS?=.o .po .so .So
-MDLIB_SED= -e '/:/s,^\([^\.:]*\)\.[psS]*o,${OBJ_EXTENSIONS:S,^,\1,},'
+OBJ_SUFFIXES ?= .o .po .so .So
+MDLIB_SED = -e '/:/s,^\([^\.:]*\)\.[psS]*o,${OBJ_SUFFIXES:S,^,\1,},'
.ifdef NOMD_SED
.ifdef LIB
-MD_SED=sed ${MDLIB_SED}
+MD_SED = sed ${MDLIB_SED}
.else
-MD_SED=cat
+MD_SED = cat
.endif
.else
# arrange to put some variable names into ${DEPENDFILE}
.ifdef LIB
-MD_SED=sed ${MDLIB_SED}
+MD_SED = sed ${MDLIB_SED}
.else
-MD_SED=sed
+MD_SED = sed
.endif
-SUBST_DEPVARS+= SB TOP BACKING SRC SRCDIR BASE BASEDIR
+SUBST_DEPVARS += SB TOP BACKING SRC SRCDIR BASE BASEDIR
.for v in ${SUBST_DEPVARS}
.if defined(${v}) && !empty(${v})
-MD_SED+= -e 's,${$v},$${$v},'
+MD_SED += -e 's,${$v},$${$v},'
.endif
.endfor
.endif
.if (${MD_SED} == "sed")
-MD_SED=cat
+MD_SED = cat
.endif
# this will be done whenever make finishes successfully
@@ -216,4 +222,6 @@ beforedepend:
afterdepend:
.endif
+.-include <ccm.dep.mk>
+
.endif
diff --git a/contrib/bmake/mk/cc-wrap.mk b/contrib/bmake/mk/cc-wrap.mk
new file mode 100644
index 000000000000..7c1d6165e9df
--- /dev/null
+++ b/contrib/bmake/mk/cc-wrap.mk
@@ -0,0 +1,68 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: cc-wrap.mk,v 1.7 2024/02/17 17:26:57 sjg Exp $
+#
+# @(#) Copyright (c) 2022, Simon J. Gerraty
+#
+# This file is provided in the hope that it will
+# be of use. There is absolutely NO WARRANTY.
+# Permission to copy, redistribute or otherwise
+# use this file is hereby granted provided that
+# the above copyright notice and this notice are
+# left intact.
+#
+# Please send copies of changes and bug-fixes to:
+# sjg@crufty.net
+#
+
+.if ${MAKE_VERSION} >= 20220126
+# which targets are we interested in?
+CC_WRAP_TARGETS ?= ${OBJS:U} ${POBJS:U} ${SOBJS:U}
+
+.if !empty(CC_WRAP_TARGETS)
+# cleanup
+# all the target assignments below are effectively := anyway
+# so we might as well do this once
+CC_WRAP_TARGETS := ${CC_WRAP_TARGETS:O:u}
+
+# what do we wrap?
+CC_WRAP_LIST += CC CXX
+CC_WRAP_LIST := ${CC_WRAP_LIST:O:u}
+
+# what might we wrap them with?
+CC_WRAPPERS += ccache distcc icecc
+CC_WRAPPERS := ${CC_WRAPPERS:O:u}
+# $W can be as simple or complicated as you like (default is just $w)
+# eg.
+# CCACHE ?= ${CCACHE_ENV_VARS:@v@$v='${$v}'@} ${CCACHE_CMD} ${CCACHE_FLAGS}
+# or if you want global vars to be used modifiable after this include:
+# CCACHE ?= $${CCACHE_ENV_VARS:@v@$$v='$${$$v}'@} $${CCACHE_CMD} $${CCACHE_FLAGS}
+.for w in ${CC_WRAPPERS}
+${w:tu} ?= $w
+.endfor
+
+# we do not want to make all these targets out-of-date
+# just because one of the above wrappers are enabled/disabled
+${CC_WRAP_TARGETS}: .MAKE.META.CMP_FILTER = ${CC_WRAPPERS:tu:@W@${$W}@:S,^,N,}
+
+# some object src types we should not wrap
+CC_WRAP_SKIP_EXTS += s
+
+# We add the sequence we care about - excluding CC_WRAP_SKIP_EXTS
+# but prior filters can apply to full value of .IMPSRC
+CC_WRAP_FILTER += E:tl:${CC_WRAP_SKIP_EXTS:${M_ListToSkip}}
+CC_WRAP_FILTER := ${CC_WRAP_FILTER:ts:}
+
+# last one enabled wins!
+.for W in ${CC_WRAPPERS:tu}
+.if ${MK_$W:U} == "yes"
+.for C in ${CC_WRAP_LIST}
+# we have to protect the check of .IMPSRC from Global expansion
+${CC_WRAP_TARGETS}: $C = $${"$${.IMPSRC:${CC_WRAP_FILTER}}":?${$W}:} ${$C}
+.endfor
+.endif
+.endfor
+
+.endif
+.endif
+
diff --git a/contrib/bmake/mk/ccm.dep.mk b/contrib/bmake/mk/ccm.dep.mk
new file mode 100644
index 000000000000..1809a72e7310
--- /dev/null
+++ b/contrib/bmake/mk/ccm.dep.mk
@@ -0,0 +1,60 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# RCSid:
+# $Id: ccm.dep.mk,v 1.3 2024/02/17 17:26:57 sjg Exp $
+#
+# @(#) Copyright (c) 2024, Simon J. Gerraty
+#
+# This file is provided in the hope that it will
+# be of use. There is absolutely NO WARRANTY.
+# Permission to copy, redistribute or otherwise
+# use this file is hereby granted provided that
+# the above copyright notice and this notice are
+# left intact.
+#
+# Please send copies of changes and bug-fixes to:
+# sjg@crufty.net
+#
+
+# C++ Modules need extra help - at least in a clean tree
+
+.if !target(__${_this}__)
+__${_this}__: .NOTMAIN
+
+# These should all be set by now
+CCM_SUFFIXES ?= ${.SUFFIXES:M*.c*m}
+PCM ?= .pcm
+OBJ_SUFFIXES ?= ${.SUFFIXES:M*o}
+
+.ccm_srcs := ${CCM_SUFFIXES:@s@${SRCS:M*$s}@}
+.if !empty(.ccm_srcs)
+.ccm_srcs += ${CXX_SUFFIXES:@s@${SRCS:M*$s}@}
+# this is likely overkill, but
+# with .dinclude it should not matter
+.ccm_depend: ${.ccm_srcs}
+ @rm -f ${.TARGET}
+.for s r e in ${.ccm_srcs:@x@$x ${x:T:R} ${x:E}@}
+ @mlist=`sed -n '/^import/s,.*[[:space:]]\([^[:space:];]*\);.*,\1,p' ${.ALLSRC:M*$s}`; \
+ for o in ${OBJ_SUFFIXES:O:u}; do \
+ for m in $$mlist; do \
+ echo $r$$o: $$m${PCM}; \
+ done; \
+ done >> ${.TARGET}
+.endfor
+
+.if make(depend)
+x != echo; rm -f .ccm_depend
+.endif
+
+.if !make(.ccm_depend)
+.if !exists(.ccm_depend)
+# ugly, but it does the trick
+x != echo; ${.MAKE} -B -C ${.CURDIR} -f ${MAKEFILE} .ccm_depend
+.endif
+
+CLEANFILES += .ccm_depend
+# the ${.OBJDIR}/ is necessary!
+.dinclude <${.OBJDIR}/.ccm_depend>
+.endif
+.endif
+.endif
diff --git a/contrib/bmake/mk/compiler.mk b/contrib/bmake/mk/compiler.mk
index b20ecaa047e3..e7a13f0f11d7 100644
--- a/contrib/bmake/mk/compiler.mk
+++ b/contrib/bmake/mk/compiler.mk
@@ -1,4 +1,6 @@
-# $Id: compiler.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: compiler.mk,v 1.14 2024/02/19 00:05:50 sjg Exp $
#
# @(#) Copyright (c) 2019, Simon J. Gerraty
#
@@ -14,7 +16,7 @@
#
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+__${.PARSEFILE}__: .NOTMAIN
.if ${MACHINE} == "common"
COMPILER_TYPE = none
@@ -22,17 +24,17 @@ 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:Uegrep} -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:Ucc:T:M*gcc*} != ""
COMPILER_TYPE = gcc
.endif
.endif
.if empty(COMPILER_VERSION)
-COMPILER_VERSION != echo "${_v:M[1-9].[0-9]*}:[1]" | \
+COMPILER_VERSION != echo "${_v:M[1-9][0-9]*.[0-9]*}:[1]" | \
awk -F. '{print $$1 * 10000 + $$2 * 100 + $$3;}'
.endif
.undef _v
diff --git a/contrib/bmake/mk/cython.mk b/contrib/bmake/mk/cython.mk
index d3c229c7269a..3c28eb0a4525 100644
--- a/contrib/bmake/mk/cython.mk
+++ b/contrib/bmake/mk/cython.mk
@@ -1,5 +1,7 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
# RCSid:
-# $Id: cython.mk,v 1.8 2020/08/19 17:51:53 sjg Exp $
+# $Id: cython.mk,v 1.9 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2014, Simon J. Gerraty
#
diff --git a/contrib/bmake/mk/dep.mk b/contrib/bmake/mk/dep.mk
index b07191a09cbf..ebc624ff163c 100644
--- a/contrib/bmake/mk/dep.mk
+++ b/contrib/bmake/mk/dep.mk
@@ -1,7 +1,10 @@
-# $Id: dep.mk,v 1.17 2014/08/04 05:12:27 sjg Exp $
+# $Id: dep.mk,v 1.22 2024/02/19 00:06:19 sjg Exp $
-.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+# should be set properly in sys.mk
+_this ?= ${.PARSEFILE:S,bsd.,,}
+
+.if !target(__${_this}__)
+__${_this}__: .NOTMAIN
# handle Proc*C as well...
.if defined(SRCS)
@@ -9,13 +12,18 @@ __${.PARSEFILE}__:
.include <proc.mk>
.endif
+.if ${MAKE_VERSION:U0} >= 20211212
+OBJ_SUFFIXES += ${.SUFFIXES:M*o}
+.else
# it would be nice to be able to query .SUFFIXES
-OBJ_EXTENSIONS+= .o .po .lo .So
+OBJ_SUFFIXES += .o .po .lo ${PICO}
+.endif
+OBJ_SUFFIXES += ${PCM}
# explicit dependencies help short-circuit .SUFFIX searches
SRCS_DEP_FILTER+= N*.[hly]
.for s in ${SRCS:${SRCS_DEP_FILTER:O:u:ts:}}
-.for e in ${OBJ_EXTENSIONS:O:u}
+.for e in ${OBJ_SUFFIXES:O:u}
.if !target(${s:T:R}$e)
${s:T:R}$e: $s
.endif
@@ -94,6 +102,7 @@ depend: beforedepend .depend _SUBDIRUSE afterdepend
${CXXFLAGS:M-[ID]*} ${CPPFLAGS} $$files;; \
esac
.endif
+.-include <ccm.dep.mk>
.else
.depend:
.endif
diff --git a/contrib/bmake/mk/dirdeps-cache-update.mk b/contrib/bmake/mk/dirdeps-cache-update.mk
index e442efd1d497..9e7cb8281110 100644
--- a/contrib/bmake/mk/dirdeps-cache-update.mk
+++ b/contrib/bmake/mk/dirdeps-cache-update.mk
@@ -1,4 +1,6 @@
-# $Id: dirdeps-cache-update.mk,v 1.22 2020/09/10 00:14:38 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: dirdeps-cache-update.mk,v 1.23 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2020, Simon J. Gerraty
#
diff --git a/contrib/bmake/mk/dirdeps-options.mk b/contrib/bmake/mk/dirdeps-options.mk
index 4e907e66141e..e12dcec40ac7 100644
--- a/contrib/bmake/mk/dirdeps-options.mk
+++ b/contrib/bmake/mk/dirdeps-options.mk
@@ -1,6 +1,8 @@
-# $Id: dirdeps-options.mk,v 1.18 2020/12/22 18:10:34 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
#
-# @(#) Copyright (c) 2018-2020, Simon J. Gerraty
+# $Id: dirdeps-options.mk,v 1.22 2024/02/17 17:26:57 sjg Exp $
+#
+# @(#) Copyright (c) 2018-2022, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@@ -42,6 +44,13 @@
# so we qualify MK_FOO with .${TARGET_SPEC} and each component
# TARGET_SPEC_VAR (in reverse order) before using MK_FOO.
#
+# Because Makefile.depend.options are processed at both level 0 (when
+# computing DIRDEPS to build) and higher (when updating
+# Makefile.depend* after successful build), it is important that
+# all references to TARGET_SPEC_VARs should use the syntax
+# ${DEP_${TARGET_SPEC_VAR}:U${TARGET_SPEC_VAR}} to ensure correct
+# behavior.
+#
# This should have been set by Makefile.depend.options
# before including us
@@ -54,6 +63,7 @@ DIRDEPS_OPTIONS ?=
# :U below avoids potential errors when we :=
# some options can depend on TARGET_SPEC!
DIRDEPS_OPTIONS_QUALIFIER_LIST ?= \
+ ${DEP_RELDIR} \
${DEP_TARGET_SPEC:U${TARGET_SPEC}} \
${TARGET_SPEC_VARSr:U${TARGET_SPEC_VARS}:@v@${DEP_$v:U${$v}}@}
# note that we need to include $o in the variable _o$o
@@ -61,7 +71,9 @@ DIRDEPS_OPTIONS_QUALIFIER_LIST ?= \
.for o in ${DIRDEPS_OPTIONS}
.undef _o$o
.undef _v$o
-.for x in ${DIRDEPS_OPTIONS_QUALIFIER_LIST}
+.for x in ${DIRDEPS_OPTIONS_QUALIFIER_LIST:S,^,${DEP_RELDIR}.,} \
+ ${DIRDEPS_OPTIONS_QUALIFIER_LIST}
+#.info MK_$o.$x=${MK_$o.$x:Uundefined}
.if defined(MK_$o.$x)
_o$o ?= MK_$o.$x
_v$o ?= ${MK_$o.$x}
diff --git a/contrib/bmake/mk/dirdeps-targets.mk b/contrib/bmake/mk/dirdeps-targets.mk
index 6201efe1e402..9e3fb814fada 100644
--- a/contrib/bmake/mk/dirdeps-targets.mk
+++ b/contrib/bmake/mk/dirdeps-targets.mk
@@ -1,5 +1,7 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
# RCSid:
-# $Id: dirdeps-targets.mk,v 1.24 2020/12/11 18:15:43 sjg Exp $
+# $Id: dirdeps-targets.mk,v 1.27 2024/02/25 19:12:13 sjg Exp $
#
# @(#) Copyright (c) 2019-2020 Simon J. Gerraty
#
@@ -40,6 +42,9 @@
# pickup customizations
.-include <local.dirdeps-targets.mk>
+# this is what we are here for
+.MAIN: dirdeps
+
# for DIRDEPS_BUILD this is how we prime the pump
# include . to allow any directory to work as a target
DIRDEPS_TARGETS_DIRS ?= targets targets/pseudo
@@ -113,16 +118,17 @@ tqtdeps := ${DIRDEPS_TARGETS_MACHINE_LIST:@m@${tdeps:M*.$m,*}@:S,/${.MAKE.DEPEND
.endif
# now work out what we want in DIRDEPS
+DIRDEPS = ${ptdeps}
.if empty(REQUESTED_MACHINE)
# we want them all just as found
-DIRDEPS = ${ptdeps} ${mqtdeps} ${tqtdeps}
+DIRDEPS += ${mqtdeps} ${tqtdeps}
.else
# we only want those that match REQUESTED_MACHINE/REQUESTED_TARGET_SPEC
# or REQUESTED_TARGET_SPEC (TARGET_SPEC)
-DIRDEPS = \
- ${ptdeps:@d@$d.${REQUESTED_TARGET_SPEC:U${TARGET_SPEC:U${REQUESTED_MACHINE}}}@} \
+DIRDEPS += \
${mqtdeps:M*.${REQUESTED_MACHINE}} \
- ${tqtdeps:M*.${REQUESTED_TARGET_SPEC:U${TARGET_SPEC}}}
+ ${tqtdeps:M*.${REQUESTED_TARGET_SPEC:U${TARGET_SPEC}}} \
+
.endif
# clean up
DIRDEPS := ${DIRDEPS:O:u}
diff --git a/contrib/bmake/mk/dirdeps.mk b/contrib/bmake/mk/dirdeps.mk
index 38ead3de37cd..7a9ecd881d7b 100644
--- a/contrib/bmake/mk/dirdeps.mk
+++ b/contrib/bmake/mk/dirdeps.mk
@@ -1,6 +1,8 @@
-# $Id: dirdeps.mk,v 1.140 2021/06/20 23:42:38 sjg Exp $
+# $Id: dirdeps.mk,v 1.167 2024/05/06 20:41:08 sjg Exp $
-# Copyright (c) 2010-2021, Simon J. Gerraty
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2010-2023, Simon J. Gerraty
# Copyright (c) 2010-2018, Juniper Networks, Inc.
# All rights reserved.
#
@@ -66,6 +68,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
@@ -130,6 +136,16 @@
# A list of MACHINEs the current directory should not be
# built for.
#
+# DIRDEPS_EXPORT_VARS (DEP_EXPORT_VARS)
+# It is discouraged, but sometimes necessary for a
+# Makefile.depend file to influence the environment.
+# Doing this is correctly (especially if using DIRDEPS_CACHE) is
+# tricky so a Makefile.depend file can set DIRDEPS_EXPORT_VARS
+# and dirdeps.mk will do the deed:
+#
+# MK_UEFI = yes
+# DIRDEPS_EXPORT_VARS = MK_UEFI
+#
# _build_xtra_dirs
# local.dirdeps.mk can add targets to this variable.
# They will be hooked into the build, but independent of
@@ -139,7 +155,7 @@
# if any test fails, but without the risk of introducing
# circular dependencies.
-now_utc ?= ${%s:L:gmtime}
+now_utc ?= ${%s:L:localtime}
.if !defined(start_utc)
start_utc := ${now_utc}
.endif
@@ -157,13 +173,15 @@ _DIRDEP_USE_LEVEL?= 0
.if ${.MAKE.LEVEL} == ${_DIRDEP_USE_LEVEL}
# only the first instance is interested in all this
+# the first time we are included the _DIRDEP_USE target will not be defined
+# we can use this as a clue to do initialization and other one time things.
.if !target(_DIRDEP_USE)
# do some setup we only need once
_CURDIR ?= ${.CURDIR}
_OBJDIR ?= ${.OBJDIR}
-.if ${MAKEFILE:T} == ${.PARSEFILE} && empty(DIRDEPS) && ${.TARGETS:Uall:M*/*} != ""
+.if ${MAKEFILE:T} == ${.PARSEFILE} && empty(DIRDEPS) && ${.TARGETS:Uall:M*[/.]*} != ""
# This little trick let's us do
#
# mk -f dirdeps.mk some/dir.${TARGET_SPEC}
@@ -180,6 +198,8 @@ TARGET_MACHINE := ${MACHINE}
.endif
# disable DIRDEPS_CACHE as it does not like this trick
MK_DIRDEPS_CACHE = no
+# incase anyone needs to know
+_dirdeps_cmdline:
.endif
# make sure we get the behavior we expect
@@ -192,12 +212,16 @@ _DEP_TARGET_SPEC =
# it should be set by sys.mk or similar by now.
# TARGET_SPEC must not contain any '.'s.
TARGET_SPEC_VARS ?= MACHINE
+# we allow for this to be a subset
+TARGET_SPEC_VARS.host ?= MACHINE
+TARGET_SPEC_VARS.host32 = ${TARGET_SPEC_VARS.host}
# this is what we started with
TARGET_SPEC = ${TARGET_SPEC_VARS:@v@${$v:U}@:ts,}
# this is what we mostly use below
-DEP_TARGET_SPEC = ${TARGET_SPEC_VARS:S,^,DEP_,:@v@${$v:U}@:ts,}
+DEP_TARGET_SPEC_VARS = ${TARGET_SPEC_VARS.${DEP_MACHINE}:U${TARGET_SPEC_VARS}}
+DEP_TARGET_SPEC = ${DEP_TARGET_SPEC_VARS:S,^,DEP_,:@v@${$v:U}@:ts,}
# make sure we have defaults
-.for v in ${TARGET_SPEC_VARS}
+.for v in ${DEP_TARGET_SPEC_VARS}
DEP_$v ?= ${$v}
.endfor
@@ -207,12 +231,7 @@ DEP_$v ?= ${$v}
# we compute below are fully qualified wrt DEP_TARGET_SPEC.
# The makefiles may only partially specify (eg. MACHINE only),
# so we need to construct a set of modifiers to fill in the gaps.
-.if ${MAKE_VERSION} >= 20170130
-_tspec_x := ${TARGET_SPEC_VARS:range}
-.else
-# do it the hard way
-_tspec_x := ${TARGET_SPEC_VARS:[#]:@x@i=1;while [ $$i -le $x ]; do echo $$i; i=$$((i + 1)); done;@:sh}
-.endif
+_tspec_x := ${TARGET_SPEC_VARS:${M_RANGE:Urange}}
# this handles unqualified entries
M_dep_qual_fixes = C;(/[^/.,]+)$$;\1.$${DEP_TARGET_SPEC};
# there needs to be at least one item missing for these to make sense
@@ -222,10 +241,27 @@ _tspec_a$i := ,${TARGET_SPEC_VARS:[$i..-1]:@v@$$$${DEP_$v}@:ts,}
M_dep_qual_fixes += C;(\.${_tspec_m$i})$$;\1${_tspec_a$i};
.endfor
TARGET_SPEC_VARSr := ${TARGET_SPEC_VARS:[-1..1]}
+.if ${TARGET_SPEC_VARS.host} == ${TARGET_SPEC_VARS}
+M_dep_qual_fixes.host = ${M_dep_qual_fixes}
+.elif ${TARGET_SPEC_VARS.host:[#]} > 1
+_htspec_x := ${TARGET_SPEC_VARS.host:${M_RANGE:Urange}}
+# this handles unqualified entries
+M_dep_qual_fixes.host = C;(/[^/.,]+)$$;\1.$${DEP_TARGET_SPEC};
+# there needs to be at least one item missing for these to make sense
+.for i in ${_htspec_x:[2..-1]}
+_htspec_m$i := ${TARGET_SPEC_VARS.host:[2..$i]:@w@[^,]+@:ts,}
+_htspec_a$i := ,${TARGET_SPEC_VARS.host:[$i..-1]:@v@$$$${DEP_$v}@:ts,}
+M_dep_qual_fixes.host += C;(\.${_htspec_m$i})$$;\1${_htspec_a$i};
+.endfor
+.else
+M_dep_qual_fixes.host = U
+.endif
.else
# A harmless? default.
M_dep_qual_fixes = U
.endif
+M_dep_qual_fixes.host ?= ${M_dep_qual_fixes}
+M_dep_qual_fixes.host32 = ${M_dep_qual_fixes.host}
.if !defined(.MAKE.DEPENDFILE_PREFERENCE)
# .MAKE.DEPENDFILE_PREFERENCE makes the logic below neater?
@@ -253,12 +289,40 @@ _machine_dependfiles := ${.MAKE.DEPENDFILE_PREFERENCE:T:M*${MACHINE}*}
.endif
.endif
+# turn a list into a set of :N modifiers
+# NskipFoo = ${Foo:${M_ListToSkip}}
+M_ListToSkip ?= O:u:S,^,N,:ts:
# this is how we identify non-machine specific dependfiles
N_notmachine := ${.MAKE.DEPENDFILE_PREFERENCE:E:N*${MACHINE}*:${M_ListToSkip}}
+# this gets reset for each dirdep we check
+DEP_RELDIR ?= ${RELDIR}
+
+# remember the initial value of DEP_RELDIR - we test for it below.
+_DEP_RELDIR := ${DEP_RELDIR}
+
+# this can cause lots of output!
+# set to a set of glob expressions that might match RELDIR
+DEBUG_DIRDEPS ?= no
+
+# make sure this target exists
+dirdeps: beforedirdeps .WAIT
+beforedirdeps:
+
.endif # !target(_DIRDEP_USE)
+.if ${DEBUG_DIRDEPS:@x@${DEP_RELDIR:M$x}${${DEP_RELDIR}.${DEP_MACHINE}:L:M$x}@} != ""
+_debug_reldir = 1
+.else
+_debug_reldir = 0
+.endif
+.if ${DEBUG_DIRDEPS:@x@${DEP_RELDIR:M$x}${${DEP_RELDIR}.depend depend:L:M$x}@} != ""
+_debug_search = 1
+.else
+_debug_search = 0
+.endif
+
# First off, we want to know what ${MACHINE} to build for.
# This can be complicated if we are using a mixture of ${MACHINE} specific
# and non-specific Makefile.depend*
@@ -280,7 +344,7 @@ _tspec := ${_DEP_TARGET_SPEC:S/,/ /g}
.for i in ${_tspec_x}
DEP_${TARGET_SPEC_VARS:[$i]} := ${_tspec:[$i]}
.endfor
-.for v in ${TARGET_SPEC_VARS:O:u}
+.for v in ${DEP_TARGET_SPEC_VARS:O:u}
.if empty(DEP_$v)
.undef DEP_$v
.endif
@@ -293,26 +357,6 @@ DEP_MACHINE := ${_DEP_TARGET_SPEC}
_build_all_dirs =
_build_xtra_dirs =
-# the first time we are included the _DIRDEP_USE target will not be defined
-# we can use this as a clue to do initialization and other one time things.
-.if !target(_DIRDEP_USE)
-# make sure this target exists
-dirdeps: beforedirdeps .WAIT
-beforedirdeps:
-
-# We normally expect to be included by Makefile.depend.*
-# which sets the DEP_* macros below.
-DEP_RELDIR ?= ${RELDIR}
-
-# this can cause lots of output!
-# set to a set of glob expressions that might match RELDIR
-DEBUG_DIRDEPS ?= no
-
-# remember the initial value of DEP_RELDIR - we test for it below.
-_DEP_RELDIR := ${DEP_RELDIR}
-
-.endif
-
# DIRDEPS_CACHE can be very handy for debugging.
# Also if repeatedly building the same target,
# we can avoid the overhead of re-computing the tree dependencies.
@@ -325,15 +369,9 @@ BUILD_DIRDEPS ?= yes
DIRDEPS_CACHE ?= ${_OBJDIR:tA}/dirdeps.cache${_TARGETS:U${.TARGETS}:Nall:O:u:ts-:S,/,_,g:S,^,.,:N.}
.endif
-.if ${DEBUG_DIRDEPS:@x@${DEP_RELDIR:M$x}${${DEP_RELDIR}.${DEP_MACHINE}:L:M$x}@} != ""
-_debug_reldir = 1
-.else
-_debug_reldir = 0
-.endif
-.if ${DEBUG_DIRDEPS:@x@${DEP_RELDIR:M$x}${${DEP_RELDIR}.depend:L:M$x}@} != ""
-_debug_search = 1
-.else
-_debug_search = 0
+# sanity check: Makefile.depend.options should *not* include us
+.if ${.INCLUDEDFROMFILE:U:M${.MAKE.DEPENDFILE_PREFIX}.options} != ""
+.error ${DEP_RELDIR}/${.MAKE.DEPENDFILE_PREFIX}.options: should include dirdeps-options.mk
.endif
# pickup customizations
@@ -377,6 +415,8 @@ DIRDEPS_FILTER += M${_DEP_RELDIR}
# this is what we run below
DIRDEP_MAKE ?= ${.MAKE}
DIRDEP_DIR ?= ${.TARGET:R}
+# we normally want the default target
+DIRDEP_TARGETS ?=
# if you want us to report load averages during build
# DIRDEP_USE_PRELUDE += ${DIRDEP_LOADAVG_REPORT};
@@ -389,7 +429,7 @@ DIRDEP_LOADAVG_LAST = 0
# Note: expr(1) will exit 1 if the expression evaluates to 0
# hence the || true
DIRDEP_LOADAVG_REPORT = \
- test -z "${"${expr ${now_utc} - ${DIRDEP_LOADAVG_INTEVAL:U60} - ${DIRDEP_LOADAVG_LAST} || true:L:sh:N-*}":?yes${DIRDEP_LOADAVG_LAST::=${now_utc}}:}" || \
+ test -z "${"${expr ${now_utc} - ${DIRDEP_LOADAVG_INTERVAL:U60} - ${DIRDEP_LOADAVG_LAST} || true:L:sh:N-*}":?yes${DIRDEP_LOADAVG_LAST::=${now_utc}}:}" || \
echo "${TRACER}`${DIRDEP_LOADAVG_CMD}`"
# we suppress SUBDIR when visiting the leaves
@@ -399,12 +439,12 @@ 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} \
MACHINE=${.TARGET:E} \
- ${DIRDEP_MAKE} -C ${DIRDEP_DIR} || exit 1; \
+ ${DIRDEP_MAKE} -C ${DIRDEP_DIR} ${DIRDEP_TARGETS} || exit 1; \
break; \
done
@@ -528,7 +568,10 @@ BUILD_DIRDEPS = no
dirdeps: dirdeps-cached
dirdeps-cached: ${DIRDEPS_CACHE} .MAKE
@echo "${TRACER}Using ${DIRDEPS_CACHE}"
- @MAKELEVEL=${.MAKE.LEVEL} ${.MAKE} -C ${_CURDIR} -f ${DIRDEPS_CACHE} \
+ @MAKELEVEL=${.MAKE.LEVEL} \
+ TARGET_SPEC=${TARGET_SPEC} \
+ ${TARGET_SPEC_VARS:@v@$v=${$v}@} \
+ ${.MAKE} -C ${_CURDIR} -f ${DIRDEPS_CACHE} \
dirdeps MK_DIRDEPS_CACHE=no BUILD_DIRDEPS=no
# leaf makefiles rarely work for building DIRDEPS_CACHE
@@ -538,6 +581,7 @@ BUILD_DIRDEPS_MAKEFILE ?= -f dirdeps.mk
# these should generally do
BUILD_DIRDEPS_MAKEFILE ?=
+BUILD_DIRDEPS_OVERRIDES ?=
BUILD_DIRDEPS_TARGETS ?= ${.TARGETS}
.if ${DIRDEPS_CACHE} != ${STATIC_DIRDEPS_CACHE:Uno} && ${DIRDEPS_CACHE:M${SRCTOP}/*} == ""
@@ -557,12 +601,14 @@ ${DIRDEPS_CACHE}: .META .NOMETA_CMP
TARGET_SPEC=${TARGET_SPEC} \
MAKEFLAGS= ${DIRDEP_CACHE_MAKE:U${.MAKE}} -C ${_CURDIR} \
${BUILD_DIRDEPS_MAKEFILE} \
- ${BUILD_DIRDEPS_TARGETS} BUILD_DIRDEPS_CACHE=yes \
+ ${BUILD_DIRDEPS_TARGETS} \
+ ${BUILD_DIRDEPS_OVERRIDES} \
+ BUILD_DIRDEPS_CACHE=yes \
.MAKE.DEPENDFILE=.none \
${"${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,_{SRCTOP}/_{SRCTOP},_{SRCTOP},g;s,_{,$${,g' >> ${.TARGET}.new && \
mv ${.TARGET}.new ${.TARGET}
.endif
@@ -618,27 +664,28 @@ _machines += host
_machines := ${_machines:O:u}
.endif
-.if ${TARGET_SPEC_VARS:[#]} > 1
+.if ${DEP_TARGET_SPEC_VARS:[#]} > 1
# we need to tweak _machines
_dm := ${DEP_MACHINE}
# apply the same filtering that we do when qualifying DIRDEPS.
# M_dep_qual_fixes expects .${MACHINE}* so add (and remove) '.'
# Again we expect that any already qualified machines are fully qualified.
-_machines := ${_machines:M*,*} ${_machines:N*,*:@DEP_MACHINE@${DEP_TARGET_SPEC}@:S,^,.,:${M_dep_qual_fixes:ts:}:O:u:S,^.,,}
+_machines := ${_machines:M*,*} ${_machines:N*,*:@DEP_MACHINE@${DEP_TARGET_SPEC}@:S,^,.,:S,^.,,}
DEP_MACHINE := ${_dm}
-_machines := ${_machines:O:u}
+_machines := ${_machines:${M_dep_qual_fixes.${DEP_MACHINE}:U${M_dep_qual_fixes}:ts:}:O:u}
.endif
# reset each time through
_build_dirs =
-.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+.if ${DEP_RELDIR} == ${_DEP_RELDIR} && ${_CURDIR} != ${SRCTOP}
# pickup other machines for this dir if necessary
_build_dirs += ${_machines:@m@${_CURDIR}.$m@}
.endif
.if ${_debug_reldir}
-.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: DIRDEPS='${DIRDEPS}'
+.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: nDIRDEPS=${DIRDEPS:[#]}
+.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: DIRDEPS=${DIRDEPS:${DEBUG_DIRDEPS_LIST_FILTER:U:N/:ts:}}
.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: _machines='${_machines}'
.endif
@@ -656,11 +703,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 +717,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}/,,:${DEBUG_DIRDEPS_LIST_FILTER:U:N/:ts:}}
+.info qualified=${__qual_depdirs:S,^${SRCTOP}/,,:${DEBUG_DIRDEPS_LIST_FILTER:U:N/:ts:}}
+.info unqualified=${__unqual_depdirs:S,^${SRCTOP}/,,:${DEBUG_DIRDEPS_LIST_FILTER:U:N/:ts:}}
.endif
# _build_dirs is what we will feed to _DIRDEP_USE
@@ -684,7 +730,16 @@ _build_dirs += \
${_machines:Nhost*:@m@${__unqual_depdirs:@d@$d.$m@}@}
# qualify everything now
-_build_dirs := ${_build_dirs:${M_dep_qual_fixes:ts:}:O:u}
+.if ${_debug_reldir}
+.info _build_dirs=${_build_dirs:${DEBUG_DIRDEPS_LIST_FILTER:U:N/:ts:}}
+.endif
+# make sure we do not mess with qualifying "host" entries
+_build_dirs := ${_build_dirs:M*.host*:${M_dep_qual_fixes.host:ts:}} \
+ ${_build_dirs:N*.host*:${M_dep_qual_fixes:ts:}}
+_build_dirs := ${_build_dirs:O:u}
+.if ${_debug_reldir}
+.info _build_dirs=${_build_dirs:${DEBUG_DIRDEPS_LIST_FILTER:U:N/:ts:}}
+.endif
.endif # empty DIRDEPS
@@ -694,17 +749,20 @@ _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
+# we use _cache_script to minimize the number of times we fork the shell
+_cache_script = echo '\# ${DEP_RELDIR}.${DEP_TARGET_SPEC}';
# guard against _new_dirdeps being too big for a single command line
-_new_dirdeps := ${_build_all_dirs:@x@${target($x):?:$x}@}
-.export _build_xtra_dirs _new_dirdeps
-.if !empty(DEP_EXPORT_VARS)
+_new_dirdeps := ${_build_all_dirs:@x@${target($x):?:$x}@:S,^${SRCTOP}/,,}
+_cache_xtra_deps := ${_build_xtra_dirs:S,^${SRCTOP}/,,}
+.if !empty(DIRDEPS_EXPORT_VARS) || !empty(DEP_EXPORT_VARS)
# Discouraged, but there are always exceptions.
# Handle it here rather than explain how.
-x!= echo; { echo; ${DEP_EXPORT_VARS:@v@echo '$v=${$v}';@} echo '.export ${DEP_EXPORT_VARS}'; echo; } >&3
+DIRDEPS_EXPORT_VARS ?= ${DEP_EXPORT_VARS}
+_cache_xvars := echo; ${DIRDEPS_EXPORT_VARS:@v@echo '$v = ${$v}';@} echo '.export ${DIRDEPS_EXPORT_VARS}'; echo;
+_cache_script += ${_cache_xvars}
.endif
.else
# this makes it all happen
@@ -713,48 +771,62 @@ 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}/,,:${DEBUG_DIRDEPS_LIST_FILTER:U:N/:ts:}}
.endif
-.if !empty(DEP_EXPORT_VARS)
-.export ${DEP_EXPORT_VARS}
-DEP_EXPORT_VARS=
+.if !empty(DIRDEPS_EXPORT_VARS) || !empty(DEP_EXPORT_VARS)
+.export ${DIRDEPS_EXPORT_VARS} ${DEP_EXPORT_VARS}
+DIRDEPS_EXPORT_VARS =
+DEP_EXPORT_VARS =
.endif
# 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 =
+_cache_script += echo; echo 'DIRDEPS.${_this_dir}.$m = \';
.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}
+.for q in ${__qual_depdirs:M*.host*:${M_dep_qual_fixes.host:ts:}:E:O:u:N$m} \
+ ${__qual_depdirs:N*.host*:${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: q=$q 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}/,,}
+# anything in _{build,env}_xtra_dirs is hooked to dirdeps: only
+.if ${MAKE_VERSION} < 20240105
.if !empty(_cache_deps)
.export _cache_deps
-x!= echo; for x in $$_cache_deps; do echo " $$x \\"; done >&3
+_cache_script += for x in $$_cache_deps; do echo " _{SRCTOP}/$$x \\"; done;
+.endif
+.export _cache_xtra_deps _new_dirdeps
+x!= echo; { echo; ${_cache_script} echo; echo '${_this_dir}.$m: $${DIRDEPS.${_this_dir}.$m}'; \
+ echo; echo 'dirdeps: ${_this_dir}.$m \'; \
+ for x in $$_cache_xtra_deps; do echo " _{SRCTOP}/$$x \\"; done; \
+ echo; for x in $$_new_dirdeps; do echo "_{SRCTOP}/$$x: _DIRDEP_USE"; done; } >&3
+.else
+# we do not have the same limits on command lines
+.if !empty(_cache_deps)
+_cache_script += for x in ${_cache_deps}; do echo " _{SRCTOP}/$$x \\"; done;
.endif
-# anything in _build_xtra_dirs is hooked to dirdeps: only
-x!= echo; { echo; echo '${_this_dir}.$m: $${DIRDEPS.${_this_dir}.$m}'; \
+x!= echo; { echo; ${_cache_script} 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
.endif
.else
${_this_dir}.$m: ${_build_dirs:M*.$m:N${_this_dir}.$m}
@@ -768,8 +840,9 @@ ${_this_dir}.$m: ${_build_dirs:M*.$m:N${_this_dir}.$m}
.if !target(_dirdeps_checked.$d)
# once only
_dirdeps_checked.$d:
+_dr := ${d:S,^${SRCTOP}/,,}
.if ${_debug_search}
-.info checking $d
+.info checking ${_dr}
.endif
# Note: _build_all_dirs is fully qualifed so d:R is always the directory
.if exists(${d:R})
@@ -777,14 +850,13 @@ _dirdeps_checked.$d:
_DEP_TARGET_SPEC := ${d:E}
# some makefiles may still look at this
_DEP_MACHINE := ${d:E:C/,.*//}
+DEP_MACHINE := ${_DEP_MACHINE}
# set these too in case Makefile.depend* uses them
-.if ${TARGET_SPEC_VARS:[#]} > 1
+.if ${DEP_TARGET_SPEC_VARS:[#]} > 1
_dtspec := ${_DEP_TARGET_SPEC:S/,/ /g}
.for i in ${_tspec_x}
-DEP_${TARGET_SPEC_VARS:[$i]} := ${_dtspec:[$i]}
+DEP_${DEP_TARGET_SPEC_VARS:[$i]} := ${_dtspec:[$i]}
.endfor
-.else
-DEP_MACHINE := ${_DEP_MACHINE}
.endif
# Warning: there is an assumption here that MACHINE is always
# the first entry in TARGET_SPEC_VARS.
@@ -792,20 +864,26 @@ DEP_MACHINE := ${_DEP_MACHINE}
_m := ${.MAKE.DEPENDFILE_PREFERENCE:T:S;${TARGET_SPEC}$;${d:E};:C;${MACHINE}((,.+)?)$;${d:E:C/,.*//}\1;:@m@${exists(${d:R}/$m):?${d:R}/$m:}@:[1]}
.if !empty(_m)
# M_dep_qual_fixes isn't geared to Makefile.depend
-_qm := ${_m:C;(\.depend)$;\1.${d:E};:${M_dep_qual_fixes:ts:}}
+_qm := ${_m:C;(\.depend)$;\1.${d:E};:${M_dep_qual_fixes.${d:E}:U${M_dep_qual_fixes}:ts:}}
.if ${_debug_search}
.info Looking for ${_qm}
.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}
-.info loading ${_m} for ${d:E}
+.info loading ${_m:S,${SRCTOP}/,,} for ${_dr}
.endif
.include <${_m}>
.else
+# set these as if we found Makefile.depend*
+DEP_RELDIR := ${_dr:R}
+DIRDEPS =
+.if ${_debug_reldir}
+.info loading local.dirdeps-missing.mk for ${_dr}
+.endif
.-include <local.dirdeps-missing.mk>
.endif
.endif
diff --git a/contrib/bmake/mk/doc.mk b/contrib/bmake/mk/doc.mk
index b6ebd8ad3d3e..34202371fc5c 100644
--- a/contrib/bmake/mk/doc.mk
+++ b/contrib/bmake/mk/doc.mk
@@ -1,7 +1,10 @@
-# $Id: doc.mk,v 1.7 2019/06/09 16:22:08 sjg Exp $
+# $Id: doc.mk,v 1.9 2024/02/19 00:06:19 sjg Exp $
-.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+# should be set properly in sys.mk
+_this ?= ${.PARSEFILE:S,bsd.,,}
+
+.if !target(__${_this}__)
+__${_this}__: .NOTMAIN
.include <init.mk>
diff --git a/contrib/bmake/mk/dpadd.mk b/contrib/bmake/mk/dpadd.mk
index aef12528f163..4e430b97b3b8 100644
--- a/contrib/bmake/mk/dpadd.mk
+++ b/contrib/bmake/mk/dpadd.mk
@@ -1,6 +1,8 @@
-# $Id: dpadd.mk,v 1.29 2021/04/20 02:30:44 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
#
-# @(#) Copyright (c) 2004, Simon J. Gerraty
+# $Id: dpadd.mk,v 1.33 2024/02/17 17:26:57 sjg Exp $
+#
+# @(#) Copyright (c) 2004-2023, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@@ -75,9 +77,13 @@
# and -L${STAGE_OBJTOP}/usr/lib are sufficient, and we should
# have no need of anything else.
#
+# Sometimes things are more complicated so allow for
+# DPLIBS to be qualified with each of the variables in
+# DPLIBS_QUALIFIER_LIST (default is VAR_QUALIFIER_LIST same as
+# init.mk)
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+__${.PARSEFILE}__: .NOTMAIN
# sometimes we play games with .CURDIR etc
# _* hold the original values of .*
@@ -110,7 +116,9 @@ CXXFLAGS_LAST += ${CXXFLAGS_DEBUG_XTRA}
.-include <local.dpadd.mk>
# DPLIBS helps us ensure we keep DPADD and LDADD in sync
-DPLIBS+= ${DPLIBS_LAST}
+DPLIBS_QUALIFIER_LIST ?= ${VAR_QUALIFIER_LIST}
+DPLIBS += ${DPLIBS_QUALIFIER_LIST:u:@Q@${DPLIBS.$Q:U}@}
+DPLIBS+= ${DPLIBS_LAST} ${DPLIBS_QUALIFIER_LIST:u:@Q@${DPLIBS_LAST.$Q:U}@}
DPADD+= ${DPLIBS:N-*}
.for __lib in ${DPLIBS}
.if "${__lib:M-*}" != ""
@@ -131,7 +139,7 @@ __dpadd_libs := ${DPADD:M*/lib*}
# dups will be dealt with later.
# Note: libfoo_pic uses DPLIBS_libfoo
__ldadd_all_xtras=
-.for __lib in ${__dpadd_libs:@d@${DPLIBS_${d:T:R:S,_pic,,}}@}
+.for __lib in ${__dpadd_libs:@d@${DPLIBS_${d:T:R:S,_pic,,}} ${DPLIBS_QUALIFIER_LIST:u:@Q@${DPLIBS_${d:T:R:S,_pic,,}.$Q:U}@}@}
__ldadd_all_xtras+= ${LDADD_${__lib}:U${__lib:T:R:S/lib/-l/:C/\.so.*//}}
.if "${DPADD:M${__lib}}" == ""
DPADD+= ${__lib}
@@ -248,7 +256,7 @@ SHLDADD+= -L${__lib:H}
# Now for the bits we actually need
__dpadd_incs=
.for __lib in ${__dpadd_libs:u}
-.if (make(${PROG}_p) || defined(NEED_GPROF)) && exists(${__lib:R}_p.a)
+.if (make(${PROG:U}_p) || defined(NEED_GPROF)) && exists(${__lib:R}_p.a)
__ldadd=-l${__lib:T:R:S,lib,,}
LDADD := ${LDADD:S,^${__ldadd}$,${__ldadd}_p,g}
.endif
diff --git a/contrib/bmake/mk/files.mk b/contrib/bmake/mk/files.mk
index 513ab1fd819e..e8de8600d2c6 100644
--- a/contrib/bmake/mk/files.mk
+++ b/contrib/bmake/mk/files.mk
@@ -1,4 +1,6 @@
-# $Id: files.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: files.mk,v 1.8 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2017, Simon J. Gerraty
#
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/gendirdeps.mk b/contrib/bmake/mk/gendirdeps.mk
index b977f3c48d99..5d54aa232152 100644
--- a/contrib/bmake/mk/gendirdeps.mk
+++ b/contrib/bmake/mk/gendirdeps.mk
@@ -1,5 +1,7 @@
-# $Id: gendirdeps.mk,v 1.46 2020/08/19 17:51:53 sjg Exp $
+# $Id: gendirdeps.mk,v 1.50 2023/11/04 16:47:34 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
# Copyright (c) 2011-2020, Simon J. Gerraty
# Copyright (c) 2010-2018, Juniper Networks, Inc.
# All rights reserved.
@@ -41,6 +43,37 @@
# symlink to another filesystem.
# _objroot must be a prefix match for _objtop
+# If any of GENDIRDEPS_FILTER, GENDIRDEPS_FILTER_DIR_VARS
+# or GENDIRDEPS_FILTER_VARS are set, we use them to filter the
+# output from filemon(4).
+# Any references to variables that dirdeps.mk will set
+# such as DEP_MACHINE, DEP_RELDIR etc, should use that form.
+# Thus we want ${DEP_MACHINE} not ${MACHINE} used in DIRDEPS.
+#
+# If any manually maintained Makefile.depend files will use any
+# DEP_* variables in conditionals, precautions are needed to avoid
+# errors when Makefile.depend is read at level 1+ (ie not via
+# dirdeps.mk)
+# Using MACHINE as an example; such makefiles can do:
+#
+# DEP_MACHINE ?= ${MACHINE}
+# .if ${DEP_MACHINE} == "xyz"
+#
+# or:
+#
+# .if ${DEP_MACHINE:U${MACHINE}} == "xyz"
+#
+# but it might be safer to set GENDIRDEPS_FILTER_DIR_VARS and
+# GENDIRDEPS_FILTER_VARS via local.meta.sys.mk rather than
+# local.gendirdeps.mk and then:
+#
+# .if ${.MAKE.LEVEL} > 0
+# .for V in ${GENDIRDEPS_FILTER_DIR_VARS:MDEP_*} \
+# ${GENDIRDEPS_FILTER_VARS:MDEP_*}
+# $V ?= ${${V:S,DEP_,,}}
+# .endfor
+# .endif
+#
.MAIN: all
# keep this simple
@@ -69,6 +102,10 @@ _DEPENDFILE := ${_CURDIR}/${.MAKE.DEPENDFILE:T}
# caller should have set this
META_FILES ?= ${.MAKE.META.FILES}
+# this sometimes needs to be passed separately
+.if !empty(META_XTRAS)
+META_FILES += ${META_XTRAS:N\*.meta}
+.endif
.if !empty(META_FILES)
@@ -88,7 +125,7 @@ META_FILES := ${META_FILES:T:O:u}
# they should all be absolute paths
SKIP_GENDIRDEPS ?=
.if !empty(SKIP_GENDIRDEPS)
-_skip_gendirdeps = egrep -v '^(${SKIP_GENDIRDEPS:O:u:ts|})' |
+_skip_gendirdeps = ${EGREP:Uegrep} -v '^(${SKIP_GENDIRDEPS:O:u:ts|})' |
.else
_skip_gendirdeps =
.endif
@@ -339,6 +376,8 @@ CAT_DEPEND ?= .depend
.PHONY: ${_DEPENDFILE}
.endif
+# set this to 'no' and we will not capture any
+# local depends
LOCAL_DEPENDS_GUARD ?= _{.MAKE.LEVEL} > 0
# 'cat .depend' should suffice, but if we are mixing build modes
@@ -351,6 +390,7 @@ ${_DEPENDFILE}: .NOMETA ${CAT_DEPEND:M.depend} ${META_FILES:O:u:@m@${exists($m):
echo '${DIRDEPS:@d@ $d \\${.newline}@}'; echo; \
${_include_src_dirdeps} \
echo '.include <dirdeps.mk>'; \
+ [ "${LOCAL_DEPENDS_GUARD:[1]:tl}" != no ] || exit 0; \
echo; \
echo '.if ${LOCAL_DEPENDS_GUARD}'; \
echo '# local dependencies - needed for -jN in clean tree'; \
diff --git a/contrib/bmake/mk/host-target.mk b/contrib/bmake/mk/host-target.mk
index 3e6094f16730..0316cacb2713 100644
--- a/contrib/bmake/mk/host-target.mk
+++ b/contrib/bmake/mk/host-target.mk
@@ -1,32 +1,54 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
# RCSid:
-# $Id: host-target.mk,v 1.13 2020/08/05 23:32:08 sjg Exp $
+# $Id: host-target.mk,v 1.20 2024/02/17 17:26:57 sjg Exp $
+#
+# @(#) Copyright (c) 2007-2023 Simon J. Gerraty
+#
+# This file is provided in the hope that it will
+# be of use. There is absolutely NO WARRANTY.
+# Permission to copy, redistribute or otherwise
+# use this file is hereby granted provided that
+# the above copyright notice and this notice are
+# left intact.
+#
+# Please send copies of changes and bug-fixes to:
+# sjg@crufty.net
# Host platform information; may be overridden
+.if !target(__${.PARSEFILE}__)
+__${.PARSEFILE}__: .NOTMAIN
+
.if !defined(_HOST_OSNAME)
-_HOST_OSNAME != uname -s
+# use .MAKE.OS if available
+_HOST_OSNAME := ${.MAKE.OS:U${uname -s:L:sh}}
.export _HOST_OSNAME
.endif
.if !defined(_HOST_OSREL)
_HOST_OSREL != uname -r
.export _HOST_OSREL
.endif
-.if !defined(_HOST_MACHINE)
-_HOST_MACHINE != uname -m
-.export _HOST_MACHINE
-.endif
.if !defined(_HOST_ARCH)
-# for NetBSD prefer $MACHINE (amd64 rather than x86_64)
-.if ${_HOST_OSNAME:NDarwin:NNetBSD} == ""
-_HOST_ARCH := ${_HOST_MACHINE}
-.else
_HOST_ARCH != uname -p 2> /dev/null || uname -m
# uname -p may produce garbage on linux
.if ${_HOST_ARCH:[\#]} > 1 || ${_HOST_ARCH:Nunknown} == ""
-_HOST_ARCH := ${_HOST_MACHINE}
-.endif
+_HOST_ARCH = ${_HOST_MACHINE}
+.elif ${_HOST_OSNAME:NDarwin} == "" && ${_HOST_ARCH:Narm:Ni386} == ""
+# _HOST_MACHINE is more explicit/useful
+_HOST_ARCH = ${_HOST_MACHINE}
.endif
.export _HOST_ARCH
.endif
+.if !defined(_HOST_MACHINE)
+_HOST_MACHINE != uname -m
+# just in case
+_HOST_ARCH := ${_HOST_ARCH}
+# uname -m may produce garbage on darwin ppc
+.if ${_HOST_MACHINE:[\#]} > 1
+_HOST_MACHINE := ${_HOST_ARCH}
+.endif
+.export _HOST_MACHINE
+.endif
.if !defined(HOST_MACHINE)
HOST_MACHINE := ${_HOST_MACHINE}
.export HOST_MACHINE
@@ -40,10 +62,17 @@ HOST_TARGET := ${host_os:S,/,,g}${HOST_OSMAJOR}-${_HOST_ARCH}
# sometimes we want HOST_TARGET32
MACHINE32.amd64 = i386
MACHINE32.x86_64 = i386
+.if !defined(_HOST_ARCH32)
_HOST_ARCH32 := ${MACHINE32.${_HOST_ARCH}:U${_HOST_ARCH:S,64$,,}}
+.export _HOST_ARCH32
+.endif
HOST_TARGET32 := ${host_os:S,/,,g}${HOST_OSMAJOR}-${_HOST_ARCH32}
+.export HOST_TARGET HOST_TARGET32
+
# tr is insanely non-portable, accommodate the lowest common denominator
TR ?= tr
toLower = ${TR} 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'
toUpper = ${TR} 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+
+.endif
diff --git a/contrib/bmake/mk/inc.mk b/contrib/bmake/mk/inc.mk
index 5fc14b32d88d..9ea93bd1573d 100644
--- a/contrib/bmake/mk/inc.mk
+++ b/contrib/bmake/mk/inc.mk
@@ -1,4 +1,6 @@
-# $Id: inc.mk,v 1.8 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: inc.mk,v 1.9 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2008, Simon J. Gerraty
#
diff --git a/contrib/bmake/mk/init.mk b/contrib/bmake/mk/init.mk
index 8f6f8f2fbaf4..b6619721c943 100644
--- a/contrib/bmake/mk/init.mk
+++ b/contrib/bmake/mk/init.mk
@@ -1,6 +1,8 @@
-# $Id: init.mk,v 1.25 2020/11/27 17:59:46 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
#
-# @(#) Copyright (c) 2002, Simon J. Gerraty
+# $Id: init.mk,v 1.38 2024/04/09 17:18:24 sjg Exp $
+#
+# @(#) Copyright (c) 2002-2024, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@@ -13,8 +15,11 @@
# sjg@crufty.net
#
-.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+# should be set properly in sys.mk
+_this ?= ${.PARSEFILE:S,bsd.,,}
+
+.if !target(__${_this}__)
+__${_this}__: .NOTMAIN
.if ${MAKE_VERSION:U0} > 20100408
_this_mk_dir := ${.PARSEDIR:tA}
@@ -27,10 +32,21 @@ _this_mk_dir := ${.PARSEDIR}
.include <own.mk>
.include <compiler.mk>
-.MAIN: all
-
# should have been set by sys.mk
-CXX_SUFFIXES?= .cc .cpp .cxx .C
+CXX_SUFFIXES ?= .cc .cpp .cxx .C
+CCM_SUFFIXES ?= .ccm
+PCM ?= .pcm
+# ${PICO} is used for PIC object files.
+PICO ?= .pico
+
+# SRCS which do not end up in OBJS
+NO_OBJS_SRCS_SUFFIXES ?= .h ${CCM_SUFFIXES} .sh
+OBJS_SRCS_FILTER += ${NO_OBJS_SRCS_SUFFIXES:@x@N*$x@:ts:}
+
+.if defined(PROG_CXX) || ${SRCS:Uno:${CXX_SUFFIXES:S,^,N*,:ts:}} != ${SRCS:Uno:N/}
+_CCLINK ?= ${CXX}
+.endif
+_CCLINK ?= ${CC}
.if !empty(WARNINGS_SET) || !empty(WARNINGS_SET_${MACHINE_ARCH})
.include <warnings.mk>
@@ -51,11 +67,12 @@ QUALIFIED_VAR_LIST += \
CPPFLAGS \
CPUFLAGS \
LDFLAGS \
+ SRCS \
# a final :U avoids errors if someone uses :=
.for V in ${QUALIFIED_VAR_LIST:O:u:@q@$q $q_LAST@}
.for Q in ${VAR_QUALIFIER_LIST:u}
-$V += ${$V.$Q:U} ${$V.$Q.${COMPILER_TYPE}:U}
+$V += ${$V_$Q:U${$V.$Q:U}} ${V_$Q_${COMPILER_TYPE}:U${$V.$Q.${COMPILER_TYPE}:U}}
.endfor
.endfor
@@ -66,15 +83,23 @@ CXX_PIC?= ${CC_PIC}
PROFFLAGS?= -DGPROF -DPROF
.if ${.MAKE.LEVEL:U1} == 0 && ${MK_DIRDEPS_BUILD:Uno} == "yes"
-# targets that are ok at level 0
+.if ${RELDIR} == "."
+# top-level targets that are ok at level 0
DIRDEPS_BUILD_LEVEL0_TARGETS += clean* destroy*
M_ListToSkip?= O:u:S,^,N,:ts:
.if ${.TARGETS:Uall:${DIRDEPS_BUILD_LEVEL0_TARGETS:${M_ListToSkip}}} != ""
# this tells lib.mk and prog.mk to not actually build anything
_SKIP_BUILD = not building at level 0
.endif
+.elif ${.TARGETS:U:Nall} == ""
+_SKIP_BUILD = not building at level 0
+# first .MAIN is what counts
+.MAIN: dirdeps
+.endif
.endif
+.MAIN: all
+
.if !defined(.PARSEDIR)
# no-op is the best we can do if not bmake.
.WAIT:
diff --git a/contrib/bmake/mk/install-mk b/contrib/bmake/mk/install-mk
index 96c35b1052ec..c2962ffee6dc 100755..100644
--- a/contrib/bmake/mk/install-mk
+++ b/contrib/bmake/mk/install-mk
@@ -28,6 +28,10 @@
# -g "group"
# Use "group" for installed files.
#
+# -U "umask"
+# Use "umask" so directories are created with suitable
+# mode (default is 022).
+#
# var=val
# Set "var" to "val". See below.
#
@@ -55,9 +59,9 @@
# 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.254 2024/05/06 20:41:08 sjg Exp $
#
-# @(#) Copyright (c) 1994 Simon J. Gerraty
+# @(#) Copyright (c) 1994-2024 Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@@ -70,7 +74,7 @@
# sjg@crufty.net
#
-MK_VERSION=20210616
+MK_VERSION=20240504
OWNER=
GROUP=
MODE=444
@@ -79,6 +83,8 @@ ECHO=:
SKIP=
cp_f=-f
+umask 022
+
while :
do
case "$1" in
@@ -91,6 +97,7 @@ do
-v) ECHO=echo; shift;;
-q) ECHO=:; shift;;
-n) ECHO=echo SKIP=:; shift;;
+ -U) umask $2; shift;;
--) shift; break;;
*) break;;
esac
@@ -131,6 +138,12 @@ realpath() {
echo $1
}
+# some Linux systems have deprecated egrep in favor of grep -E
+case "`echo bmake | egrep 'a' 2>&1`" in
+*"grep -E"*) egrep='grep -E';;
+*) egrep=egrep;;
+esac
+
if [ -s $SYS_MK -a -d $dest ]; then
# if this is a BSD system we don't want to touch $SYS_MK
dest=`realpath $dest`
@@ -160,8 +173,8 @@ if [ $mksrc = $dest ]; then
SKIP_MKFILES=:
else
# we do not install the examples
- mk_files=`grep '^[a-z].*\.mk' FILES | egrep -v '(examples/|^sys\.mk|sys/)'`
- mk_scripts=`egrep '^[a-z].*\.(sh|py)' FILES | egrep -v '/'`
+ mk_files=`grep '^[a-z].*\.mk' FILES | $egrep -v '(examples/|^sys\.mk|sys/)'`
+ mk_scripts=`$egrep '^[a-z].*[.-](sh|py)' FILES | $egrep -v '/'`
sys_mk_files=`grep 'sys/.*\.mk' FILES`
SKIP_MKFILES=
[ -z "$SKIP_SYS_MK" ] && mk_files="sys.mk $mk_files"
diff --git a/contrib/bmake/mk/install-new.mk b/contrib/bmake/mk/install-new.mk
index d312bdc26d46..0f2e395a8238 100644
--- a/contrib/bmake/mk/install-new.mk
+++ b/contrib/bmake/mk/install-new.mk
@@ -1,4 +1,6 @@
-# $Id: install-new.mk,v 1.4 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: install-new.mk,v 1.5 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2009, Simon J. Gerraty
#
diff --git a/contrib/bmake/mk/install-sh b/contrib/bmake/mk/install-sh
new file mode 100755
index 000000000000..aa35fa94c83a
--- /dev/null
+++ b/contrib/bmake/mk/install-sh
@@ -0,0 +1,228 @@
+#!/bin/sh
+
+# NAME:
+# install.sh - portable version of install(1)
+#
+# SYNOPSIS:
+# install [-CNcs] [-f flags] [-i errs] [-o owner] [-g group] [-m mode] file1 file2 ...
+# install -d [-i errs] [-o owner] [-g group] [-m mode] directory ...
+#
+# DESCRIPTION:
+# Compatible with BSD install(1). Except that '-c' is always
+# true and we always move an already installed target aside as
+# this is important on many systems. Recent BSD install(1)
+# versions have a '-b' option for this.
+#
+#
+# OPTIONS:
+# -b move previous target file aside (always true).
+#
+# -B "suffix"
+# use "suffix" instead of .old for saving existing target.
+#
+# -c copy rather than move the file into place (always true).
+#
+# -C compare. Only install if target is missing or
+# different.
+#
+# -N newer. Only install if target is missing or older.
+#
+# -s strip target
+#
+# -o "owner"
+# make target owned by "owner"
+#
+# -g "group"
+# make target group owned by "group"
+#
+# -m "mode"
+# set permissions to "mode"
+#
+# -f "flags"
+# Pass "flags" onto chflags(1)
+#
+# -i "errs"
+# Ignore errors from steps indicated by "errs" (``s,o,g,m'').
+#
+# BUGS:
+# The '-i' option is to save your sanity when 'bsd.prog.mk'
+# insists on haveing a '-o' "owner" option which is doomed to
+# fail on many systems. We ignore '-b' and '-c' options.
+#
+# AUTHOR:
+# Simon J. Gerraty <sjg@crufty.net>
+#
+
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# RCSid:
+# $Id: install-sh,v 1.26 2024/02/17 17:26:57 sjg Exp $
+#
+# @(#) Copyright (c) 1993-2023 Simon J. Gerraty
+#
+# This file is provided in the hope that it will
+# be of use. There is absolutely NO WARRANTY.
+# Permission to copy, redistribute or otherwise
+# use this file is hereby granted provided that
+# the above copyright notice and this notice are
+# left intact.
+#
+# Please send copies of changes and bug-fixes to:
+# sjg@crufty.net
+#
+
+set -- `getopt B:bpxCNcsdo:g:m:i:f: $*`
+
+Mydir=`dirname $0`
+[ -s $Mydir/.installrc ] && . $Mydir/.installrc
+
+OLD_EXT=.old
+owner=:
+group=:
+mode=:
+MODE=0
+strip=:
+mkdirs=
+compare=:
+newer=:
+chflags=:
+LS_1=
+CP_p=
+
+while :
+do
+ case "$1" in
+ --) shift; break;;
+ -[bc]) ;; # ignore
+ -p) CP_p=-p;;
+ -x) set -x;;
+ -B) OLD_EXT=$2; shift;;
+ -C) compare=Different;;
+ -N) newer=Newer;
+ # check if /bin/ls supports -1
+ 'ls' -1 $0 > /dev/null 2>&1 && LS_1=1
+ ;;
+ -o) owner="${CHOWN:-chown} $2 "; shift;;
+ -g) group="${CHGRP:-chgrp} $2 "; shift;;
+ -m) MODE=$2 mode="${CHMOD:-chmod} $2 "; shift;;
+ -s) strip=${STRIP:-strip};;
+ -d) mkdirs="mkdir -p";;
+ -i) ignore_err="$ignore_err$2"; shift;;
+ -f) chflags="${CHFLAGS:-chflags} $2 "; shift;;
+ *) break;;
+ esac
+ shift
+done
+
+Newer() {
+ n=`'ls' -t$LS_1 $* 2> /dev/null | head -1`
+ [ $1 = $n ]
+}
+
+Different() {
+ cmp -s $*
+ [ $? != 0 ]
+}
+
+Err() {
+ case "$ignore_err" in
+ *$1*) ;;
+ *) exit 1;;
+ esac
+}
+
+Setem() {
+ # the order is important
+ if [ ! -d $1 ]; then
+ $strip $1 || Err s
+ fi
+ $group $1 || Err g
+ $owner $1 || Err o
+ $mode $1 || Err m
+ $chflags $1 || Err f
+ return 0
+}
+
+# a bug in HP-UX's /bin/sh, means we need to re-set $*
+# after any calls to add_path()
+args="$*"
+
+add_path () {
+ test -d $1 || return
+ case ":$PATH:" in
+ *:$1:*) return;;
+ esac
+ PATH=$PATH:$1
+}
+
+add_path /sbin
+add_path /usr/sbin
+
+case "$owner" in
+:) ;;
+*) # some systems put chown in odd places
+ add_path /etc
+ add_path /usr/etc
+ ;;
+esac
+
+# restore saved $*
+set -- $args
+
+# make directories if needed
+# and ensure mode etc are as desired
+if [ "$mkdirs" ]; then
+ case "$MODE" in
+ [1-7]*)
+ # make sure umask is compatible
+ case "$MODE" in
+ ????*) MODE=`echo $MODE | sed 's,.*\(...\)$,\1,'`;;
+ esac
+ umask `expr 0777 - 0$MODE |
+ sed 's,^,000,;s,^.*\(...\)$,\1,'`;;
+ esac
+ for d in $*
+ do
+ [ ! -d $d ] && $mkdirs $d
+ Setem $d
+ done
+ exit 0 # that's all we do
+fi
+
+# install files
+if [ $# -eq 1 ]; then
+ echo "what should I do with $*?" >&2
+ exit 1
+fi
+
+# get list of files
+files=
+while [ $# -gt 1 ]
+do
+ test "x$files" = x || dest_dir=yes
+ files="$files $1"
+ shift
+done
+# last one is dest
+dest=$1
+shift
+
+if [ "$dest_dir" = yes -a ! -d $dest ]; then
+ echo "no directory $dest" >&2
+ exit 1
+fi
+
+for f in $files
+do
+ b=`basename $f`
+ if [ -d $dest ]; then
+ t=$dest/$b
+ else
+ t=$dest
+ fi
+ $newer $f $t || continue
+ $compare $f $t || continue
+ [ -f $t ] && { mv -f $t $t$OLD_EXT || exit 1; }
+ { cp $CP_p $f $t && Setem $t; } || exit 1
+done
+exit 0
diff --git a/contrib/bmake/mk/java.mk b/contrib/bmake/mk/java.mk
index e2149e7089a7..752aea121953 100644
--- a/contrib/bmake/mk/java.mk
+++ b/contrib/bmake/mk/java.mk
@@ -1,6 +1,7 @@
+# SPDX-License-Identifier: BSD-2-Clause
#
# RCSid:
-# $Id: java.mk,v 1.15 2020/08/19 17:51:53 sjg Exp $
+# $Id: java.mk,v 1.17 2024/02/17 17:26:57 sjg Exp $
# @(#) Copyright (c) 1998-2001, Simon J. Gerraty
#
@@ -16,7 +17,7 @@
#
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+__${.PARSEFILE}__: .NOTMAIN
.include <init.mk>
diff --git a/contrib/bmake/mk/jobs.mk b/contrib/bmake/mk/jobs.mk
new file mode 100644
index 000000000000..f39f1eb178ae
--- /dev/null
+++ b/contrib/bmake/mk/jobs.mk
@@ -0,0 +1,108 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: jobs.mk,v 1.17 2024/02/17 17:26:57 sjg Exp $
+#
+# @(#) Copyright (c) 2012-2023, Simon J. Gerraty
+#
+# This file is provided in the hope that it will
+# be of use. There is absolutely NO WARRANTY.
+# Permission to copy, redistribute or otherwise
+# use this file is hereby granted provided that
+# the above copyright notice and this notice are
+# left intact.
+#
+# Please send copies of changes and bug-fixes to:
+# sjg@crufty.net
+#
+
+# This makefile is used by top-level makefile.
+# With the following:
+#
+# .if make(*-jobs)
+# .include <jobs.mk>
+# .endif
+#
+#
+# Then if you do:
+#
+# mk target-jobs
+#
+# We will run:
+#
+# ${MAKE} -j${JOB_MAX} target > ${JOB_LOGDIR}/target.log 2>&1
+#
+# JOB_MAX should be something like 1.2 - 1.5 times the number of
+# available CPUs.
+# If bmake sets .MAKE.JOBS.C=yes we can use -jC and
+# JOB_MAX defaults to JOB_MAX_C (default 1.33C).
+# Otherwise we use 8.
+#
+
+now_utc ?= ${%s:L:localtime}
+.if !defined(start_utc)
+start_utc := ${now_utc}
+.endif
+
+.if make(*-jobs)
+.info ${.newline}${TIME_STAMP} Start ${.TARGETS}
+
+JOB_LOGDIR ?= ${SRCTOP:H}
+JOB_LOG = ${JOB_LOGDIR}/${.TARGET:S,-jobs,,:S,/,_,g}.log
+JOB_LOG_GENS ?= 4
+# we like to rotate logs
+.if empty(NEWLOG_SH)
+.for d in ${.SYSPATH:U${.PARSEDIR}:@x@$x $x/scripts@}
+.if exists($d/newlog.sh)
+NEWLOG_SH := $d/newlog.sh
+.if ${MAKE_VERSION} > 20220924
+.break
+.endif
+.endif
+.endfor
+.if empty(NEWLOG_SH)
+.ifdef M_whence
+NEWLOG_SH := ${newlog.sh:L:${M_whence}}
+.else
+NEWLOG_SH := ${(type newlog.sh) 2> /dev/null:L:sh:M/*}
+.endif
+.endif
+.endif
+.if !empty(NEWLOG_SH) && exists(${NEWLOG_SH})
+NEWLOG := sh ${NEWLOG_SH}
+JOB_NEWLOG_ARGS ?= -S -n ${JOB_LOG_GENS}
+.else
+NEWLOG = :
+.endif
+
+.if ${.MAKE.JOBS:U0} > 0
+JOB_MAX = ${.MAKE.JOBS}
+.else
+# This should be derrived from number of cpu's
+.if ${.MAKE.JOBS.C:Uno} == "yes"
+# 1.2 - 1.5 times nCPU works well on most machines that support -jC
+# if the factor is floating point, the C suffix isn't needed
+JOB_MAX_C ?= 1.33
+JOB_MAX ?= ${JOB_MAX_C}
+.endif
+JOB_MAX ?= 8
+JOB_ARGS += -j${JOB_MAX}
+.endif
+
+# we need to reset .MAKE.LEVEL to 0 so that
+# build orchestration works as expected (DIRDEPS_BUILD)
+${.TARGETS:M*-jobs}:
+ @${NEWLOG} ${JOB_NEWLOG_ARGS} ${JOB_LOG}
+ @echo "${TIME_STAMP} Start ${.TARGET:S,-jobs,,} ${JOB_ARGS} ${JOB_LOG_START} log=${JOB_LOG}" | tee ${JOB_LOG}
+ @cd ${.CURDIR} && env MAKELEVEL=0 \
+ ${.MAKE} ${JOB_ARGS} _TARGETS=${.TARGET:S,-jobs,,} ${.TARGET:S,-jobs,,} >> ${JOB_LOG} 2>&1
+
+.endif
+
+.END: _build_finish
+.ERROR: _build_failed
+
+_build_finish: .NOMETA
+ @echo "${TIME_STAMP} Finished ${.TARGETS} seconds=`expr ${now_utc} - ${start_utc}`"
+
+_build_failed: .NOMETA
+ @echo "${TIME_STAMP} Failed ${.TARGETS} seconds=`expr ${now_utc} - ${start_utc}`"
diff --git a/contrib/bmake/mk/ldorder.mk b/contrib/bmake/mk/ldorder.mk
index 3d44df5874fe..b612b1b9d3be 100644
--- a/contrib/bmake/mk/ldorder.mk
+++ b/contrib/bmake/mk/ldorder.mk
@@ -1,4 +1,6 @@
-# $Id: ldorder.mk,v 1.25 2018/04/24 23:50:26 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: ldorder.mk,v 1.27 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2015, Simon J. Gerraty
#
@@ -130,7 +132,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..95f95118c028 100644
--- a/contrib/bmake/mk/lib.mk
+++ b/contrib/bmake/mk/lib.mk
@@ -1,7 +1,10 @@
-# $Id: lib.mk,v 1.71 2020/08/19 17:51:53 sjg Exp $
+# $Id: lib.mk,v 1.84 2024/02/19 00:06:19 sjg Exp $
-.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+# should be set properly in sys.mk
+_this ?= ${.PARSEFILE:S,bsd.,,}
+
+.if !target(__${_this}__)
+__${_this}__: .NOTMAIN
.include <init.mk>
@@ -30,10 +33,9 @@ SHLIB_FULLVERSION := ${SHLIB_FULLVERSION}
# add additional suffixes not exported.
# .po is used for profiling object files.
-# ${PICO} is used for PIC object files.
-PICO?= .pico
-.SUFFIXES: .out .a .ln ${PICO} .po .o .s .S .c .cc .C .m .F .f .r .y .l .cl .p .h
-.SUFFIXES: .sh .m4 .m
+.SUFFIXES: .out .a .ln ${PICO} ${PCM} .po .o .s .S .c ${CXX_SUFFIXES} \
+ ${CCM_SUFFIXES} .m .F .f .r .y .l .cl .p .h \
+ .sh .m4 .m
CFLAGS+= ${COPTS}
@@ -62,6 +64,10 @@ META_NOECHO?= echo
# (usually just ${CPPPICFLAGS} ${CPICFLAGS})
# APICFLAGS: flags for ${AS} to assemble .[sS] to ${PICO} objects.
+# we simplify life by letting the toolchain do most of the work
+# _CCLINK is set by init.mk based on whether we are doing C++ or not
+SHLIB_LD ?= ${_CCLINK}
+
.if ${TARGET_OSNAME} == "NetBSD"
.if ${MACHINE_ARCH} == "alpha"
# Alpha-specific shared library flags
@@ -111,7 +117,7 @@ APICFLAGS?= -k
# Platform-independent linker flags for ELF shared libraries
.if ${OBJECT_FMT} == "ELF"
SHLIB_SOVERSION= ${SHLIB_MAJOR}
-SHLIB_SHFLAGS= -soname lib${LIB}.so.${SHLIB_SOVERSION}
+SHLIB_SHFLAGS= -Wl,-soname,lib${LIB}.so.${SHLIB_SOVERSION}
SHLIB_LDSTARTFILE?= /usr/lib/crtbeginS.o
SHLIB_LDENDFILE?= /usr/lib/crtendS.o
.endif
@@ -125,7 +131,7 @@ LD_shared=${SHLIB_SHFLAGS}
.if ${TARGET_OSNAME} == "FreeBSD"
.if ${OBJECT_FMT} == "ELF"
SHLIB_SOVERSION= ${SHLIB_MAJOR}
-SHLIB_SHFLAGS= -soname lib${LIB}.so.${SHLIB_SOVERSION}
+SHLIB_SHFLAGS= -Wl,-soname,lib${LIB}.so.${SHLIB_SOVERSION}
.else
SHLIB_SHFLAGS= -assert pure-text
.endif
@@ -168,9 +174,8 @@ AR_cq= -cqs
.elif ${TARGET_OSNAME} == "FreeBSD"
LD_solib= lib${LIB}_pic.a
.elif ${TARGET_OSNAME} == "Linux"
-SHLIB_LD = ${CC}
# this is ambiguous of course
-LD_shared=-shared -Wl,"-soname lib${LIB}.so.${SHLIB_MAJOR}"
+LD_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
@@ -180,7 +185,6 @@ LD_pobjs = ${POBJS}
LD_sobjs = ${SOBJS}
.endif
.elif ${TARGET_OSNAME} == "Darwin"
-SHLIB_LD = ${CC}
SHLIB_INSTALL_VERSION ?= ${SHLIB_MAJOR}
SHLIB_COMPATABILITY_VERSION ?= ${SHLIB_MAJOR}.${SHLIB_MINOR:U0}
SHLIB_COMPATABILITY ?= \
@@ -213,8 +217,6 @@ PICFLAG ?= -fPIC -fno-common
RANLIB = :
.endif
-SHLIB_LD ?= ${LD}
-
.if !empty(SHLIB_MAJOR)
.if ${NEED_SOLINKS} && empty(SHLIB_LINKS)
.if ${MK_LINKLIB} != "no"
@@ -267,6 +269,10 @@ SHLIB_AGE != . ${.CURDIR}/shlib_version ; echo $$age
.c.o:
${COMPILE.c} ${.IMPSRC}
+# precompiled C++ Modules
+${CCM_SUFFIXES:%=%${PCM}}:
+ ${COMPILE.pcm} ${.IMPSRC}
+
# for the normal .a we do not want to strip symbols
${CXX_SUFFIXES:%=%.o}:
${COMPILE.cc} ${.IMPSRC}
@@ -380,6 +386,8 @@ _LIBS+= ${libLDORDER_INC}
.include <ldorder.mk>
.endif
+# avoid -dL errors
+LDADD_LDORDER ?=
.if !defined(_SKIP_BUILD)
realbuild: ${_LIBS}
@@ -387,11 +395,13 @@ realbuild: ${_LIBS}
all: _SUBDIRUSE
-.for s in ${SRCS:N*.h:M*/*}
+OBJS_SRCS = ${SRCS:${OBJS_SRCS_FILTER}}
+
+.for s in ${OBJS_SRCS:M*/*}
${.o ${PICO} .po .lo:L:@o@${s:T:R}$o@}: $s
.endfor
-OBJS+= ${SRCS:T:N*.h:R:S/$/.o/g}
+OBJS+= ${OBJS_SRCS:T:R:S/$/.o/g}
.NOPATH: ${OBJS}
.if ${MK_LIBTOOL} == "yes"
@@ -441,25 +451,23 @@ lib${LIB}_pic.a: ${SOBJS}
@${AR} ${AR_cq} ${.TARGET} ${LD_sobjs}
${RANLIB} ${.TARGET}
-#SHLIB_LDADD?= ${LDADD}
-
# bound to be non-portable...
# this is known to work for NetBSD 1.6 and FreeBSD 4.2
lib${LIB}.${LD_so}: ${SOLIB} ${DPADD}
@${META_NOECHO} building shared ${LIB} library \(version ${SHLIB_FULLVERSION}\)
@rm -f ${.TARGET}
-.if ${TARGET_OSNAME} == "NetBSD" || ${TARGET_OSNAME} == "FreeBSD"
+.if ${TARGET_OSNAME:NFreeBSD:NNetBSD} == ""
.if ${OBJECT_FMT} == "ELF"
- ${SHLIB_LD} -x -shared ${SHLIB_SHFLAGS} -o ${.TARGET} \
- ${SHLIB_LDSTARTFILE} \
- --whole-archive ${SOLIB} --no-whole-archive ${SHLIB_LDADD} \
- ${SHLIB_LDENDFILE}
+ ${SHLIB_LD} -shared -Wl,-x ${SHLIB_SHFLAGS} ${LDFLAGS} -o ${.TARGET} \
+ -Wl,--whole-archive ${SOLIB} -Wl,--no-whole-archive \
+ ${LDADD} ${SHLIB_LDADD}
.else
- ${SHLIB_LD} ${LD_x} ${LD_shared} \
- -o ${.TARGET} ${SOLIB} ${SHLIB_LDADD}
+ ${SHLIB_LD} ${LD_x} ${LD_shared} ${LDFLAGS} \
+ -o ${.TARGET} ${SOLIB} ${LDADD} ${SHLIB_LDADD}
.endif
.else
- ${SHLIB_LD} -o ${.TARGET} ${LD_shared} ${LD_solib} ${DLLIB} ${SHLIB_LDADD}
+ ${SHLIB_LD} ${LDFLAGS} -o ${.TARGET} \
+ ${LD_shared} ${LD_solib} ${DLLIB} ${LDADD} ${SHLIB_LDADD}
.endif
.endif
.if !empty(SHLIB_LINKS)
@@ -479,7 +487,7 @@ cleanlib: .PHONY
rm -f a.out [Ee]rrs mklog core *.core ${CLEANFILES}
rm -f lib${LIB}.a ${OBJS}
rm -f lib${LIB}_p.a ${POBJS}
- rm -f lib${LIB}_pic.a lib${LIB}.so.*.* ${SOBJS}
+ rm -f lib${LIB}_pic.a lib${LIB}*${LD_solink} lib${LIB}*${LD_solink}.* ${SOBJS}
rm -f llib-l${LIB}.ln ${LOBJS}
.if !empty(SHLIB_LINKS)
rm -f ${SHLIB_LINKS}
@@ -603,6 +611,10 @@ realinstall: beforeinstall
.if !empty(LIB)
STAGE_LIBDIR?= ${STAGE_OBJTOP}${LIBDIR}
stage_libs: ${_LIBS}
+
+__libtoken ?= __lib${LIB:C,[^a-zA-Z0-9_],_,g}__
+__libtoken := ${__libtoken}
+COPTS += -D${__libtoken}
.endif
.include <final.mk>
diff --git a/contrib/bmake/mk/libnames.mk b/contrib/bmake/mk/libnames.mk
index 8140360714df..f2d99d337675 100644
--- a/contrib/bmake/mk/libnames.mk
+++ b/contrib/bmake/mk/libnames.mk
@@ -1,4 +1,6 @@
-# $Id: libnames.mk,v 1.9 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: libnames.mk,v 1.10 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2007-2009, Simon J. Gerraty
#
diff --git a/contrib/bmake/mk/libs.mk b/contrib/bmake/mk/libs.mk
index 9f0079d6e511..fef339c03bce 100644
--- a/contrib/bmake/mk/libs.mk
+++ b/contrib/bmake/mk/libs.mk
@@ -1,4 +1,6 @@
-# $Id: libs.mk,v 1.6 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: libs.mk,v 1.7 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2006, Simon J. Gerraty
#
diff --git a/contrib/bmake/mk/links.mk b/contrib/bmake/mk/links.mk
index 6bf0db080c23..cbf9685ccd37 100644
--- a/contrib/bmake/mk/links.mk
+++ b/contrib/bmake/mk/links.mk
@@ -1,4 +1,6 @@
-# $Id: links.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: links.mk,v 1.8 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2005, Simon J. Gerraty
#
diff --git a/contrib/bmake/mk/man.mk b/contrib/bmake/mk/man.mk
index 391b08afe491..13c4d8e866f0 100644
--- a/contrib/bmake/mk/man.mk
+++ b/contrib/bmake/mk/man.mk
@@ -1,9 +1,15 @@
-# $Id: man.mk,v 1.20 2012/12/13 01:51:01 sjg Exp $
+# $Id: man.mk,v 1.29 2024/02/19 00:06:19 sjg Exp $
-.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+# should be set properly in sys.mk
+_this ?= ${.PARSEFILE:S,bsd.,,}
+
+.if !target(__${_this}__)
+__${_this}__: .NOTMAIN
+
+OPTIONS_DEFAULT_NO += CMT2DOC
.include <init.mk>
+.include <options.mk>
# unlike bsd.man.mk we handle 3 approaches
# 1. install unformated nroff (default)
@@ -13,79 +19,120 @@ __${.PARSEFILE}__:
# so we have to use sed(1).
# set MANTARGET=cat for formatted pages
-MANTARGET?= man
+MANTARGET ?= man
# set this to .0 for same behavior as bsd.man.mk
-MCATEXT?=
+MCATEXT ?=
-NROFF?= nroff
-MANDIR?= /usr/share/man
-MANDOC?= man
+NROFF ?= nroff
+MANDIR ?= /usr/share/man
+MANDOC ?= man
-.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 )
-
-.if defined(MAN) && !empty(MAN)
+ @${NROFF} -${MANDOC} ${.IMPSRC} > ${.TARGET:T}.new && \
+ mv ${.TARGET:T}.new ${.TARGET:T}
-# 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
-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:
- @echo "${CMT2DOC} ${.IMPSRC} > ${.TARGET:T}"
- @${CMT2DOC} ${CMT2DOC_OPTS} ${.IMPSRC} > ${.TARGET:T} || ( rm -f ${.TARGET:T} ; false )
+.if !empty(MANOWN)
+MAN_INSTALL_OWN ?= -o ${MANOWN} -g ${MANGRP}
+MAN_CHOWN ?= chown
.else
-MAN=
+MAN_CHOWN = :
.endif
+
+MINSTALL = ${INSTALL} ${COPY} ${MAN_INSTALL_OWN} -m ${MANMODE}
+
+.if defined(MAN) && !empty(MAN)
+
+.if ${MANTARGET} == "cat"
+MANALL ?= ${MAN:T:@p@${p:R}.cat${p:E}@}
+.else
+MANALL ?= ${MAN}
.endif
+
+.if ${MK_CMT2DOC} == "yes"
+# use cmt2doc.py to extract manpages from source
+CMT2DOC ?= cmt2doc.py
+# -m produces man(7)
+# -mm produces mdoc(7)
+CMT2DOC_FLAGS ?= -pm
+CMT2DOC_OPTS ?= ${CMT2DOC_ORGOPT} -S${.TARGET:E}
+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_FLAGS} ${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/.*\.//'`
+# none of this is relevant unless doing maninstall
+.if make(*install)
+_mandir = ${DESTDIR}${MANDIR}/${MANTARGET}`echo $$page | sed -e 's/.*\.cat/./' -e 's/.*\.//'`
.if ${MANTARGET} == "cat"
-_mfromdir?=.
-MANALL= ${MAN: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}
+_mfromdir ?= .
.if ${MCATEXT} == ""
-_minstpage=`echo $$page | sed 's/\.cat/./'`
+_minstpage = `echo $$page | sed 's/\.cat/./'`
.else
-_minstpage=`echo $$page | sed 's/\.cat.*//'`${MCATEXT}
+_minstpage = `echo $$page | sed 's/\.cat.*//'`${MCATEXT}
.endif
.endif
.if target(${MAN:[1]})
-_mfromdir?=.
+_mfromdir ?= .
.endif
-_mfromdir?=${.CURDIR}
-MANALL?= ${MAN}
-_minstpage?=$${page}
+_mfromdir ?= ${.CURDIR}
+_minstpage ?= $${page}
.endif
-.if !empty(MANOWN)
-MAN_INSTALL_OWN ?= -o ${MANOWN} -g ${MANGRP}
-MAN_CHOWN ?= chown
+.if defined(MANZ)
+# chown and chmod are done afterward automatically
+MCOMPRESS_CMD ?= gzip -cf
+MCOMPRESS_EXT ?= .gz
+
+_MANZ_USE: .USE
+ @${MCOMPRESS_CMD} ${.ALLSRC} > ${.TARGET}
+
+.for _page in ${MANALL}
+${_page:T}${MCOMPRESS_EXT}: ${_page} _MANZ_USE
+.endfor
+.endif
+
+.if ${MK_STAGING_MAN} == "yes"
+_mansets := ${MAN:E:O:u:M*[1-9]:@s@man$s@}
+.if ${MANTARGET} == "cat"
+STAGE_AS_SETS += ${_mansets}
+_stage_man = stage_as
.else
-MAN_CHOWN = :
+STAGE_SETS += ${_mansets}
+_stage_man = stage_files
+.endif
+STAGE_TARGETS += ${_stage_man}
+.for _page _as in ${MANALL:@x@$x ${x:T:S/.cat/./}@}
+${_stage_man}.man${_as:E}: ${_page}
+.if target(${_page:T}${MCOMPRESS_EXT:Umanz})
+${_man_stage}.man${_as:E}: ${_page:T}${MCOMPRESS_EXT}
+.endif
+STAGE_DIR.man${_as:E} ?= ${STAGE_OBJTOP}${MANDIR}/${MANTARGET}${_as:E}${MANSUBDIR}
+.if ${MANTARGET} == "cat"
+STAGE_AS_${_page} = ${_as}
+.endif
+.endfor
+.if !defined(NO_MLINKS) && !empty(MLINKS)
+STAGE_SETS += mlinks
+STAGE_TARGETS += stage_links
+STAGE_LINKS.mlinks := ${MLINKS:M*.[1-9]:@f@${f:S,^,${MANDIR}/${MANTARGET}${f:E}${MANSUBDIR}/,}@}
+stage_links.mlinks: ${_mansets:@s@stage_files.$s@}
+.endif
.endif
-MINSTALL= ${INSTALL} ${COPY} ${MAN_INSTALL_OWN} -m ${MANMODE}
-.if defined(MANZ)
-# chown and chmod are done afterward automatically
-MCOMPRESS= gzip -cf
-MCOMPRESSSUFFIX= .gz
.endif
maninstall:
diff --git a/contrib/bmake/mk/manifest.mk b/contrib/bmake/mk/manifest.mk
index 1e2f728f094e..d619d17432bd 100644
--- a/contrib/bmake/mk/manifest.mk
+++ b/contrib/bmake/mk/manifest.mk
@@ -1,4 +1,6 @@
-# $Id: manifest.mk,v 1.3 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: manifest.mk,v 1.4 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2014, Simon J. Gerraty
#
diff --git a/contrib/bmake/mk/meta.autodep.mk b/contrib/bmake/mk/meta.autodep.mk
index 5d09dbd88e81..55f2d66e56aa 100644
--- a/contrib/bmake/mk/meta.autodep.mk
+++ b/contrib/bmake/mk/meta.autodep.mk
@@ -1,4 +1,6 @@
-# $Id: meta.autodep.mk,v 1.54 2021/03/06 17:03:18 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: meta.autodep.mk,v 1.63 2024/04/24 18:56:41 sjg Exp $
#
# @(#) Copyright (c) 2010, Simon J. Gerraty
@@ -23,13 +25,17 @@ __${_this}__: .NOTMAIN
PICO?= .pico
.if defined(SRCS)
+.if ${MAKE_VERSION:U0} >= 20211212
+OBJ_SUFFIXES += ${.SUFFIXES:M*o}
+.else
# it would be nice to be able to query .SUFFIXES
-OBJ_EXTENSIONS+= .o .po .lo ${PICO}
+OBJ_SUFFIXES += .o .po .lo ${PICO}
+.endif
# explicit dependencies help short-circuit .SUFFIX searches
SRCS_DEP_FILTER+= N*.[hly]
.for s in ${SRCS:${SRCS_DEP_FILTER:O:u:ts:}}
-.for e in ${OBJ_EXTENSIONS:O:u}
+.for e in ${OBJ_SUFFIXES:O:u}
.if !target(${s:T:R}$e)
${s:T:R}$e: $s
.endif
@@ -135,6 +141,10 @@ FORCE_DPADD += ${_nonlibs:@x@${DPADD:M*/$x}@}
.END: gendirdeps
.endif
+.if ${LOCAL_DEPENDS_GUARD:U} == "no"
+.depend:
+.endif
+
# if we don't have OBJS, then .depend isn't useful
.if !target(.depend) && (!empty(OBJS) || ${.ALLTARGETS:M*.o} != "")
# some makefiles and/or targets contain
@@ -163,10 +173,14 @@ _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} | \
+ @${EGREP:Uegrep} -i '^R .*\.(${DEPEND_SUFFIXES:tl:O:u:S,^.,,:ts|})$$' /dev/null ${.MAKE.META.FILES:T:O:u:${META_FILE_FILTER:ts:}:M*o.meta} | \
sed -e 's, \./, ,${OBJDIR_REFS:O:u:@d@;s, $d/, ,@};/\//d' \
-e 's,^\([^/][^/]*\).meta...[0-9]* ,\1: ,' | \
sort -u | \
@@ -178,7 +192,7 @@ DEPEND_SUFFIXES += .c .h .cpp .hpp .cxx .hxx .cc .hh
@case "${.MAKE.META.FILES:T:M*.po.*}" in \
*.po.*) mv $@.${.MAKE.PID} $@;; \
*) { cat $@.${.MAKE.PID}; \
- sed ${OBJ_EXTENSIONS:N.o:N.po:@o@-e 's,\$o:,.o:,'@} \
+ sed ${OBJ_SUFFIXES:N.o:N.po:@o@-e 's,\$o:,.o:,'@} \
-e 's,\.o:,.po:,' $@.${.MAKE.PID}; } | sort -u > $@; \
rm -f $@.${.MAKE.PID};; \
esac
@@ -198,7 +212,8 @@ _depend =
.endif
.if ${UPDATE_DEPENDFILE} == "yes"
-gendirdeps: ${_DEPENDFILE}
+gendirdeps: beforegendirdeps .WAIT ${_DEPENDFILE}
+beforegendirdeps:
.endif
.if !target(${_DEPENDFILE})
@@ -292,8 +307,10 @@ ${_DEPENDFILE}: .PRECIOUS
CLEANFILES += *.meta filemon.* *.db
# these make it easy to gather some stats
-now_utc = ${%s:L:gmtime}
+now_utc ?= ${%s:L:localtime}
+.if !defined(start_utc)
start_utc := ${now_utc}
+.endif
meta_stats= meta=${empty(.MAKE.META.FILES):?0:${.MAKE.META.FILES:[#]}} \
created=${empty(.MAKE.META.CREATED):?0:${.MAKE.META.CREATED:[#]}}
@@ -318,4 +335,6 @@ _reldir_failed: .NOMETA
.ERROR: _reldir_failed
.endif
+.-include <ccm.dep.mk>
+
.endif
diff --git a/contrib/bmake/mk/meta.stage.mk b/contrib/bmake/mk/meta.stage.mk
index 463521b79595..345df6aae16b 100644
--- a/contrib/bmake/mk/meta.stage.mk
+++ b/contrib/bmake/mk/meta.stage.mk
@@ -1,4 +1,6 @@
-# $Id: meta.stage.mk,v 1.61 2021/01/31 04:43:12 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: meta.stage.mk,v 1.69 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2011-2017, Simon J. Gerraty
#
@@ -54,7 +56,7 @@ _objroot ?= ${_OBJROOT:tA}
# make sure this is global
_STAGED_DIRS ?=
.export _STAGED_DIRS
-# add each dir we stage to to _STAGED_DIRS
+# add each dir we stage to _STAGED_DIRS
# and make sure we have absolute paths so that bmake
# will match against .MAKE.META.BAILIWICK
STAGE_DIR_FILTER = tA:@d@$${_STAGED_DIRS::+=$$d}$$d@
@@ -66,7 +68,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.
@@ -173,7 +175,7 @@ stage_libs: .dirdep
.if !defined(NO_SHLIB_LINKS)
.if !empty(SHLIB_LINKS)
@${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_LIBDIR:${STAGE_DIR_FILTER}} \
- ${SHLIB_LINKS:@t@${STAGE_LIBS:T:M$t.*} $t@}
+ ${SHLIB_LINKS:@t@${STAGE_LIBS:T:M$t.*:${STAGE_SHLIB_LINKS_FILTER:U}} $t@}
.elif !empty(SHLIB_LINK) && !empty(SHLIB_NAME)
@${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_LIBDIR:${STAGE_DIR_FILTER}} ${SHLIB_NAME} ${SHLIB_LINK}
.endif
@@ -212,7 +214,7 @@ stage_files.$s: .dirdep
STAGE_FILES ?= ${.ALLSRC:N.dirdep:Nstage_*}
stage_files: .dirdep
.endif
- @${STAGE_FILE_SCRIPT}; StageFiles ${FLAGS.$@} ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_FILES.$s:O}
+ @${STAGE_FILE_SCRIPT}; StageFiles ${FLAGS.$@:U} ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_FILES.$s:O}
@touch $@
.endif
.endif
@@ -264,7 +266,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 +297,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 +309,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
@@ -353,7 +356,7 @@ all: stale_staged
# get a list of paths that we have previously staged to those same dirs
# anything in the 2nd list but not the first is stale - remove it.
stale_staged: staging .NOMETA
- @egrep '^[WL] .*${STAGE_OBJTOP}' /dev/null ${.MAKE.META.FILES:M*stage_*} | \
+ @${EGREP:Uegrep} '^[WL] .*${STAGE_OBJTOP}' /dev/null ${.MAKE.META.FILES:M*stage_*} | \
sed "/\.dirdep/d;s,.* '*\(${STAGE_OBJTOP}/[^ '][^ ']*\).*,\1," | \
sort > ${.TARGET}.staged1
@grep -l '${_dirdep}' /dev/null ${_STAGED_DIRS:M${STAGE_OBJTOP}*:O:u:@d@$d/*.dirdep@} | \
diff --git a/contrib/bmake/mk/meta.subdir.mk b/contrib/bmake/mk/meta.subdir.mk
index d9caae4edbcc..aee8a1a9a39b 100644
--- a/contrib/bmake/mk/meta.subdir.mk
+++ b/contrib/bmake/mk/meta.subdir.mk
@@ -1,4 +1,6 @@
-# $Id: meta.subdir.mk,v 1.13 2021/01/05 22:24:37 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: meta.subdir.mk,v 1.15 2024/04/19 15:10:22 sjg Exp $
#
# @(#) Copyright (c) 2010, Simon J. Gerraty
@@ -68,7 +70,6 @@ DIRDEPS := ${DIRDEPS:S,^./,,:S,/./,/,g:${SUBDIRDEPS_FILTER:Uu}}
# dirdeps.mk will compute some interesting combinations.
.undef ALL_MACHINES
-DEP_RELDIR = ${RELDIR}
.include <dirdeps.mk>
.endif
.endif
diff --git a/contrib/bmake/mk/meta.sys.mk b/contrib/bmake/mk/meta.sys.mk
index 77b4893a8785..6e4216ab5383 100644
--- a/contrib/bmake/mk/meta.sys.mk
+++ b/contrib/bmake/mk/meta.sys.mk
@@ -1,7 +1,9 @@
-# $Id: meta.sys.mk,v 1.38 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: meta.sys.mk,v 1.54 2024/03/10 15:53:51 sjg Exp $
#
-# @(#) Copyright (c) 2010-2020, Simon J. Gerraty
+# @(#) Copyright (c) 2010-2023, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@@ -17,34 +19,46 @@
# include this if you want to enable meta mode
# for maximum benefit, requires filemon(4) driver.
-.if ${MAKE_VERSION:U0} > 20100901
-.if !target(.ERROR)
-
-.-include <local.meta.sys.mk>
-
# absolute path to what we are reading.
-_PARSEDIR = ${.PARSEDIR:tA}
+_PARSEDIR ?= ${.PARSEDIR:tA}
+
+.-include <local.meta.sys.env.mk>
.if !defined(SYS_MK_DIR)
SYS_MK_DIR := ${_PARSEDIR}
.endif
-META_MODE += meta verbose
+.if !target(.ERROR)
+
+META_MODE += meta
+.if empty(.MAKEFLAGS:M-s)
+META_MODE += verbose
+.endif
+.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
@@ -55,19 +69,7 @@ META_MODE += silent=yes
.endif
.endif
-# we use the pseudo machine "host" for the build host.
-# this should be taken care of before we get here
-.if ${OBJTOP:Ua} == ${HOST_OBJTOP:Ub}
-MACHINE = host
-.endif
-
-.if !defined(MACHINE0)
-# it can be handy to know which MACHINE kicked off the build
-# for example, if using Makefild.depend for multiple machines,
-# allowing only MACHINE0 to update can keep things simple.
-MACHINE0 := ${MACHINE}
-.export MACHINE0
-.endif
+.if ${MK_DIRDEPS_BUILD:Uno} == "yes"
.if !defined(META2DEPS)
.if defined(PYTHON) && exists(${PYTHON})
@@ -82,41 +84,41 @@ META2DEPS := ${META2DEPS}
MAKE_PRINT_VAR_ON_ERROR += \
.ERROR_TARGET \
+ .ERROR_EXIT \
.ERROR_META_FILE \
.MAKE.LEVEL \
MAKEFILE \
.MAKE.MODE
+MK_META_ERROR_TARGET = yes
+.endif
+
+.if ${MK_META_ERROR_TARGET:Uno} == "yes"
+
.if !defined(SB) && defined(SRCTOP)
SB = ${SRCTOP:H}
.endif
ERROR_LOGDIR ?= ${SB}/error
meta_error_log = ${ERROR_LOGDIR}/meta-${.MAKE.PID}.log
-# we are not interested in make telling us a failure happened elsewhere
.ERROR: _metaError
+# We are interested here in the target(s) that caused the build to fail.
+# We want to ignore targets that were "aborted" due to failure
+# elsewhere per the message below or a sub-make may just exit 6.
_metaError: .NOMETA .NOTMAIN
- -@[ "${.ERROR_META_FILE}" ] && { \
+ -@[ ${.ERROR_EXIT:U0} = 6 ] && exit 0; \
+ [ "${.ERROR_META_FILE}" ] && { \
grep -q 'failure has been detected in another branch' ${.ERROR_META_FILE} && exit 0; \
mkdir -p ${meta_error_log:H}; \
cp ${.ERROR_META_FILE} ${meta_error_log}; \
echo "ERROR: log ${meta_error_log}" >&2; }; :
.endif
+.endif
# Are we, after all, in meta mode?
.if ${.MAKE.MODE:Uno:Mmeta*} != ""
-MKDEP_MK = meta.autodep.mk
-
-.if ${.MAKE.MAKEFILES:M*sys.dependfile.mk} == ""
-# this does all the smarts of setting .MAKE.DEPENDFILE
-.-include <sys.dependfile.mk>
-# check if we got anything sane
-.if ${.MAKE.DEPENDFILE} == ".depend"
-.undef .MAKE.DEPENDFILE
-.endif
-.MAKE.DEPENDFILE ?= Makefile.depend
-.endif
+MKDEP_MK ?= meta.autodep.mk
# we can afford to use cookies to prevent some targets
# re-running needlessly
@@ -140,28 +142,18 @@ 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
-# make sure dirdeps target exists and do it first
-all: dirdeps .WAIT
-dirdeps:
-.NOPATH: dirdeps
-
-.if defined(ALL_MACHINES)
-# the first .MAIN: is what counts
-# by default dirdeps is all we want at level0
-.MAIN: dirdeps
-.endif
+.else # in meta mode?
-.endif
-.else
META_COOKIE_TOUCH=
# some targets need to be .PHONY in non-meta mode
META_NOPHONY= .PHONY
META_NOECHO= echo
-.endif
-.endif
+
+.endif # in meta mode?
+
+.-include <local.meta.sys.mk>
diff --git a/contrib/bmake/mk/meta2deps.py b/contrib/bmake/mk/meta2deps.py
index 193e303de3da..f188d0f01de8 100755
--- a/contrib/bmake/mk/meta2deps.py
+++ b/contrib/bmake/mk/meta2deps.py
@@ -36,8 +36,10 @@ We only pay attention to a subset of the information in the
"""
"""
+SPDX-License-Identifier: BSD-2-Clause
+
RCSid:
- $Id: meta2deps.py,v 1.38 2021/06/17 05:20:08 sjg Exp $
+ $Id: meta2deps.py,v 1.47 2024/02/17 17:26:57 sjg Exp $
Copyright (c) 2011-2020, Simon J. Gerraty
Copyright (c) 2011-2017, Juniper Networks, Inc.
@@ -66,13 +68,18 @@ RCSid:
"""
-import os, re, sys
+import os
+import re
+import sys
+import stat
def resolve(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr):
"""
Return an absolute path, resolving via cwd or last_dir if needed.
+
+ Cleanup any leading ``./`` and trailing ``/.``
"""
- if path.endswith('/.'):
+ while path.endswith('/.'):
path = path[0:-2]
if len(path) > 0 and path[0] == '/':
if os.path.exists(path):
@@ -83,7 +90,9 @@ def resolve(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr):
if path == '.':
return cwd
if path.startswith('./'):
- return cwd + path[1:]
+ while path.startswith('./'):
+ path = path[1:]
+ return cwd + path
if last_dir == cwd:
last_dir = None
for d in [last_dir, cwd]:
@@ -141,6 +150,7 @@ def abspath(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr):
return None
if (path.find('/') < 0 or
path.find('./') > 0 or
+ path.find('/../') > 0 or
path.endswith('/..')):
path = cleanpath(path)
return path
@@ -165,6 +175,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,10 +249,12 @@ 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')
+ self.pids = {}
self.line = 0
if not self.conf:
@@ -250,7 +275,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 +305,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 +381,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
@@ -429,12 +455,13 @@ class MetaFile:
pid_cwd = {}
pid_last_dir = {}
last_pid = 0
+ eof_token = False
self.line = 0
if self.curdir:
self.seenit(self.curdir) # we ignore this
- interesting = 'CEFLRV'
+ interesting = '#CEFLRVX'
for line in f:
self.line += 1
# ignore anything we don't care about
@@ -461,6 +488,12 @@ class MetaFile:
print("%s: CWD=%s" % (self.name, cwd), file=self.debug_out)
continue
+ if w[0] == '#':
+ # check the file has not been truncated
+ if line.find('Bye') > 0:
+ eof_token = True
+ continue
+
pid = int(w[1])
if pid != last_pid:
if last_pid:
@@ -490,6 +523,13 @@ class MetaFile:
print("cwd=", cwd, file=self.debug_out)
continue
+ if w[0] == 'X':
+ try:
+ del self.pids[pid]
+ except KeyError:
+ pass
+ continue
+
if w[2] in self.seen:
if self.debug > 2:
print("seen:", w[2], file=self.debug_out)
@@ -503,11 +543,34 @@ class MetaFile:
continue
elif w[0] in 'ERWS':
path = w[2]
- if path == '.':
+ if w[0] == 'E':
+ self.pids[pid] = path
+ elif path == '.':
continue
self.parse_path(path, cwd, w[0], w)
- assert(version > 0)
+ if version == 0:
+ raise AssertionError('missing filemon data')
+ if not eof_token:
+ raise AssertionError('truncated filemon data')
+
+ setid_pids = []
+ # self.pids should be empty!
+ for pid,path in self.pids.items():
+ try:
+ # no guarantee that path is still valid
+ if os.stat(path).st_mode & (stat.S_ISUID|stat.S_ISGID):
+ # we do not expect anything after Exec
+ setid_pids.append(pid)
+ continue
+ except:
+ # we do not care why the above fails,
+ # we do not want to miss the ERROR below.
+ pass
+ print("ERROR: missing eXit for {} pid {}".format(path, pid))
+ for pid in setid_pids:
+ del self.pids[pid]
+ assert(len(self.pids) == 0)
if not file:
f.close()
diff --git a/contrib/bmake/mk/meta2deps.sh b/contrib/bmake/mk/meta2deps.sh
index d2064bc5cb59..4c1b674f7b63 100755
--- a/contrib/bmake/mk/meta2deps.sh
+++ b/contrib/bmake/mk/meta2deps.sh
@@ -75,8 +75,10 @@
# RCSid:
-# $Id: meta2deps.sh,v 1.15 2020/11/08 06:31:08 sjg Exp $
+# $Id: meta2deps.sh,v 1.21 2024/02/17 17:26:57 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
# Copyright (c) 2010-2013, Juniper Networks, Inc.
# All rights reserved.
#
@@ -137,6 +139,13 @@ add_list() {
eval "$name=\"$list\""
}
+# some Linux systems have deprecated egrep in favor of grep -E
+# but not everyone supports that
+case "`echo bmake | egrep 'a|b' 2>&1`" in
+bmake) ;;
+*) egrep() { grep -E "$@"; }
+esac
+
_excludes_f() {
egrep -v "$EXCLUDES"
}
@@ -239,8 +248,8 @@ meta2deps() {
;;
*) cat /dev/null "$@";;
esac 2> /dev/null |
- sed -e 's,^CWD,C C,;/^[CREFLMV] /!d' -e "s,',,g" |
- $_excludes | ( version=no
+ sed -e 's,^CWD,C C,;/^[#CREFLMVX] /!d' -e "s,',,g" |
+ $_excludes | ( version=no epids= xpids= eof_token=no
while read op pid path junk
do
: op=$op pid=$pid path=$path
@@ -258,10 +267,15 @@ meta2deps() {
*) ;;
esac
version=0
+ case "$eof_token" in
+ no) ;; # ignore
+ 0) error "truncated filemon data";;
+ esac
+ eof_token=0
continue
;;
$pid,$pid) ;;
- *)
+ [1-9]*)
case "$lpid" in
"") ;;
*) eval ldir_$lpid=$ldir;;
@@ -271,8 +285,9 @@ meta2deps() {
;;
esac
+ : op=$op path=$path
case "$op,$path" in
- V,*) version=$path; continue;;
+ V,*) version=$pid; continue;;
W,*srcrel|*.dirdep) continue;;
C,*)
case "$path" in
@@ -288,7 +303,18 @@ meta2deps() {
eval cwd_$path=$cwd ldir_$path=$ldir
continue
;;
+ \#,bye) eof_token=1; continue;;
+ \#*) continue;;
*) dir=${path%/*}
+ case "$op" in
+ E) # setid apps get no tracing so we won't see eXit
+ case `'ls' -l $path 2> /dev/null | sed 's, .*,,'` in
+ *s*) ;;
+ *) epids="$epids $pid";;
+ esac
+ ;;
+ X) xpids="$xpids $pid"; continue;;
+ esac
case "$path" in
$src_re|$obj_re) ;;
/*/stage/*) ;;
@@ -378,9 +404,22 @@ meta2deps() {
echo $dir;;
esac
done > $tf.dirdep
+ : version=$version
case "$version" in
0) error "no filemon data";;
- esac ) || exit 1
+ esac
+ : eof_token=$eof_token
+ case "$eof_token" in
+ 0) error "truncated filemon data";;
+ esac
+ for p in $epids
+ do
+ : p=$p
+ case " $xpids " in
+ *" $p "*) ;;
+ *) error "missing eXit for pid $p";;
+ esac
+ done ) || exit 1
_nl=echo
for f in $tf.dirdep $tf.qual $tf.srcdep
do
diff --git a/contrib/bmake/mk/mk-files.txt b/contrib/bmake/mk/mk-files.txt
index 282f9b87a63c..6e8a4ff691f6 100644
--- a/contrib/bmake/mk/mk-files.txt
+++ b/contrib/bmake/mk/mk-files.txt
@@ -69,6 +69,14 @@ mk-files
This section provides a brief description of some of the ``*.mk``
files.
+The makefiles ``lib.mk``, ``prog.mk``, ``init.mk``, ``own.mk``,
+``dep.mk`` and ``man.mk`` are more or less equivalent to ``bsd.*.mk``
+found in BSD, and when installed on non-BSD platforms get symlinked as
+``bsd.*.mk`` as well.
+
+The other makefiles (apart from ``sys.mk``) can be used in conjunction
+with ``bsd.*.mk`` on BSD.
+
sys.mk
------
@@ -76,7 +84,8 @@ When bmake starts, it looks for ``sys.mk`` and reads it before doing
anything else. Thus, this is the place to setup the environment for
everyone else.
-In this distribution, ``sys.mk`` avoids doing anything platform dependent.
+In this distribution, ``sys.mk`` avoids doing anything platform or
+site dependent.
It is quite short, and includes a number of other files (which may or
may not exists)
@@ -143,6 +152,8 @@ examples/sys.clean-env.mk
host-target.mk
Is used to set macros like ``HOST_TARGET``, ``HOST_OS`` and
``host_os`` which are used to find the next step.
+ Note: since 20130303 bmake provides ``.MAKE.OS`` set to
+ the equivalent of ``HOST_OS``.
sys/\*.mk
Platform specific additions, such as ``Darwin.mk`` or ``SunOS.mk``
@@ -159,27 +170,40 @@ local.sys.mk
The above arrangement makes it easy for the mk files to be part of a
src tree on an NFS volume and to allow building on multiple platforms.
+options.mk
+----------
+
+Inspired by FreeBSD's ``bsd.own.mk`` but more flexible.
+FreeBSD now have similar functionality in ``bsd.mkopt.mk``.
+
+It allows users to express their intent with respect to options
+``MK_*`` by setting ``WITH_*`` or ``WITHOUT_*``.
+
+Note: ``WITHOUT_*`` wins if both are set, and makefiles can set
+``NO_*`` to say they cannot handle that option, or even ``MK_*`` if
+they really need to.
+
lib.mk
------
This file is used to build a number of different libraries from the
same SRCS.
-lib${LIB}.a
+``lib${LIB}.a``
An archive lib of ``.o`` files, this is the default
-lib${LIB}_p.a
+``lib${LIB}_p.a``
A profiled lib of ``.po`` files.
Still an archive lib, but all the objects are built with
profiling in mind - hence the different extension.
- It is skipped if ``MKPROFILE`` is "no".
+ It is skipped if ``MK_PROFILE`` is "no".
-lib${LIB}_pic.a
+``lib${LIB}_pic.a``
An archive of ``.so`` objects compiled for relocation.
On NetBSD this is the input to ``lib${LIB}.${LD_so}``, it is
- skipped if ``MKPICLIB`` is "no".
+ skipped if ``MK_PIC`` or ``MK_PICLIB`` are "no".
-lib${LIB}.${LD_so}
+``lib${LIB}.${LD_so}``
A shared library. The value of ``LD_so`` is very platform
specific. For example::
@@ -190,7 +214,7 @@ lib${LIB}.${LD_so}
libsslfd.1.dylib
This library will only be built if ``SHLIB_MAJOR`` has
- a value, and ``MKPIC`` is not set to "no".
+ a value, and ``MK_PIC`` is not set to "no".
There is a lot of platform specific tweaking in ``lib.mk``, largely the
result of the original distributions trying to avoid interfering with
@@ -202,7 +226,7 @@ libnames.mk
This is included by both ``prog.mk`` and ``lib.mk`` and tries to
include ``*.libnames.mk`` of which:
-local.libnames.mk
+``local.libnames.mk``
does not exist unless you create it. It is a handy way for you
to customize without touching the distributed files.
For example, on a test machine I needed to build openssl but
@@ -217,7 +241,7 @@ local.libnames.mk
The makefile created an openssl dir in ``${OBJ_libcrypto}`` to
gather all the headers. dpadd.mk_ did the rest.
-host.libnames.mk
+``host.libnames.mk``
contains logic to find any libs named in ``HOST_LIBS`` in
``HOST_LIBDIRS``.
@@ -256,15 +280,15 @@ else in various ways::
Any library (referenced by its full path) in any of the above, is
added to ``DPMAGIC_LIBS`` with the following results, for each lib *foo*.
-SRC_libfoo
+``SRC_libfoo``
Is set to indicate where the src for libfoo is.
By default it is derived from ``LIBFOO`` by replacing
``${OBJTOP}`` with ``${SRCTOP}``.
-OBJ_libfoo
+``OBJ_libfoo``
Not very exciting, is just the dir where libfoo lives.
-INCLUDES_libfoo
+``INCLUDES_libfoo``
What to add to ``CFLAGS`` to find the public headers.
The default varies. If ``${SRC_libfoo}/h`` exists, it is assumed
to be the home of all public headers and thus the default is
@@ -273,7 +297,7 @@ INCLUDES_libfoo
Otherwise we make no assumptions and the default is
``-I${SRC_libfoo} -I${OBJ_libfoo}``
-LDADD_libfoo
+``LDADD_libfoo``
This only applies to libs reference via ``DPLIBS``.
The default is ``-lfoo``, ``LDADD_*`` provides a hook to
instantiate other linker flags at the appropriate point
@@ -300,16 +324,23 @@ obj.mk
One of the cool aspects of BSD make, is its support for separating
object files from the src tree. This is also the source of much
-confusion to some.
+confusion for people unfamiliar with it.
Traditionally one had to do a separate ``make obj`` pass through the
-tree. If ``MKOBJDIRS`` is "auto", we include auto.obj.mk_.
+tree. If ``MK_AUTO_OBJ`` is set we include auto.obj.mk_.
+
+In fact if ``MKOBJDIRS`` is set to "auto", `sys.mk`_ will set
+``MK_AUTO_OBJ=yes`` and include auto.obj.mk_ since it is best done early.
auto.obj.mk
-----------
-This leverages the ``.OBJDIR`` target introduced some years ago to
-NetBSD make, to automatically create the desired object dir.
+Creates object dirs and leverages the ``.OBJDIR`` target introduced
+some years ago to NetBSD make, to use them.
+
+Note that if ``auto.obj.mk`` is to be used it should be included
+early - before bmake has established ``.PATH``, thus we include it
+from ``sys.mk`` rather than ``obj.mk``.
subdir.mk
---------
@@ -334,6 +365,8 @@ you can suppress that - or enhance it by setting ``ECHO_DIR``::
# print time stamps
ECHO_DIR=echo @ `date "+%s [%Y-%m-%d %T] "`
+I prefer to use `dirdeps.mk`_ which makes ``subdir.mk`` irrelevant.
+
links.mk
--------
@@ -367,9 +400,12 @@ dep.mk
Deals with collecting dependencies. Another useful feature of BSD
make is the separation of this sort of information into a ``.depend``
-file. ``MKDEP`` needs to point to a suitable tool (like mkdeps.sh_)
+file. ``MKDEP_CMD`` needs to point to a suitable tool (like mkdeps.sh_)
+
+If ``MK_AUTODEP`` is "yes" it sets ``MKDEP_MK`` to autodep.mk_ by default.
-If ``USE_AUTODEP_MK`` is "yes" includes autodep.mk_
+``MKDEP_MK`` can also be set to `auto.dep.mk`_ which is more efficient
+but does not support an explicit ``depend`` target.
autodep.mk
----------
@@ -397,19 +433,9 @@ to avoid possible conflicts during parallel builds.
This precludes the use of suffix rules to drive ``make depend``, so
dep.mk_ handles that if specifically requested.
-options.mk
-----------
-
-Inspired by FreeBSD's ``bsd.own.mk`` more flexible.
-FreeBSD now have similar functionality in ``bsd.mkopt.mk``.
-
-It allows users to express their intent with respect to options
-``MK_*`` by setting ``WITH_*`` or ``WITHOUT_*``.
-
-Note: ``WITHOUT_*`` wins if both are set, and makefiles can set
-``NO_*`` to say they cannot handle that option, or even ``MK_*`` if
-they really need to.
-
+If ``bmake`` is 20160218 or newer, ``auto.dep.mk`` uses ``.dinclude``
+to includes the ``*.d`` files directly thus avoiding the need to
+create a ``.depend`` file from them.
own.mk
------
@@ -424,7 +450,9 @@ ldorder.mk
Leverages ``bmake`` to compute optimal link order for libraries.
This works nicely and makes refactoring a breeze - so long as you
-have not (or few) cicular dependencies between libraries.
+have no (or few) cicular dependencies between libraries.
+
+Consider this experimental.
man.mk
------
@@ -448,7 +476,7 @@ would add all the warnings in ``${HIGH_WARNINGS}`` to CFLAGS, but
on sparc, ``-Wno-unused`` would replace ``-Wunused``.
You should never need to edit ``warnings.mk``, it will include
-``warnings-sets.mk`` if it exists and you use that to make any local
+``warnings-sets.mk`` and/or ``local.warnings.mk`` to pick up
customizations.
rst2htm.mk
@@ -463,17 +491,88 @@ Logic to build Python C interface modules using Cython_
.. _Cython: http://www.cython.org/
-Meta mode
+cc-wrap.mk
+----------
+
+This makefile leverages two new features in bmake 20220126 and later.
+
+First is the ablity to set target local variables (GNU make has done
+this for ages).
+
+The second (only intersting if using `meta mode`_)
+allows filtering commands before comparison with previous run to
+decide if a target is out-of-date.
+
+In the past, making use of compiler wrappers like ``ccache``,
+``distcc`` or the newer ``icecc`` could get quite ugly.
+Using ``cc-wrap.mk`` it could not be simpler.
+
+jobs.mk
+-------
+
+This should be included by the top-level makefile.
+If you do::
+
+ make something-jobs
+
+then ``jobs.mk`` will run::
+
+ make -j${JOB_MAX} someting > ${JOB_LOGDIR}/something.log 2>&1
+
+this ensures you get a build log and JOB_MAX is assumed to be set
+optimally for the host.
+
+META_MODE
=========
The 20110505 and later versions of ``mk-files`` include a number of
makefiles contributed by Juniper Networks, Inc.
These allow the latest version of bmake_ to run in `meta mode`_
-see `dirdeps.mk`_
+see `dirdeps.mk`_ and DIRDEPS_BUILD_ below.
.. _`dirdeps.mk`: /help/sjg/dirdeps.htm
.. _`meta mode`: bmake-meta-mode.htm
+DIRDEPS_BUILD
+=============
+
+When the `meta mode`_ was originally done, there was no distinction
+between META_MODE_ and ``DIRDEPS_BUILD``, but as these were integrated
+into FreeBSD it became clear that META_MODE_ could be useful to many
+developers independently of ``DIRDEPS_BUILD``.
+
+Thus today we distinguish between the two.
+We have the following makefiles which are relevant to
+``DIRDEPS_BUILD`` or META_MODE_::
+
+ share/mk/auto.obj.mk
+ share/mk/dirdeps-cache-update.mk
+ share/mk/dirdeps-options.mk
+ share/mk/dirdeps-targets.mk
+ share/mk/dirdeps.mk
+ share/mk/gendirdeps.mk
+ share/mk/host-target.mk
+ share/mk/install-new.mk
+ share/mk/meta.autodep.mk
+ share/mk/meta.stage.mk
+ share/mk/meta.sys.mk
+ share/mk/meta2deps.py
+ share/mk/meta2deps.sh
+ share/mk/sys.dependfile.mk
+ share/mk/sys.dirdeps.mk
+
+and the following are typically used for customization.
+See `freebsd-meta-mode`_ and `netbsd-meta-mode`_::
+
+ share/mk/local.dirdeps-build.mk
+ share/mk/local.dirdeps-missing.mk
+ share/mk/local.dirdeps.mk
+ share/mk/local.meta.sys.mk
+ share/mk/local.sys.dirdeps.env.mk
+ share/mk/local.sys.dirdeps.mk
+ share/mk/local.sys.mk
+
+
Install
=======
@@ -484,7 +583,7 @@ destination directory, and unless told not to, create ``bsd.*.mk`` links
for ``lib.mk`` etc.
If you just want to create the ``bsd.*.mk`` links in the directory
-where you unpacked the tar file, you can::
+where you unpacked the tar file, you can use::
./mk/install-mk ./mk
@@ -492,9 +591,11 @@ where you unpacked the tar file, you can::
.. _bmake: bmake.htm
.. _NetBSD: http://www.netbsd.org/
-.. _mkdeps.sh: http://www.crufty.net/ftp/pub/sjg/mkdeps.sh
-.. _mk.tar.gz: http://www.crufty.net/ftp/pub/sjg/mk.tar.gz
+.. _mkdeps.sh: https://www.crufty.net/ftp/pub/sjg/mkdeps.sh
+.. _mk.tar.gz: https://www.crufty.net/ftp/pub/sjg/mk.tar.gz
+.. _`freebsd-meta-mode`: https://www.crufty.net/sjg/docs/freebsd-meta-mode.htm
+.. _`netbsd-meta-mode`: https://www.crufty.net/sjg/docs/netbsd-meta-mode.htm
:Author: sjg@crufty.net
-:Revision: $Id: mk-files.txt,v 1.20 2020/08/19 17:51:53 sjg Exp $
+:Revision: $Id: mk-files.txt,v 1.25 2023/07/14 23:51:11 sjg Exp $
:Copyright: Crufty.NET
diff --git a/contrib/bmake/mk/mkopt.sh b/contrib/bmake/mk/mkopt.sh
index 4a42c0ddf122..24320c257250 100644
--- a/contrib/bmake/mk/mkopt.sh
+++ b/contrib/bmake/mk/mkopt.sh
@@ -1,8 +1,10 @@
#!/bin/sh
-# $Id: mkopt.sh,v 1.13 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
#
-# @(#) Copyright (c) 2014, 2020, Simon J. Gerraty
+# $Id: mkopt.sh,v 1.16 2024/02/17 17:26:57 sjg Exp $
+#
+# @(#) Copyright (c) 2014-2022, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@@ -84,9 +86,36 @@ _mk_opts_defaults() {
$OPTIONS_DEFAULT_DEPENDENT $__DEFAULT_DEPENDENT_OPTIONS
}
+# _mk_cmdline_opts opt ...
+# look at the command line (saved in _cmdline)
+# to see any options we care about are being set with -DWITH*
+# or MK_*= if 'opt' is '*' then all options are of interest.
+_cmdline="$0 $@"
+_mk_cmdline_opts() {
+ for _x in $_cmdline
+ do
+ case "$_x" in
+ -DWITH*|${_MKOPT_PREFIX:-MK_}*)
+ for _o in "$@"
+ do
+ case "$_x" in
+ -DWITH_$_o|-DWITHOUT_$_o) eval ${_x#-D}=1;;
+ -DWITH_$_o=*|-DWITHOUT_$_o=*) eval ${_x#-D};;
+ ${_MKOPT_PREFIX:-MK_}$_o=*) eval "$_x";;
+ esac
+ done
+ ;;
+ esac
+ done
+}
+
+
case "/$0" in
*/mkopt*)
_list=no
+ _mk_cmdline_opts '*'
+ _mk_opts no DEBUG
+ [ $MK_DEBUG = no ] || set -x
while :
do
case "$1" in
diff --git a/contrib/bmake/mk/newlog.sh b/contrib/bmake/mk/newlog.sh
new file mode 100755
index 000000000000..5c65e5dd5fb6
--- /dev/null
+++ b/contrib/bmake/mk/newlog.sh
@@ -0,0 +1,414 @@
+#!/bin/sh
+
+# NAME:
+# newlog - rotate log files
+#
+# SYNOPSIS:
+# newlog.sh [options] "log"[:"num"] ...
+#
+# DESCRIPTION:
+# This script saves multiple generations of each "log".
+# The "logs" are kept compressed except for the current and
+# previous ones.
+#
+# Options:
+#
+# -C "compress"
+# Compact old logs (other than .0) with "compress"
+# (default is 'gzip' or 'compress' if no 'gzip').
+#
+# -E "ext"
+# If "compress" produces a file extention other than
+# '.Z' or '.gz' we need to know.
+#
+# -G "gens"
+# "gens" is a comma separated list of "log":"num" pairs
+# that allows certain logs to handled differently.
+#
+# -N Don't actually do anything, just show us.
+#
+# -R Rotate rather than save logs by default.
+# This is the default anyway.
+#
+# -S Save rather than rotate logs by default.
+# Each log is saved to a unique name that remains
+# unchanged. This results in far less churn.
+#
+# -f "fmt"
+# Format ('%Y%m%d.%H%M%S') for suffix added to "log" to
+# uniquely name it when using the '-S' option.
+# If a "log" is saved more than once per second we add
+# an extra suffix of our process-id.
+#
+# -d The "log" to be rotated/saved is a directory.
+# We leave the mode of old directories alone.
+#
+# -e Normally logs are only cycled if non-empty, this
+# option forces empty logs to be cycled as well.
+#
+# -g "group"
+# Set the group of "log" to "group".
+#
+# -m "mode"
+# Set the mode of "log".
+#
+# -M "mode"
+# Set the mode of old logs (default 444).
+#
+# -n "num"
+# Keep "num" generations of "log".
+#
+# -o "owner"
+# Set the owner of "log".
+#
+# Regardless of whether '-R' or '-S' is provided, we attempt to
+# choose the correct behavior based on observation of "log.0" if
+# it exists; if it is a symbolic link, we save, otherwise
+# we rotate.
+#
+# BUGS:
+# 'Newlog.sh' tries to avoid being fooled by symbolic links, but
+# multiply indirect symlinks are only handled on machines where
+# test(1) supports a check for symlinks.
+#
+# AUTHOR:
+# Simon J. Gerraty <sjg@crufty.net>
+#
+
+# RCSid:
+# $Id: newlog.sh,v 1.27 2024/02/17 17:26:57 sjg Exp $
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# @(#) Copyright (c) 1993-2016 Simon J. Gerraty
+#
+# This file is provided in the hope that it will
+# be of use. There is absolutely NO WARRANTY.
+# Permission to copy, redistribute or otherwise
+# use this file is hereby granted provided that
+# the above copyright notice and this notice are
+# left intact.
+#
+# Please send copies of changes and bug-fixes to:
+# sjg@crufty.net
+#
+
+Mydir=`dirname $0`
+case $Mydir in
+/*) ;;
+*) Mydir=`cd $Mydir; pwd`;;
+esac
+
+# places to find chown (and setopts.sh)
+PATH=$PATH:/usr/etc:/sbin:/usr/sbin:/usr/local/share/bin:/share/bin:$Mydir
+
+# linux doesn't necessarily have compress,
+# and gzip appears in various locations...
+Which() {
+ case "$1" in
+ -*) t=$1; shift;;
+ *) t=-x;;
+ esac
+ case "$1" in
+ /*) test $t $1 && echo $1;;
+ *)
+ for d in `IFS=:; echo ${2:-$PATH}`
+ do
+ test $t $d/$1 && { echo $d/$1; break; }
+ done
+ ;;
+ esac
+}
+
+# shell's typically have test(1) as built-in
+# and not all support all options.
+test_opt() {
+ _o=$1
+ _a=$2
+ _t=${3:-/}
+
+ case `test -$_o $_t 2>&1` in
+ *:*) eval test_$_o=$_a;;
+ *) eval test_$_o=-$_o;;
+ esac
+}
+
+# convert find/ls mode to octal
+fmode() {
+ eval `echo $1 |
+ sed 's,\(.\)\(...\)\(...\)\(...\),ft=\1 um=\2 gm=\3 om=\4,'`
+ sm=
+ case "$um" in
+ *s*) sm=r
+ um=`echo $um | sed 's,s,x,'`
+ ;;
+ *) sm=-;;
+ esac
+ case "$gm" in
+ *[Ss]*)
+ sm=${sm}w
+ gm=`echo $gm | sed 's,s,x,;s,S,-,'`
+ ;;
+ *) sm=${sm}-;;
+ esac
+ case "$om" in
+ *t)
+ sm=${sm}x
+ om=`echo $om | sed 's,t,x,'`
+ ;;
+ *) sm=${sm}-;;
+ esac
+ echo $sm $um $gm $om |
+ sed 's,rwx,7,g;s,rw-,6,g;s,r-x,5,g;s,r--,4,g;s,-wx,3,g;s,-w-,2,g;s,--x,1,g;s,---,0,g;s, ,,g'
+}
+
+get_mode() {
+ case "$OS,$STAT" in
+ FreeBSD,*)
+ $STAT -f %Op $1 | sed 's,.*\(....\),\1,'
+ return
+ ;;
+ esac
+ # fallback to find
+ fmode `find $1 -ls -prune | awk '{ print $3 }'`
+}
+
+get_mtime_suffix() {
+ case "$OS,$STAT" in
+ FreeBSD,*)
+ $STAT -t "${2:-$opt_f}" -f %Sm $1
+ return
+ ;;
+ esac
+ # this will have to do
+ date "+${2:-$opt_f}"
+}
+
+case /$0 in
+*/newlog*) rotate_func=rotate_log;;
+*/save*) rotate_func=save_log;;
+*) rotate_func=rotate_log;;
+esac
+
+opt_n=7
+opt_m=
+opt_M=444
+opt_f=%Y%m%d.%H%M%S
+opt_str=dNn:o:g:G:C:M:m:eE:f:RS
+
+. setopts.sh
+
+test $# -gt 0 || exit 0 # nothing to do.
+
+OS=${OS:-`uname`}
+STAT=${STAT:-`Which stat`}
+
+# sorry, setops semantics for booleans changed.
+case "${opt_d:-0}" in
+0) rm_f=-f
+ opt_d=-f
+ for x in $opt_C gzip compress
+ do
+ opt_C=`Which $x "/bin:/usr/bin:$PATH"`
+ test -x $opt_C && break
+ done
+ empty() { test ! -s $1; }
+ ;;
+*) rm_f=-rf
+ opt_d=-d
+ opt_M=
+ opt_C=:
+ empty() {
+ if [ -d $1 ]; then
+ n=`'ls' -a1 $1/. | wc -l`
+ [ $n -gt 2 ] && return 1
+ fi
+ return 0
+ }
+ ;;
+esac
+case "${opt_N:-0}" in
+0) ECHO=;;
+*) ECHO=echo;;
+esac
+case "${opt_e:-0}" in
+0) force=;;
+*) force=yes;;
+esac
+case "${opt_R:-0}" in
+0) ;;
+*) rotate_func=rotate_log;;
+esac
+case "${opt_S:-0}" in
+0) ;;
+*) rotate_func=save_log opt_S=;;
+esac
+
+# see whether test handles -h or -L
+test_opt L -h
+test_opt h ""
+case "$test_L,$test_h" in
+-h,) test_L= ;; # we don't support either!
+esac
+
+case "$test_L" in
+"") # No, so this is about all we can do...
+ logs=`'ls' -ld $* | awk '{ print $NF }'`
+ ;;
+*) # it does
+ logs="$*"
+ ;;
+esac
+
+read_link() {
+ case "$test_L" in
+ "") 'ls' -ld $1 | awk '{ print $NF }'; return;;
+ esac
+ if test $test_L $1; then
+ 'ls' -ld $1 | sed 's,.*> ,,'
+ else
+ echo $1
+ fi
+}
+
+# create the new log
+new_log() {
+ log=$1
+ mode=$2
+ if test "x$opt_M" != x; then
+ $ECHO chmod $opt_M $log.0 2> /dev/null
+ fi
+ # someone may have managed to write to it already
+ # so don't truncate it.
+ case "$opt_d" in
+ -d) $ECHO mkdir -p $log;;
+ *) $ECHO touch $log;;
+ esac
+ # the order here matters
+ test "x$opt_o" = x || $ECHO chown $opt_o $log
+ test "x$opt_g" = x || $ECHO chgrp $opt_g $log
+ test "x$mode" = x || $ECHO chmod $mode $log
+}
+
+rotate_log() {
+ log=$1
+ n=${2:-$opt_n}
+
+ # make sure excess generations are trimmed
+ $ECHO rm $rm_f `echo $log.$n | sed 's/\([0-9]\)$/[\1-9]*/'`
+
+ mode=${opt_m:-`get_mode $log`}
+ while test $n -gt 0
+ do
+ p=`expr $n - 1`
+ if test -s $log.$p; then
+ $ECHO rm $rm_f $log.$p.*
+ $ECHO $opt_C $log.$p
+ if test "x$opt_M" != x; then
+ $ECHO chmod $opt_M $log.$p.* 2> /dev/null
+ fi
+ fi
+ for ext in $opt_E .gz .Z ""
+ do
+ test $opt_d $log.$p$ext || continue
+ $ECHO mv $log.$p$ext $log.$n$ext
+ done
+ n=$p
+ done
+ # leave $log.0 uncompressed incase some one still has it open.
+ $ECHO mv $log $log.0
+ new_log $log $mode
+}
+
+# unlike rotate_log we do not rotate files,
+# but give each log a unique (but stable name).
+# This avoids churn for folk who rsync things.
+# We make log.0 a symlink to the most recent log
+# so it can be found and compressed next time around.
+save_log() {
+ log=$1
+ n=${2:-$opt_n}
+ fmt=$3
+
+ last=`read_link $log.0`
+ case "$last" in
+ $log.0) # should never happen
+ test -s $last && $ECHO mv $last $log.$$;;
+ $log.*)
+ $ECHO $opt_C $last
+ ;;
+ *.*) $ECHO $opt_C `dirname $log`/$last
+ ;;
+ esac
+ $ECHO rm -f $log.0
+ # remove excess logs - we rely on mtime!
+ $ECHO rm $rm_f `'ls' -1td $log.* 2> /dev/null | sed "1,${n}d"`
+
+ mode=${opt_m:-`get_mode $log`}
+ # this is our default suffix
+ opt_S=${opt_S:-`get_mtime_suffix $log $fmt`}
+ case "$fmt" in
+ ""|$opt_f) suffix=$opt_S;;
+ *) suffix=`get_mtime_suffix $log $fmt`;;
+ esac
+
+ # find a unique name to save current log as
+ for nlog in $log.$suffix $log.$suffix.$$
+ do
+ for f in $nlog*
+ do
+ break
+ done
+ test $opt_d $f || break
+ done
+ # leave $log.0 uncompressed incase some one still has it open.
+ $ECHO mv $log $nlog
+ test "x$opt_M" = x || $ECHO chmod $opt_M $nlog 2> /dev/null
+ $ECHO ln -s `basename $nlog` $log.0
+ new_log $log $mode
+}
+
+for f in $logs
+do
+ n=$opt_n
+ save=
+ case "$f" in
+ *:[1-9]*)
+ set -- `IFS=:; echo $f`; f=$1; n=$2;;
+ *:n=*|*:save=*)
+ eval `echo "f=$f" | tr ':' ' '`;;
+ esac
+ # try and pick the right function to use
+ rfunc=$rotate_func # default
+ if test $opt_d $f.0; then
+ case `read_link $f.0` in
+ $f.0) rfunc=rotate_log;;
+ *) rfunc=save_log;;
+ esac
+ fi
+ case "$test_L" in
+ -?)
+ while test $test_L $f # it is [still] a symlink
+ do
+ f=`read_link $f`
+ done
+ ;;
+ esac
+ case ",${opt_G}," in
+ *,${f}:n=*|,${f}:save=*)
+ eval `echo ",${opt_G}," | sed "s!.*,${f}:\([^,]*\),.*!\1!;s,:, ,g"`
+ ;;
+ *,${f}:*)
+ # opt_G is a , separated list of log:n pairs
+ n=`echo ,$opt_G, | sed -e "s,.*${f}:\([0-9][0-9]*\).*,\1,"`
+ ;;
+ esac
+
+ if empty $f; then
+ test "$force" || continue
+ fi
+
+ test "$save" && rfunc=save_log
+
+ $rfunc $f $n $save
+done
diff --git a/contrib/bmake/mk/obj.mk b/contrib/bmake/mk/obj.mk
index 487e25a55b6c..eac8e9e05891 100644
--- a/contrib/bmake/mk/obj.mk
+++ b/contrib/bmake/mk/obj.mk
@@ -1,4 +1,6 @@
-# $Id: obj.mk,v 1.16 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: obj.mk,v 1.19 2024/02/19 00:06:19 sjg Exp $
#
# @(#) Copyright (c) 1999-2010, Simon J. Gerraty
#
@@ -13,8 +15,11 @@
# sjg@crufty.net
#
-.if !target(__${.PARSEFILE:S,bsd.,,}__)
-__${.PARSEFILE:S,bsd.,,}__:
+# should be set properly in sys.mk
+_this ?= ${.PARSEFILE:S,bsd.,,}
+
+.if !target(__${_this}__)
+__${_this}__: .NOTMAIN
.include <init.mk>
diff --git a/contrib/bmake/mk/options.mk b/contrib/bmake/mk/options.mk
index eb5253a6b7e8..363fc53baf14 100644
--- a/contrib/bmake/mk/options.mk
+++ b/contrib/bmake/mk/options.mk
@@ -1,4 +1,6 @@
-# $Id: options.mk,v 1.13 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: options.mk,v 1.20 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2012, Simon J. Gerraty
#
@@ -26,8 +28,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 +38,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 +83,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..72b2f70d4bbe 100644
--- a/contrib/bmake/mk/own.mk
+++ b/contrib/bmake/mk/own.mk
@@ -1,7 +1,10 @@
-# $Id: own.mk,v 1.42 2020/11/27 18:00:08 sjg Exp $
+# $Id: own.mk,v 1.48 2024/04/09 21:52:52 sjg Exp $
-.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+# should be set properly in sys.mk
+_this ?= ${.PARSEFILE:S,bsd.,,}
+
+.if !target(__${_this}__)
+__${_this}__: .NOTMAIN
.if !target(__init.mk__)
.include "init.mk"
@@ -37,27 +40,6 @@ libprefix?= /usr
# FreeBSD at least does not set this
MACHINE_ARCH?= ${MACHINE}
# we need to make sure these are defined too in case sys.mk fails to.
-COMPILE.s?= ${CC} ${AFLAGS} -c
-LINK.s?= ${CC} ${AFLAGS} ${LDFLAGS}
-COMPILE.S?= ${CC} ${AFLAGS} ${CPPFLAGS} -c -traditional-cpp
-LINK.S?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS}
-COMPILE.c?= ${CC} ${CFLAGS} ${CPPFLAGS} -c
-LINK.c?= ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS}
-CXXFLAGS?= ${CFLAGS}
-COMPILE.cc?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} -c
-LINK.cc?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS}
-COMPILE.m?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} -c
-LINK.m?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} ${LDFLAGS}
-COMPILE.f?= ${FC} ${FFLAGS} -c
-LINK.f?= ${FC} ${FFLAGS} ${LDFLAGS}
-COMPILE.F?= ${FC} ${FFLAGS} ${CPPFLAGS} -c
-LINK.F?= ${FC} ${FFLAGS} ${CPPFLAGS} ${LDFLAGS}
-COMPILE.r?= ${FC} ${FFLAGS} ${RFLAGS} -c
-LINK.r?= ${FC} ${FFLAGS} ${RFLAGS} ${LDFLAGS}
-LEX.l?= ${LEX} ${LFLAGS}
-COMPILE.p?= ${PC} ${PFLAGS} ${CPPFLAGS} -c
-LINK.p?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS}
-YACC.y?= ${YACC} ${YFLAGS}
# for suffix rules
IMPFLAGS?= ${COPTS.${.IMPSRC:T}} ${CPUFLAGS.${.IMPSRC:T}} ${CPPFLAGS.${.IMPSRC:T}}
@@ -119,6 +101,7 @@ OPTIONS_DEFAULT_DEPENDENT+= \
PICINSTALL/LINKLIB \
PICLIB/PIC \
PROFILE/LINKLIB \
+ STAGING_MAN/STAGING \
STAGING_PROG/STAGING \
.include <options.mk>
@@ -153,8 +136,7 @@ INCDIR?= ${INCLUDEDIR}
# Define MANZ to have the man pages compressed (gzip)
#MANZ= 1
-MANTARGET?= cat
-MANDIR?= ${prefix}/share/man/${MANTARGET}
+MANDIR?= ${prefix}/share/man
MANGRP?= ${BINGRP}
MANOWN?= ${BINOWN}
MANMODE?= ${NONBINMODE}
@@ -257,11 +239,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/posix.mk b/contrib/bmake/mk/posix.mk
new file mode 100644
index 000000000000..b7cb9ef32108
--- /dev/null
+++ b/contrib/bmake/mk/posix.mk
@@ -0,0 +1,106 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: posix.mk,v 1.3 2024/02/17 17:26:57 sjg Exp $
+#
+# @(#) Copyright (c) 2022, Simon J. Gerraty
+#
+# This file is provided in the hope that it will
+# be of use. There is absolutely NO WARRANTY.
+# Permission to copy, redistribute or otherwise
+# use this file is hereby granted provided that
+# the above copyright notice and this notice are
+# left intact.
+#
+# Please send copies of changes and bug-fixes to:
+# sjg@crufty.net
+#
+
+# The minimal set of rules required by POSIX
+
+.if !defined(%POSIX)
+.error ${.newline}Do not inlcude this directly, put .POSIX: at start of Makefile
+.endif
+
+.if ${.MAKEFLAGS:M-r} == ""
+# undo some work done by sys.mk
+.SUFFIXES:
+.undef ARFLAGS
+.undef CC CFLAGS
+.undef FC FFLAGS
+.undef LDFLAGS LFLAGS
+.undef RANLIBFLAGS
+.undef YFLAGS
+.endif
+
+.SUFFIXES: .o .c .y .l .a .sh .f
+
+# these can still be set via environment
+AR ?= ar
+ARFLAGS ?= -rv
+CC ?= c99
+CFLAGS ?= -O
+FC ?= fort77
+FFLAGS ?= -O 1
+LDFLAGS ?=
+LEX ?= lex
+LFLAGS ?=
+RANLIBFLAGS ?= -D
+YACC ?= yacc
+YFLAGS ?=
+
+.c:
+ ${CC} ${CFLAGS} ${LDFLAGS} -o $@ $<
+
+
+.f:
+ ${FC} ${FFLAGS} ${LDFLAGS} -o $@ $<
+
+
+.sh:
+ cp $< $@
+ chmod a+x $@
+
+
+.c.o:
+ ${CC} ${CFLAGS} -c $<
+
+
+.f.o:
+ ${FC} ${FFLAGS} -c $<
+
+
+.y.o:
+ ${YACC} ${YFLAGS} $<
+ ${CC} ${CFLAGS} -c y.tab.c
+ rm -f y.tab.c
+ mv y.tab.o $@
+
+
+.l.o:
+ ${LEX} ${LFLAGS} $<
+ ${CC} ${CFLAGS} -c lex.yy.c
+ rm -f lex.yy.c
+ mv lex.yy.o $@
+
+
+.y.c:
+ ${YACC} ${YFLAGS} $<
+ mv y.tab.c $@
+
+
+.l.c:
+ ${LEX} ${LFLAGS} $<
+ mv lex.yy.c $@
+
+
+.c.a:
+ ${CC} -c ${CFLAGS} $<
+ ${AR} ${ARFLAGS} $@ $*.o
+ rm -f $*.o
+
+
+.f.a:
+ ${FC} -c ${FFLAGS} $<
+ ${AR} ${ARFLAGS} $@ $*.o
+ rm -f $*.o
+
diff --git a/contrib/bmake/mk/prlist.mk b/contrib/bmake/mk/prlist.mk
index aca1fde25555..b6912a8d9601 100644
--- a/contrib/bmake/mk/prlist.mk
+++ b/contrib/bmake/mk/prlist.mk
@@ -1,4 +1,6 @@
-# $Id: prlist.mk,v 1.4 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: prlist.mk,v 1.6 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2006, Simon J. Gerraty
#
@@ -14,7 +16,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..aac2a4d3da28 100644
--- a/contrib/bmake/mk/prog.mk
+++ b/contrib/bmake/mk/prog.mk
@@ -1,7 +1,10 @@
-# $Id: prog.mk,v 1.36 2020/08/19 17:51:53 sjg Exp $
+# $Id: prog.mk,v 1.44 2024/02/19 00:06:19 sjg Exp $
-.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+# should be set properly in sys.mk
+_this ?= ${.PARSEFILE:S,bsd.,,}
+
+.if !target(__${_this}__)
+__${_this}__: .NOTMAIN
.include <init.mk>
@@ -11,7 +14,7 @@ _sect:=${MAN:E}
MAN${_sect}=${MAN}
.endif
-.SUFFIXES: .out .o .c .cc .C .y .l .s .8 .7 .6 .5 .4 .3 .2 .1 .0
+.SUFFIXES: .out .o .c ${CXX_SUFFIXES} .y .l ${CCM_SUFFIXES} ${PCM}
CFLAGS+= ${COPTS}
@@ -62,27 +65,45 @@ LDADD_LAST+= ${LDADD_LIBC_P}
.if defined(SHAREDSTRINGS)
CLEANFILES+=strings
.c.o:
- ${CC} -E ${CFLAGS} ${.IMPSRC} | xstr -c -
- @${CC} ${CFLAGS} -c x.c -o ${.TARGET}
+ @${COMPILE.c:N-c} -E ${.IMPSRC} | xstr -c -
+ @${COMPILE.c} x.c -o ${.TARGET}
@rm -f x.c
-${CXX_SUFFIXES:%=%.o}:
- ${CXX} -E ${CXXFLAGS} ${.IMPSRC} | xstr -c -
+# precompiled C++ Modules
+${CCM_SUFFIXES:%=%${PCM}}:
+ @${COMIPILE.cc:N-c} -E ${.IMPSRC} | xstr -c -
+ @mv -f x.c x.cc
+ @${COMPILE.pcm} x.cc -o ${.TARGET}
+ @rm -f x.cc
+
+${CXX_SUFFIXES:N.c*m:%=%.o}:
+ @${COMIPILE.cc:N-c} -E ${.IMPSRC} | xstr -c -
@mv -f x.c x.cc
- @${CXX} ${CXXFLAGS} -c x.cc -o ${.TARGET}
+ @${COMPILE.cc} x.cc -o ${.TARGET}
@rm -f x.cc
.endif
+.if defined(PROG_CXX)
+PROG= ${PROG_CXX}
+_SUPCXX?= -lstdc++ -lm
+.endif
.if defined(PROG)
BINDIR ?= ${prefix}/bin
-SRCS?= ${PROG}.c
-.for s in ${SRCS:N*.h:N*.sh:M*/*}
+.if empty(SRCS)
+# init.mk handling of QUALIFIED_VAR_LIST means
+# SRCS will be defined - even if empty.
+SRCS = ${PROG}.c
+.endif
+
+SRCS ?= ${PROG}.c
+OBJS_SRCS = ${SRCS:${OBJS_SRCS_FILTER}}
+.for s in ${OBJS_SRCS:M*/*}
${.o .po .lo:L:@o@${s:T:R}$o@}: $s
.endfor
-.if !empty(SRCS:N*.h:N*.sh)
-OBJS+= ${SRCS:T:N*.h:N*.sh:R:S/$/.o/g}
+.if !empty(OBJS_SRCS)
+OBJS+= ${OBJS_SRCS:T:R:S/$/.o/g}
LOBJS+= ${LSRCS:.c=.ln} ${SRCS:M*.c:.c=.ln}
.endif
@@ -90,7 +111,7 @@ LOBJS+= ${LSRCS:.c=.ln} ${SRCS:M*.c:.c=.ln}
.NOPATH: ${OBJS} ${PROG} ${SRCS:M*.[ly]:C/\..$/.c/} ${YHEADER:D${SRCS:M*.y:.y=.h}}
# this is known to work for NetBSD 1.6 and FreeBSD 4.2
-.if ${TARGET_OSNAME} == "NetBSD" || ${TARGET_OSNAME} == "FreeBSD"
+.if ${TARGET_OSNAME:NFreeBSD:NNetBSD} == ""
_PROGLDOPTS=
.if ${SHLINKDIR} != "/usr/libexec" # XXX: change or remove if ld.so moves
_PROGLDOPTS+= -Wl,-dynamic-linker=${_SHLINKER}
@@ -100,20 +121,16 @@ _PROGLDOPTS+= -Wl,-rpath-link,${DESTDIR}${SHLIBDIR}:${DESTDIR}/usr/lib \
-L${DESTDIR}${SHLIBDIR}
.endif
_PROGLDOPTS+= -Wl,-rpath,${SHLIBDIR}:/usr/lib
-
-.if defined(PROG_CXX)
-_CCLINK= ${CXX}
-_SUPCXX= -lstdc++ -lm
-.endif
.endif # NetBSD
-_CCLINK?= ${CC}
-
.if ${MK_PROG_LDORDER_MK} != "no"
${PROG}: ldorder
.include <ldorder.mk>
.endif
+# avoid -dL errors
+LDADD_LDORDER ?=
+LDSTATIC ?=
.if defined(DESTDIR) && exists(${LIBCRT0}) && ${LIBCRT0} != "/dev/null"
diff --git a/contrib/bmake/mk/progs.mk b/contrib/bmake/mk/progs.mk
index 16c381a50bf9..ada942db8621 100644
--- a/contrib/bmake/mk/progs.mk
+++ b/contrib/bmake/mk/progs.mk
@@ -1,4 +1,6 @@
-# $Id: progs.mk,v 1.16 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: progs.mk,v 1.18 2024/04/09 17:18:24 sjg Exp $
#
# @(#) Copyright (c) 2006, Simon J. Gerraty
#
@@ -37,16 +39,26 @@ PROG ?= $t
# just one of many
PROG_VARS += \
BINDIR \
- CFLAGS \
- COPTS \
- CPPFLAGS \
CXXFLAGS \
DPADD \
DPLIBS \
LDADD \
- LDFLAGS \
MAN \
- SRCS
+
+.ifndef SYS_OS_MK
+# assume we are not using init.mk, otherwise
+# we need to avoid overlap with its
+# QUALIFIED_VAR_LIST which includes these and its
+# VAR_QUALIFIER_LIST includes .TARGET which
+# would match PROG
+PROG_VARS += \
+ CFLAGS \
+ COPTS \
+ CPPFLAGS \
+ LDFLAGS \
+ SRCS \
+
+.endif
.for v in ${PROG_VARS:O:u}
.if defined(${v}.${PROG}) || defined(${v}_${PROG})
diff --git a/contrib/bmake/mk/rst2htm.mk b/contrib/bmake/mk/rst2htm.mk
index 66eb8552f875..4da16f144e9e 100644
--- a/contrib/bmake/mk/rst2htm.mk
+++ b/contrib/bmake/mk/rst2htm.mk
@@ -1,4 +1,6 @@
-# $Id: rst2htm.mk,v 1.12 2021/05/26 04:20:31 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: rst2htm.mk,v 1.15 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2009, Simon J. Gerraty
#
@@ -16,11 +18,15 @@
# convert reStructuredText to HTML, using rst2html.py from
# docutils - http://docutils.sourceforge.net/
+# pickup customizations
+.-include <local.rst2htm.mk>
+
.if empty(TXTSRCS)
TXTSRCS != 'ls' -1t ${.CURDIR}/*.txt ${.CURDIR}/*.rst 2>/dev/null; echo
.endif
RSTSRCS ?= ${TXTSRCS}
HTMFILES ?= ${RSTSRCS:R:T:O:u:%=%.htm}
+PDFFILES ?= ${RSTSRCS:R:T:O:u:%=%.pdf}
# can be empty, 4 or 5
HTML_VERSION ?=
RST2HTML ?= rst2html${HTML_VERSION}.py
@@ -37,9 +43,10 @@ RST2PDF_FLAGS ?= ${"${.TARGET:T:M*slides*}":?${RST2PDF_SLIDES_FLAGS}:${RST2PDF_D
RST_SUFFIXES ?= .rst .txt
-CLEANFILES += ${HTMFILES}
+CLEANFILES += ${HTMFILES} ${PDFFILES}
html: ${HTMFILES}
+pdf: ${PDFFILES}
.SUFFIXES: ${RST_SUFFIXES} .htm .pdf
diff --git a/contrib/bmake/mk/scripts.mk b/contrib/bmake/mk/scripts.mk
index 5ea2474e65a3..36d8f2901397 100644
--- a/contrib/bmake/mk/scripts.mk
+++ b/contrib/bmake/mk/scripts.mk
@@ -1,4 +1,6 @@
-# $Id: scripts.mk,v 1.4 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: scripts.mk,v 1.5 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2006, Simon J. Gerraty
#
diff --git a/contrib/bmake/mk/srctop.mk b/contrib/bmake/mk/srctop.mk
index 91594c7a98e4..ba4034e6d802 100644
--- a/contrib/bmake/mk/srctop.mk
+++ b/contrib/bmake/mk/srctop.mk
@@ -1,4 +1,6 @@
-# $Id: srctop.mk,v 1.4 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: srctop.mk,v 1.5 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2012, Simon J. Gerraty
#
diff --git a/contrib/bmake/mk/stage-install.sh b/contrib/bmake/mk/stage-install.sh
index 674652d1d482..d9182e32feff 100755
--- a/contrib/bmake/mk/stage-install.sh
+++ b/contrib/bmake/mk/stage-install.sh
@@ -37,7 +37,9 @@
#
# RCSid:
-# $Id: stage-install.sh,v 1.9 2020/08/28 01:04:13 sjg Exp $
+# $Id: stage-install.sh,v 1.11 2024/02/17 17:26:57 sjg Exp $
+#
+# SPDX-License-Identifier: BSD-2-Clause
#
# @(#) Copyright (c) 2013-2020, Simon J. Gerraty
#
@@ -117,8 +119,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/subdir.mk b/contrib/bmake/mk/subdir.mk
index 1d5001acd2a2..0feb63af44d2 100644
--- a/contrib/bmake/mk/subdir.mk
+++ b/contrib/bmake/mk/subdir.mk
@@ -1,64 +1,70 @@
-# $Id: subdir.mk,v 1.16 2017/02/08 22:16:59 sjg Exp $
-# skip missing directories...
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: subdir.mk,v 1.24 2024/04/10 01:47:23 sjg Exp $
+#
+# @(#) Copyright (c) 2002-2024, Simon J. Gerraty
+#
+# This file is provided in the hope that it will
+# be of use. There is absolutely NO WARRANTY.
+# Permission to copy, redistribute or otherwise
+# use this file is hereby granted provided that
+# the above copyright notice and this notice are
+# left intact.
+#
+# Please send copies of changes and bug-fixes to:
+# sjg@crufty.net
-# $NetBSD: bsd.subdir.mk,v 1.11 1996/04/04 02:05:06 jtc Exp $
-# @(#)bsd.subdir.mk 5.9 (Berkeley) 2/1/91
+# if SUBDIR=@auto replace that with each subdir that has
+# a [Mm]akefile.
+#
+# Unless SUBDIR_MUST_EXIST is defined, missing subdirs
+# are ignored (to allow for sparse checkout).
+#
+# If you use _SUBDIRUSE for a target you may need to add it to
+# SUBDIR_TARGETS.
-.if ${.MAKE.LEVEL} == 0 && ${.MAKE.MODE:Uno:Mmeta*} != ""
+# should be set properly in sys.mk
+_this ?= ${.PARSEFILE:S,bsd.,,}
+
+.if !target(__${_this}__)
+__${_this}__: .NOTMAIN
+
+.if defined(SUBDIR)
+
+.if ${.MAKE.LEVEL} == 0 && ${MK_DIRDEPS_BUILD:Uno} == "yes"
.include <meta.subdir.mk>
# keep everyone happy
_SUBDIRUSE:
.elif !commands(_SUBDIRUSE) && !defined(NO_SUBDIR) && !defined(NOSUBDIR)
-.-include <${.CURDIR}/Makefile.inc>
+.-include <local.subdir.mk>
.if !target(.MAIN)
.MAIN: all
.endif
ECHO_DIR ?= echo
.ifdef SUBDIR_MUST_EXIST
-MISSING_DIR=echo "Missing ===> ${.CURDIR}/$${entry}"; exit 1
+MISSING_DIR=echo "Missing ===> ${.CURDIR}/$$_dir"; exit 1
.else
-MISSING_DIR=echo "Skipping ===> ${.CURDIR}/$${entry}"; continue
+MISSING_DIR=echo "Skipping ===> ${.CURDIR}/$$_dir"; exit 0
.endif
-_SUBDIRUSE: .USE
-.if defined(SUBDIR)
+# the actual implementation
+# our target should be of the form ${_target}-${_dir}
+_SUBDIR_USE: .USE
@Exists() { test -f $$1; }; \
- for entry in ${SUBDIR}; do \
- (set -e; \
- if Exists ${.CURDIR}/$${entry}.${MACHINE}/[mM]akefile; then \
- _newdir_="$${entry}.${MACHINE}"; \
- elif Exists ${.CURDIR}/$${entry}/[mM]akefile; then \
- _newdir_="$${entry}"; \
- else \
- ${MISSING_DIR}; \
- fi; \
- if test X"${_THISDIR_}" = X""; then \
- _nextdir_="$${_newdir_}"; \
- else \
- _nextdir_="$${_THISDIR_}/$${_newdir_}"; \
- fi; \
- ${ECHO_DIR} "===> $${_nextdir_}"; \
- cd ${.CURDIR}/$${_newdir_}; \
- ${.MAKE} _THISDIR_="$${_nextdir_}" \
- ${.TARGET:S/realinstall/install/:S/.depend/depend/}) || exit 1; \
- done
-
-${SUBDIR}::
- @set -e; _r=${.CURDIR}/; \
- if test -z "${.TARGET:M/*}"; then \
- if test -d ${.CURDIR}/${.TARGET}.${MACHINE}; then \
- _newdir_=${.TARGET}.${MACHINE}; \
- else \
- _newdir_=${.TARGET}; \
- fi; \
+ _dir=${.TARGET:C/^.*-//} \
+ _target=${.TARGET:C/-.*//:S/real//:S/.depend/depend/}; \
+ if ! Exists ${.CURDIR}/$$_dir/[mM]akefile; then \
+ ${MISSING_DIR}; \
+ fi; \
+ if test X"${_THISDIR_}" = X""; then \
+ _nextdir_="$$_dir"; \
else \
- _r= _newdir_=${.TARGET}; \
+ _nextdir_="$${_THISDIR_}/$$_dir"; \
fi; \
- ${ECHO_DIR} "===> $${_newdir_}"; \
- cd $${_r}$${_newdir_}; \
- ${.MAKE} _THISDIR_="$${_newdir_}" all
-.endif
+ ${ECHO_DIR} "===> $${_nextdir_} ($$_target)"; \
+ (cd ${.CURDIR}/$$_dir && \
+ ${.MAKE} _THISDIR_="$${_nextdir_}" $$_target)
.if !target(install)
.if !target(beforeinstall)
@@ -73,26 +79,53 @@ afterinstall: realinstall
realinstall: beforeinstall _SUBDIRUSE
.endif
-.if defined(SRCS)
-etags: ${SRCS}
- -cd ${.CURDIR}; etags `echo ${.ALLSRC:N*.h} | sed 's;${.CURDIR}/;;'`
-.endif
+# the interface from others
+# this may require additions to SUBDIR_TAREGTS
+_SUBDIRUSE: .USE subdir-${.TARGET:C/-.*//:S/real//:S/.depend/depend/}
SUBDIR_TARGETS += \
all \
clean \
cleandir \
includes \
+ install \
depend \
lint \
obj \
tags \
etags
+.if ${SUBDIR} == "@auto"
+SUBDIR = ${echo ${.CURDIR}/*/[Mm]akefile:L:sh:H:T:O:N\*}
+.endif
+
+__subdirs =
+.for d in ${SUBDIR}
+.if $d != ".WAIT" && exists(${.CURDIR}/$d.${MACHINE})
+__subdirs += $d.${MACHINE}
+.else
+__subdirs += $d
+.endif
+.endfor
+
.for t in ${SUBDIR_TARGETS:O:u}
-$t: _SUBDIRUSE
+__subdir_$t =
+.for d in ${__subdirs}
+.if $d == ".WAIT"
+__subdir_$t += $d
+.elif !commands($t-$d)
+$t-$d: .PHONY .MAKE _SUBDIR_USE
+__subdir_$t += $t-$d
+.endif
+.endfor
+subdir-$t: .PHONY ${__subdir_$t}
+$t: subdir-$t
.endfor
+.else
+_SUBDIRUSE:
+.endif # SUBDIR
+
.include <own.mk>
.if make(destroy*)
.include <obj.mk>
@@ -100,3 +133,5 @@ $t: _SUBDIRUSE
.endif
# make sure this exists
all:
+
+.endif
diff --git a/contrib/bmake/mk/suffixes.mk b/contrib/bmake/mk/suffixes.mk
new file mode 100644
index 000000000000..4c4c85e68220
--- /dev/null
+++ b/contrib/bmake/mk/suffixes.mk
@@ -0,0 +1,195 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: suffixes.mk,v 1.3 2024/02/17 17:26:57 sjg Exp $
+#
+# @(#) Copyright (c) 2024, Simon J. Gerraty
+#
+# This file is provided in the hope that it will
+# be of use. There is absolutely NO WARRANTY.
+# Permission to copy, redistribute or otherwise
+# use this file is hereby granted provided that
+# the above copyright notice and this notice are
+# left intact.
+#
+# Please send copies of changes and bug-fixes to:
+# sjg@crufty.net
+#
+
+# some reasonable defaults
+.SUFFIXES: .out .a .ln .o ${PICO} ${PCM} .s .S .c ${CXX_SUFFIXES} \
+ ${CCM_SUFFIXES} .F .f .r .y .l .cl .p .h \
+ .sh .m4 .cpp-out
+
+#
+AFLAGS ?=
+ARFLAGS ?= r
+.if ${MACHINE_ARCH} == "sparc64"
+AFLAGS+= -Wa,-Av9a
+.endif
+AS ?= as
+CC ?= cc
+CFLAGS ?= ${DBG}
+CXX ?= c++
+CXXFLAGS ?= ${CFLAGS}
+CXXFLAGS ?= ${CFLAGS}
+DBG ?= -O2
+FC ?= f77
+FFLAGS ?= -O
+INSTALL ?= install
+LD ?= ld
+LEX ?= lex
+LFLAGS ?=
+NM ?= nm
+OBJC ?= ${CC}
+OBJCFLAGS ?= ${CFLAGS}
+PC ?= pc
+PFLAGS ?=
+RFLAGS ?=
+SIZE ?= size
+YACC ?= yacc
+YFLAGS ?=
+
+COMPILE.s ?= ${CC} ${AFLAGS} -c
+LINK.s ?= ${CC} ${AFLAGS} ${LDFLAGS}
+COMPILE.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} -c -traditional-cpp
+LINK.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS}
+COMPILE.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} -c
+LINK.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS}
+COMPILE.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} -c
+COMPILE.pcm ?= ${COMPILE.cc:N-c} --precompile -c
+LINK.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS}
+COMPILE.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} -c
+LINK.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} ${LDFLAGS}
+COMPILE.f ?= ${FC} ${FFLAGS} -c
+LINK.f ?= ${FC} ${FFLAGS} ${LDFLAGS}
+COMPILE.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} -c
+LINK.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} ${LDFLAGS}
+COMPILE.r ?= ${FC} ${FFLAGS} ${RFLAGS} -c
+LINK.r ?= ${FC} ${FFLAGS} ${RFLAGS} ${LDFLAGS}
+LEX.l ?= ${LEX} ${LFLAGS}
+COMPILE.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} -c
+LINK.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS}
+YACC.y ?= ${YACC} ${YFLAGS}
+LEX.l ?= ${LEX} ${LFLAGS}
+
+# C
+.c:
+ ${LINK.c} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
+.c.o:
+ ${COMPILE.c} ${.IMPSRC}
+.c.a:
+ ${COMPILE.c} ${.IMPSRC}
+ ${AR} ${ARFLAGS} $@ $*.o
+ rm -f $*.o
+.c.ln:
+ ${LINT} ${LINTFLAGS} ${CPPFLAGS:M-[IDU]*} -i ${.IMPSRC}
+
+# C++
+${CXX_SUFFIXES}:
+ ${LINK.cc} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
+${CXX_SUFFIXES:%=%.o}:
+ ${COMPILE.cc} ${.IMPSRC}
+${CXX_SUFFIXES:%=%.a}:
+ ${COMPILE.cc} ${.IMPSRC}
+ ${AR} ${ARFLAGS} $@ $*.o
+ rm -f $*.o
+
+# C++ precompiled modules
+${CCM_SUFFIXES:%=%${PCM}}:
+ @${COMPILE.pcm} ${.IMPSRC}
+
+# Fortran/Ratfor
+.f:
+ ${LINK.f} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
+.f.o:
+ ${COMPILE.f} ${.IMPSRC}
+.f.a:
+ ${COMPILE.f} ${.IMPSRC}
+ ${AR} ${ARFLAGS} $@ $*.o
+ rm -f $*.o
+
+.F:
+ ${LINK.F} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
+.F.o:
+ ${COMPILE.F} ${.IMPSRC}
+.F.a:
+ ${COMPILE.F} ${.IMPSRC}
+ ${AR} ${ARFLAGS} $@ $*.o
+ rm -f $*.o
+
+.r:
+ ${LINK.r} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
+.r.o:
+ ${COMPILE.r} ${.IMPSRC}
+.r.a:
+ ${COMPILE.r} ${.IMPSRC}
+ ${AR} ${ARFLAGS} $@ $*.o
+ rm -f $*.o
+
+# Pascal
+.p:
+ ${LINK.p} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
+.p.o:
+ ${COMPILE.p} ${.IMPSRC}
+.p.a:
+ ${COMPILE.p} ${.IMPSRC}
+ ${AR} ${ARFLAGS} $@ $*.o
+ rm -f $*.o
+
+# Assembly
+.s:
+ ${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
+.s.o:
+ ${COMPILE.s} ${.IMPSRC}
+.s.a:
+ ${COMPILE.s} ${.IMPSRC}
+ ${AR} ${ARFLAGS} $@ $*.o
+ rm -f $*.o
+.S:
+ ${LINK.S} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
+.S.o:
+ ${COMPILE.S} ${.IMPSRC}
+.S.a:
+ ${COMPILE.S} ${.IMPSRC}
+ ${AR} ${ARFLAGS} $@ $*.o
+ rm -f $*.o
+
+# Lex
+.l:
+ ${LEX.l} ${.IMPSRC}
+ ${LINK.c} -o ${.TARGET} lex.yy.c ${LDLIBS} -ll
+ rm -f lex.yy.c
+.l.c:
+ ${LEX.l} ${.IMPSRC}
+ mv lex.yy.c ${.TARGET}
+.l.o:
+ ${LEX.l} ${.IMPSRC}
+ ${COMPILE.c} -o ${.TARGET} lex.yy.c
+ rm -f lex.yy.c
+
+# Yacc
+.y:
+ ${YACC.y} ${.IMPSRC}
+ ${LINK.c} -o ${.TARGET} y.tab.c ${LDLIBS}
+ rm -f y.tab.c
+.y.c:
+ ${YACC.y} ${.IMPSRC}
+ mv y.tab.c ${.TARGET}
+.y.o:
+ ${YACC.y} ${.IMPSRC}
+ ${COMPILE.c} -o ${.TARGET} y.tab.c
+ rm -f y.tab.c
+
+# Shell
+.sh:
+ rm -f ${.TARGET}
+ cp ${.IMPSRC} ${.TARGET}
+ chmod a+x ${.TARGET}
+
+
+# this often helps with debugging
+.c.cpp-out:
+ @${COMPILE.c:N-c} -E ${.IMPSRC} | grep -v '^[ ]*$$'
+
+${CXX_SUFFIXES:%=%.cpp-out}:
+ @${COMPILE.cc:N-c} -E ${.IMPSRC} | grep -v '^[ ]*$$'
diff --git a/contrib/bmake/mk/sys.clean-env.mk b/contrib/bmake/mk/sys.clean-env.mk
index 88d32cb3c6e9..6cdb42718da0 100644
--- a/contrib/bmake/mk/sys.clean-env.mk
+++ b/contrib/bmake/mk/sys.clean-env.mk
@@ -1,4 +1,6 @@
-# $Id: sys.clean-env.mk,v 1.23 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: sys.clean-env.mk,v 1.26 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2009, Simon J. Gerraty
#
@@ -52,7 +54,7 @@ MAKE_ENV_SAVE_PREFIX_LIST += \
# This could be a list of vars or patterns to explicitly exclude.
-MAKE_ENV_SAVE_EXCLUDE_LIST ?= _
+MAKE_ENV_SAVE_EXCLUDE_LIST += _
# This is the actual list that we will save
# HOME is probably something worth clobbering eg.
@@ -68,7 +70,7 @@ MAKE_ENV_SAVE_VAR_LIST += \
USER \
${_env_vars:${MAKE_ENV_SAVE_EXCLUDE_LIST:${M_ListToSkip}}}
-_env_vars != env | egrep '^(${MAKE_ENV_SAVE_PREFIX_LIST:ts|})' | sed 's,=.*,,'; echo
+_env_vars != env | ${EGREP:Uegrep} '^(${MAKE_ENV_SAVE_PREFIX_LIST:ts|})' | sed 's,=.*,,'; echo
_export_list =
.for v in ${MAKE_ENV_SAVE_VAR_LIST:O:u}
@@ -115,7 +117,7 @@ MAKEOBJDIR = $${.CURDIR:S,${_srctop},$${OBJTOP},}
$v := ${$v}
.endfor
.else
-# we cannot use the '$$' trick, anymore
+# we cannot rely on the '$$' trick (depending on .MAKE.SAVE_DOLLARS)
# but we can export a literal (unexpanded) value
SRCTOP := ${_srctop}
OBJROOT := ${_objroot}
@@ -125,6 +127,6 @@ MAKEOBJDIR = ${.CURDIR:S,${SRCTOP},${OBJTOP},}
.endif
#.info ${_tricky_env_vars:@v@${.newline}$v=${$v}@}
#showenv:
-# @env | egrep 'OBJ|SRC'
+# @env | ${EGREP:Uegrep} 'OBJ|SRC'
.endif # MAKEOBJDIR
.endif # level 0
diff --git a/contrib/bmake/mk/sys.debug.mk b/contrib/bmake/mk/sys.debug.mk
index 7fde27c24fdb..3b253ad73ed4 100644
--- a/contrib/bmake/mk/sys.debug.mk
+++ b/contrib/bmake/mk/sys.debug.mk
@@ -1,4 +1,6 @@
-# $Id: sys.debug.mk,v 1.2 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: sys.debug.mk,v 1.3 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2009, Simon J. Gerraty
#
diff --git a/contrib/bmake/mk/sys.dependfile.mk b/contrib/bmake/mk/sys.dependfile.mk
index 7c1fd94d3eb8..3c13b1c92bff 100644
--- a/contrib/bmake/mk/sys.dependfile.mk
+++ b/contrib/bmake/mk/sys.dependfile.mk
@@ -1,6 +1,8 @@
-# $Id: sys.dependfile.mk,v 1.9 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
#
-# @(#) Copyright (c) 2012, Simon J. Gerraty
+# $Id: sys.dependfile.mk,v 1.11 2024/02/17 17:26:57 sjg Exp $
+#
+# @(#) Copyright (c) 2012-2023, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@@ -13,7 +15,10 @@
# sjg@crufty.net
#
-# This only makes sense in meta mode.
+.if !target(__${.PARSEFILE}__)
+__${.PARSEFILE}__: .NOTMAIN
+
+# This only makes sense for DIRDEPS_BUILD.
# This allows a mixture of auto generated as well as manually edited
# dependency files, which can be differentiated by their names.
# As per dirdeps.mk we only require:
@@ -57,3 +62,5 @@ MACHINE := ${_m}
.endif
.endif
.MAKE.DEPENDFILE ?= ${.MAKE.DEPENDFILE_DEFAULT}
+
+.endif
diff --git a/contrib/bmake/mk/sys.dirdeps.mk b/contrib/bmake/mk/sys.dirdeps.mk
new file mode 100644
index 000000000000..4d2dfa8416fa
--- /dev/null
+++ b/contrib/bmake/mk/sys.dirdeps.mk
@@ -0,0 +1,205 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: sys.dirdeps.mk,v 1.15 2024/04/18 17:18:31 sjg Exp $
+#
+# @(#) Copyright (c) 2012-2023, Simon J. Gerraty
+#
+# This file is provided in the hope that it will
+# be of use. There is absolutely NO WARRANTY.
+# Permission to copy, redistribute or otherwise
+# use this file is hereby granted provided that
+# the above copyright notice and this notice are
+# left intact.
+#
+# Please send copies of changes and bug-fixes to:
+# sjg@crufty.net
+#
+
+# Originally DIRDEPS_BUILD and META_MODE were the same thing.
+# So, much of this was done in *meta.sys.mk and local*mk
+# but properly belongs here.
+
+# Include from [local.]sys.mk - if doing DIRDEPS_BUILD
+# we should not be here otherwise
+MK_DIRDEPS_BUILD ?= yes
+# these are all implied
+MK_AUTO_OBJ ?= yes
+MK_META_MODE ?= yes
+MK_STAGING ?= yes
+
+_PARSEDIR ?= ${.PARSEDIR:tA}
+
+.-include <local.sys.dirdeps.env.mk>
+
+.if ${.MAKE.LEVEL} == 0
+# make sure dirdeps target exists and do it first
+# init.mk will set .MAIN to 'dirdeps' if appropriate
+# as will dirdeps-targets.mk for top-level builds.
+# This allows a Makefile to have more control.
+dirdeps:
+.NOPATH: dirdeps
+all: dirdeps .WAIT
+.endif
+
+.if empty(SRCTOP)
+# fallback assumes share/mk!
+SRCTOP := ${SB_SRC:U${.PARSEDIR:tA:H:H}}
+.export SRCTOP
+.endif
+
+# fake SB if not using mk wrapper
+# SB documented at http://www.crufty.net/sjg/docs/sb-tools.htm
+.if !defined(SB)
+SB := ${SRCTOP:H}
+.export SB
+.endif
+
+.if empty(OBJROOT)
+OBJROOT := ${SB_OBJROOT:U${MAKEOBJDIRPREFIX:U${SB}/obj}/}
+.export OBJROOT
+.endif
+# we expect OBJROOT to end with / (- can work too)
+.if ${OBJROOT:M*[/-]} == ""
+OBJROOT := ${OBJROOT}/
+.endif
+
+.if empty(STAGE_ROOT)
+STAGE_ROOT ?= ${OBJROOT}stage
+.export STAGE_ROOT
+.endif
+
+# We should be included before meta.sys.mk
+# If TARGET_SPEC_VARS is other than just MACHINE
+# it should be set by now.
+# TARGET_SPEC must not contain any '.'s.
+TARGET_SPEC_VARS ?= MACHINE
+
+.if ${TARGET_SPEC:Uno:M*,*} != ""
+# deal with TARGET_SPEC from env
+_tspec := ${TARGET_SPEC:S/,/ /g}
+.for i in ${TARGET_SPEC_VARS:${M_RANGE:Urange}}
+${TARGET_SPEC_VARS:[$i]} := ${_tspec:[$i]}
+.endfor
+# We need to stop that TARGET_SPEC affecting any submakes
+TARGET_SPEC=
+# so export but do not track
+.export-env TARGET_SPEC
+.export ${TARGET_SPEC_VARS}
+.for v in ${TARGET_SPEC_VARS:O:u}
+.if empty($v)
+.undef $v
+.endif
+.endfor
+.endif
+
+# Now make sure we know what TARGET_SPEC is
+# as we may need it to find Makefile.depend*
+.if ${MACHINE:Mhost*} != ""
+# host is special
+TARGET_SPEC = ${MACHINE}
+.else
+TARGET_SPEC = ${TARGET_SPEC_VARS:@v@${$v:U}@:ts,}
+.endif
+
+.if ${TARGET_SPEC_VARS:[#]} > 1
+TARGET_SPEC_VARSr := ${TARGET_SPEC_VARS:[-1..1]}
+# alternatives might be
+# TARGET_OBJ_SPEC = ${TARGET_SPEC_VARSr:@v@${$v:U}@:ts/}
+# TARGET_OBJ_SPEC = ${TARGET_SPEC_VARS:@v@${$v:U}@:ts/}
+TARGET_OBJ_SPEC ?= ${TARGET_SPEC_VARS:@v@${$v:U}@:ts.}
+.else
+TARGET_OBJ_SPEC ?= ${MACHINE}
+.endif
+
+MAKE_PRINT_VAR_ON_ERROR += ${TARGET_SPEC_VARS}
+
+.if !defined(MACHINE0)
+# it can be handy to know which MACHINE kicked off the build
+# for example, if using Makefild.depend for multiple machines,
+# allowing only MACHINE0 to update can keep things simple.
+MACHINE0 := ${MACHINE}
+.export MACHINE0
+.endif
+
+MACHINE_OBJ.host = ${HOST_TARGET}
+MACHINE_OBJ.host32 = ${HOST_TARGET32}
+MACHINE_OBJ.${MACHINE} ?= ${TARGET_OBJ_SPEC}
+MACHINE_OBJDIR = ${MACHINE_OBJ.${MACHINE}}
+
+# we likely want to override env for OBJTOP
+.if ${MACHINE} == "host"
+OBJTOP = ${HOST_OBJTOP}
+.elif ${MACHINE} == "host32"
+OBJTOP = ${HOST_OBJTOP32}
+.else
+OBJTOP = ${OBJROOT}${MACHINE_OBJDIR}
+.endif
+.if ${.MAKE.LEVEL} > 0
+# should not change from level 1 onwards
+# this only matters for cases like bmake/unit-tests
+# where we do ${MAKE} -r
+.export OBJTOP
+.endif
+
+.if ${MAKEOBJDIR:U:M*/*} == ""
+# we do not use MAKEOBJDIRPREFIX
+# though we may have used it above to initialize OBJROOT
+.undef MAKEOBJDIRPREFIX
+# this is what we expected in env
+MAKEOBJDIR = $${.CURDIR:S,^$${SRCTOP},$${OBJTOP},}
+# export that but do not track
+.export-env MAKEOBJDIR
+# this what we need here
+MAKEOBJDIR = ${.CURDIR:S,${SRCTOP},${OBJTOP},}
+.endif
+
+STAGE_MACHINE ?= ${MACHINE_OBJDIR}
+STAGE_OBJTOP ?= ${STAGE_ROOT}/${STAGE_MACHINE}
+STAGE_COMMON_OBJTOP ?= ${STAGE_ROOT}/common
+STAGE_HOST_OBJTOP ?= ${STAGE_ROOT}/${HOST_TARGET}
+STAGE_HOST_OBJTOP32 ?= ${STAGE_ROOT}/${HOST_TARGET32}
+
+STAGE_INCLUDEDIR ?= ${STAGE_OBJTOP}${INCLUDEDIR:U/usr/include}
+STAGE_LIBDIR ?= ${STAGE_OBJTOP}${LIBDIR:U/lib}
+
+TIME_STAMP_FMT ?= @ %s [%Y-%m-%d %T] ${:U}
+DATE_TIME_STAMP ?= `date '+${TIME_STAMP_FMT}'`
+TIME_STAMP ?= ${TIME_STAMP_FMT:localtime}
+
+.if ${MK_TIME_STAMPS:Uyes} == "yes"
+TRACER = ${TIME_STAMP}
+ECHO_DIR = echo ${TIME_STAMP}
+ECHO_TRACE = echo ${TIME_STAMP}
+.endif
+
+.if ${.CURDIR} == ${SRCTOP}
+RELDIR= .
+RELTOP= .
+.elif ${.CURDIR:M${SRCTOP}/*}
+RELDIR:= ${.CURDIR:S,${SRCTOP}/,,}
+.else
+RELDIR:= ${.OBJDIR:S,${OBJTOP}/,,}
+.endif
+RELTOP?= ${RELDIR:C,[^/]+,..,g}
+RELOBJTOP?= ${RELTOP}
+RELSRCTOP?= ${RELTOP}
+
+# this does all the smarts of setting .MAKE.DEPENDFILE
+.-include <sys.dependfile.mk>
+
+.-include <local.sys.dirdeps.mk>
+
+# check if we got anything sane
+.if ${.MAKE.DEPENDFILE} == ".depend"
+.undef .MAKE.DEPENDFILE
+.endif
+# just in case
+.MAKE.DEPENDFILE ?= Makefile.depend
+
+# Makefile.depend* often refer to DEP_MACHINE etc,
+# we need defaults for both first include in a leaf dir
+# and when level > 0
+# so ensure DEP_* for TARGET_SPEC_VARS and RELDIR are set
+.for V in ${TARGET_SPEC_VARS} RELDIR
+DEP_$V ?= ${$V}
+.endfor
diff --git a/contrib/bmake/mk/sys.mk b/contrib/bmake/mk/sys.mk
index 7ef8f724ef10..b950d991e321 100644
--- a/contrib/bmake/mk/sys.mk
+++ b/contrib/bmake/mk/sys.mk
@@ -1,6 +1,8 @@
-# $Id: sys.mk,v 1.52 2020/12/22 20:44:24 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
#
-# @(#) Copyright (c) 2003-2009, Simon J. Gerraty
+# $Id: sys.mk,v 1.60 2024/02/17 17:26:57 sjg Exp $
+#
+# @(#) Copyright (c) 2003-2023, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@@ -15,6 +17,9 @@
# Avoid putting anything platform specific in here.
+# just in case we are an older bmake
+.MAKE.OS ?= ${HOST_OS}
+
# _DEBUG_MAKE_FLAGS etc.
.include <sys.debug.mk>
@@ -43,13 +48,24 @@ _TARGETS := ${.TARGETS}
# Popular suffixes for C++
CXX_SUFFIXES += .cc .cpp .cxx .C
CXX_SUFFIXES := ${CXX_SUFFIXES:O:u}
+# and C++ Modules
+CCM_SUFFIXES += .ccm
+CCM_SUFFIXES := ${CCM_SUFFIXES:O:u}
+# precompiled modules
+PCM ?= .pcm
+
+SYS_MK ?= ${.PARSEDIR:tA}/${.PARSEFILE}
+SYS_MK := ${SYS_MK}
+
+# for systems that have an incompatible install
+INSTALL_SH ?= ${SYS_MK:H}/install-sh
# find the OS specifics
.if defined(SYS_OS_MK)
.include <${SYS_OS_MK}>
.else
_sys_mk =
-.for x in ${HOST_OSTYPE} ${HOST_TARGET} ${HOST_OS} ${MACHINE} Generic
+.for x in ${HOST_TARGET} ${.MAKE.OS} ${.MAKE.OS:S,64,,} ${HOST_OSTYPE} ${MACHINE} Generic
.if empty(_sys_mk)
.-include <sys/$x.mk>
_sys_mk := ${.MAKE.MAKEFILES:M*/$x.mk}
@@ -62,12 +78,18 @@ _sys_mk := sys/${_sys_mk:T}
.-include <$x.sys.mk>
_sys_mk := ${.MAKE.MAKEFILES:M*/$x.sys.mk:T}
.endif
+.if !empty(_sys_mk) && ${MAKE_VERSION} >= 20220924
+.break
+.endif
.endfor
SYS_OS_MK := ${_sys_mk}
.export SYS_OS_MK
.endif
+# some sys/ may have set this to grep -E
+EGREP ?= egrep
+
# some options we need to know early
OPTIONS_DEFAULT_NO += \
DIRDEPS_BUILD \
@@ -81,11 +103,13 @@ OPTIONS_DEFAULT_DEPENDENT += \
.-include <options.mk>
+# :Uno incase options.mk not installed
.if ${MK_DIRDEPS_BUILD:Uno} == "yes"
-MK_META_MODE = yes
+.-include <sys.dirdeps.mk>
+.endif
+.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
@@ -111,7 +135,7 @@ MACHINE_ARCH = ${MACHINE_ARCH.${MACHINE}}
.endif
.ifndef ROOT_GROUP
-ROOT_GROUP != sed -n /:0:/s/:.*//p /etc/group
+ROOT_GROUP != sed -n '/:0:/{s/:.*//p;q;}' /etc/group
.export ROOT_GROUP
.endif
@@ -135,14 +159,8 @@ Mkdirs= Mkdirs() { \
mkdir $$d || exit $$?; \
done; }
-# this often helps with debugging
-.SUFFIXES: .cpp-out
-
-.c.cpp-out:
- @${COMPILE.c:N-c} -E ${.IMPSRC} | grep -v '^[ ]*$$'
-
-${CXX_SUFFIXES:%=%.cpp-out}:
- @${COMPILE.cc:N-c} -E ${.IMPSRC} | grep -v '^[ ]*$$'
+# pick up generic suffix rules
+.include <suffixes.mk>
# late customizations
.-include <local.sys.mk>
diff --git a/contrib/bmake/mk/sys.vars.mk b/contrib/bmake/mk/sys.vars.mk
index 592cbdc644dc..d8e0dd4f2930 100644
--- a/contrib/bmake/mk/sys.vars.mk
+++ b/contrib/bmake/mk/sys.vars.mk
@@ -1,6 +1,8 @@
-# $Id: sys.vars.mk,v 1.6 2020/10/28 20:50:04 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
#
-# @(#) Copyright (c) 2003-2009, Simon J. Gerraty
+# $Id: sys.vars.mk,v 1.16 2024/02/17 17:26:57 sjg Exp $
+#
+# @(#) Copyright (c) 2003-2023, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@@ -17,15 +19,10 @@
# It relies on the fact that conditionals and dependencies are resolved
# at the time they are read.
#
-# _this ?= ${.PARSEFILE}
+# _this ?= ${.PARSEDIR:tA}/${.PARSEFILE}
# .if !target(__${_this}__)
-# __${_this}__:
+# __${_this}__: .NOTMAIN
#
-.if ${MAKE_VERSION:U0} > 20100408
-_this = ${.PARSEDIR:tA}/${.PARSEFILE}
-.else
-_this = ${.PARSEDIR}/${.PARSEFILE}
-.endif
# if this is an ancient version of bmake
MAKE_VERSION ?= 0
@@ -34,12 +31,18 @@ MAKE_VERSION ?= 0
MAKE_VERSION := ${MAKE_VERSION:[1]:C,.*-,,}
.endif
+.if ${MAKE_VERSION} < 20100414
+_this = ${.PARSEDIR}/${.PARSEFILE}
+.else
+_this = ${.PARSEDIR:tA}/${.PARSEFILE}
+.endif
+
# some useful modifiers
# A useful trick for testing multiple :M's against something
# :L says to use the variable's name as its value - ie. literal
# got = ${clean* destroy:${M_ListToMatch:S,V,.TARGETS,}}
-M_ListToMatch = L:@m@$${V:M$$m}@
+M_ListToMatch = L:@m@$${V:U:M$$m}@
# match against our initial targets (see above)
M_L_TARGETS = ${M_ListToMatch:S,V,_TARGETS,}
@@ -57,43 +60,83 @@ _type_sh = which
M_type = @x@(${_type_sh:Utype} $$x) 2> /dev/null; echo;@:sh:[0]:N* found*:[@]:C,[()],,g
M_whence = ${M_type}:M/*:[1]
-# produce similar output to jot(1)
+# produce similar output to jot(1) or seq(1)
# eg. ${LIST:[#]:${M_JOT}}
# would be 1 2 3 4 5 if LIST has 5 words
# ${9:L:${M_JOT}}
# would be 1 2 3 4 5 6 7 8 9
-M_JOT = @x@i=1;while [ $$$$i -le $$x ]; do echo $$$$i; i=$$$$((i + 1)); done;@:sh
+.if ${.MAKE.LEVEL} == 0
+.for x in jot seq
+.if empty(JOT_CMD)
+JOT_CMD := ${$x:L:${M_whence}}
+.endif
+.endfor
+.if !empty(JOT_CMD)
+.export JOT_CMD
+.endif
+.endif
+.if !empty(JOT_CMD)
+M_JOT = [1]:S,^,${JOT_CMD} ,:sh
+.else
+M_JOT = [1]:@x@i=1;while [ $$$$i -le $$x ]; do echo $$$$i; i=$$$$((i + 1)); done;@:sh
+.endif
# ${LIST:${M_RANGE}} is 1 2 3 4 5 if LIST has 5 words
-.if ${MAKE_VERSION} >= 20170130
-M_RANGE = range
-.else
+.if ${MAKE_VERSION} < 20170130
M_RANGE = [#]:${M_JOT}
+.else
+M_RANGE = range
.endif
# convert a path to a valid shell variable
M_P2V = tu:C,[./-],_,g
# convert path to absolute
-.if ${MAKE_VERSION:U0} > 20100408
-M_tA = tA
-.else
+.if ${MAKE_VERSION} < 20100414
M_tA = C,.*,('cd' & \&\& 'pwd') 2> /dev/null || echo &,:sh
+.else
+M_tA = tA
.endif
-.if ${MAKE_VERSION:U0} >= 20170130
+# absoulte path to what we are reading.
+_PARSEDIR = ${.PARSEDIR:${M_tA}}
+
+.if ${MAKE_VERSION} >= 20170130
# M_cmpv allows comparing dotted versions like 3.1.2
# ${3.1.2:L:${M_cmpv}} -> 3001002
# we use big jumps to handle 3 digits per dot:
# ${123.456.789:L:${M_cmpv}} -> 123456789
-M_cmpv.units = 1 1000 1000000
-M_cmpv = S,., ,g:_:range:@i@+ $${_:[-$$i]} \* $${M_cmpv.units:[$$i]}@:S,^,expr 0 ,1:sh
+M_cmpv.units = 1 1000 1000000 1000000000 1000000000000
+M_cmpv = S,., ,g:C,^0*([0-9]),\1,:_:range:@i@+ $${_:[-$$i]} \* $${M_cmpv.units:[$$i]}@:S,^,expr 0 ,1:sh
.endif
-# absoulte path to what we are reading.
-_PARSEDIR = ${.PARSEDIR:${M_tA}}
-
# many projects use MAJOR MINOR PATCH versioning
# ${OPENSSL:${M_M.M.P_VERSION}} is equivalent to
# ${OPENSSL_MAJOR_VERSION}.${OPENSSL_MINOR_VERSION}.${OPENSSL_PATCH_VERSION}
M_M.M.P_VERSION = L:@v@$${MAJOR MINOR PATCH:L:@t@$${$$v_$$t_VERSION:U0}@}@:ts.
+
+# numeric sort
+.if ${MAKE_VERSION} < 20210803
+M_On = O
+M_Onr = O
+.else
+M_On = On
+M_Onr = Onr
+.endif
+
+# Index of a word in a list.
+# eg. ${LIST:${M_Index:S,K,key,}} is the index of
+# the word "key" in ${LIST}, of course any pattern can be used.
+# If "key" appears more than once, there will be multiple
+# index values use ${M_Index:S,K,key,}:[1] to select only the first.
+M_Index = _:${M_RANGE}:@i@$${"$${_:[$$i]:MK}":?$$i:}@
+
+# mtime of each word - assumed to be a valid pathname
+.if ${.MAKE.LEVEL} < 20230510
+M_mtime = tW:S,^,${STAT:Ustat} -f %m ,:sh
+.else
+# M_mtime_fallback can be =error to throw an error
+# or =0 to use 0, default is to use current time
+M_mtime = mtime${M_mtime_fallback:U}
+.endif
+
diff --git a/contrib/bmake/mk/sys/AIX.mk b/contrib/bmake/mk/sys/AIX.mk
index d591385be603..7415677e4bb7 100644
--- a/contrib/bmake/mk/sys/AIX.mk
+++ b/contrib/bmake/mk/sys/AIX.mk
@@ -14,7 +14,7 @@ NOPIC ?=no # no shared libs?
.LIBS: .a
AR ?= ar
-ARFLAGS ?= rl
+ARFLAGS ?= r
RANLIB ?= ranlib
AS ?= as
@@ -182,3 +182,4 @@ ${CXX_SUFFIXES:%=%.a}:
.sh:
rm -f ${.TARGET}
cp ${.IMPSRC} ${.TARGET}
+ chmod a+x ${.TARGET}
diff --git a/contrib/bmake/mk/sys/Cygwin.mk b/contrib/bmake/mk/sys/Cygwin.mk
new file mode 100644
index 000000000000..ffc479ad9ef3
--- /dev/null
+++ b/contrib/bmake/mk/sys/Cygwin.mk
@@ -0,0 +1,21 @@
+# Minimal adjustments for Cygwin
+# SPDX-License-Identifier: BSD-2-Clause
+
+OS ?= Cygwin
+unix ?= We run ${OS}.
+
+.ifndef ROOT_GROUP
+# Cygwin maps local admin SID S-1-5-32-544 to GID 544.
+# /etc/group does no longer exist in a base installation.
+ROOT_GROUP != /usr/bin/getent group 544 2>/dev/null
+ROOT_GROUP := ${ROOT_GROUP:C,:.*$,,}
+.endif
+
+.LIBS: .a
+
+AR ?= ar
+RANLIB ?= ranlib
+TSORT ?= tsort -q
+
+# egrep is deprecated
+EGREP ?= grep -E
diff --git a/contrib/bmake/mk/sys/Darwin.mk b/contrib/bmake/mk/sys/Darwin.mk
index 06918a11a4ad..953a64d82728 100644
--- a/contrib/bmake/mk/sys/Darwin.mk
+++ b/contrib/bmake/mk/sys/Darwin.mk
@@ -14,7 +14,7 @@ HOST_LIBEXT ?= .dylib
DSHLIBEXT ?= .dylib
AR ?= ar
-ARFLAGS ?= rl
+ARFLAGS ?= r
RANLIB =
AS ?= as
@@ -220,3 +220,4 @@ ${CXX_SUFFIXES:%=%.a}:
.sh:
rm -f ${.TARGET}
cp ${.IMPSRC} ${.TARGET}
+ chmod a+x ${.TARGET}
diff --git a/contrib/bmake/mk/sys/Generic.mk b/contrib/bmake/mk/sys/Generic.mk
index 51c72990f2ea..d9c958ffff94 100644
--- a/contrib/bmake/mk/sys/Generic.mk
+++ b/contrib/bmake/mk/sys/Generic.mk
@@ -1,9 +1,7 @@
-# $Id: Generic.mk,v 1.17 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: Generic.mk,v 1.21 2024/02/17 17:26:57 sjg Exp $
#
-
-# some reasonable defaults
-.SUFFIXES: .out .a .ln .o .s .S .c ${CXX_SUFFIXES} .F .f .r .y .l .cl .p .h
-.SUFFIXES: .sh .m4
.LIBS: .a
@@ -27,178 +25,4 @@ MACHINE_ARCH = ${MACHINE_ARCH.${MACHINE}}
TSORT += -q
.endif
-ARFLAGS ?= rl
-
-AS ?= as
-AFLAGS ?=
-.if ${MACHINE_ARCH} == "sparc64"
-AFLAGS+= -Wa,-Av9a
-.endif
-COMPILE.s ?= ${CC} ${AFLAGS} -c
-LINK.s ?= ${CC} ${AFLAGS} ${LDFLAGS}
-COMPILE.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} -c -traditional-cpp
-LINK.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS}
-
-CC ?= cc
-DBG ?= -O2
-CFLAGS ?= ${DBG}
-COMPILE.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} -c
-LINK.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS}
-
-CXX ?= c++
-CXXFLAGS ?= ${CFLAGS}
-COMPILE.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} -c
-LINK.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS}
-
-OBJC ?= ${CC}
-OBJCFLAGS ?= ${CFLAGS}
-COMPILE.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} -c
-LINK.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} ${LDFLAGS}
-
-CPP ?= cpp
-CPPFLAGS ?=
-
-FC ?= f77
-FFLAGS ?= -O
-RFLAGS ?=
-COMPILE.f ?= ${FC} ${FFLAGS} -c
-LINK.f ?= ${FC} ${FFLAGS} ${LDFLAGS}
-COMPILE.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} -c
-LINK.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} ${LDFLAGS}
-COMPILE.r ?= ${FC} ${FFLAGS} ${RFLAGS} -c
-LINK.r ?= ${FC} ${FFLAGS} ${RFLAGS} ${LDFLAGS}
-
-INSTALL ?= install
-
-LEX ?= lex
-LFLAGS ?=
-LEX.l ?= ${LEX} ${LFLAGS}
-
-LD ?= ld
-LDFLAGS ?=
-
-LINT ?= lint
-LINTFLAGS ?= -chapbxzF
-
-NM ?= nm
-
-PC ?= pc
-PFLAGS ?=
-COMPILE.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} -c
-LINK.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS}
-
-SIZE ?= size
-
-YACC ?= yacc
-YFLAGS ?=
-YACC.y ?= ${YACC} ${YFLAGS}
-
-# C
-.c:
- ${LINK.c} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
-.c.o:
- ${COMPILE.c} ${.IMPSRC}
-.c.a:
- ${COMPILE.c} ${.IMPSRC}
- ${AR} ${ARFLAGS} $@ $*.o
- rm -f $*.o
-.c.ln:
- ${LINT} ${LINTFLAGS} ${CPPFLAGS:M-[IDU]*} -i ${.IMPSRC}
-
-# C++
-${CXX_SUFFIXES}:
- ${LINK.cc} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
-${CXX_SUFFIXES:%=%.o}:
- ${COMPILE.cc} ${.IMPSRC}
-${CXX_SUFFIXES:%=%.a}:
- ${COMPILE.cc} ${.IMPSRC}
- ${AR} ${ARFLAGS} $@ $*.o
- rm -f $*.o
-
-# Fortran/Ratfor
-.f:
- ${LINK.f} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
-.f.o:
- ${COMPILE.f} ${.IMPSRC}
-.f.a:
- ${COMPILE.f} ${.IMPSRC}
- ${AR} ${ARFLAGS} $@ $*.o
- rm -f $*.o
-
-.F:
- ${LINK.F} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
-.F.o:
- ${COMPILE.F} ${.IMPSRC}
-.F.a:
- ${COMPILE.F} ${.IMPSRC}
- ${AR} ${ARFLAGS} $@ $*.o
- rm -f $*.o
-
-.r:
- ${LINK.r} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
-.r.o:
- ${COMPILE.r} ${.IMPSRC}
-.r.a:
- ${COMPILE.r} ${.IMPSRC}
- ${AR} ${ARFLAGS} $@ $*.o
- rm -f $*.o
-
-# Pascal
-.p:
- ${LINK.p} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
-.p.o:
- ${COMPILE.p} ${.IMPSRC}
-.p.a:
- ${COMPILE.p} ${.IMPSRC}
- ${AR} ${ARFLAGS} $@ $*.o
- rm -f $*.o
-
-# Assembly
-.s:
- ${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
-.s.o:
- ${COMPILE.s} ${.IMPSRC}
-.s.a:
- ${COMPILE.s} ${.IMPSRC}
- ${AR} ${ARFLAGS} $@ $*.o
- rm -f $*.o
-.S:
- ${LINK.S} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
-.S.o:
- ${COMPILE.S} ${.IMPSRC}
-.S.a:
- ${COMPILE.S} ${.IMPSRC}
- ${AR} ${ARFLAGS} $@ $*.o
- rm -f $*.o
-
-# Lex
-.l:
- ${LEX.l} ${.IMPSRC}
- ${LINK.c} -o ${.TARGET} lex.yy.c ${LDLIBS} -ll
- rm -f lex.yy.c
-.l.c:
- ${LEX.l} ${.IMPSRC}
- mv lex.yy.c ${.TARGET}
-.l.o:
- ${LEX.l} ${.IMPSRC}
- ${COMPILE.c} -o ${.TARGET} lex.yy.c
- rm -f lex.yy.c
-
-# Yacc
-.y:
- ${YACC.y} ${.IMPSRC}
- ${LINK.c} -o ${.TARGET} y.tab.c ${LDLIBS}
- rm -f y.tab.c
-.y.c:
- ${YACC.y} ${.IMPSRC}
- mv y.tab.c ${.TARGET}
-.y.o:
- ${YACC.y} ${.IMPSRC}
- ${COMPILE.c} -o ${.TARGET} y.tab.c
- rm -f y.tab.c
-
-# Shell
-.sh:
- rm -f ${.TARGET}
- cp ${.IMPSRC} ${.TARGET}
diff --git a/contrib/bmake/mk/sys/HP-UX.mk b/contrib/bmake/mk/sys/HP-UX.mk
index f1c23148c186..97d8152e7b29 100644
--- a/contrib/bmake/mk/sys/HP-UX.mk
+++ b/contrib/bmake/mk/sys/HP-UX.mk
@@ -1,4 +1,4 @@
-# $Id: HP-UX.mk,v 1.15 2020/08/19 17:51:53 sjg Exp $
+# $Id: HP-UX.mk,v 1.17 2022/03/25 23:43:06 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
@@ -33,7 +33,7 @@ LDADD+= /usr/lib/end.o
.endif
AR ?= ar
-ARFLAGS ?= rl
+ARFLAGS ?= r
RANLIB ?= :
AFLAGS=
@@ -224,3 +224,4 @@ ${CXX_SUFFIXES:%=%.a}:
.sh:
rm -f ${.TARGET}
cp ${.IMPSRC} ${.TARGET}
+ chmod a+x ${.TARGET}
diff --git a/contrib/bmake/mk/sys/IRIX.mk b/contrib/bmake/mk/sys/IRIX.mk
index 00af15027f6e..698c1f002c6f 100644
--- a/contrib/bmake/mk/sys/IRIX.mk
+++ b/contrib/bmake/mk/sys/IRIX.mk
@@ -1,14 +1,12 @@
# $NetBSD: IRIX.sys.mk,v 1.2 2002/12/24 23:03:27 jschauma Exp $
# @(#)sys.mk 8.2 (Berkeley) 3/21/94
-.if ${.PARSEFILE} == "sys.mk"
.ifndef ROOT_GROUP
-OS!= uname -s
-ROOT_GROUP!= sed -n /:0:/s/:.*//p /etc/group
-.MAKEOVERRIDES+= OS ROOT_GROUP
+OS != uname -s
+ROOT_GROUP != sed -n '/:0:/{s/:.*//p;q;}' /etc/group
+.export OS ROOT_GROUP
.endif
unix ?= We run ${OS}.
-.endif
.SUFFIXES: .out .a .ln .o .s .S .c ${CXX_SUFFIXES} .F .f .r .y .l .cl .p .h
.SUFFIXES: .sh .m4
@@ -16,7 +14,7 @@ unix ?= We run ${OS}.
.LIBS: .a
AR ?= ar
-ARFLAGS ?= rl
+ARFLAGS ?= r
RANLIB ?= ranlib
AS ?= as
@@ -56,11 +54,12 @@ LINK.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} ${LDFLAGS}
COMPILE.r ?= ${FC} ${FFLAGS} ${RFLAGS} -c
LINK.r ?= ${FC} ${FFLAGS} ${RFLAGS} ${LDFLAGS}
-INSTALL ?= ${PREFIX}/bin/install-sh
+INSTALL_SH ?= install-sh
+INSTALL = ${INSTALL_SH}
LEX ?= lex
LFLAGS ?=
-LEX.l ?= ${LEX} ${LFLAGS}
+LEX.l ?= ${LEX} ${LFLAGS}
LD ?= ld
LDFLAGS ?=
@@ -193,3 +192,4 @@ ${CXX_SUFFIXES:%=%.a}:
.sh:
rm -f ${.TARGET}
cp ${.IMPSRC} ${.TARGET}
+ chmod a+x ${.TARGET}
diff --git a/contrib/bmake/mk/sys/Linux.mk b/contrib/bmake/mk/sys/Linux.mk
index 3cdc4dbe1a62..0c95419c0abe 100644
--- a/contrib/bmake/mk/sys/Linux.mk
+++ b/contrib/bmake/mk/sys/Linux.mk
@@ -1,4 +1,4 @@
-# $Id: Linux.mk,v 1.13 2020/08/19 17:51:53 sjg Exp $
+# $Id: Linux.mk,v 1.16 2022/09/09 17:44:29 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
@@ -17,7 +17,7 @@ NEED_SOLINKS ?=yes
.LIBS: .a
AR ?= ar
-ARFLAGS ?= rl
+ARFLAGS ?= r
RANLIB ?= ranlib
AS ?= as
@@ -185,3 +185,8 @@ ${CXX_SUFFIXES:%=%.a}:
.sh:
rm -f ${.TARGET}
cp ${.IMPSRC} ${.TARGET}
+ chmod a+x ${.TARGET}
+
+
+# egrep is deprecated
+EGREP = grep -E
diff --git a/contrib/bmake/mk/sys/NetBSD.mk b/contrib/bmake/mk/sys/NetBSD.mk
index 6629a4445a2e..fd3039ce497e 100644
--- a/contrib/bmake/mk/sys/NetBSD.mk
+++ b/contrib/bmake/mk/sys/NetBSD.mk
@@ -25,7 +25,7 @@ MAKE_VERSION ?= 20010606
.LIBS: .a
AR ?= ar
-ARFLAGS ?= rl
+ARFLAGS ?= r
RANLIB ?= ranlib
AS ?= as
@@ -228,3 +228,4 @@ ${CXX_SUFFIXES:%=%.a}:
.sh:
rm -f ${.TARGET}
cp ${.IMPSRC} ${.TARGET}
+ chmod a+x ${.TARGET}
diff --git a/contrib/bmake/mk/sys/OSF1.mk b/contrib/bmake/mk/sys/OSF1.mk
index 88e0ea28b930..a3128121dd9b 100644
--- a/contrib/bmake/mk/sys/OSF1.mk
+++ b/contrib/bmake/mk/sys/OSF1.mk
@@ -1,4 +1,4 @@
-# $Id: OSF1.mk,v 1.12 2020/08/19 17:51:53 sjg Exp $
+# $Id: OSF1.mk,v 1.14 2022/03/25 23:43:06 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
@@ -20,7 +20,7 @@ LD_X=
LD_x ?= -x
LD_r ?= -r
AR ?= ar
-ARFLAGS ?= rl
+ARFLAGS ?= r
RANLIB ?= ranlib
AS ?= as
@@ -196,3 +196,4 @@ ${CXX_SUFFIXES:%=%.a}:
.sh:
rm -f ${.TARGET}
cp ${.IMPSRC} ${.TARGET}
+ chmod a+x ${.TARGET}
diff --git a/contrib/bmake/mk/sys/OpenBSD.mk b/contrib/bmake/mk/sys/OpenBSD.mk
index 7440a231e3bf..850ec541c724 100644
--- a/contrib/bmake/mk/sys/OpenBSD.mk
+++ b/contrib/bmake/mk/sys/OpenBSD.mk
@@ -16,7 +16,7 @@ MACHINE_ARCH ?= ${MACHINE_ARCH.${MACHINE}}
.endif
AR ?= ar
-ARFLAGS ?= rl
+ARFLAGS ?= r
RANLIB ?= ranlib
AS ?= as
@@ -203,3 +203,4 @@ ${CXX_SUFFIXES:%=%.a}:
.sh:
rm -f ${.TARGET}
cp ${.IMPSRC} ${.TARGET}
+ chmod a+x ${.TARGET}
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/SunOS.mk b/contrib/bmake/mk/sys/SunOS.mk
index 4369c8d43b93..e4fff9b73d7b 100644
--- a/contrib/bmake/mk/sys/SunOS.mk
+++ b/contrib/bmake/mk/sys/SunOS.mk
@@ -1,4 +1,4 @@
-# $Id: SunOS.mk,v 1.12 2020/08/19 17:51:53 sjg Exp $
+# $Id: SunOS.mk,v 1.14 2022/03/25 23:43:06 sjg Exp $
.if ${.PARSEFILE} == "sys.mk"
.include <host-target.mk>
@@ -46,7 +46,7 @@ CPP ?= cpp
.LIBS: .a
AR ?= ar
-ARFLAGS ?= rl
+ARFLAGS ?= r
AS ?= as
AS_STDIN ?= -
@@ -217,3 +217,4 @@ ${CXX_SUFFIXES:%=%.a}:
.sh:
rm -f ${.TARGET}
cp ${.IMPSRC} ${.TARGET}
+ chmod a+x ${.TARGET}
diff --git a/contrib/bmake/mk/sys/UnixWare.mk b/contrib/bmake/mk/sys/UnixWare.mk
index 272d3e65c2d8..876f00ca6512 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.10 2022/03/25 23:43:06 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
@@ -20,7 +22,7 @@ PATH ?=/usr/sbin:/usr/bin:/usr/ccs/bin:/usr/ccs/lib:/usr/ucb:/usr/local/bin
LD_X=
LD_x=
AR ?= ar
-ARFLAGS ?= rl
+ARFLAGS ?= r
RANLIB ?= :
AS ?= as
@@ -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
@@ -239,3 +245,4 @@ ${CXX_SUFFIXES:%=%.a}:
.sh:
rm -f ${.TARGET}
cp ${.IMPSRC} ${.TARGET}
+ chmod a+x ${.TARGET}
diff --git a/contrib/bmake/mk/target-flags.mk b/contrib/bmake/mk/target-flags.mk
index 789f09b23115..d31e200f7ebf 100644
--- a/contrib/bmake/mk/target-flags.mk
+++ b/contrib/bmake/mk/target-flags.mk
@@ -36,8 +36,10 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
# RCSid:
-# $Id: target-flags.mk,v 1.10 2020/08/19 17:51:53 sjg Exp $
+# $Id: target-flags.mk,v 1.11 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 1998-2002, Simon J. Gerraty
#
diff --git a/contrib/bmake/mk/warnings.mk b/contrib/bmake/mk/warnings.mk
index 77635fbc8a29..8fd9a2ea2048 100644
--- a/contrib/bmake/mk/warnings.mk
+++ b/contrib/bmake/mk/warnings.mk
@@ -1,7 +1,9 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
# RCSid:
-# $Id: warnings.mk,v 1.15 2020/08/19 17:51:53 sjg Exp $
+# $Id: warnings.mk,v 1.18 2024/02/17 17:26:57 sjg Exp $
#
-# @(#) Copyright (c) 2002, Simon J. Gerraty
+# @(#) Copyright (c) 2002-2023, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@@ -20,21 +22,23 @@
# Any number of warnings sets can be added.
.-include <warnings-sets.mk>
+# This is more in keeping with our current practice
+.-include <local.warnings.mk>
# Modest defaults - put more elaborate sets in warnings-sets.mk
# -Wunused etc are here so you can set
# W_unused=-Wno-unused etc.
-MIN_WARNINGS?= -Wall \
+MIN_WARNINGS ?= -Wall \
-Wformat \
-Wimplicit \
-Wunused \
-Wuninitialized
-LOW_WARNINGS?= ${MIN_WARNINGS} -W -Wstrict-prototypes -Wmissing-prototypes
+LOW_WARNINGS ?= ${MIN_WARNINGS} -W -Wstrict-prototypes -Wmissing-prototypes
-MEDIUM_WARNINGS?= ${LOW_WARNINGS} -Werror
+MEDIUM_WARNINGS ?= ${LOW_WARNINGS}
-HIGH_WARNINGS?= ${MEDIUM_WARNINGS} \
+HIGH_WARNINGS ?= ${MEDIUM_WARNINGS} \
-Wcast-align \
-Wcast-qual \
-Wparentheses \
@@ -44,19 +48,46 @@ HIGH_WARNINGS?= ${MEDIUM_WARNINGS} \
-Wswitch \
-Wwrite-strings
-EXTRA_WARNINGS?= ${HIGH_WARNINGS} -Wextra
+EXTRA_WARNINGS ?= ${HIGH_WARNINGS} -Wextra
# The two step default makes it easier to test build with different defaults.
-DEFAULT_WARNINGS_SET?= MIN
-WARNINGS_SET?= ${DEFAULT_WARNINGS_SET}
+DEFAULT_WARNINGS_SET ?= MIN
+WARNINGS_SET ?= ${DEFAULT_WARNINGS_SET}
# There is always someone who wants more...
.if !empty(WARNINGS_XTRAS)
${WARNINGS_SET}_WARNINGS += ${WARNINGS_XTRAS}
.endif
-# If you add sets, besure to list them (you don't have to touch this list).
-ALL_WARNINGS_SETS+= MIN LOW MEDIUM HIGH EXTRA
+# Keep this list ordered!
+WARNINGS_SET_LIST ?= MIN LOW MEDIUM HIGH EXTRA
+
+# We assume WARNINGS_SET_LIST is an ordered list.
+# if WARNINGS_SET is < WERROR_SET we add WARNINGS_NO_ERROR
+# otherwise we add WARNINGS_ERROR
+DEFAULT_WERROR_SET ?= MEDIUM
+WERROR_SET ?= ${DEFAULT_WERROR_SET}
+WARNINGS_ERROR ?= -Werror
+WARNINGS_NO_ERROR ?=
+
+.if ${MAKE_VERSION} >= 20170130
+.for i in ${WARNINGS_SET_LIST:range}
+.if ${WARNINGS_SET_LIST:[$i]} == ${WARNINGS_SET}
+WARNINGS_SETx = $i
+.endif
+.if ${WARNINGS_SET_LIST:[$i]} == ${WERROR_SET}
+WERROR_SETx = $i
+.if ${MAKE_VERSION} >= 20220924
+.break
+.endif
+.endif
+.endfor
+.if ${WARNINGS_SETx:U${WERROR_SETx:U0}} < ${WERROR_SETx:U0}
+${WARNINGS_SET}_WARNINGS += ${WARNINGS_NO_ERROR:U}
+.else
+${WARNINGS_SET}_WARNINGS += ${WARNINGS_ERROR}
+.endif
+.endif
.if !empty(WARNINGS_SET)
.for ws in ${WARNINGS_SET}
@@ -68,7 +99,7 @@ _empty_warnings: .PHONY
.BEGIN:
.endif
@echo "ERROR: Invalid: WARNINGS_SET=${ws}"
- @echo "ERROR: Try one of: ${ALL_WARNINGS_SETS:O:u}"; exit 1
+ @echo "ERROR: Try one of: ${WARNINGS_SET_LIST}"; exit 1
.endif
.endfor
@@ -96,15 +127,19 @@ W_uninitialized=
# which makes it easy to turn off override individual flags
# (see W_uninitialized above).
#
-# The last bit expands to ${W_foo_${.TARGET:T}:U${W_foo}}
+# The last bit expands to
+# ${W_foo_${.TARGET:T:${TARGET_PREFIX_FILTER:ts:}}:U${W_foo}}
# which is the bit we ultimately want. It allows W_* to be set on a
# per target basis.
#
# NOTE: that we force the target extension to be .o
+# TARGET_PREFIX_FILTER defaults to R
#
+TARGET_PREFIX_FILTER ?= R
+
# define this once, we use it a couple of times below (hence the doubled $$).
-M_warnings_list = @s@$${$$s_WARNINGS}@:O:u:@w@$${$${w:C/-(.)/\1_/}::?=$$w} $${$${w:C/-(.)/\1_/}_${MACHINE_ARCH}_${.TARGET:T:R}.o:U$${$${w:C/-(.)/\1_/}_${.TARGET:T:R}.o:U$${$${w:C/-(.)/\1_/}_${MACHINE_ARCH}:U$${$${w:C/-(.)/\1_/}}}}}@
+M_warnings_list = @s@$${$$s_WARNINGS} $${$$s_WARNINGS.${COMPILER_TYPE}:U}@:O:u:@w@$${$${w:C/-(.)/\1_/}::?=$$w} $${$${w:C/-(.)/\1_/}_${MACHINE_ARCH}_${.TARGET:T:${TARGET_PREFIX_FILTER:ts:}}.o:U$${$${w:C/-(.)/\1_/}_${.TARGET:T:${TARGET_PREFIX_FILTER:ts:}}.o:U$${$${w:C/-(.)/\1_/}_${MACHINE_ARCH}:U$${$${w:C/-(.)/\1_/}}}}}@
# first a list of warnings from the chosen set
_warnings = ${WARNINGS_SET_${MACHINE_ARCH}:U${WARNINGS_SET}:${M_warnings_list}}
@@ -112,13 +147,13 @@ _warnings = ${WARNINGS_SET_${MACHINE_ARCH}:U${WARNINGS_SET}:${M_warnings_list}}
# since things like -Wall imply lots of others.
# this should be a super-set of the -Wno-* in _warnings, but
# just in case...
-_no_warnings = ${_warnings:M-Wno-*} ${ALL_WARNINGS_SETS:${M_warnings_list}:M-Wno-*}
+_no_warnings = ${_warnings:M-Wno-*} ${WARNINGS_SET_LIST:${M_warnings_list}:M-Wno-*}
# -Wno-* must follow any others
WARNINGS += ${_warnings:N-Wno-*} ${_no_warnings:O:u}
.ifndef NO_CFLAGS_WARNINGS
# Just ${WARNINGS} should do, but this is more flexible?
-CFLAGS+= ${WARNINGS_${.TARGET:T:R}.o:U${WARNINGS}}
+CFLAGS+= ${WARNINGS_${.TARGET:T:${TARGET_PREFIX_FILTER:ts:}}.o:U${WARNINGS}}
.endif
# it is rather silly that g++ blows up on some warning flags
@@ -130,9 +165,10 @@ NO_CXX_WARNINGS+= \
shadow \
strict-prototypes
-.for s in ${SRCS:M*.c*:N*.c:N*h}
+WARNINGS_CXX_SRCS += ${SRCS:M*.c*:N*.c:N*h}
+.for s in ${WARNINGS_CXX_SRCS:O:u}
.for w in ${NO_CXX_WARNINGS}
-W_$w_${s:T:R}.o=
+W_$w_${s:T:${TARGET_PREFIX_FILTER:ts:}}.o=
.endfor
.endfor
diff --git a/contrib/bmake/mk/whats.mk b/contrib/bmake/mk/whats.mk
index e10964463d4a..81708c2225b1 100644
--- a/contrib/bmake/mk/whats.mk
+++ b/contrib/bmake/mk/whats.mk
@@ -1,4 +1,6 @@
-# $Id: whats.mk,v 1.10 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: whats.mk,v 1.12 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 2014-2020, Simon J. Gerraty
#
@@ -55,7 +57,7 @@ what_location := ${WHAT_LOCATION}
# this script is done in multiple lines so we can
# use the token ${.OODATE:MNO_META_CMP}
# to prevent the variable parts making this constantly out-of-date
-${what_uuid}.c:
+${what_uuid}.c: .NOTMAIN
echo 'extern const char ${WHAT_LINE_IDS:@i@${what_var}_$i[]@:ts,};' > $@
.for i in ${WHAT_LINE_IDS}
.if ${WHAT_NOCMP_LINE_IDS:M$i} != ""
diff --git a/contrib/bmake/mk/yacc.mk b/contrib/bmake/mk/yacc.mk
index 7f7e99578d70..30e377d1991c 100644
--- a/contrib/bmake/mk/yacc.mk
+++ b/contrib/bmake/mk/yacc.mk
@@ -1,4 +1,6 @@
-# $Id: yacc.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# $Id: yacc.mk,v 1.9 2024/02/17 17:26:57 sjg Exp $
#
# @(#) Copyright (c) 1999-2011, Simon J. Gerraty
@@ -23,6 +25,28 @@ RM?= rm
YACC.y?= ${YACC} ${YFLAGS}
+# first deal with explicit *.y in SRCS
+.for y in ${SRCS:M*.y}
+.if ${YACC.y:M-d} == "" || defined(NO_RENAME_Y_TAB_H)
+.ORDER: ${y:T:R}.c y.tab.h
+y.tab.h: .NOMETA
+${y:T:R}.c y.tab.h: $y
+ ${YACC.y} ${.IMPSRC}
+ [ ! -s y.tab.c ] || mv y.tab.c ${.TARGET}
+ ${RM} -f y.tab.[!h]
+.else
+.ORDER: ${y:T:R}.c ${y:T:R}.h
+${y:T:R}.h: .NOMETA
+${y:T:R}.c ${y:T:R}.h: $y
+ ${YACC.y} ${.IMPSRC}
+ [ ! -s y.tab.c ] || mv y.tab.c ${.TARGET:T:R}.c
+ [ ! -s y.tab.h ] || cmp -s y.tab.h ${.TARGET:T:R}.h \
+ || mv y.tab.h ${.TARGET:T:R}.h
+ ${RM} -f y.tab.*
+.endif
+.endfor
+
+.if ${SRCS:M*.y} == ""
.if ${YACC.y:M-d} == "" || defined(NO_RENAME_Y_TAB_H)
.y.c:
@@ -50,8 +74,10 @@ YACC.y?= ${YACC} ${YFLAGS}
{ [ ! -s y.tab.c ] || mv y.tab.c ${.TARGET}; \
${RM} y.tab.*; }; }
.endif
+.endif
beforedepend: ${SRCS:T:M*.y:S/.y/.c/g}
CLEANFILES+= ${SRCS:T:M*.y:S/.y/.[ch]/g}
CLEANFILES+= y.tab.[ch]
+
diff --git a/contrib/bmake/mkdeps.sh b/contrib/bmake/mkdeps.sh
index dd87c4f5020e..3e4ea6711aa9 100755
--- a/contrib/bmake/mkdeps.sh
+++ b/contrib/bmake/mkdeps.sh
@@ -40,7 +40,7 @@
#
# RCSid:
-# $Id: mkdeps.sh,v 1.23 2002/11/29 06:58:59 sjg Exp $
+# $Id: mkdeps.sh,v 1.24 2022/09/09 18:44:56 sjg Exp $
#
# @(#) Copyright (c) 1993 Simon J. Gerraty
#
@@ -119,6 +119,13 @@ else
fi
fi
+# some Linux systems have deprecated egrep in favor of grep -E
+# but not everyone supports that
+case "`echo bmake | egrep 'a|b' 2>&1`" in
+bmake) ;;
+*) egrep() { grep -E "$@"; }
+esac
+
clean_up() {
trap "" 2 3
trap 0
diff --git a/contrib/bmake/nonints.h b/contrib/bmake/nonints.h
deleted file mode 100644
index 7119d798432b..000000000000
--- a/contrib/bmake/nonints.h
+++ /dev/null
@@ -1,347 +0,0 @@
-/* $NetBSD: nonints.h,v 1.213 2021/04/11 13:35:56 rillig Exp $ */
-
-/*
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * from: @(#)nonints.h 8.3 (Berkeley) 3/19/94
- */
-
-/*
- * Copyright (c) 1989 by Berkeley Softworks
- * All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Adam de Boor.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * from: @(#)nonints.h 8.3 (Berkeley) 3/19/94
- */
-
-/* arch.c */
-void Arch_Init(void);
-void Arch_End(void);
-
-bool Arch_ParseArchive(char **, GNodeList *, GNode *);
-void Arch_Touch(GNode *);
-void Arch_TouchLib(GNode *);
-void Arch_UpdateMTime(GNode *gn);
-void Arch_UpdateMemberMTime(GNode *gn);
-void Arch_FindLib(GNode *, SearchPath *);
-bool Arch_LibOODate(GNode *);
-bool Arch_IsLib(GNode *);
-
-/* compat.c */
-int Compat_RunCommand(const char *, GNode *, StringListNode *);
-void Compat_Run(GNodeList *);
-void Compat_Make(GNode *, GNode *);
-
-/* cond.c */
-CondEvalResult Cond_EvalCondition(const char *, bool *);
-CondEvalResult Cond_EvalLine(const char *);
-void Cond_restore_depth(unsigned int);
-unsigned int Cond_save_depth(void);
-
-/* dir.c; see also dir.h */
-
-MAKE_INLINE const char *
-str_basename(const char *pathname)
-{
- const char *lastSlash = strrchr(pathname, '/');
- return lastSlash != NULL ? lastSlash + 1 : pathname;
-}
-
-MAKE_INLINE SearchPath *
-SearchPath_New(void)
-{
- SearchPath *path = bmake_malloc(sizeof *path);
- Lst_Init(&path->dirs);
- return path;
-}
-
-void SearchPath_Free(SearchPath *);
-
-/* for.c */
-int For_Eval(const char *);
-bool For_Accum(const char *);
-void For_Run(int);
-
-/* job.c */
-#ifdef WAIT_T
-void JobReapChild(pid_t, WAIT_T, bool);
-#endif
-
-/* main.c */
-bool GetBooleanExpr(const char *, bool);
-void Main_ParseArgLine(const char *);
-char *Cmd_Exec(const char *, const char **);
-void Error(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
-void Fatal(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD;
-void Punt(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD;
-void DieHorribly(void) MAKE_ATTR_DEAD;
-void Finish(int) MAKE_ATTR_DEAD;
-int eunlink(const char *);
-void execDie(const char *, const char *);
-char *getTmpdir(void);
-bool ParseBoolean(const char *, bool);
-char *cached_realpath(const char *, char *);
-
-/* parse.c */
-void Parse_Init(void);
-void Parse_End(void);
-
-typedef enum VarAssignOp {
- VAR_NORMAL, /* = */
- VAR_SUBST, /* := */
- VAR_SHELL, /* != or :sh= */
- VAR_APPEND, /* += */
- VAR_DEFAULT /* ?= */
-} VarAssignOp;
-
-typedef struct VarAssign {
- char *varname; /* unexpanded */
- VarAssignOp op;
- const char *value; /* unexpanded */
-} VarAssign;
-
-typedef char *(*ReadMoreProc)(void *, size_t *);
-
-void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
-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_MainName(GNodeList *);
-int Parse_GetFatals(void);
-
-
-#ifndef HAVE_STRLCPY
-/* strlcpy.c */
-size_t strlcpy(char *, const char *, size_t);
-#endif
-
-/* suff.c */
-void Suff_Init(void);
-void Suff_End(void);
-
-void Suff_ClearSuffixes(void);
-bool Suff_IsTransform(const char *);
-GNode *Suff_AddTransform(const char *);
-void Suff_EndTransform(GNode *);
-void Suff_AddSuffix(const char *, GNode **);
-SearchPath *Suff_GetPath(const char *);
-void Suff_ExtendPaths(void);
-void Suff_AddInclude(const char *);
-void Suff_AddLib(const char *);
-void Suff_FindDeps(GNode *);
-SearchPath *Suff_FindPath(GNode *);
-void Suff_SetNull(const char *);
-void Suff_PrintAll(void);
-
-/* targ.c */
-void Targ_Init(void);
-void Targ_End(void);
-
-void Targ_Stats(void);
-GNodeList *Targ_List(void);
-GNode *GNode_New(const char *);
-GNode *Targ_FindNode(const char *);
-GNode *Targ_GetNode(const char *);
-GNode *Targ_NewInternalNode(const char *);
-GNode *Targ_GetEndNode(void);
-void Targ_FindList(GNodeList *, StringList *);
-bool Targ_Precious(const GNode *);
-void Targ_SetMain(GNode *);
-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_PrintGraph(int);
-void Targ_Propagate(void);
-const char *GNodeMade_Name(GNodeMade);
-
-/* var.c */
-void Var_Init(void);
-void Var_End(void);
-
-typedef enum VarEvalMode {
-
- /*
- * Only parse the expression but don't evaluate any part of it.
- *
- * TODO: Document what Var_Parse and Var_Subst return in this mode.
- * As of 2021-03-15, they return unspecified, inconsistent results.
- */
- VARE_PARSE_ONLY,
-
- /* Parse and evaluate the expression. */
- VARE_WANTRES,
-
- /*
- * Parse and evaluate the expression. It is an error if a
- * subexpression evaluates to undefined.
- */
- VARE_UNDEFERR,
-
- /*
- * Parse and evaluate the expression. Keep '$$' as '$$' instead of
- * reducing it to a single '$'. Subexpressions that evaluate to
- * undefined expand to an empty string.
- *
- * Used in variable assignments using the ':=' operator. It allows
- * multiple such assignments to be chained without accidentally
- * expanding '$$file' to '$file' in the first assignment and
- * interpreting it as '${f}' followed by 'ile' in the next assignment.
- */
- VARE_EVAL_KEEP_DOLLAR,
-
- /*
- * Parse and evaluate the expression. Keep undefined variables as-is
- * instead of expanding them to an empty string.
- *
- * Example for a ':=' assignment:
- * CFLAGS = $(.INCLUDES)
- * CFLAGS := -I.. $(CFLAGS)
- * # If .INCLUDES (an undocumented special variable, by the
- * # way) is still undefined, the updated CFLAGS becomes
- * # "-I.. $(.INCLUDES)".
- */
- VARE_EVAL_KEEP_UNDEF,
-
- /*
- * Parse and evaluate the expression. Keep '$$' as '$$' and preserve
- * undefined subexpressions.
- */
- VARE_KEEP_DOLLAR_UNDEF
-} VarEvalMode;
-
-typedef enum VarSetFlags {
- VAR_SET_NONE = 0,
-
- /* do not export */
- VAR_SET_NO_EXPORT = 1 << 0,
-
- /* Make the variable read-only. No further modification is possible,
- * except for another call to Var_Set with the same flag. */
- VAR_SET_READONLY = 1 << 1
-} VarSetFlags;
-
-/* The state of error handling returned by Var_Parse. */
-typedef enum VarParseResult {
-
- /* Both parsing and evaluation succeeded. */
- VPR_OK,
-
- /* Parsing or evaluating failed, with an error message. */
- VPR_ERR,
-
- /*
- * Parsing succeeded, undefined expressions are allowed and the
- * expression was still undefined after applying all modifiers.
- * No error message is printed in this case.
- *
- * Some callers handle this case differently, so return this
- * information to them, for now.
- *
- * TODO: Instead of having this special return value, rather ensure
- * that VARE_EVAL_KEEP_UNDEF is processed properly.
- */
- VPR_UNDEF
-
-} VarParseResult;
-
-typedef enum VarExportMode {
- /* .export-env */
- VEM_ENV,
- /* .export: Initial export or update an already exported variable. */
- VEM_PLAIN,
- /* .export-literal: Do not expand the variable value. */
- VEM_LITERAL
-} VarExportMode;
-
-void Var_Delete(GNode *, const char *);
-void Var_DeleteExpand(GNode *, const char *);
-void Var_Undef(const char *);
-void Var_Set(GNode *, const char *, const char *);
-void Var_SetExpand(GNode *, const char *, const char *);
-void Var_SetWithFlags(GNode *, const char *, const char *, VarSetFlags);
-void Var_SetExpandWithFlags(GNode *, const char *, const char *, VarSetFlags);
-void Var_Append(GNode *, const char *, const char *);
-void Var_AppendExpand(GNode *, const char *, const char *);
-bool Var_Exists(GNode *, const char *);
-bool Var_ExistsExpand(GNode *, const char *);
-FStr Var_Value(GNode *, const char *);
-const char *GNode_ValueDirect(GNode *, const char *);
-VarParseResult Var_Parse(const char **, GNode *, VarEvalMode, FStr *);
-VarParseResult Var_Subst(const char *, GNode *, VarEvalMode, char **);
-void Var_Stats(void);
-void Var_Dump(GNode *);
-void Var_ReexportVars(void);
-void Var_Export(VarExportMode, const char *);
-void Var_ExportVars(const char *);
-void Var_UnExport(bool, const char *);
-
-void Global_Set(const char *, const char *);
-void Global_SetExpand(const char *, const char *);
-void Global_Append(const char *, const char *);
-void Global_Delete(const char *);
-
-/* util.c */
-typedef void (*SignalProc)(int);
-SignalProc bmake_signal(int, SignalProc);
diff --git a/contrib/bmake/os.sh b/contrib/bmake/os.sh
index 7e6823b240c3..648ea1d5993c 100644..100755
--- 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.64 2024/03/19 16:03:23 sjg Exp $
#
# @(#) Copyright (c) 1994 Simon J. Gerraty
#
@@ -41,12 +41,16 @@ OSMAJOR=`IFS=.; set $OSREL; echo $1`
MACHINE=`uname -m`
MACHINE_ARCH=`uname -p 2>/dev/null || echo $MACHINE`
-# there is at least one case of `uname -p` outputting
-# a bunch of usless drivel
+# there is at least one case of `uname -p`
+# and even `uname -m` outputting usless info
+# fortunately not both together
+case "$MACHINE" in
+*[!A-Za-z0-9_-]*) MACHINE="$MACHINE_ARCH";;
+esac
case "$MACHINE_ARCH" in
unknown|*[!A-Za-z0-9_-]*) MACHINE_ARCH="$MACHINE";;
esac
-
+
# we need this here, and it is not always available...
Which() {
case "$1" in
@@ -78,15 +82,20 @@ toLower() {
}
K=
-case $OS in
+case "$OS" in
AIX) # everyone loves to be different...
OSMAJOR=`uname -v`
- OSREL="$OSMAJOR.`uname -r`"
+ OSMINOR=`uname -r`
+ OSREL="$OSMAJOR.$OSMINOR"
LOCAL_FS=jfs
PS_AXC=-e
SHARE_ARCH=$OS/$OSMAJOR.X
;;
-Darwin) # a bit like BSD
+CYGWIN*) # uname -s not very useful
+ # uname -o produces just Cygwin which is better
+ OS=Cygwin
+ ;;
+Darwin) # this is more explicit (arm64 vs arm)
HOST_ARCH=$MACHINE
;;
SunOS)
@@ -139,10 +148,10 @@ SunOS)
esac
# NetBSD at least has good backward compatibility
# so NetBSD/i386 is good enough
+ # recent NetBSD uses x86_64 for MACHINE_ARCH
case $OS in
NetBSD)
LOCALBASE=/usr/pkg
- HOST_ARCH=$MACHINE
SHARE_ARCH=$OS/$HOST_ARCH
;;
OpenBSD)
@@ -172,7 +181,7 @@ Interix)
MACHINE=i386
MACHINE_ARCH=i386
;;
-UnixWare)
+UnixWare|SCO_SV)
OSREL=`uname -v`
OSMAJOR=`IFS=.; set $OSREL; echo $1`
MACHINE_ARCH=`uname -m`
@@ -229,16 +238,21 @@ HOST_TARGET=`echo ${OS}${OSMAJOR}-$HOST_ARCH | tr -d / | toLower`
HOST_TARGET32=`echo ${OS}${OSMAJOR}-$HOST_ARCH32 | tr -d / | toLower`
export HOST_TARGET HOST_TARGET32
-case `echo -n .` in -n*) N=; C="\c";; *) N=-n; C=;; esac
+case `echo -n .` in -n*) echo_n=; echo_c="\c";; *) echo_n=-n; echo_c=;; esac
Echo() {
case "$1" in
- -n) _n=$N _c=$C; shift;;
- *) _n= _c=;;
+ -n) shift; echo $echo_n "$@$echo_c";;
+ *) echo "$@";;
esac
- echo $_n "$@" $_c
}
+# for systems that deprecate egrep
+case "`echo egrep | egrep 'e|g' 2>&1`" in
+egrep) ;;
+*) egrep() { grep -E "$@"; };;
+esac
+
export HOSTNAME HOST
export OS MACHINE MACHINE_ARCH OSREL OSMAJOR LOCAL_FS TMP_DIRS MAILER N C K PS_AXC
export LN SHARE_ARCH TR
diff --git a/contrib/bmake/parse.c b/contrib/bmake/parse.c
index 3c2d75acdf9a..54f29ba5ff13 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.722 2024/04/27 17:33:46 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -74,10 +74,6 @@
* Parse_File is the main entry point and controls most of the other
* functions in this module.
*
- * The directories for the .include "..." directive are kept in
- * 'parseIncPath', while those for .include <...> are kept in 'sysIncPath'.
- * The targets currently being defined are kept in 'targets'.
- *
* Interface:
* Parse_Init Initialize the module
*
@@ -86,15 +82,16 @@
* Parse_File Parse a top-level makefile. Included files are
* handled by IncludeFile instead.
*
- * Parse_IsVar Return true if the given line is a variable
- * assignment. Used by MainParseArgs to determine if
- * an argument is a target or a variable assignment.
- * Used internally for pretty much the same thing.
+ * Parse_VarAssign
+ * Try to parse the given line as a variable assignment.
+ * Used by MainParseArgs to determine if an argument is
+ * a target or a variable assignment. Used internally
+ * for pretty much the same thing.
*
* Parse_Error Report a parse error, a warning or an informational
* message.
*
- * Parse_MainName Returns a list of the main target to create.
+ * Parse_MainName Populate the list of targets to create.
*/
#include <sys/types.h>
@@ -124,36 +121,42 @@
#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 $");
-
-/* types and constants */
-
-/*
- * Structure for a file being read ("included file")
- */
-typedef struct IFile {
- char *fname; /* name of file (relative? absolute?) */
- bool fromForLoop; /* simulated .include by the .for loop */
- int lineno; /* current line number in file */
- int first_lineno; /* line number of start of text */
- 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. */
- char *buf_freeIt;
- char *buf_ptr; /* next char to be read */
- char *buf_end;
-
- /* Function to read more data, with a single opaque argument. */
- ReadMoreProc readMore;
- void *readMoreArg;
-
- struct loadedfile *lf; /* loadedfile object, if any */
-} IFile;
-
-/*
- * Tokens for target attributes
- */
+MAKE_RCSID("$NetBSD: parse.c,v 1.722 2024/04/27 17:33:46 rillig Exp $");
+
+/* Detects a multiple-inclusion guard in a makefile. */
+typedef enum {
+ GS_START, /* at the beginning of the file */
+ GS_COND, /* after the guard condition */
+ GS_DONE, /* after the closing .endif */
+ GS_NO /* the file is not guarded */
+} GuardState;
+
+/* A file being parsed. */
+typedef struct IncludedFile {
+ FStr name; /* absolute or relative to the cwd */
+ unsigned lineno; /* 1-based */
+ unsigned readLines; /* the number of physical lines that have
+ * been read from the file */
+ unsigned forHeadLineno; /* 1-based */
+ unsigned forBodyReadLines; /* the number of physical lines that have
+ * been read from the file above the body of
+ * the .for loop */
+ unsigned int condMinDepth; /* depth of nested 'if' directives, at the
+ * beginning of the file */
+ bool depending; /* state of doing_depend on EOF */
+
+ Buffer buf; /* the file's content or the body of the .for
+ * loop; either empty or ends with '\n' */
+ char *buf_ptr; /* next char to be read from buf */
+ char *buf_end; /* buf_end[-1] == '\n' */
+
+ GuardState guardState;
+ Guard *guard;
+
+ struct ForLoop *forLoop;
+} IncludedFile;
+
+/* Special attributes for target nodes. */
typedef enum ParseSpecial {
SP_ATTRIBUTE, /* Generic attribute */
SP_BEGIN, /* .BEGIN */
@@ -165,13 +168,13 @@ typedef enum ParseSpecial {
SP_INCLUDES, /* .INCLUDES; not mentioned in the manual page */
SP_INTERRUPT, /* .INTERRUPT */
SP_LIBS, /* .LIBS; not mentioned in the manual page */
- /* .MAIN and we don't have anything user-specified to make */
- SP_MAIN,
+ SP_MAIN, /* .MAIN and no user-specified targets to make */
SP_META, /* .META */
SP_MFLAGS, /* .MFLAGS or .MAKEFLAGS */
SP_NOMETA, /* .NOMETA */
SP_NOMETA_CMP, /* .NOMETA_CMP */
SP_NOPATH, /* .NOPATH */
+ SP_NOREADONLY, /* .NOREADONLY */
SP_NOT, /* Not special */
SP_NOTPARALLEL, /* .NOTPARALLEL or .NO_PARALLEL */
SP_NULL, /* .NULL; not mentioned in the manual page */
@@ -180,30 +183,44 @@ typedef enum ParseSpecial {
SP_PARALLEL, /* .PARALLEL; not mentioned in the manual page */
SP_PATH, /* .PATH or .PATH.suffix */
SP_PHONY, /* .PHONY */
-#ifdef POSIX
SP_POSIX, /* .POSIX; not mentioned in the manual page */
-#endif
SP_PRECIOUS, /* .PRECIOUS */
+ SP_READONLY, /* .READONLY */
SP_SHELL, /* .SHELL */
SP_SILENT, /* .SILENT */
SP_SINGLESHELL, /* .SINGLESHELL; not mentioned in the manual page */
SP_STALE, /* .STALE */
SP_SUFFIXES, /* .SUFFIXES */
+ SP_SYSPATH, /* .SYSPATH */
SP_WAIT /* .WAIT */
} ParseSpecial;
typedef List SearchPathList;
typedef ListNode SearchPathListNode;
-/* result data */
+
+typedef enum VarAssignOp {
+ VAR_NORMAL, /* = */
+ VAR_APPEND, /* += */
+ VAR_DEFAULT, /* ?= */
+ VAR_SUBST, /* := */
+ VAR_SHELL /* != or :sh= */
+} VarAssignOp;
+
+typedef struct VarAssign {
+ char *varname; /* unexpanded */
+ VarAssignOp op;
+ const char *value; /* unexpanded */
+} VarAssign;
+
+static bool Parse_IsVar(const char *, VarAssign *);
+static void Parse_Var(VarAssign *, GNode *);
/*
- * The main target to create. This is the first target on the first
- * dependency line in the first makefile.
+ * The target to be made if no targets are specified in the command line.
+ * This is the first target defined in any of the makefiles.
*/
-static GNode *mainNode;
-
-/* eval state */
+GNode *mainNode;
/*
* During parsing, the targets from the left-hand side of the currently
@@ -226,18 +243,11 @@ static StringList targCmds = LST_INIT;
/*
* Predecessor node for handling .ORDER. Initialized to NULL when .ORDER
- * seen, then set to each successive source on the line.
+ * is seen, then set to each successive source on the line.
*/
static GNode *order_pred;
-/* parser state */
-
-/* number of fatal errors */
-static int fatals = 0;
-
-/*
- * Variables for doing includes
- */
+static int parseErrors;
/*
* The include chain of makefiles. At index 0 is the top-level makefile from
@@ -246,39 +256,20 @@ static int fatals = 0;
*
* See PrintStackTrace for how to interpret the data.
*/
-static Vector /* of IFile */ includes;
+static Vector /* of IncludedFile */ includes;
-static IFile *
-GetInclude(size_t i)
-{
- return Vector_Get(&includes, i);
-}
-
-/* The file that is currently being read. */
-static IFile *
-CurFile(void)
-{
- return GetInclude(includes.len - 1);
-}
-
-/* include paths */
SearchPath *parseIncPath; /* directories for "..." includes */
SearchPath *sysIncPath; /* directories for <...> includes */
SearchPath *defSysIncPath; /* default for sysIncPath */
-/* parser tables */
-
/*
* The parseKeywords table is searched using binary search when deciding
- * if a target or source is special. The 'spec' field is the ParseSpecial
- * type of the keyword (SP_NOT if the keyword isn't special as a target) while
- * the 'op' field is the operator to apply to the list of targets if the
- * keyword is used as a source ("0" if the keyword isn't special as a source)
+ * if a target or source is special.
*/
static const struct {
- const char *name; /* Name of keyword */
- ParseSpecial spec; /* Type when used as a target */
- GNodeType op; /* Operator when used as a source */
+ const char name[17];
+ ParseSpecial special; /* when used as a target */
+ GNodeType targetAttr; /* when used as a source */
} parseKeywords[] = {
{ ".BEGIN", SP_BEGIN, OP_NONE },
{ ".DEFAULT", SP_DEFAULT, OP_NONE },
@@ -301,6 +292,7 @@ static const struct {
{ ".NOMETA", SP_NOMETA, OP_NOMETA },
{ ".NOMETA_CMP", SP_NOMETA_CMP, OP_NOMETA_CMP },
{ ".NOPATH", SP_NOPATH, OP_NOPATH },
+ { ".NOREADONLY", SP_NOREADONLY, OP_NONE },
{ ".NOTMAIN", SP_ATTRIBUTE, OP_NOTMAIN },
{ ".NOTPARALLEL", SP_NOTPARALLEL, OP_NONE },
{ ".NO_PARALLEL", SP_NOTPARALLEL, OP_NONE },
@@ -311,138 +303,78 @@ static const struct {
{ ".PARALLEL", SP_PARALLEL, OP_NONE },
{ ".PATH", SP_PATH, OP_NONE },
{ ".PHONY", SP_PHONY, OP_PHONY },
-#ifdef POSIX
{ ".POSIX", SP_POSIX, OP_NONE },
-#endif
{ ".PRECIOUS", SP_PRECIOUS, OP_PRECIOUS },
+ { ".READONLY", SP_READONLY, OP_NONE },
{ ".RECURSIVE", SP_ATTRIBUTE, OP_MAKE },
{ ".SHELL", SP_SHELL, OP_NONE },
{ ".SILENT", SP_SILENT, OP_SILENT },
{ ".SINGLESHELL", SP_SINGLESHELL, OP_NONE },
{ ".STALE", SP_STALE, OP_NONE },
{ ".SUFFIXES", SP_SUFFIXES, OP_NONE },
+ { ".SYSPATH", SP_SYSPATH, OP_NONE },
{ ".USE", SP_ATTRIBUTE, OP_USE },
{ ".USEBEFORE", SP_ATTRIBUTE, OP_USEBEFORE },
{ ".WAIT", SP_WAIT, OP_NONE },
};
-/* file loader */
+enum PosixState posix_state = PS_NOT_YET;
-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 */
-};
+static HashTable /* full file name -> Guard */ guards;
-/* 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)
-{
- struct loadedfile *lf;
- lf = bmake_malloc(sizeof *lf);
- lf->path = path == NULL ? "(stdin)" : path;
- lf->buf = buf;
- lf->len = buflen;
- lf->used = false;
- return lf;
+static List *
+Lst_New(void)
+{
+ List *list = bmake_malloc(sizeof *list);
+ Lst_Init(list);
+ return list;
}
static void
-loadedfile_destroy(struct loadedfile *lf)
+Lst_Free(List *list)
{
- free(lf->buf);
- free(lf);
+
+ Lst_Done(list);
+ free(list);
}
-/*
- * readMore() operation for loadedfile, as needed by the weird and twisted
- * logic below. Once that's cleaned up, we can get rid of lf->used.
- */
-static char *
-loadedfile_readMore(void *x, size_t *len)
+static IncludedFile *
+GetInclude(size_t i)
{
- struct loadedfile *lf = x;
-
- if (lf->used)
- return NULL;
-
- lf->used = true;
- *len = lf->len;
- return lf->buf;
+ assert(i < includes.len);
+ return Vector_Get(&includes, i);
}
-/*
- * Try to get the size of a file.
- */
-static bool
-load_getsize(int fd, size_t *ret)
+/* The makefile or the body of a .for loop that is currently being read. */
+static IncludedFile *
+CurFile(void)
{
- struct stat st;
-
- if (fstat(fd, &st) < 0)
- return false;
-
- if (!S_ISREG(st.st_mode))
- return false;
-
- /*
- * st_size is an off_t, which is 64 bits signed; *ret is
- * size_t, which might be 32 bits unsigned or 64 bits
- * unsigned. Rather than being elaborate, just punt on
- * files that are more than 1 GiB. We should never
- * see a makefile that size in practice.
- *
- * While we're at it reject negative sizes too, just in case.
- */
- if (st.st_size < 0 || st.st_size > 0x3fffffff)
- return false;
+ return GetInclude(includes.len - 1);
+}
- *ret = (size_t)st.st_size;
- return true;
+unsigned int
+CurFile_CondMinDepth(void)
+{
+ return CurFile()->condMinDepth;
}
-/*
- * Read in a file.
- *
- * Until the path search logic can be moved under here instead of
- * being in the caller in another source file, we need to have the fd
- * passed in already open. Bleh.
- *
- * If the path is NULL, use stdin.
- */
-static struct loadedfile *
-loadfile(const char *path, int fd)
+static Buffer
+LoadFile(const char *path, int fd)
{
ssize_t n;
Buffer buf;
- size_t filesize;
-
-
- if (path == NULL) {
- assert(fd == -1);
- fd = STDIN_FILENO;
- }
+ size_t bufSize;
+ struct stat st;
- if (load_getsize(fd, &filesize)) {
- /*
- * Avoid resizing the buffer later for no reason.
- *
- * At the same time leave space for adding a final '\n',
- * just in case it is missing in the file.
- */
- filesize++;
- } else
- filesize = 1024;
- Buf_InitSize(&buf, filesize);
+ bufSize = fstat(fd, &st) == 0 && S_ISREG(st.st_mode) &&
+ st.st_size > 0 && st.st_size < 1024 * 1024 * 1024
+ ? (size_t)st.st_size : 1024;
+ Buf_InitSize(&buf, bufSize);
for (;;) {
- assert(buf.len <= buf.cap);
if (buf.len == buf.cap) {
- if (buf.cap > 0x1fffffff) {
- errno = EFBIG;
+ if (buf.cap >= 512 * 1024 * 1024) {
Error("%s: file too large", path);
exit(2); /* Not 1 so -q can distinguish error */
}
@@ -461,94 +393,74 @@ loadfile(const char *path, int fd)
}
assert(buf.len <= buf.cap);
- if (!Buf_EndsWith(&buf, '\n'))
+ if (buf.len > 0 && !Buf_EndsWith(&buf, '\n'))
Buf_AddByte(&buf, '\n');
- if (path != NULL)
- close(fd);
-
- {
- struct loadedfile *lf = loadedfile_create(path,
- buf.data, buf.len);
- Buf_DoneData(&buf);
- return lf;
- }
+ return buf; /* may not be null-terminated */
}
-static void
-PrintStackTrace(void)
+/*
+ * Print the current chain of .include and .for directives. In Parse_Fatal
+ * or other functions that already print the location, includingInnermost
+ * would be redundant, but in other cases like Error or Fatal it needs to be
+ * included.
+ */
+void
+PrintStackTrace(bool includingInnermost)
{
- const IFile *entries;
+ const IncludedFile *entries;
size_t i, n;
- if (!(DEBUG(PARSE)))
- return;
-
- entries = GetInclude(0);
n = includes.len;
if (n == 0)
return;
- n--; /* This entry is already in the diagnostic. */
- /*
- * For the IFiles with fromForLoop, lineno seems to be sorted
- * backwards. This is because lineno is the number of completely
- * parsed lines, which for a .for loop is right after the
- * corresponding .endfor. The intuitive line number comes from
- * first_lineno instead, which points at the start of the .for loop.
- *
- * To make the stack trace intuitive, the entry below each chain of
- * .for loop entries must be ignored completely since neither its
- * lineno nor its first_lineno is useful. Instead, the topmost of
- * each chain of .for loop entries needs to be printed twice, once
- * with its first_lineno and once with its lineno.
- */
+ entries = GetInclude(0);
+ if (!includingInnermost && entries[n - 1].forLoop == NULL)
+ n--; /* already in the diagnostic */
for (i = n; i-- > 0;) {
- const IFile *entry = entries + i;
- const char *fname = entry->fname;
- bool printLineno;
+ const IncludedFile *entry = entries + i;
+ const char *fname = entry->name.str;
char dirbuf[MAXPATHLEN + 1];
- if (fname[0] != '/' && strcmp(fname, "(stdin)") != 0)
- fname = realpath(fname, dirbuf);
-
- printLineno = !entry->fromForLoop;
- if (i + 1 < n && entries[i + 1].fromForLoop == printLineno)
- printLineno = entry->fromForLoop;
+ if (fname[0] != '/' && strcmp(fname, "(stdin)") != 0) {
+ const char *realPath = realpath(fname, dirbuf);
+ if (realPath != NULL)
+ fname = realPath;
+ }
- if (printLineno)
- debug_printf("\tin .include from %s:%d\n",
- fname, entry->lineno);
- if (entry->fromForLoop)
- debug_printf("\tin .for loop from %s:%d\n",
- fname, entry->first_lineno);
+ if (entry->forLoop != NULL) {
+ char *details = ForLoop_Details(entry->forLoop);
+ debug_printf("\tin .for loop from %s:%u with %s\n",
+ fname, entry->forHeadLineno, details);
+ free(details);
+ } else if (i + 1 < n && entries[i + 1].forLoop != NULL) {
+ /* entry->lineno is not a useful line number */
+ } else
+ debug_printf("\tin %s:%u\n", fname, entry->lineno);
}
}
/* Check if the current character is escaped on the current line. */
static bool
-ParseIsEscaped(const char *line, const char *c)
+IsEscaped(const char *line, const char *p)
{
- bool active = false;
- for (;;) {
- if (line == c)
- return active;
- if (*--c != '\\')
- return active;
- active = !active;
- }
+ bool escaped = false;
+ while (p > line && *--p == '\\')
+ escaped = !escaped;
+ return escaped;
}
/*
- * Add the filename and lineno to the GNode so that we remember where it
- * was first defined.
+ * Remember the location (filename and lineno) where the last command was
+ * added or where the node was mentioned in a .depend file.
*/
static void
-ParseMark(GNode *gn)
+RememberLocation(GNode *gn)
{
- IFile *curFile = CurFile();
- gn->fname = curFile->fname;
+ IncludedFile *curFile = CurFile();
+ gn->fname = Str_Intern(curFile->name.str);
gn->lineno = curFile->lineno;
}
@@ -557,12 +469,12 @@ ParseMark(GNode *gn)
* Return the index of the keyword, or -1 if it isn't there.
*/
static int
-ParseFindKeyword(const char *str)
+FindKeyword(const char *str)
{
int start = 0;
int end = sizeof parseKeywords / sizeof parseKeywords[0] - 1;
- do {
+ while (start <= end) {
int curr = start + (end - start) / 2;
int diff = strcmp(str, parseKeywords[curr].name);
@@ -572,25 +484,34 @@ ParseFindKeyword(const char *str)
end = curr - 1;
else
start = curr + 1;
- } while (start <= end);
+ }
return -1;
}
-static void
-PrintLocation(FILE *f, const char *fname, size_t lineno)
+void
+PrintLocation(FILE *f, bool useVars, const GNode *gn)
{
char dirbuf[MAXPATHLEN + 1];
FStr dir, base;
+ const char *fname;
+ unsigned lineno;
+
+ if (gn != NULL) {
+ fname = gn->fname;
+ lineno = gn->lineno;
+ } else if (includes.len > 0) {
+ IncludedFile *curFile = CurFile();
+ fname = curFile->name.str;
+ lineno = curFile->lineno;
+ } else
+ return;
- if (*fname == '/' || strcmp(fname, "(stdin)") == 0) {
- (void)fprintf(f, "\"%s\" line %u: ", fname, (unsigned)lineno);
+ if (!useVars || fname[0] == '/' || strcmp(fname, "(stdin)") == 0) {
+ (void)fprintf(f, "\"%s\" line %u: ", fname, lineno);
return;
}
- /* Find out which makefile is the culprit.
- * We try ${.PARSEDIR} and apply realpath(3) if not absolute. */
-
dir = Var_Value(SCOPE_GLOBAL, ".PARSEDIR");
if (dir.str == NULL)
dir.str = ".";
@@ -601,64 +522,63 @@ PrintLocation(FILE *f, const char *fname, size_t lineno)
if (base.str == NULL)
base.str = str_basename(fname);
- (void)fprintf(f, "\"%s/%s\" line %u: ",
- dir.str, base.str, (unsigned)lineno);
+ (void)fprintf(f, "\"%s/%s\" line %u: ", dir.str, base.str, lineno);
FStr_Done(&base);
FStr_Done(&dir);
}
-static void
-ParseVErrorInternal(FILE *f, const char *fname, size_t lineno,
- ParseErrorLevel type, const char *fmt, va_list ap)
+static void MAKE_ATTR_PRINTFLIKE(5, 0)
+ParseVErrorInternal(FILE *f, bool useVars, const GNode *gn,
+ ParseErrorLevel level, const char *fmt, va_list ap)
{
static bool fatal_warning_error_printed = false;
(void)fprintf(f, "%s: ", progname);
- if (fname != NULL)
- PrintLocation(f, fname, lineno);
- if (type == PARSE_WARNING)
+ PrintLocation(f, useVars, gn);
+ fprintf(f, "%s", EvalStack_Details());
+ if (level == PARSE_WARNING)
(void)fprintf(f, "warning: ");
(void)vfprintf(f, fmt, ap);
(void)fprintf(f, "\n");
(void)fflush(f);
- if (type == PARSE_INFO)
- goto print_stack_trace;
- if (type == PARSE_WARNING && !opts.parseWarnFatal)
- goto print_stack_trace;
- fatals++;
- if (type == PARSE_WARNING && !fatal_warning_error_printed) {
- Error("parsing warnings being treated as errors");
- fatal_warning_error_printed = true;
+ if (level == PARSE_FATAL)
+ parseErrors++;
+ if (level == PARSE_WARNING && opts.parseWarnFatal) {
+ if (!fatal_warning_error_printed) {
+ Error("parsing warnings being treated as errors");
+ fatal_warning_error_printed = true;
+ }
+ parseErrors++;
}
-print_stack_trace:
- PrintStackTrace();
+ if (DEBUG(PARSE))
+ PrintStackTrace(false);
}
-static void
-ParseErrorInternal(const char *fname, size_t lineno,
- ParseErrorLevel type, const char *fmt, ...)
+static void MAKE_ATTR_PRINTFLIKE(3, 4)
+ParseErrorInternal(const GNode *gn,
+ ParseErrorLevel level, const char *fmt, ...)
{
va_list ap;
(void)fflush(stdout);
va_start(ap, fmt);
- ParseVErrorInternal(stderr, fname, lineno, type, fmt, ap);
+ ParseVErrorInternal(stderr, false, gn, level, fmt, ap);
va_end(ap);
- if (opts.debug_file != stderr && opts.debug_file != stdout) {
+ if (opts.debug_file != stdout && opts.debug_file != stderr) {
va_start(ap, fmt);
- ParseVErrorInternal(opts.debug_file, fname, lineno, type,
- fmt, ap);
+ ParseVErrorInternal(opts.debug_file, false, gn,
+ level, fmt, ap);
va_end(ap);
}
}
/*
- * Print a parse error message, including location information.
+ * Print a message, including location information.
*
* If the level is PARSE_FATAL, continue parsing until the end of the
* current top-level makefile, then exit (see Parse_File).
@@ -666,41 +586,30 @@ ParseErrorInternal(const char *fname, size_t lineno,
* Fmt is given without a trailing newline.
*/
void
-Parse_Error(ParseErrorLevel type, const char *fmt, ...)
+Parse_Error(ParseErrorLevel level, const char *fmt, ...)
{
va_list ap;
- const char *fname;
- size_t lineno;
-
- if (includes.len == 0) {
- fname = NULL;
- lineno = 0;
- } else {
- IFile *curFile = CurFile();
- fname = curFile->fname;
- lineno = (size_t)curFile->lineno;
- }
- va_start(ap, fmt);
(void)fflush(stdout);
- ParseVErrorInternal(stderr, fname, lineno, type, fmt, ap);
+ va_start(ap, fmt);
+ ParseVErrorInternal(stderr, true, NULL, level, fmt, ap);
va_end(ap);
- if (opts.debug_file != stderr && opts.debug_file != stdout) {
+ if (opts.debug_file != stdout && opts.debug_file != stderr) {
va_start(ap, fmt);
- ParseVErrorInternal(opts.debug_file, fname, lineno, type,
- fmt, ap);
+ ParseVErrorInternal(opts.debug_file, true, NULL,
+ level, fmt, ap);
va_end(ap);
}
}
/*
- * Parse and handle an .info, .warning or .error directive.
- * For an .error directive, immediately exit.
+ * Handle an .info, .warning or .error directive. For an .error directive,
+ * exit immediately.
*/
static void
-ParseMessage(ParseErrorLevel level, const char *levelName, const char *umsg)
+HandleMessage(ParseErrorLevel level, const char *levelName, const char *umsg)
{
char *xmsg;
@@ -710,24 +619,21 @@ ParseMessage(ParseErrorLevel level, const char *levelName, const char *umsg)
return;
}
- (void)Var_Subst(umsg, SCOPE_CMDLINE, VARE_WANTRES, &xmsg);
+ xmsg = Var_Subst(umsg, SCOPE_CMDLINE, VARE_WANTRES);
/* TODO: handle errors */
Parse_Error(level, "%s", xmsg);
free(xmsg);
if (level == PARSE_FATAL) {
- PrintOnError(NULL, NULL);
+ PrintOnError(NULL, "\n");
exit(1);
}
}
/*
- * Add the child to the parent's children.
- *
- * Additionally, add the parent to the child's parents, but only if the
- * target is not special. An example for such a special target is .END,
- * which does not need to be informed once the child target has been made.
+ * Add the child to the parent's children, and for non-special targets, vice
+ * versa.
*/
static void
LinkSource(GNode *pgn, GNode *cgn, bool isSpecial)
@@ -738,13 +644,16 @@ LinkSource(GNode *pgn, GNode *cgn, bool isSpecial)
Lst_Append(&pgn->children, cgn);
pgn->unmade++;
- /* Special targets like .END don't need any children. */
+ /*
+ * Special targets like .END do not need to be informed once the child
+ * target has been made.
+ */
if (!isSpecial)
Lst_Append(&cgn->parents, pgn);
if (DEBUG(PARSE)) {
- debug_printf("# %s: added child %s - %s\n",
- __func__, pgn->name, cgn->name);
+ debug_printf("# LinkSource: added child %s - %s\n",
+ pgn->name, cgn->name);
Targ_PrintNode(pgn, 0);
Targ_PrintNode(cgn, 0);
}
@@ -776,11 +685,10 @@ TryApplyDependencyOperator(GNode *gn, GNodeType op)
if (op == OP_DOUBLEDEP && (gn->type & OP_OPMASK) == OP_DOUBLEDEP) {
/*
- * If the node was of the left-hand side of a '::' operator,
- * we need to create a new instance of it for the children
- * and commands on this dependency line since each of these
- * dependency groups has its own attributes and commands,
- * separate from the others.
+ * If the node was on the left-hand side of a '::' operator,
+ * create a new node for the children and commands on this
+ * dependency line, since each of these dependency groups has
+ * its own attributes and commands, separate from the others.
*
* The new instance is placed on the 'cohorts' list of the
* initial one (note the initial one is not on its own
@@ -793,13 +701,13 @@ TryApplyDependencyOperator(GNode *gn, GNodeType op)
* Propagate copied bits to the initial node. They'll be
* propagated back to the rest of the cohorts later.
*/
- gn->type |= op & ~OP_OPMASK;
+ gn->type |= op & (unsigned)~OP_OPMASK;
cohort = Targ_NewInternalNode(gn->name);
if (doing_depend)
- ParseMark(cohort);
+ RememberLocation(cohort);
/*
- * Make the cohort invisible as well to avoid duplicating it
+ * Make the cohort invisible to avoid duplicating it
* into other variables. True, parents of this target won't
* tend to do anything with their local variables, but better
* safe than sorry.
@@ -814,11 +722,7 @@ TryApplyDependencyOperator(GNode *gn, GNodeType op)
snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d",
(unsigned int)gn->unmade_cohorts % 1000000);
} else {
- /*
- * We don't want to nuke any previous flags (whatever they
- * were) so we just OR the new operator into the old.
- */
- gn->type |= op;
+ gn->type |= op; /* preserve any previous flags */
}
return true;
@@ -835,65 +739,64 @@ ApplyDependencyOperator(GNodeType op)
}
/*
- * We add a .WAIT node in the dependency list. After any dynamic dependencies
+ * Add a .WAIT node in the dependency list. After any dynamic dependencies
* (and filename globbing) have happened, it is given a dependency on each
* previous child, back until the previous .WAIT node. The next child won't
* be scheduled until the .WAIT node is built.
*
- * We give each .WAIT node a unique name (mainly for diagnostics).
+ * Give each .WAIT node a unique name (mainly for diagnostics).
*/
static void
-ParseDependencySourceWait(bool isSpecial)
+ApplyDependencySourceWait(bool isSpecial)
{
- static int wait_number = 0;
- char wait_src[16];
+ static unsigned wait_number = 0;
+ char name[6 + 10 + 1];
GNode *gn;
- snprintf(wait_src, sizeof wait_src, ".WAIT_%u", ++wait_number);
- gn = Targ_NewInternalNode(wait_src);
+ snprintf(name, sizeof name, ".WAIT_%u", ++wait_number);
+ gn = Targ_NewInternalNode(name);
if (doing_depend)
- ParseMark(gn);
+ RememberLocation(gn);
gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN;
LinkToTargets(gn, isSpecial);
-
}
static bool
-ParseDependencySourceKeyword(const char *src, ParseSpecial specType)
+ApplyDependencySourceKeyword(const char *src, ParseSpecial special)
{
int keywd;
- GNodeType op;
+ GNodeType targetAttr;
if (*src != '.' || !ch_isupper(src[1]))
return false;
- keywd = ParseFindKeyword(src);
+ keywd = FindKeyword(src);
if (keywd == -1)
return false;
- op = parseKeywords[keywd].op;
- if (op != OP_NONE) {
- ApplyDependencyOperator(op);
+ targetAttr = parseKeywords[keywd].targetAttr;
+ if (targetAttr != OP_NONE) {
+ ApplyDependencyOperator(targetAttr);
return true;
}
- if (parseKeywords[keywd].spec == SP_WAIT) {
- ParseDependencySourceWait(specType != SP_NOT);
+ if (parseKeywords[keywd].special == SP_WAIT) {
+ ApplyDependencySourceWait(special != SP_NOT);
return true;
}
return false;
}
+/*
+ * In a line like ".MAIN: source1 source2", add all sources to the list of
+ * things to create, but only if the user didn't specify a target on the
+ * command line and .MAIN occurs for the first time.
+ *
+ * See HandleDependencyTargetSpecial, branch SP_MAIN.
+ * See unit-tests/cond-func-make-main.mk.
+ */
static void
-ParseDependencySourceMain(const char *src)
+ApplyDependencySourceMain(const char *src)
{
- /*
- * In a line like ".MAIN: source1 source2", add all sources to the
- * list of things to create, but only if the user didn't specify a
- * target on the command line and .MAIN occurs for the first time.
- *
- * See ParseDependencyTargetSpecial, branch SP_MAIN.
- * See unit-tests/cond-func-make-main.mk.
- */
Lst_Append(&opts.create, bmake_strdup(src));
/*
* Add the name to the .TARGETS variable as well, so the user can
@@ -902,83 +805,69 @@ ParseDependencySourceMain(const char *src)
Global_Append(".TARGETS", src);
}
+/*
+ * For the sources of a .ORDER target, create predecessor/successor links
+ * between the previous source and the current one.
+ */
static void
-ParseDependencySourceOrder(const char *src)
+ApplyDependencySourceOrder(const char *src)
{
GNode *gn;
- /*
- * Create proper predecessor/successor links between the previous
- * source and the current one.
- */
+
gn = Targ_GetNode(src);
if (doing_depend)
- ParseMark(gn);
+ RememberLocation(gn);
if (order_pred != NULL) {
Lst_Append(&order_pred->order_succ, gn);
Lst_Append(&gn->order_pred, order_pred);
if (DEBUG(PARSE)) {
- debug_printf("# %s: added Order dependency %s - %s\n",
- __func__, order_pred->name, gn->name);
+ debug_printf(
+ "# .ORDER forces '%s' to be made before '%s'\n",
+ order_pred->name, gn->name);
Targ_PrintNode(order_pred, 0);
Targ_PrintNode(gn, 0);
}
}
- /*
- * The current source now becomes the predecessor for the next one.
- */
+ /* The current source now becomes the predecessor for the next one. */
order_pred = gn;
}
+/* The source is not an attribute, so find/create a node for it. */
static void
-ParseDependencySourceOther(const char *src, GNodeType tOp,
- ParseSpecial specType)
+ApplyDependencySourceOther(const char *src, GNodeType targetAttr,
+ ParseSpecial special)
{
GNode *gn;
- /*
- * The source is not an attribute, so find/create a node for it.
- * After that, apply any operator to it from a special target or
- * link it to its parents, as appropriate.
- *
- * In the case of a source that was the object of a '::' operator,
- * the attribute is applied to all of its instances (as kept in
- * the 'cohorts' list of the node) or all the cohorts are linked
- * to all the targets.
- */
-
- /* Find/create the 'src' node and attach to all targets */
gn = Targ_GetNode(src);
if (doing_depend)
- ParseMark(gn);
- if (tOp != OP_NONE)
- gn->type |= tOp;
+ RememberLocation(gn);
+ if (targetAttr != OP_NONE)
+ gn->type |= targetAttr;
else
- LinkToTargets(gn, specType != SP_NOT);
+ LinkToTargets(gn, special != SP_NOT);
}
/*
* Given the name of a source in a dependency line, figure out if it is an
- * attribute (such as .SILENT) and apply it to the targets if it is. Else
+ * attribute (such as .SILENT) and if so, apply it to all targets. Otherwise
* decide if there is some attribute which should be applied *to* the source
* because of some special target (such as .PHONY) and apply it if so.
- * Otherwise, make the source a child of the targets in the list 'targets'.
- *
- * Input:
- * tOp operator (if any) from special targets
- * src name of the source to handle
+ * Otherwise, make the source a child of the targets.
*/
static void
-ParseDependencySource(GNodeType tOp, const char *src, ParseSpecial specType)
+ApplyDependencySource(GNodeType targetAttr, const char *src,
+ ParseSpecial special)
{
- if (ParseDependencySourceKeyword(src, specType))
+ if (ApplyDependencySourceKeyword(src, special))
return;
- if (specType == SP_MAIN)
- ParseDependencySourceMain(src);
- else if (specType == SP_ORDER)
- ParseDependencySourceOrder(src);
+ if (special == SP_MAIN)
+ ApplyDependencySourceMain(src);
+ else if (special == SP_ORDER)
+ ApplyDependencySourceOrder(src);
else
- ParseDependencySourceOther(src, tOp, specType);
+ ApplyDependencySourceOther(src, targetAttr, special);
}
/*
@@ -987,7 +876,7 @@ ParseDependencySource(GNodeType tOp, const char *src, ParseSpecial specType)
* actually a real target (i.e. isn't a .USE or .EXEC rule) to be made.
*/
static void
-FindMainTarget(void)
+MaybeUpdateMainTarget(void)
{
GNodeListNode *ln;
@@ -996,32 +885,20 @@ FindMainTarget(void)
for (ln = targets->first; ln != NULL; ln = ln->next) {
GNode *gn = ln->datum;
- if (!(gn->type & OP_NOTARGET)) {
- DEBUG1(MAKE, "Setting main node to \"%s\"\n", gn->name);
+ if (GNode_IsMainCandidate(gn)) {
+ DEBUG1(MAKE, "Setting main node to \"%s\"\n",
+ gn->name);
mainNode = gn;
- Targ_SetMain(gn);
return;
}
}
}
-/*
- * We got to the end of the line while we were still looking at targets.
- *
- * Ending a dependency line without an operator is a Bozo no-no. As a
- * heuristic, this is also often triggered by undetected conflicts from
- * cvs/rcs merges.
- */
static void
-ParseErrorNoDependency(const char *lstart)
+InvalidLineType(const char *line, const char *unexpanded_line)
{
- if ((strncmp(lstart, "<<<<<<", 6) == 0) ||
- (strncmp(lstart, "======", 6) == 0) ||
- (strncmp(lstart, ">>>>>>", 6) == 0))
- Parse_Error(PARSE_FATAL,
- "Makefile appears to contain unresolved cvs/rcs/??? merge conflicts");
- else if (lstart[0] == '.') {
- const char *dirstart = lstart + 1;
+ if (unexpanded_line[0] == '.') {
+ const char *dirstart = unexpanded_line + 1;
const char *dirend;
cpp_skip_whitespace(&dirstart);
dirend = dirstart;
@@ -1029,44 +906,34 @@ ParseErrorNoDependency(const char *lstart)
dirend++;
Parse_Error(PARSE_FATAL, "Unknown directive \"%.*s\"",
(int)(dirend - dirstart), dirstart);
- } else
- Parse_Error(PARSE_FATAL, "Invalid line type");
+ } else if (strcmp(line, unexpanded_line) == 0)
+ Parse_Error(PARSE_FATAL, "Invalid line '%s'", line);
+ else
+ Parse_Error(PARSE_FATAL,
+ "Invalid line '%s', expanded to '%s'",
+ unexpanded_line, line);
}
static void
-ParseDependencyTargetWord(const char **pp, const char *lstart)
+ParseDependencyTargetWord(char **pp, const char *lstart)
{
- const char *cp = *pp;
+ const char *p = *pp;
- while (*cp != '\0') {
- if ((ch_isspace(*cp) || *cp == '!' || *cp == ':' ||
- *cp == '(') &&
- !ParseIsEscaped(lstart, cp))
+ while (*p != '\0') {
+ if ((ch_isspace(*p) || *p == '!' || *p == ':' || *p == '(')
+ && !IsEscaped(lstart, p))
break;
- if (*cp == '$') {
- /*
- * Must be a dynamic source (would have been expanded
- * otherwise), so call the Var module to parse the
- * puppy so we can safely advance beyond it.
- *
- * There should be no errors in this, as they would
- * have been discovered in the initial Var_Subst and
- * we wouldn't be here.
- */
- const char *nested_p = cp;
- FStr nested_val;
-
- (void)Var_Parse(&nested_p, SCOPE_CMDLINE,
- VARE_PARSE_ONLY, &nested_val);
+ if (*p == '$') {
+ FStr val = Var_Parse(&p, SCOPE_CMDLINE,
+ VARE_PARSE_ONLY);
/* TODO: handle errors */
- FStr_Done(&nested_val);
- cp += nested_p - cp;
+ FStr_Done(&val);
} else
- cp++;
+ p++;
}
- *pp = cp;
+ *pp += p - *pp;
}
/*
@@ -1075,23 +942,28 @@ ParseDependencyTargetWord(const char **pp, const char *lstart)
* See the tests deptgt-*.mk.
*/
static void
-ParseDependencyTargetSpecial(ParseSpecial *inout_specType,
- const char *targetName,
- SearchPathList **inout_paths)
+HandleDependencyTargetSpecial(const char *targetName,
+ ParseSpecial *inout_special,
+ SearchPathList **inout_paths)
{
- switch (*inout_specType) {
+ switch (*inout_special) {
case SP_PATH:
if (*inout_paths == NULL)
*inout_paths = Lst_New();
Lst_Append(*inout_paths, &dirSearchPath);
break;
+ case SP_SYSPATH:
+ if (*inout_paths == NULL)
+ *inout_paths = Lst_New();
+ Lst_Append(*inout_paths, sysIncPath);
+ break;
case SP_MAIN:
/*
* Allow targets from the command line to override the
* .MAIN node.
*/
if (!Lst_IsEmpty(&opts.create))
- *inout_specType = SP_NOT;
+ *inout_special = SP_NOT;
break;
case SP_BEGIN:
case SP_END:
@@ -1100,7 +972,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;
@@ -1136,13 +1008,9 @@ ParseDependencyTargetSpecial(ParseSpecial *inout_specType,
}
}
-/*
- * .PATH<suffix> has to be handled specially.
- * Call on the suffix module to give us a path to modify.
- */
static bool
-ParseDependencyTargetPath(const char *suffixName,
- SearchPathList **inout_paths)
+HandleDependencyTargetPath(const char *suffixName,
+ SearchPathList **inout_paths)
{
SearchPath *path;
@@ -1160,13 +1028,12 @@ ParseDependencyTargetPath(const char *suffixName,
return true;
}
-/*
- * See if it's a special target and if so set specType to match it.
- */
+/* See if it's a special target and if so set inout_special to match it. */
static bool
-ParseDependencyTarget(const char *targetName,
- ParseSpecial *inout_specType,
- GNodeType *out_tOp, SearchPathList **inout_paths)
+HandleDependencyTarget(const char *targetName,
+ ParseSpecial *inout_special,
+ GNodeType *inout_targetAttr,
+ SearchPathList **inout_paths)
{
int keywd;
@@ -1177,93 +1044,86 @@ ParseDependencyTarget(const char *targetName,
* See if the target is a special target that must have it
* or its sources handled specially.
*/
- keywd = ParseFindKeyword(targetName);
+ keywd = FindKeyword(targetName);
if (keywd != -1) {
- if (*inout_specType == SP_PATH &&
- parseKeywords[keywd].spec != SP_PATH) {
+ if (*inout_special == SP_PATH &&
+ parseKeywords[keywd].special != SP_PATH) {
Parse_Error(PARSE_FATAL, "Mismatched special targets");
return false;
}
- *inout_specType = parseKeywords[keywd].spec;
- *out_tOp = parseKeywords[keywd].op;
+ *inout_special = parseKeywords[keywd].special;
+ *inout_targetAttr = parseKeywords[keywd].targetAttr;
- ParseDependencyTargetSpecial(inout_specType, targetName,
+ HandleDependencyTargetSpecial(targetName, inout_special,
inout_paths);
} else if (strncmp(targetName, ".PATH", 5) == 0) {
- *inout_specType = SP_PATH;
- if (!ParseDependencyTargetPath(targetName + 5, inout_paths))
+ *inout_special = SP_PATH;
+ if (!HandleDependencyTargetPath(targetName + 5, inout_paths))
return false;
}
return true;
}
static void
-ParseDependencyTargetMundane(char *targetName, StringList *curTargs)
+HandleSingleDependencyTargetMundane(const char *name)
{
- if (Dir_HasWildcards(targetName)) {
- /*
- * Targets are to be sought only in the current directory,
- * so create an empty path for the thing. Note we need to
- * use Dir_Destroy in the destruction of the path as the
- * Dir module could have added a directory to the path...
- */
- SearchPath *emptyPath = SearchPath_New();
-
- SearchPath_Expand(emptyPath, targetName, curTargs);
+ GNode *gn = Suff_IsTransform(name)
+ ? Suff_AddTransform(name)
+ : Targ_GetNode(name);
+ if (doing_depend)
+ RememberLocation(gn);
- SearchPath_Free(emptyPath);
- } else {
- /*
- * No wildcards, but we want to avoid code duplication,
- * so create a list with the word on it.
- */
- Lst_Append(curTargs, targetName);
- }
+ Lst_Append(targets, gn);
+}
- /* Apply the targets. */
+static void
+HandleDependencyTargetMundane(const char *targetName)
+{
+ if (Dir_HasWildcards(targetName)) {
+ StringList targetNames = LST_INIT;
- while (!Lst_IsEmpty(curTargs)) {
- char *targName = Lst_Dequeue(curTargs);
- GNode *gn = Suff_IsTransform(targName)
- ? Suff_AddTransform(targName)
- : Targ_GetNode(targName);
- if (doing_depend)
- ParseMark(gn);
+ SearchPath *emptyPath = SearchPath_New();
+ SearchPath_Expand(emptyPath, targetName, &targetNames);
+ SearchPath_Free(emptyPath);
- Lst_Append(targets, gn);
- }
+ while (!Lst_IsEmpty(&targetNames)) {
+ char *targName = Lst_Dequeue(&targetNames);
+ HandleSingleDependencyTargetMundane(targName);
+ free(targName);
+ }
+ } else
+ HandleSingleDependencyTargetMundane(targetName);
}
static void
-ParseDependencyTargetExtraWarn(char **pp, const char *lstart)
+SkipExtraTargets(char **pp, const char *lstart)
{
bool warning = false;
- char *cp = *pp;
+ const char *p = *pp;
- while (*cp != '\0') {
- if (!ParseIsEscaped(lstart, cp) && (*cp == '!' || *cp == ':'))
+ while (*p != '\0') {
+ if (!IsEscaped(lstart, p) && (*p == '!' || *p == ':'))
break;
- if (ParseIsEscaped(lstart, cp) || (*cp != ' ' && *cp != '\t'))
+ if (IsEscaped(lstart, p) || (*p != ' ' && *p != '\t'))
warning = true;
- cp++;
+ p++;
+ }
+ if (warning) {
+ const char *start = *pp;
+ cpp_skip_whitespace(&start);
+ Parse_Error(PARSE_WARNING, "Extra target '%.*s' ignored",
+ (int)(p - start), start);
}
- if (warning)
- Parse_Error(PARSE_WARNING, "Extra target ignored");
- *pp = cp;
+ *pp += p - *pp;
}
static void
-ParseDependencyCheckSpec(ParseSpecial specType)
+CheckSpecialMundaneMixture(ParseSpecial special)
{
- switch (specType) {
- default:
- Parse_Error(PARSE_WARNING,
- "Special and mundane targets don't mix. "
- "Mundane ones ignored");
- break;
+ switch (special) {
case SP_DEFAULT:
case SP_STALE:
case SP_BEGIN:
@@ -1275,7 +1135,12 @@ ParseDependencyCheckSpec(ParseSpecial specType)
* shouldn't be empty.
*/
case SP_NOT:
- /* Nothing special here -- targets can be empty if it wants. */
+ /* Nothing special here -- targets may be empty. */
+ break;
+ default:
+ Parse_Error(PARSE_WARNING,
+ "Special and mundane targets don't mix. "
+ "Mundane ones ignored");
break;
}
}
@@ -1284,61 +1149,159 @@ ParseDependencyCheckSpec(ParseSpecial specType)
* In a dependency line like 'targets: sources' or 'targets! sources', parse
* the operator ':', '::' or '!' from between the targets and the sources.
*/
-static bool
-ParseDependencyOp(char **pp, const char *lstart, GNodeType *out_op)
-{
- const char *cp = *pp;
-
- if (*cp == '!') {
- *out_op = OP_FORCE;
- (*pp)++;
- return true;
- }
-
- if (*cp == ':') {
- if (cp[1] == ':') {
- *out_op = OP_DOUBLEDEP;
- (*pp) += 2;
- } else {
- *out_op = OP_DEPENDS;
- (*pp)++;
- }
- return true;
- }
-
- {
- const char *msg = lstart[0] == '.'
- ? "Unknown directive" : "Missing dependency operator";
- Parse_Error(PARSE_FATAL, "%s", msg);
- return false;
- }
+static GNodeType
+ParseDependencyOp(char **pp)
+{
+ if (**pp == '!')
+ return (*pp)++, OP_FORCE;
+ if (**pp == ':' && (*pp)[1] == ':')
+ return *pp += 2, OP_DOUBLEDEP;
+ else if (**pp == ':')
+ return (*pp)++, OP_DEPENDS;
+ else
+ return OP_NONE;
}
static void
-ClearPaths(SearchPathList *paths)
+ClearPaths(ParseSpecial special, SearchPathList *paths)
{
if (paths != NULL) {
SearchPathListNode *ln;
for (ln = paths->first; ln != NULL; ln = ln->next)
SearchPath_Clear(ln->datum);
}
+ if (special == SP_SYSPATH)
+ Dir_SetSYSPATH();
+ else
+ Dir_SetPATH();
+}
+
+static char *
+FindInDirOfIncludingFile(const char *file)
+{
+ char *fullname, *incdir, *slash, *newName;
+ int i;
+
+ fullname = NULL;
+ incdir = bmake_strdup(CurFile()->name.str);
+ slash = strrchr(incdir, '/');
+ if (slash != NULL) {
+ *slash = '\0';
+ /*
+ * Now do lexical processing of leading "../" on the
+ * filename.
+ */
+ for (i = 0; strncmp(file + i, "../", 3) == 0; i += 3) {
+ slash = strrchr(incdir + 1, '/');
+ if (slash == NULL || strcmp(slash, "/..") == 0)
+ break;
+ *slash = '\0';
+ }
+ newName = str_concat3(incdir, "/", file + i);
+ fullname = Dir_FindFile(newName, parseIncPath);
+ if (fullname == NULL)
+ fullname = Dir_FindFile(newName, &dirSearchPath);
+ free(newName);
+ }
+ free(incdir);
+ return fullname;
+}
+
+static char *
+FindInQuotPath(const char *file)
+{
+ const char *suff;
+ SearchPath *suffPath;
+ char *fullname;
+
+ fullname = FindInDirOfIncludingFile(file);
+ if (fullname == NULL &&
+ (suff = strrchr(file, '.')) != NULL &&
+ (suffPath = Suff_GetPath(suff)) != NULL)
+ fullname = Dir_FindFile(file, suffPath);
+ if (fullname == NULL)
+ fullname = Dir_FindFile(file, parseIncPath);
+ if (fullname == NULL)
+ fullname = Dir_FindFile(file, &dirSearchPath);
+ return fullname;
+}
+
+static bool
+SkipGuarded(const char *fullname)
+{
+ Guard *guard = HashTable_FindValue(&guards, fullname);
+ if (guard != NULL && guard->kind == GK_VARIABLE
+ && GNode_ValueDirect(SCOPE_GLOBAL, guard->name) != NULL)
+ goto skip;
+ if (guard != NULL && guard->kind == GK_TARGET
+ && Targ_FindNode(guard->name) != NULL)
+ goto skip;
+ return false;
- Dir_SetPATH();
+skip:
+ DEBUG2(PARSE, "Skipping '%s' because '%s' is defined\n",
+ fullname, guard->name);
+ return true;
}
/*
- * Several special targets take different actions if present with no
- * sources:
- * a .SUFFIXES line with no sources clears out all old suffixes
- * a .PRECIOUS line makes all targets precious
- * a .IGNORE line ignores errors for all targets
- * a .SILENT line creates silence when making all targets
- * a .PATH removes all directories from the search path(s).
+ * 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_PushInput
+ * and ParseEOF.
+ *
+ * System includes are looked up in sysIncPath, any other includes are looked
+ * up in the parsedir and then in the directories specified by the -I command
+ * line options.
*/
static void
-ParseDependencySourcesEmpty(ParseSpecial specType, SearchPathList *paths)
+IncludeFile(const char *file, bool isSystem, bool depinc, bool silent)
{
- switch (specType) {
+ Buffer buf;
+ char *fullname; /* full pathname of file */
+ int fd;
+
+ fullname = file[0] == '/' ? bmake_strdup(file) : NULL;
+
+ if (fullname == NULL && !isSystem)
+ fullname = FindInQuotPath(file);
+
+ if (fullname == NULL) {
+ SearchPath *path = Lst_IsEmpty(&sysIncPath->dirs)
+ ? defSysIncPath : sysIncPath;
+ fullname = Dir_FindFile(file, path);
+ }
+
+ if (fullname == NULL) {
+ if (!silent)
+ Parse_Error(PARSE_FATAL, "Could not find %s", file);
+ return;
+ }
+
+ if (SkipGuarded(fullname))
+ return;
+
+ if ((fd = open(fullname, O_RDONLY)) == -1) {
+ if (!silent)
+ Parse_Error(PARSE_FATAL, "Cannot open %s", fullname);
+ free(fullname);
+ return;
+ }
+
+ buf = LoadFile(fullname, fd);
+ (void)close(fd);
+
+ Parse_PushInput(fullname, 1, 0, buf, NULL);
+ if (depinc)
+ doing_depend = depinc; /* only turn it on */
+ free(fullname);
+}
+
+/* Handle a "dependency" line like '.SPECIAL:' without any sources. */
+static void
+HandleDependencySourcesEmpty(ParseSpecial special, SearchPathList *paths)
+{
+ switch (special) {
case SP_SUFFIXES:
Suff_ClearSuffixes();
break;
@@ -1349,16 +1312,23 @@ ParseDependencySourcesEmpty(ParseSpecial specType, SearchPathList *paths)
opts.ignoreErrors = true;
break;
case SP_SILENT:
- opts.beSilent = true;
+ opts.silent = true;
break;
case SP_PATH:
- ClearPaths(paths);
+ case SP_SYSPATH:
+ ClearPaths(special, paths);
break;
-#ifdef POSIX
case SP_POSIX:
- Global_Set("%POSIX", "1003.2");
+ if (posix_state == PS_NOW_OR_NEVER) {
+ /*
+ * With '-r', 'posix.mk' (if it exists)
+ * can effectively substitute for 'sys.mk',
+ * otherwise it is an extension.
+ */
+ Global_Set("%POSIX", "1003.2");
+ IncludeFile("posix.mk", true, false, true);
+ }
break;
-#endif
default:
break;
}
@@ -1375,41 +1345,20 @@ AddToPaths(const char *dir, SearchPathList *paths)
}
/*
- * If the target was one that doesn't take files as its sources
- * but takes something like suffixes, we take each
- * space-separated word on the line as a something and deal
- * with it accordingly.
- *
- * If the target was .SUFFIXES, we take each source as a
- * suffix and add it to the list of suffixes maintained by the
- * Suff module.
- *
- * If the target was a .PATH, we add the source as a directory
- * to search on the search path.
- *
- * If it was .INCLUDES, the source is taken to be the suffix of
- * files which will be #included and whose search path should
- * be present in the .INCLUDES variable.
- *
- * If it was .LIBS, the source is taken to be the suffix of
- * files which are considered libraries and whose search path
- * should be present in the .LIBS variable.
- *
- * If it was .NULL, the source is the suffix to use when a file
- * has no valid suffix.
- *
- * If it was .OBJDIR, the source is a new definition for .OBJDIR,
- * and will cause make to do a new chdir to that path.
+ * If the target was one that doesn't take files as its sources but takes
+ * something like suffixes, we take each space-separated word on the line as
+ * a something and deal with it accordingly.
*/
static void
-ParseDependencySourceSpecial(ParseSpecial specType, char *word,
+ParseDependencySourceSpecial(ParseSpecial special, const char *word,
SearchPathList *paths)
{
- switch (specType) {
+ switch (special) {
case SP_SUFFIXES:
- Suff_AddSuffix(word, &mainNode);
+ Suff_AddSuffix(word);
break;
case SP_PATH:
+ case SP_SYSPATH:
AddToPaths(word, paths);
break;
case SP_INCLUDES:
@@ -1418,146 +1367,151 @@ ParseDependencySourceSpecial(ParseSpecial specType, char *word,
case SP_LIBS:
Suff_AddLib(word);
break;
+ case SP_NOREADONLY:
+ Var_ReadOnly(word, false);
+ break;
case SP_NULL:
Suff_SetNull(word);
break;
case SP_OBJDIR:
Main_SetObjdir(false, "%s", word);
break;
+ case SP_READONLY:
+ Var_ReadOnly(word, true);
+ break;
default:
break;
}
}
static bool
-ParseDependencyTargets(char **inout_cp,
- char **inout_line,
+ApplyDependencyTarget(char *name, char *nameEnd, ParseSpecial *inout_special,
+ GNodeType *inout_targetAttr,
+ SearchPathList **inout_paths)
+{
+ char savedNameEnd = *nameEnd;
+ *nameEnd = '\0';
+
+ if (!HandleDependencyTarget(name, inout_special,
+ inout_targetAttr, inout_paths))
+ return false;
+
+ if (*inout_special == SP_NOT && *name != '\0')
+ HandleDependencyTargetMundane(name);
+ else if (*inout_special == SP_PATH && *name != '.' && *name != '\0')
+ Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", name);
+
+ *nameEnd = savedNameEnd;
+ return true;
+}
+
+static bool
+ParseDependencyTargets(char **pp,
const char *lstart,
- ParseSpecial *inout_specType,
- GNodeType *inout_tOp,
+ ParseSpecial *inout_special,
+ GNodeType *inout_targetAttr,
SearchPathList **inout_paths,
- StringList *curTargs)
+ const char *unexpanded_line)
{
- char *cp;
- char *tgt = *inout_line;
- char savec;
- const char *p;
+ char *p = *pp;
for (;;) {
- /*
- * Here LINE points to the beginning of the next word, and
- * LSTART points to the actual beginning of the line.
- */
+ char *tgt = p;
- /* Find the end of the next word. */
- cp = tgt;
- p = cp;
ParseDependencyTargetWord(&p, lstart);
- cp += p - cp;
/*
* If the word is followed by a left parenthesis, it's the
- * name of an object file inside an archive (ar file).
+ * name of one or more files inside an archive.
*/
- if (!ParseIsEscaped(lstart, cp) && *cp == '(') {
- /*
- * Archives must be handled specially to make sure the
- * OP_ARCHV flag is set in their 'type' field, for one
- * thing, and because things like "archive(file1.o
- * file2.o file3.o)" are permissible.
- *
- * Arch_ParseArchive will set 'line' to be the first
- * non-blank after the archive-spec. It creates/finds
- * nodes for the members and places them on the given
- * list, returning true if all went well and false if
- * there was an error in the specification. On error,
- * line should remain untouched.
- */
- if (!Arch_ParseArchive(&tgt, targets, SCOPE_CMDLINE)) {
+ if (!IsEscaped(lstart, p) && *p == '(') {
+ p = tgt;
+ if (!Arch_ParseArchive(&p, targets, SCOPE_CMDLINE)) {
Parse_Error(PARSE_FATAL,
"Error in archive specification: \"%s\"",
tgt);
return false;
}
-
- cp = tgt;
continue;
}
- if (*cp == '\0') {
- ParseErrorNoDependency(lstart);
+ if (*p == '\0') {
+ InvalidLineType(lstart, unexpanded_line);
return false;
}
- /* Insert a null terminator. */
- savec = *cp;
- *cp = '\0';
-
- if (!ParseDependencyTarget(tgt, inout_specType, inout_tOp,
- inout_paths))
+ if (!ApplyDependencyTarget(tgt, p, inout_special,
+ inout_targetAttr, inout_paths))
return false;
- /*
- * Have word in line. Get or create its node and stick it at
- * the end of the targets list
- */
- if (*inout_specType == SP_NOT && *tgt != '\0')
- ParseDependencyTargetMundane(tgt, curTargs);
- else if (*inout_specType == SP_PATH && *tgt != '.' &&
- *tgt != '\0')
- Parse_Error(PARSE_WARNING, "Extra target (%s) ignored",
- tgt);
-
- /* Don't need the inserted null terminator any more. */
- *cp = savec;
-
- /*
- * If it is a special type and not .PATH, it's the only target
- * we allow on this line.
- */
- if (*inout_specType != SP_NOT && *inout_specType != SP_PATH)
- ParseDependencyTargetExtraWarn(&cp, lstart);
+ if (*inout_special != SP_NOT && *inout_special != SP_PATH)
+ SkipExtraTargets(&p, lstart);
else
- pp_skip_whitespace(&cp);
+ pp_skip_whitespace(&p);
- tgt = cp;
- if (*tgt == '\0')
+ if (*p == '\0')
break;
- if ((*tgt == '!' || *tgt == ':') &&
- !ParseIsEscaped(lstart, tgt))
+ if ((*p == '!' || *p == ':') && !IsEscaped(lstart, p))
break;
}
- *inout_cp = cp;
- *inout_line = tgt;
+ *pp = p;
return true;
}
static void
-ParseDependencySourcesSpecial(char *start, char *end,
- ParseSpecial specType, SearchPathList *paths)
+ParseDependencySourcesSpecial(char *start,
+ ParseSpecial special, SearchPathList *paths)
{
- char savec;
while (*start != '\0') {
+ char savedEnd;
+ char *end = start;
while (*end != '\0' && !ch_isspace(*end))
end++;
- savec = *end;
+ savedEnd = *end;
*end = '\0';
- ParseDependencySourceSpecial(specType, start, paths);
- *end = savec;
- if (savec != '\0')
+ ParseDependencySourceSpecial(special, start, paths);
+ *end = savedEnd;
+ if (savedEnd != '\0')
end++;
pp_skip_whitespace(&end);
start = end;
}
}
+static void
+LinkVarToTargets(VarAssign *var)
+{
+ GNodeListNode *ln;
+
+ for (ln = targets->first; ln != NULL; ln = ln->next)
+ Parse_Var(var, ln->datum);
+}
+
static bool
-ParseDependencySourcesMundane(char *start, char *end,
- ParseSpecial specType, GNodeType tOp)
+ParseDependencySourcesMundane(char *start,
+ ParseSpecial special, GNodeType targetAttr)
{
while (*start != '\0') {
+ char *end = start;
+ VarAssign var;
+
+ /*
+ * Check for local variable assignment,
+ * rest of the line is the value.
+ */
+ if (Parse_IsVar(start, &var)) {
+ bool targetVarsEnabled = GetBooleanExpr(
+ "${.MAKE.TARGET_LOCAL_VARIABLES}", true);
+
+ if (targetVarsEnabled)
+ LinkVarToTargets(&var);
+ free(var.varname);
+ if (targetVarsEnabled)
+ return true;
+ }
+
/*
* The targets take real sources, so we must beware of archive
* specifications (i.e. things with left parentheses in them)
@@ -1588,7 +1542,8 @@ ParseDependencySourcesMundane(char *start, char *end,
while (!Lst_IsEmpty(&sources)) {
GNode *gn = Lst_Dequeue(&sources);
- ParseDependencySource(tOp, gn->name, specType);
+ ApplyDependencySource(targetAttr, gn->name,
+ special);
}
Lst_Done(&sources);
end = start;
@@ -1598,7 +1553,7 @@ ParseDependencySourcesMundane(char *start, char *end,
end++;
}
- ParseDependencySource(tOp, start, specType);
+ ApplyDependencySource(targetAttr, start, special);
}
pp_skip_whitespace(&end);
start = end;
@@ -1607,56 +1562,59 @@ ParseDependencySourcesMundane(char *start, char *end,
}
/*
- * In a dependency line like 'targets: sources', parse the sources.
+ * From a dependency line like 'targets: sources', parse the sources.
*
* See the tests depsrc-*.mk.
*/
static void
-ParseDependencySources(char *const line, char *const cp,
- GNodeType const tOp,
- ParseSpecial const specType,
- SearchPathList ** inout_paths)
-{
- if (line[0] == '\0') {
- ParseDependencySourcesEmpty(specType, *inout_paths);
- } else if (specType == SP_MFLAGS) {
- Main_ParseArgLine(line);
- /*
- * Set the initial character to a null-character so the loop
- * to get sources won't get anything.
- */
- *line = '\0';
- } else if (specType == SP_SHELL) {
- if (!Job_ParseShell(line)) {
+ParseDependencySources(char *p, GNodeType targetAttr,
+ ParseSpecial special, SearchPathList **inout_paths)
+{
+ if (*p == '\0') {
+ HandleDependencySourcesEmpty(special, *inout_paths);
+ } else if (special == SP_MFLAGS) {
+ Main_ParseArgLine(p);
+ return;
+ } else if (special == SP_SHELL) {
+ if (!Job_ParseShell(p)) {
Parse_Error(PARSE_FATAL,
"improper shell specification");
return;
}
- *line = '\0';
- } else if (specType == SP_NOTPARALLEL || specType == SP_SINGLESHELL ||
- specType == SP_DELETE_ON_ERROR) {
- *line = '\0';
- }
-
- /* Now go for the sources. */
- if (specType == SP_SUFFIXES || specType == SP_PATH ||
- specType == SP_INCLUDES || specType == SP_LIBS ||
- specType == SP_NULL || specType == SP_OBJDIR) {
- ParseDependencySourcesSpecial(line, cp, specType,
- *inout_paths);
+ return;
+ } else if (special == SP_NOTPARALLEL || special == SP_SINGLESHELL ||
+ special == SP_DELETE_ON_ERROR) {
+ return;
+ }
+
+ switch (special) {
+ case SP_INCLUDES:
+ case SP_LIBS:
+ case SP_NOREADONLY:
+ case SP_NULL:
+ case SP_OBJDIR:
+ case SP_PATH:
+ case SP_READONLY:
+ case SP_SUFFIXES:
+ case SP_SYSPATH:
+ ParseDependencySourcesSpecial(p, special, *inout_paths);
if (*inout_paths != NULL) {
Lst_Free(*inout_paths);
*inout_paths = NULL;
}
- if (specType == SP_PATH)
+ if (special == SP_PATH)
Dir_SetPATH();
- } else {
+ if (special == SP_SYSPATH)
+ Dir_SetSYSPATH();
+ break;
+ default:
assert(*inout_paths == NULL);
- if (!ParseDependencySourcesMundane(line, cp, specType, tOp))
+ if (!ParseDependencySourcesMundane(p, special, targetAttr))
return;
+ break;
}
- FindMainTarget();
+ MaybeUpdateMainTarget();
}
/*
@@ -1667,8 +1625,7 @@ ParseDependencySources(char *const line, char *const cp,
* targets. Nodes are created as necessary.
*
* The operator is applied to each node in the global 'targets' list,
- * which is where the nodes found for the targets are kept, by means of
- * the ParseOp function.
+ * which is where the nodes found for the targets are kept.
*
* The sources are parsed in much the same way as the targets, except
* that they are expanded using the wildcarding scheme of the C-Shell,
@@ -1676,109 +1633,68 @@ ParseDependencySources(char *const line, char *const cp,
* nodes is then linked to each of the targets as one of its children.
*
* Certain targets and sources such as .PHONY or .PRECIOUS are handled
- * specially. These are the ones detailed by the specType variable.
+ * specially, see ParseSpecial.
*
- * The storing of transformation rules such as '.c.o' is also taken care of
- * here. A target is recognized as a transformation rule by calling
- * Suff_IsTransform. If it is a transformation rule, its node is gotten
- * from the suffix module via Suff_AddTransform rather than the standard
- * Targ_FindNode in the target module.
+ * Transformation rules such as '.c.o' are also handled here, see
+ * Suff_AddTransform.
*
- * Upon return, the value of the line is unspecified.
+ * Upon return, the value of expandedLine is unspecified.
*/
static void
-ParseDependency(char *line)
-{
- char *cp; /* our current position */
- GNodeType op; /* the operator on the line */
- SearchPathList *paths; /* search paths to alter when parsing
- * a list of .PATH targets */
- GNodeType tOp; /* operator from special target */
- /* target names to be found and added to the targets list */
- StringList curTargs = LST_INIT;
- char *lstart = line;
-
- /*
- * specType contains the SPECial TYPE of the current target. It is
- * SP_NOT if the target is unspecial. If it *is* special, however, the
- * children are linked as children of the parent but not vice versa.
- */
- ParseSpecial specType = SP_NOT;
-
- DEBUG1(PARSE, "ParseDependency(%s)\n", line);
- tOp = OP_NONE;
+ParseDependency(char *expandedLine, const char *unexpandedLine)
+{
+ char *p;
+ SearchPathList *paths; /* search paths to alter when parsing a list
+ * of .PATH targets */
+ GNodeType targetAttr; /* from special sources */
+ ParseSpecial special; /* in special targets, the children are
+ * linked as children of the parent but not
+ * vice versa */
+ GNodeType op;
+ DEBUG1(PARSE, "ParseDependency(%s)\n", expandedLine);
+ p = expandedLine;
paths = NULL;
+ targetAttr = OP_NONE;
+ special = SP_NOT;
- /*
- * First, grind through the targets.
- */
- /* XXX: don't use 'line' as an iterator variable */
- if (!ParseDependencyTargets(&cp, &line, lstart, &specType, &tOp,
- &paths, &curTargs))
+ if (!ParseDependencyTargets(&p, expandedLine, &special, &targetAttr,
+ &paths, unexpandedLine))
goto out;
- /*
- * Don't need the list of target names anymore.
- * The targets themselves are now in the global variable 'targets'.
- */
- Lst_Done(&curTargs);
- Lst_Init(&curTargs);
-
if (!Lst_IsEmpty(targets))
- ParseDependencyCheckSpec(specType);
+ CheckSpecialMundaneMixture(special);
- /*
- * Have now parsed all the target names. Must parse the operator next.
- */
- if (!ParseDependencyOp(&cp, lstart, &op))
+ op = ParseDependencyOp(&p);
+ if (op == OP_NONE) {
+ InvalidLineType(expandedLine, unexpandedLine);
goto out;
-
- /*
- * Apply the operator to the target. This is how we remember which
- * operator a target was defined with. It fails if the operator
- * used isn't consistent across all references.
- */
+ }
ApplyDependencyOperator(op);
- /*
- * Onward to the sources.
- *
- * LINE will now point to the first source word, if any, or the
- * end of the string if not.
- */
- pp_skip_whitespace(&cp);
- line = cp; /* XXX: 'line' is an inappropriate name */
+ pp_skip_whitespace(&p);
- ParseDependencySources(line, cp, tOp, specType, &paths);
+ ParseDependencySources(p, targetAttr, special, &paths);
out:
if (paths != NULL)
Lst_Free(paths);
- Lst_Done(&curTargs);
}
-typedef struct VarAssignParsed {
- const char *nameStart; /* unexpanded */
- const char *nameEnd; /* before operator adjustment */
- const char *eq; /* the '=' of the assignment operator */
-} VarAssignParsed;
-
/*
* Determine the assignment operator and adjust the end of the variable
* name accordingly.
*/
-static void
-AdjustVarassignOp(const VarAssignParsed *pvar, const char *value,
- VarAssign *out_var)
+static VarAssign
+AdjustVarassignOp(const char *name, const char *nameEnd, const char *op,
+ const char *value)
{
- const char *op = pvar->eq;
- const char *const name = pvar->nameStart;
VarAssignOp type;
+ VarAssign va;
if (op > name && op[-1] == '+') {
- type = VAR_APPEND;
op--;
+ type = VAR_APPEND;
} else if (op > name && op[-1] == '?') {
op--;
@@ -1794,24 +1710,19 @@ AdjustVarassignOp(const VarAssignParsed *pvar, const char *value,
} else {
type = VAR_NORMAL;
-#ifdef SUNSHCMD
while (op > name && ch_isspace(op[-1]))
op--;
- if (op >= name + 3 && op[-3] == ':' && op[-2] == 's' &&
- op[-1] == 'h') {
- type = VAR_SHELL;
+ if (op - name >= 3 && memcmp(op - 3, ":sh", 3) == 0) {
op -= 3;
+ type = VAR_SHELL;
}
-#endif
}
- {
- const char *nameEnd = pvar->nameEnd < op ? pvar->nameEnd : op;
- out_var->varname = bmake_strsedup(pvar->nameStart, nameEnd);
- out_var->op = type;
- out_var->value = value;
- }
+ va.varname = bmake_strsedup(name, nameEnd < op ? nameEnd : op);
+ va.op = type;
+ va.value = value;
+ return va;
}
/*
@@ -1827,31 +1738,25 @@ AdjustVarassignOp(const VarAssignParsed *pvar, const char *value,
*
* Used for both lines in a file and command line arguments.
*/
-bool
+static bool
Parse_IsVar(const char *p, VarAssign *out_var)
{
- VarAssignParsed pvar;
- const char *firstSpace = NULL;
+ const char *nameStart, *nameEnd, *firstSpace, *eq;
int level = 0;
cpp_skip_hspace(&p); /* Skip to variable name */
/*
- * During parsing, the '+' of the '+=' operator is initially parsed
+ * During parsing, the '+' of the operator '+=' is initially parsed
* as part of the variable name. It is later corrected, as is the
- * ':sh' modifier. Of these two (nameEnd and op), the earlier one
+ * ':sh' modifier. Of these two (nameEnd and eq), the earlier one
* determines the actual end of the variable name.
*/
- pvar.nameStart = p;
-#ifdef CLEANUP
- pvar.nameEnd = NULL;
- pvar.eq = NULL;
-#endif
- /*
- * Scan for one of the assignment operators outside a variable
- * expansion.
- */
+ nameStart = p;
+ firstSpace = NULL;
+
+ /* Scan for one of the assignment operators outside an expression. */
while (*p != '\0') {
char ch = *p++;
if (ch == '(' || ch == '{') {
@@ -1866,36 +1771,32 @@ Parse_IsVar(const char *p, VarAssign *out_var)
if (level != 0)
continue;
- if (ch == ' ' || ch == '\t')
- if (firstSpace == NULL)
- firstSpace = p - 1;
+ if ((ch == ' ' || ch == '\t') && firstSpace == NULL)
+ firstSpace = p - 1;
while (ch == ' ' || ch == '\t')
ch = *p++;
-#ifdef SUNSHCMD
+ if (ch == '\0')
+ return false;
if (ch == ':' && p[0] == 's' && p[1] == 'h') {
p += 2;
continue;
}
-#endif
- if (ch == '=') {
- pvar.eq = p - 1;
- pvar.nameEnd = firstSpace != NULL ? firstSpace : p - 1;
- cpp_skip_whitespace(&p);
- AdjustVarassignOp(&pvar, p, out_var);
- return true;
- }
- if (*p == '=' &&
- (ch == '+' || ch == ':' || ch == '?' || ch == '!')) {
- pvar.eq = p;
- pvar.nameEnd = firstSpace != NULL ? firstSpace : p;
- p++;
- cpp_skip_whitespace(&p);
- AdjustVarassignOp(&pvar, p, out_var);
- return true;
- }
- if (firstSpace != NULL)
+ if (ch == '=')
+ eq = p - 1;
+ else if (*p == '=' &&
+ (ch == '+' || ch == ':' || ch == '?' || ch == '!'))
+ eq = p;
+ else if (firstSpace != NULL)
return false;
+ else
+ continue;
+
+ nameEnd = firstSpace != NULL ? firstSpace : eq;
+ p = eq + 1;
+ cpp_skip_whitespace(&p);
+ *out_var = AdjustVarassignOp(nameStart, nameEnd, eq, p);
+ return true;
}
return false;
@@ -1905,20 +1806,19 @@ Parse_IsVar(const char *p, VarAssign *out_var)
* Check for syntax errors such as unclosed expressions or unknown modifiers.
*/
static void
-VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *scope)
+VarCheckSyntax(VarAssignOp op, const char *uvalue, GNode *scope)
{
if (opts.strict) {
- if (type != VAR_SUBST && strchr(uvalue, '$') != NULL) {
- char *expandedValue;
-
- (void)Var_Subst(uvalue, scope, VARE_PARSE_ONLY,
- &expandedValue);
+ if (op != VAR_SUBST && strchr(uvalue, '$') != NULL) {
+ char *parsedValue = Var_Subst(uvalue,
+ scope, VARE_PARSE_ONLY);
/* TODO: handle errors */
- free(expandedValue);
+ free(parsedValue);
}
}
}
+/* Perform a variable assignment that uses the operator ':='. */
static void
VarAssign_EvalSubst(GNode *scope, const char *name, const char *uvalue,
FStr *out_avalue)
@@ -1926,16 +1826,18 @@ VarAssign_EvalSubst(GNode *scope, const char *name, const char *uvalue,
char *evalue;
/*
- * make sure that we set the variable the first time to nothing
+ * Make sure that we set the variable the first time to nothing
* so that it gets substituted.
*
* TODO: Add a test that demonstrates why this code is needed,
* apart from making the debug log longer.
+ *
+ * XXX: The variable name is expanded up to 3 times.
*/
if (!Var_ExistsExpand(scope, name))
Var_SetExpand(scope, name, "");
- (void)Var_Subst(uvalue, scope, VARE_KEEP_DOLLAR_UNDEF, &evalue);
+ evalue = Var_Subst(uvalue, scope, VARE_KEEP_DOLLAR_UNDEF);
/* TODO: handle errors */
Var_SetExpand(scope, name, evalue);
@@ -1943,29 +1845,24 @@ VarAssign_EvalSubst(GNode *scope, const char *name, const char *uvalue,
*out_avalue = FStr_InitOwn(evalue);
}
+/* Perform a variable assignment that uses the operator '!='. */
static void
VarAssign_EvalShell(const char *name, const char *uvalue, GNode *scope,
FStr *out_avalue)
{
FStr cmd;
- const char *errfmt;
- char *cmdOut;
+ char *output, *error;
cmd = FStr_InitRefer(uvalue);
- if (strchr(cmd.str, '$') != NULL) {
- char *expanded;
- (void)Var_Subst(cmd.str, SCOPE_CMDLINE, VARE_UNDEFERR,
- &expanded);
- /* TODO: handle errors */
- cmd = FStr_InitOwn(expanded);
- }
+ Var_Expand(&cmd, SCOPE_CMDLINE, VARE_UNDEFERR);
- cmdOut = Cmd_Exec(cmd.str, &errfmt);
- Var_SetExpand(scope, name, cmdOut);
- *out_avalue = FStr_InitOwn(cmdOut);
-
- if (errfmt != NULL)
- Parse_Error(PARSE_WARNING, errfmt, cmd.str);
+ output = Cmd_Exec(cmd.str, &error);
+ Var_SetExpand(scope, name, output);
+ *out_avalue = FStr_InitOwn(output);
+ if (error != NULL) {
+ Parse_Error(PARSE_WARNING, "%s", error);
+ free(error);
+ }
FStr_Done(&cmd);
}
@@ -1994,6 +1891,7 @@ VarAssign_Eval(const char *name, VarAssignOp op, const char *uvalue,
else if (op == VAR_SHELL)
VarAssign_EvalShell(name, uvalue, scope, &avalue);
else {
+ /* XXX: The variable name is expanded up to 2 times. */
if (op == VAR_DEFAULT && Var_ExistsExpand(scope, name))
return false;
@@ -2008,8 +1906,8 @@ VarAssign_Eval(const char *name, VarAssignOp op, const char *uvalue,
static void
VarAssignSpecial(const char *name, const char *avalue)
{
- if (strcmp(name, MAKEOVERRIDES) == 0)
- Main_ExportMAKEFLAGS(false); /* re-export MAKEFLAGS */
+ if (strcmp(name, ".MAKEOVERRIDES") == 0)
+ Main_ExportMAKEFLAGS(false); /* re-export MAKEFLAGS */
else if (strcmp(name, ".CURDIR") == 0) {
/*
* Someone is being (too?) clever...
@@ -2018,30 +1916,28 @@ VarAssignSpecial(const char *name, const char *avalue)
*/
Dir_InitCur(avalue);
Dir_SetPATH();
- } else if (strcmp(name, MAKE_JOB_PREFIX) == 0)
+ } else if (strcmp(name, ".MAKE.JOB.PREFIX") == 0)
Job_SetPrefix();
- else if (strcmp(name, MAKE_EXPORTED) == 0)
+ else if (strcmp(name, ".MAKE.EXPORTED") == 0)
Var_ExportVars(avalue);
}
-/* Perform the variable variable assignment in the given scope. */
-void
+/* Perform the variable assignment in the given scope. */
+static void
Parse_Var(VarAssign *var, GNode *scope)
{
- FStr avalue; /* actual value (maybe expanded) */
+ FStr avalue; /* actual value (maybe expanded) */
VarCheckSyntax(var->op, var->value, scope);
if (VarAssign_Eval(var->varname, var->op, var->value, scope, &avalue)) {
VarAssignSpecial(var->varname, avalue.str);
FStr_Done(&avalue);
}
-
- free(var->varname);
}
/*
- * See if the command possibly calls a sub-make by using the variable
+ * See if the command possibly calls a sub-make by using the
* expressions ${.MAKE}, ${MAKE} or the plain word "make".
*/
static bool
@@ -2054,7 +1950,7 @@ MaybeSubMake(const char *cmd)
char endc;
/* XXX: What if progname != "make"? */
- if (p[0] == 'm' && p[1] == 'a' && p[2] == 'k' && p[3] == 'e')
+ if (strncmp(p, "make", 4) == 0)
if (start == cmd || !ch_isalnum(p[-1]))
if (!ch_isalnum(p[4]))
return true;
@@ -2074,23 +1970,16 @@ MaybeSubMake(const char *cmd)
if (*p == '.') /* Accept either ${.MAKE} or ${MAKE}. */
p++;
- if (p[0] == 'M' && p[1] == 'A' && p[2] == 'K' && p[3] == 'E')
- if (p[4] == endc)
- return true;
+ if (strncmp(p, "MAKE", 4) == 0 && p[4] == endc)
+ return true;
}
return false;
}
-/*
- * Append the command to the target node.
- *
- * The node may be marked as a submake node if the command is determined to
- * be that.
- */
+/* Append the command to the target node. */
static void
-ParseAddCmd(GNode *gn, char *cmd)
+GNode_AddCommand(GNode *gn, char *cmd)
{
- /* Add to last (ie current) cohort for :: targets */
if ((gn->type & OP_DOUBLEDEP) && gn->cohorts.last != NULL)
gn = gn->cohorts.last->datum;
@@ -2099,200 +1988,57 @@ 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 */
- Lst_Append(&gn->commands, cmd);
- Parse_Error(PARSE_WARNING,
- "overriding commands for target \"%s\"; "
- "previous commands defined at %s: %d ignored",
- gn->name, gn->fname, gn->lineno);
-#else
Parse_Error(PARSE_WARNING,
"duplicate script for target \"%s\" ignored",
gn->name);
- ParseErrorInternal(gn->fname, (size_t)gn->lineno, PARSE_WARNING,
+ ParseErrorInternal(gn, PARSE_WARNING,
"using previous script for \"%s\" defined here",
gn->name);
-#endif
}
}
/*
- * Add a directory to the path searched for included makefiles bracketed
- * by double-quotes.
- */
-void
-Parse_AddIncludeDir(const char *dir)
-{
- (void)SearchPath_Add(parseIncPath, dir);
-}
-
-/*
- * 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
- * and ParseEOF.
+ * Parse a directive like '.include' or '.-include'.
*
- * System includes are looked up in sysIncPath, any other includes are looked
- * up in the parsedir and then in the directories specified by the -I command
- * line options.
+ * .include "user-makefile.mk"
+ * .include <system-makefile.mk>
*/
static void
-IncludeFile(char *file, bool isSystem, bool depinc, bool silent)
-{
- struct loadedfile *lf;
- char *fullname; /* full pathname of file */
- char *newName;
- char *slash, *incdir;
- int fd;
- int i;
-
- fullname = file[0] == '/' ? bmake_strdup(file) : NULL;
-
- if (fullname == NULL && !isSystem) {
- /*
- * Include files contained in double-quotes are first searched
- * relative to the including file's location. We don't want to
- * cd there, of course, so we just tack on the old file's
- * leading path components and call Dir_FindFile to see if
- * we can locate the file.
- */
-
- incdir = bmake_strdup(CurFile()->fname);
- slash = strrchr(incdir, '/');
- if (slash != NULL) {
- *slash = '\0';
- /*
- * Now do lexical processing of leading "../" on the
- * filename.
- */
- for (i = 0; strncmp(file + i, "../", 3) == 0; i += 3) {
- slash = strrchr(incdir + 1, '/');
- if (slash == NULL || strcmp(slash, "/..") == 0)
- break;
- *slash = '\0';
- }
- newName = str_concat3(incdir, "/", file + i);
- fullname = Dir_FindFile(newName, parseIncPath);
- if (fullname == NULL)
- fullname = Dir_FindFile(newName,
- &dirSearchPath);
- free(newName);
- }
- free(incdir);
-
- if (fullname == NULL) {
- /*
- * Makefile wasn't found in same directory as included
- * makefile.
- *
- * Search for it first on the -I search path, then on
- * the .PATH search path, if not found in a -I
- * directory. If we have a suffix-specific path, we
- * should use that.
- */
- const char *suff;
- SearchPath *suffPath = NULL;
-
- if ((suff = strrchr(file, '.')) != NULL) {
- suffPath = Suff_GetPath(suff);
- if (suffPath != NULL)
- fullname = Dir_FindFile(file, suffPath);
- }
- if (fullname == NULL) {
- fullname = Dir_FindFile(file, parseIncPath);
- if (fullname == NULL)
- fullname = Dir_FindFile(file,
- &dirSearchPath);
- }
- }
- }
-
- /* Looking for a system file or file still not found */
- if (fullname == NULL) {
- /*
- * Look for it on the system path
- */
- SearchPath *path = Lst_IsEmpty(&sysIncPath->dirs)
- ? defSysIncPath : sysIncPath;
- fullname = Dir_FindFile(file, path);
- }
-
- if (fullname == NULL) {
- if (!silent)
- Parse_Error(PARSE_FATAL, "Could not find %s", file);
- return;
- }
-
- /* Actually open the file... */
- fd = open(fullname, O_RDONLY);
- if (fd == -1) {
- if (!silent)
- Parse_Error(PARSE_FATAL, "Cannot open %s", fullname);
- free(fullname);
- return;
- }
-
- /* load it */
- lf = loadfile(fullname, fd);
-
- /* Start reading from this file next */
- Parse_SetInput(fullname, 0, -1, loadedfile_readMore, lf);
- CurFile()->lf = lf;
- if (depinc)
- doing_depend = depinc; /* only turn it on */
-}
-
-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 == '<')
- endc = '>';
- else
- endc = '"';
+ endc = *p++ == '<' ? '>' : '"';
+ 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';
-
- /*
- * 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 */
+ *p = '\0';
- IncludeFile(file, endc == '>', directive[0] == 'd', silent);
- free(file);
+ Var_Expand(&file, SCOPE_CMDLINE, VARE_WANTRES);
+ IncludeFile(file.str, endc == '>', directive[0] == 'd', silent);
+ FStr_Done(&file);
}
/*
@@ -2314,11 +2060,11 @@ 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);
+ DEBUG4(PARSE, "SetFilenameVars: ${%s} = `%s' ${%s} = `%s'\n",
+ dirvar, dirname.str, filevar, basename);
FStr_Done(&dirname);
}
@@ -2332,17 +2078,17 @@ static const char *
GetActuallyIncludingFile(void)
{
size_t i;
- const IFile *incs = GetInclude(0);
+ const IncludedFile *incs = GetInclude(0);
for (i = includes.len; i >= 2; i--)
- if (!incs[i - 1].fromForLoop)
- return incs[i - 2].fname;
+ if (incs[i - 1].forLoop == NULL)
+ return incs[i - 2].name.str;
return NULL;
}
/* Set .PARSEDIR, .PARSEFILE, .INCLUDEDFROMDIR and .INCLUDEDFROMFILE. */
static void
-ParseSetParseFile(const char *filename)
+SetParseFile(const char *filename)
{
const char *including;
@@ -2363,17 +2109,16 @@ StrContainsWord(const char *str, const char *word)
{
size_t strLen = strlen(str);
size_t wordLen = strlen(word);
- const char *p, *end;
+ const char *p;
if (strLen < wordLen)
- return false; /* str is too short to contain word */
+ return false;
- end = str + strLen - wordLen;
for (p = str; p != NULL; p = strchr(p, ' ')) {
if (*p == ' ')
p++;
- if (p > end)
- return false; /* cannot contain word */
+ if (p > str + strLen - wordLen)
+ return false;
if (memcmp(p, word, wordLen) == 0 &&
(p[wordLen] == '\0' || p[wordLen] == ' '))
@@ -2405,67 +2150,47 @@ VarContainsWord(const char *varname, const char *word)
* of makefiles that have been loaded.
*/
static void
-ParseTrackInput(const char *name)
+TrackInput(const char *name)
{
- if (!VarContainsWord(MAKE_MAKEFILES, name))
- Global_Append(MAKE_MAKEFILES, name);
+ if (!VarContainsWord(".MAKE.MAKEFILES", name))
+ Global_Append(".MAKE.MAKEFILES", name);
}
-/*
- * Start parsing from the given source.
- *
- * The given file is added to the includes stack.
- */
+/* Parse from the given buffer, later return to the current file. */
void
-Parse_SetInput(const char *name, int lineno, int fd,
- ReadMoreProc readMore, void *readMoreArg)
+Parse_PushInput(const char *name, unsigned lineno, unsigned readLines,
+ Buffer buf, struct ForLoop *forLoop)
{
- IFile *curFile;
- char *buf;
- size_t len;
- bool fromForLoop = name == NULL;
+ IncludedFile *curFile;
- if (fromForLoop)
- name = CurFile()->fname;
+ if (forLoop != NULL)
+ name = CurFile()->name.str;
else
- ParseTrackInput(name);
-
- DEBUG3(PARSE, "Parse_SetInput: %s %s, line %d\n",
- readMore == loadedfile_readMore ? "file" : ".for loop in",
- name, lineno);
+ TrackInput(name);
- if (fd == -1 && readMore == NULL)
- /* sanity */
- return;
+ DEBUG3(PARSE, "Parse_PushInput: %s %s, line %u\n",
+ forLoop != NULL ? ".for loop in": "file", name, lineno);
curFile = Vector_Push(&includes);
- curFile->fname = bmake_strdup(name);
- curFile->fromForLoop = fromForLoop;
+ curFile->name = FStr_InitOwn(bmake_strdup(name));
curFile->lineno = lineno;
- curFile->first_lineno = lineno;
- curFile->readMore = readMore;
- curFile->readMoreArg = readMoreArg;
- curFile->lf = NULL;
+ curFile->readLines = readLines;
+ curFile->forHeadLineno = lineno;
+ curFile->forBodyReadLines = readLines;
+ curFile->buf = buf;
curFile->depending = doing_depend; /* restore this on EOF */
+ curFile->guardState = forLoop == NULL ? GS_START : GS_NO;
+ curFile->guard = NULL;
+ curFile->forLoop = forLoop;
- assert(readMore != NULL);
+ if (forLoop != NULL && !For_NextIteration(forLoop, &curFile->buf))
+ abort(); /* see For_Run */
- /* Get first block of input data */
- buf = curFile->readMore(curFile->readMoreArg, &len);
- if (buf == NULL) {
- /* Was all a waste of time ... */
- if (curFile->fname != NULL)
- free(curFile->fname);
- free(curFile);
- return;
- }
- curFile->buf_freeIt = buf;
- curFile->buf_ptr = buf;
- curFile->buf_end = buf + len;
-
- curFile->cond_depth = Cond_save_depth();
- ParseSetParseFile(name);
+ curFile->buf_ptr = curFile->buf.data;
+ curFile->buf_end = curFile->buf.data + curFile->buf.len;
+ curFile->condMinDepth = cond_depth;
+ SetParseFile(name);
}
/* Check if the directive is an include directive. */
@@ -2483,7 +2208,6 @@ IsInclude(const char *dir, bool sysv)
}
-#ifdef SYSVINCLUDE
/* Check if the line is a SYSV include directive. */
static bool
IsSysVInclude(const char *line)
@@ -2511,46 +2235,35 @@ IsSysVInclude(const char *line)
static void
ParseTraditionalInclude(char *line)
{
- char *cp; /* current position in file spec */
+ char *p; /* current position in file spec */
bool done = false;
bool silent = line[0] != 'i';
char *file = line + (silent ? 8 : 7);
char *all_files;
- DEBUG2(PARSE, "%s: %s\n", __func__, file);
+ DEBUG1(PARSE, "ParseTraditionalInclude: %s\n", file);
pp_skip_whitespace(&file);
- /*
- * Substitute for any variables in the file name before trying to
- * find the thing.
- */
- (void)Var_Subst(file, SCOPE_CMDLINE, VARE_WANTRES, &all_files);
+ all_files = Var_Subst(file, SCOPE_CMDLINE, VARE_WANTRES);
/* TODO: handle errors */
- if (*file == '\0') {
- Parse_Error(PARSE_FATAL, "Filename missing from \"include\"");
- goto out;
- }
-
- for (file = all_files; !done; file = cp + 1) {
+ for (file = all_files; !done; file = p + 1) {
/* Skip to end of line or next whitespace */
- for (cp = file; *cp != '\0' && !ch_isspace(*cp); cp++)
+ for (p = file; *p != '\0' && !ch_isspace(*p); p++)
continue;
- if (*cp != '\0')
- *cp = '\0';
+ if (*p != '\0')
+ *p = '\0';
else
done = true;
IncludeFile(file, false, false, silent);
}
-out:
+
free(all_files);
}
-#endif
-#ifdef GMAKEEXPORT
/* Parse "export <variable>=<value>", and actually export it. */
static void
ParseGmakeExport(char *line)
@@ -2558,7 +2271,7 @@ ParseGmakeExport(char *line)
char *variable = line + 6;
char *value;
- DEBUG2(PARSE, "%s: %s\n", __func__, variable);
+ DEBUG1(PARSE, "ParseGmakeExport: %s\n", variable);
pp_skip_whitespace(&variable);
@@ -2575,18 +2288,16 @@ ParseGmakeExport(char *line)
/*
* Expand the value before putting it in the environment.
*/
- (void)Var_Subst(value, SCOPE_CMDLINE, VARE_WANTRES, &value);
+ value = Var_Subst(value, SCOPE_CMDLINE, VARE_WANTRES);
/* TODO: handle errors */
setenv(variable, value, 1);
free(value);
}
-#endif
/*
- * Called when EOF is reached in the current file. If we were reading an
- * include file or a .for loop, the includes stack is popped and things set
- * up to go back to reading the previous file at the previous location.
+ * When the end of the current file or .for loop is reached, continue reading
+ * the previous file at the previous location.
*
* Results:
* true to continue parsing, i.e. it had only reached the end of an
@@ -2595,33 +2306,30 @@ ParseGmakeExport(char *line)
static bool
ParseEOF(void)
{
- char *ptr;
- size_t len;
- IFile *curFile = CurFile();
-
- assert(curFile->readMore != NULL);
+ IncludedFile *curFile = CurFile();
- doing_depend = curFile->depending; /* restore this */
- /* get next input buffer, if any */
- ptr = curFile->readMore(curFile->readMoreArg, &len);
- curFile->buf_ptr = ptr;
- curFile->buf_freeIt = ptr;
- curFile->buf_end = ptr == NULL ? NULL : ptr + len;
- curFile->lineno = curFile->first_lineno;
- if (ptr != NULL)
- return true; /* Iterate again */
+ doing_depend = curFile->depending;
+ if (curFile->forLoop != NULL &&
+ For_NextIteration(curFile->forLoop, &curFile->buf)) {
+ curFile->buf_ptr = curFile->buf.data;
+ curFile->buf_end = curFile->buf.data + curFile->buf.len;
+ curFile->readLines = curFile->forBodyReadLines;
+ return true;
+ }
- /* Ensure the makefile (or loop) didn't have mismatched conditionals */
- Cond_restore_depth(curFile->cond_depth);
+ Cond_EndFile();
- if (curFile->lf != NULL) {
- loadedfile_destroy(curFile->lf);
- curFile->lf = NULL;
+ if (curFile->guardState == GS_DONE)
+ HashTable_Set(&guards, curFile->name.str, curFile->guard);
+ else if (curFile->guard != NULL) {
+ free(curFile->guard->name);
+ free(curFile->guard);
}
- /* Dispose of curFile info */
- /* Leak curFile->fname because all the GNodes have pointers to it. */
- free(curFile->buf_freeIt);
+ FStr_Done(&curFile->name);
+ Buf_Done(&curFile->buf);
+ if (curFile->forLoop != NULL)
+ ForLoop_Free(curFile->forLoop);
Vector_Pop(&includes);
if (includes.len == 0) {
@@ -2634,10 +2342,10 @@ ParseEOF(void)
}
curFile = CurFile();
- DEBUG2(PARSE, "ParseEOF: returning to file %s, line %d\n",
- curFile->fname, curFile->lineno);
+ DEBUG2(PARSE, "ParseEOF: returning to file %s, line %u\n",
+ curFile->name.str, curFile->readLines + 1);
- ParseSetParseFile(curFile->fname);
+ SetParseFile(curFile->name.str);
return true;
}
@@ -2649,34 +2357,35 @@ 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)
+ParseRawLine(IncludedFile *curFile, char **out_line, char **out_line_end,
+ char **out_firstBackslash, char **out_commentLineEnd)
{
char *line = curFile->buf_ptr;
+ char *buf_end = curFile->buf_end;
char *p = line;
char *line_end = line;
char *firstBackslash = NULL;
- char *firstComment = NULL;
+ char *commentLineEnd = NULL;
ParseRawLineResult res = PRLR_LINE;
- curFile->lineno++;
+ curFile->readLines++;
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')) {
+ if (ch == '\0' || (ch == '\\' && p[1] == '\0')) {
Parse_Error(PARSE_FATAL, "Zero byte read from file");
- return PRLR_ERROR;
+ exit(2);
}
/* Treat next character after '\' as literal. */
@@ -2684,8 +2393,8 @@ ParseRawLine(IFile *curFile, char **out_line, char **out_line_end,
if (firstBackslash == NULL)
firstBackslash = p;
if (p[1] == '\n') {
- curFile->lineno++;
- if (p + 2 == curFile->buf_end) {
+ curFile->readLines++;
+ if (p + 2 == buf_end) {
line_end = p;
*line_end = '\n';
p += 2;
@@ -2694,7 +2403,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;
}
@@ -2702,9 +2411,9 @@ ParseRawLine(IFile *curFile, char **out_line, char **out_line_end,
* Remember the first '#' for comment stripping, unless
* the previous char was '[', as in the modifier ':[#]'.
*/
- if (ch == '#' && firstComment == NULL &&
+ if (ch == '#' && commentLineEnd == NULL &&
!(p > line && p[-1] == '['))
- firstComment = line_end;
+ commentLineEnd = line_end;
p++;
if (ch == '\n')
@@ -2715,11 +2424,11 @@ ParseRawLine(IFile *curFile, char **out_line, char **out_line_end,
line_end = p;
}
- *out_line = line;
curFile->buf_ptr = p;
+ *out_line = line;
*out_line_end = line_end;
*out_firstBackslash = firstBackslash;
- *out_firstComment = firstComment;
+ *out_commentLineEnd = commentLineEnd;
return res;
}
@@ -2730,7 +2439,7 @@ ParseRawLine(IFile *curFile, char **out_line, char **out_line_end,
static void
UnescapeBackslash(char *line, char *start)
{
- char *src = start;
+ const char *src = start;
char *dst = start;
char *spaceStart = line;
@@ -2745,35 +2454,24 @@ UnescapeBackslash(char *line, char *start)
ch = *src++;
if (ch == '\0') {
- /* Delete '\\' at end of buffer */
+ /* Delete '\\' at the end of the buffer. */
dst--;
break;
}
- /* Delete '\\' from before '#' on non-command lines */
- if (ch == '#' && line[0] != '\t') {
+ /* Delete '\\' from before '#' on non-command lines. */
+ if (ch == '#' && line[0] != '\t')
*dst++ = ch;
- continue;
- }
-
- if (ch != '\n') {
- /* Leave '\\' in buffer for later */
+ else if (ch == '\n') {
+ cpp_skip_hspace(&src);
+ *dst++ = ' ';
+ } else {
+ /* Leave '\\' in the buffer for later. */
*dst++ = '\\';
- /*
- * Make sure we don't delete an escaped ' ' from the
- * line end.
- */
- spaceStart = dst + 1;
*dst++ = ch;
- continue;
+ /* Keep an escaped ' ' at the line end. */
+ spaceStart = dst;
}
-
- /*
- * Escaped '\n' -- replace following whitespace with a single
- * ' '.
- */
- pp_skip_hspace(&src);
- *dst++ = ' ';
}
/* Delete any trailing spaces - eg from empty continuations */
@@ -2782,13 +2480,13 @@ UnescapeBackslash(char *line, char *start)
*dst = '\0';
}
-typedef enum GetLineMode {
+typedef enum LineKind {
/*
* Return the next line that is neither empty nor a comment.
* Backslash line continuations are folded into a single space.
* A trailing comment, if any, is discarded.
*/
- GLM_NONEMPTY,
+ LK_NONEMPTY,
/*
* Return the next line, even if it is empty or a comment.
@@ -2797,7 +2495,7 @@ typedef enum GetLineMode {
* Used in .for loops to collect the body of the loop while waiting
* for the corresponding .endfor.
*/
- GLM_FOR_BODY,
+ LK_FOR_BODY,
/*
* Return the next line that starts with a dot.
@@ -2807,29 +2505,34 @@ typedef enum GetLineMode {
* Used in .if directives to skip over irrelevant branches while
* waiting for the corresponding .endif.
*/
- GLM_DOT
-} GetLineMode;
+ LK_DOT
+} LineKind;
-/* Return the next "interesting" logical line from the current file. */
+/*
+ * Return the next "interesting" logical line from the current file. The
+ * returned string will be freed at the end of including the file.
+ */
static char *
-ParseGetLine(GetLineMode mode)
+ReadLowLevelLine(LineKind kind)
{
- IFile *curFile = CurFile();
+ IncludedFile *curFile = CurFile();
+ ParseRawLineResult res;
char *line;
char *line_end;
char *firstBackslash;
- char *firstComment;
+ char *commentLineEnd;
for (;;) {
- ParseRawLineResult res = ParseRawLine(curFile,
- &line, &line_end, &firstBackslash, &firstComment);
+ curFile->lineno = curFile->readLines + 1;
+ res = ParseRawLine(curFile,
+ &line, &line_end, &firstBackslash, &commentLineEnd);
if (res == PRLR_ERROR)
return NULL;
- if (line_end == line || firstComment == line) {
+ if (line == line_end || line == commentLineEnd) {
if (res == PRLR_EOF)
return NULL;
- if (mode != GLM_FOR_BODY)
+ if (kind != LK_FOR_BODY)
continue;
}
@@ -2837,59 +2540,39 @@ ParseGetLine(GetLineMode mode)
assert(ch_isspace(*line_end));
*line_end = '\0';
- if (mode == GLM_FOR_BODY)
+ if (kind == LK_FOR_BODY)
return line; /* Don't join the physical lines. */
- if (mode == GLM_DOT && line[0] != '.')
+ if (kind == LK_DOT && line[0] != '.')
continue;
break;
}
- /* Brutally ignore anything after a non-escaped '#' in non-commands. */
- if (firstComment != NULL && line[0] != '\t')
- *firstComment = '\0';
-
- /* If we didn't see a '\\' then the in-situ data is fine. */
- if (firstBackslash == NULL)
- return line;
-
- /* Remove escapes from '\n' and '#' */
- UnescapeBackslash(line, firstBackslash);
-
+ if (commentLineEnd != NULL && line[0] != '\t')
+ *commentLineEnd = '\0';
+ if (firstBackslash != NULL)
+ UnescapeBackslash(line, firstBackslash);
return line;
}
static bool
-ParseSkippedBranches(void)
+SkipIrrelevantBranches(void)
{
- char *line;
+ const char *line;
- while ((line = ParseGetLine(GLM_DOT)) != NULL) {
- if (Cond_EvalLine(line) == COND_PARSE)
- break;
- /*
- * TODO: Check for typos in .elif directives
- * such as .elsif or .elseif.
- *
- * This check will probably duplicate some of
- * the code in ParseLine. Most of the code
- * there cannot apply, only ParseVarassign and
- * ParseDependencyLine can, and to prevent code
- * duplication, these would need to be called
- * with a flag called onlyCheckSyntax.
- *
- * See directive-elif.mk for details.
- */
- }
-
- return line != NULL;
+ while ((line = ReadLowLevelLine(LK_DOT)) != NULL)
+ if (Cond_EvalLine(line) == CR_TRUE)
+ return true;
+ return false;
}
static bool
ParseForLoop(const char *line)
{
int rval;
- int firstLineno;
+ unsigned forHeadLineno;
+ unsigned bodyReadLines;
+ int forLevel;
rval = For_Eval(line);
if (rval == 0)
@@ -2897,59 +2580,76 @@ ParseForLoop(const char *line)
if (rval < 0)
return true; /* Syntax error - error printed, ignore line */
- firstLineno = CurFile()->lineno;
+ forHeadLineno = CurFile()->lineno;
+ bodyReadLines = CurFile()->readLines;
- /* Accumulate loop lines until matching .endfor */
+ /* Accumulate the loop body until the matching '.endfor'. */
+ forLevel = 1;
do {
- line = ParseGetLine(GLM_FOR_BODY);
+ line = ReadLowLevelLine(LK_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));
-
- For_Run(firstLineno); /* Stash each iteration as a new 'input file' */
+ } while (For_Accum(line, &forLevel));
- return true; /* Read next line from for-loop buffer */
+ For_Run(forHeadLineno, bodyReadLines);
+ return true;
}
/*
* Read an entire line from the input file.
*
- * Empty lines, .if and .for are completely handled by this function,
- * leaving only variable assignments, other directives, dependency lines
- * and shell commands to the caller.
+ * Empty lines, .if and .for are handled by this function, while variable
+ * assignments, other directives, dependency lines and shell commands are
+ * handled by the caller.
*
- * Results:
- * A line without its newline and without any trailing whitespace,
- * or NULL.
+ * Return a line without trailing whitespace, or NULL for EOF. The returned
+ * string will be freed at the end of including the file.
*/
static char *
-ParseReadLine(void)
+ReadHighLevelLine(void)
{
char *line;
+ CondResult condResult;
for (;;) {
- line = ParseGetLine(GLM_NONEMPTY);
+ IncludedFile *curFile = CurFile();
+ line = ReadLowLevelLine(LK_NONEMPTY);
+ if (posix_state == PS_MAYBE_NEXT_LINE)
+ posix_state = PS_NOW_OR_NEVER;
+ else
+ posix_state = PS_TOO_LATE;
if (line == NULL)
return NULL;
+ DEBUG2(PARSE, "Parsing line %u: %s\n", curFile->lineno, line);
+ if (curFile->guardState != GS_NO
+ && ((curFile->guardState == GS_START && line[0] != '.')
+ || curFile->guardState == GS_DONE))
+ curFile->guardState = GS_NO;
if (line[0] != '.')
return line;
- /*
- * The line might be a conditional. Ask the conditional module
- * about it and act accordingly
- */
- switch (Cond_EvalLine(line)) {
- case COND_SKIP:
- if (!ParseSkippedBranches())
+ condResult = Cond_EvalLine(line);
+ if (curFile->guardState == GS_START) {
+ Guard *guard;
+ if (condResult != CR_ERROR
+ && (guard = Cond_ExtractGuard(line)) != NULL) {
+ curFile->guardState = GS_COND;
+ curFile->guard = guard;
+ } else
+ curFile->guardState = GS_NO;
+ }
+ switch (condResult) {
+ case CR_FALSE: /* May also mean a syntax error. */
+ if (!SkipIrrelevantBranches())
return NULL;
continue;
- case COND_PARSE:
+ case CR_TRUE:
continue;
- case COND_INVALID: /* Not a conditional line */
+ case CR_ERROR: /* Not a conditional line */
if (ParseForLoop(line))
continue;
break;
@@ -3004,7 +2704,7 @@ ParseLine_ShellCommand(const char *p)
for (ln = targets->first; ln != NULL; ln = ln->next) {
GNode *gn = ln->datum;
- ParseAddCmd(gn, cmd);
+ GNode_AddCommand(gn, cmd);
}
#ifdef CLEANUP
Lst_Append(&targCmds, cmd);
@@ -3012,10 +2712,22 @@ ParseLine_ShellCommand(const char *p)
}
}
-MAKE_INLINE bool
-IsDirective(const char *dir, size_t dirlen, const char *name)
+static void
+HandleBreak(const char *arg)
{
- return dirlen == strlen(name) && memcmp(dir, name, dirlen) == 0;
+ IncludedFile *curFile = CurFile();
+
+ if (arg[0] != '\0')
+ Parse_Error(PARSE_FATAL,
+ "The .break directive does not take arguments");
+
+ if (curFile->forLoop != NULL) {
+ /* pretend we reached EOF */
+ For_Break(curFile->forLoop);
+ cond_depth = CurFile_CondMinDepth();
+ ParseEOF();
+ } else
+ Parse_Error(PARSE_FATAL, "break outside of for loop");
}
/*
@@ -3025,75 +2737,87 @@ IsDirective(const char *dir, size_t dirlen, const char *name)
static bool
ParseDirective(char *line)
{
- char *cp = line + 1;
- const char *dir, *arg;
- size_t dirlen;
+ char *p = line + 1;
+ const char *arg;
+ Substring dir;
- pp_skip_whitespace(&cp);
- if (IsInclude(cp, false)) {
- ParseInclude(cp);
+ pp_skip_whitespace(&p);
+ if (IsInclude(p, false)) {
+ ParseInclude(p);
return true;
}
- dir = cp;
- while (ch_isalpha(*cp) || *cp == '-')
- cp++;
- dirlen = (size_t)(cp - dir);
+ dir.start = p;
+ while (ch_islower(*p) || *p == '-')
+ p++;
+ dir.end = p;
- if (*cp != '\0' && !ch_isspace(*cp))
+ if (*p != '\0' && !ch_isspace(*p))
return false;
- pp_skip_whitespace(&cp);
- arg = cp;
+ pp_skip_whitespace(&p);
+ arg = p;
- if (IsDirective(dir, dirlen, "undef")) {
- Var_Undef(cp);
- return true;
- } else if (IsDirective(dir, dirlen, "export")) {
+ if (Substring_Equals(dir, "break"))
+ HandleBreak(arg);
+ else if (Substring_Equals(dir, "undef"))
+ Var_Undef(arg);
+ 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")) {
- ParseMessage(PARSE_INFO, "info", arg);
- return true;
- } else if (IsDirective(dir, dirlen, "warning")) {
- ParseMessage(PARSE_WARNING, "warning", arg);
- return true;
- } else if (IsDirective(dir, dirlen, "error")) {
- ParseMessage(PARSE_FATAL, "error", arg);
- return true;
- }
- return false;
+ else if (Substring_Equals(dir, "info"))
+ HandleMessage(PARSE_INFO, "info", arg);
+ else if (Substring_Equals(dir, "warning"))
+ HandleMessage(PARSE_WARNING, "warning", arg);
+ else if (Substring_Equals(dir, "error"))
+ HandleMessage(PARSE_FATAL, "error", arg);
+ else
+ return false;
+ return true;
}
-static bool
-ParseVarassign(const char *line)
+bool
+Parse_VarAssign(const char *line, bool finishDependencyGroup, GNode *scope)
{
VarAssign var;
if (!Parse_IsVar(line, &var))
return false;
-
- FinishDependencyGroup();
- Parse_Var(&var, SCOPE_GLOBAL);
+ if (finishDependencyGroup)
+ FinishDependencyGroup();
+ Parse_Var(&var, scope);
+ free(var.varname);
return true;
}
+void
+Parse_GuardElse(void)
+{
+ IncludedFile *curFile = CurFile();
+ if (cond_depth == curFile->condMinDepth + 1)
+ curFile->guardState = GS_NO;
+}
+
+void
+Parse_GuardEndif(void)
+{
+ IncludedFile *curFile = CurFile();
+ if (cond_depth == curFile->condMinDepth
+ && curFile->guardState == GS_COND)
+ curFile->guardState = GS_DONE;
+}
+
static char *
FindSemicolon(char *p)
{
- int level = 0;
+ int depth = 0;
for (; *p != '\0'; p++) {
if (*p == '\\' && p[1] != '\0') {
@@ -3102,19 +2826,15 @@ FindSemicolon(char *p)
}
if (*p == '$' && (p[1] == '(' || p[1] == '{'))
- level++;
- else if (level > 0 && (*p == ')' || *p == '}'))
- level--;
- else if (level == 0 && *p == ';')
+ depth++;
+ else if (depth > 0 && (*p == ')' || *p == '}'))
+ depth--;
+ else if (depth == 0 && *p == ';')
break;
}
return p;
}
-/*
- * dependency -> target... op [source...] [';' command]
- * op -> ':' | '::' | '!'
- */
static void
ParseDependencyLine(char *line)
{
@@ -3122,11 +2842,6 @@ ParseDependencyLine(char *line)
char *expanded_line;
const char *shellcmd = NULL;
- /*
- * For some reason - probably to make the parser impossible -
- * a ';' can be used to separate commands from dependencies.
- * Attempt to avoid ';' inside substitution patterns.
- */
{
char *semicolon = FindSemicolon(line);
if (*semicolon != '\0') {
@@ -3137,7 +2852,7 @@ ParseDependencyLine(char *line)
}
/*
- * We now know it's a dependency line so it needs to have all
+ * We now know it's a dependency line, so it needs to have all
* variables expanded before being parsed.
*
* XXX: Ideally the dependency line would first be split into
@@ -3148,27 +2863,29 @@ ParseDependencyLine(char *line)
* as well.
*
* Parsing the line first would also prevent that targets
- * generated from variable expressions are interpreted as the
+ * generated from expressions are interpreted as the
* dependency operator, such as in "target${:U\:} middle: source",
* in which the middle is interpreted as a source, not a target.
*/
- /* In lint mode, allow undefined variables to appear in
- * dependency lines.
+ /*
+ * In lint mode, allow undefined variables to appear in dependency
+ * lines.
*
- * Ideally, only the right-hand side would allow undefined
- * variables since it is common to have optional dependencies.
- * Having undefined variables on the left-hand side is more
- * unusual though. Since both sides are expanded in a single
- * pass, there is not much choice what to do here.
+ * Ideally, only the right-hand side would allow undefined variables
+ * since it is common to have optional dependencies. Having undefined
+ * variables on the left-hand side is more unusual though. Since
+ * both sides are expanded in a single pass, there is not much choice
+ * what to do here.
*
- * In normal mode, it does not matter whether undefined
- * variables are allowed or not since as of 2020-09-14,
- * Var_Parse does not print any parse errors in such a case.
- * It simply returns the special empty string var_Error,
- * which cannot be detected in the result of Var_Subst. */
+ * In normal mode, it does not matter whether undefined variables are
+ * allowed or not since as of 2020-09-14, Var_Parse does not print
+ * any parse errors in such a case. It simply returns the special
+ * empty string var_Error, which cannot be detected in the result of
+ * Var_Subst.
+ */
emode = opts.strict ? VARE_WANTRES : VARE_UNDEFERR;
- (void)Var_Subst(line, SCOPE_CMDLINE, emode, &expanded_line);
+ expanded_line = Var_Subst(line, SCOPE_CMDLINE, emode);
/* TODO: handle errors */
/* Need a fresh list for the target nodes */
@@ -3176,7 +2893,7 @@ ParseDependencyLine(char *line)
Lst_Free(targets);
targets = Lst_New();
- ParseDependency(expanded_line);
+ ParseDependency(expanded_line, line);
free(expanded_line);
if (shellcmd != NULL)
@@ -3186,13 +2903,6 @@ ParseDependencyLine(char *line)
static void
ParseLine(char *line)
{
- /*
- * Lines that begin with '.' can be pretty much anything:
- * - directives like '.include' or '.if',
- * - suffix rules like '.c.o:',
- * - dependencies for filenames that start with '.',
- * - variable assignments like '.tmp=value'.
- */
if (line[0] == '.' && ParseDirective(line))
return;
@@ -3201,28 +2911,18 @@ ParseLine(char *line)
return;
}
-#ifdef SYSVINCLUDE
if (IsSysVInclude(line)) {
- /*
- * It's an S3/S5-style "include".
- */
ParseTraditionalInclude(line);
return;
}
-#endif
-#ifdef GMAKEEXPORT
if (strncmp(line, "export", 6) == 0 && ch_isspace(line[6]) &&
strchr(line, ':') == NULL) {
- /*
- * It's a Gmake "export".
- */
ParseGmakeExport(line);
return;
}
-#endif
- if (ParseVarassign(line))
+ if (Parse_VarAssign(line, true, SCOPE_GLOBAL))
return;
FinishDependencyGroup();
@@ -3230,47 +2930,35 @@ ParseLine(char *line)
ParseDependencyLine(line);
}
-/*
- * Parse a top-level makefile, incorporating its content into the global
- * dependency graph.
- *
- * Input:
- * name The name of the file being read
- * fd The open file to parse; will be closed at the end
- */
+/* Interpret a top-level makefile. */
void
Parse_File(const char *name, int fd)
{
- char *line; /* the line we're working on */
- struct loadedfile *lf;
+ char *line;
+ Buffer buf;
- lf = loadfile(name, fd);
+ buf = LoadFile(name, fd != -1 ? fd : STDIN_FILENO);
+ if (fd != -1)
+ (void)close(fd);
assert(targets == NULL);
- if (name == NULL)
- name = "(stdin)";
-
- Parse_SetInput(name, 0, -1, loadedfile_readMore, lf);
- CurFile()->lf = lf;
+ Parse_PushInput(name, 1, 0, buf, NULL);
do {
- while ((line = ParseReadLine()) != NULL) {
- DEBUG2(PARSE, "ParseReadLine (%d): '%s'\n",
- CurFile()->lineno, line);
+ while ((line = ReadHighLevelLine()) != NULL) {
ParseLine(line);
}
- /* Reached EOF, but it may be just EOF of an include file. */
} while (ParseEOF());
FinishDependencyGroup();
- if (fatals != 0) {
+ if (parseErrors != 0) {
(void)fflush(stdout);
(void)fprintf(stderr,
- "%s: Fatal errors encountered -- cannot continue",
+ "%s: Fatal errors encountered -- cannot continue\n",
progname);
- PrintOnError(NULL, NULL);
+ PrintOnError(NULL, "");
exit(1);
}
}
@@ -3283,7 +2971,8 @@ Parse_Init(void)
parseIncPath = SearchPath_New();
sysIncPath = SearchPath_New();
defSysIncPath = SearchPath_New();
- Vector_Init(&includes, sizeof(IFile));
+ Vector_Init(&includes, sizeof(IncludedFile));
+ HashTable_Init(&guards);
}
/* Clean up the parsing module. */
@@ -3291,21 +2980,27 @@ void
Parse_End(void)
{
#ifdef CLEANUP
- Lst_DoneCall(&targCmds, free);
+ HashIter hi;
+
+ Lst_DoneFree(&targCmds);
assert(targets == NULL);
SearchPath_Free(defSysIncPath);
SearchPath_Free(sysIncPath);
SearchPath_Free(parseIncPath);
assert(includes.len == 0);
Vector_Done(&includes);
+ HashIter_Init(&hi, &guards);
+ while (HashIter_Next(&hi) != NULL) {
+ Guard *guard = hi.entry->value;
+ free(guard->name);
+ free(guard);
+ }
+ HashTable_Done(&guards);
#endif
}
-/*
- * Return a list containing the single main target to create.
- * If no such target exists, we Punt with an obnoxious error message.
- */
+/* Populate the list with the single main target to create, or error out. */
void
Parse_MainName(GNodeList *mainList)
{
@@ -3320,7 +3015,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/sigcompat.c b/contrib/bmake/sigcompat.c
index 608538d77d0f..c720eeba92e5 100644
--- a/contrib/bmake/sigcompat.c
+++ b/contrib/bmake/sigcompat.c
@@ -104,7 +104,7 @@
#if defined(LIBC_SCCS) && !defined(lint)
/*static char *sccsid = "from: @(#)sigcompat.c 5.3 (Berkeley) 2/24/91";*/
-static char *rcsid = "$Id: sigcompat.c,v 1.23 2011/02/14 00:07:11 sjg Exp $";
+static char *rcsid = "$Id: sigcompat.c,v 1.24 2022/09/26 17:38:10 sjg Exp $";
#endif /* LIBC_SCCS and not lint */
#undef signal
@@ -204,7 +204,7 @@ SIG_HDLR(*signal(int sig, SIG_HDLR(*handler)(int)))(int)
#ifdef SIGSET_T_INT
# define ss2m(ss) (MASK_T) *(ss)
# define m2ss(ss, m) *ss = (sigset_t) *(m)
-#else
+#elif !defined(HAVE_SIGSETMASK) || defined(FORCE_POSIX_SIGNALS)
static MASK_T
ss2m(sigset_t *ss)
{
diff --git a/contrib/bmake/str.c b/contrib/bmake/str.c
index b4baede4d417..1153d0f029fc 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.103 2024/04/14 15:21:20 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -71,7 +71,11 @@
#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.103 2024/04/14 15:21:20 rillig Exp $");
+
+
+static HashTable interned_strings;
+
/* Return the concatenation of s1 and s2, freshly allocated. */
char *
@@ -103,6 +107,10 @@ str_concat3(const char *s1, const char *s2, const char *s3)
* Fracture a string into an array of words (as delineated by tabs or spaces)
* taking quotation marks into account.
*
+ * A string that is empty or only contains whitespace nevertheless results in
+ * a single word. This is unexpected in many places, and the caller needs to
+ * correct for this edge case.
+ *
* If expand is true, quotes are removed and escape sequences such as \r, \t,
* etc... are expanded. In this case, return NULL on parse errors.
*
@@ -188,10 +196,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);
@@ -216,7 +223,7 @@ Substring_Words(const char *str, bool expand)
if (word_start == NULL)
word_start = word_end;
*word_end++ = '\\';
- /* catch '\' at end of line */
+ /* catch lonely '\' at end of string */
if (str_p[1] == '\0')
continue;
ch = *++str_p;
@@ -290,109 +297,126 @@ Str_Words(const char *str, bool expand)
}
/*
- * Str_Match -- Test if a string matches a pattern like "*.[ch]".
- * The following special characters are known *?\[] (as in fnmatch(3)).
+ * Test if a string matches a pattern like "*.[ch]". The pattern matching
+ * characters are '*', '?' and '[]', as in fnmatch(3).
*
- * XXX: this function does not detect or report malformed patterns.
+ * See varmod-match.mk for examples and edge cases.
*/
-bool
+StrMatchResult
Str_Match(const char *str, const char *pat)
{
- for (;;) {
- /*
- * See if we're at the end of both the pattern and the
- * string. If so, we succeeded. If we're at the end of the
- * pattern but not at the end of the string, we failed.
- */
- if (*pat == '\0')
- return *str == '\0';
- if (*str == '\0' && *pat != '*')
- return false;
-
- /*
- * A '*' in the pattern matches any substring. We handle this
- * by calling ourselves for each suffix of the string.
- */
- if (*pat == '*') {
- pat++;
- while (*pat == '*')
- pat++;
- if (*pat == '\0')
- return true;
- while (*str != '\0') {
- if (Str_Match(str, pat))
- return true;
- str++;
- }
- return false;
- }
+ StrMatchResult res = { NULL, false };
+ bool asterisk = false;
+ const char *fixed_str = str;
+ const char *fixed_pat = pat;
+
+match_fixed_length:
+ str = fixed_str;
+ pat = fixed_pat;
+ for (; *pat != '\0' && *pat != '*'; str++, pat++) {
+ if (*str == '\0')
+ return res;
- /* A '?' in the pattern matches any single character. */
- if (*pat == '?')
- goto thisCharOK;
-
- /*
- * A '[' in the pattern matches a character from a list.
- * The '[' is followed by the list of acceptable characters,
- * or by ranges (two characters separated by '-'). In these
- * character lists, the backslash is an ordinary character.
- */
- if (*pat == '[') {
+ if (*pat == '?') /* match any single character */
+ continue;
+
+ if (*pat == '[') { /* match a character from a list */
bool neg = pat[1] == '^';
pat += neg ? 2 : 1;
- for (;;) {
- if (*pat == ']' || *pat == '\0') {
- if (neg)
- break;
- return false;
- }
- /*
- * XXX: This naive comparison makes the
- * control flow of the pattern parser
- * dependent on the actual value of the
- * string. This is unpredictable. It may be
- * though that the code only looks wrong but
- * actually all code paths result in the same
- * behavior. This needs further tests.
- */
- if (*pat == *str)
- break;
- if (pat[1] == '-') {
- if (pat[2] == '\0')
- return neg;
- if (*pat <= *str && pat[2] >= *str)
- break;
- if (*pat >= *str && pat[2] <= *str)
- break;
- pat += 2;
- }
- pat++;
+ next_char_in_list:
+ if (*pat == '\0')
+ res.error = "Unfinished character list";
+ if (*pat == ']' || *pat == '\0') {
+ if (neg)
+ goto end_of_char_list;
+ goto no_match;
+ }
+ if (*pat == *str)
+ goto end_of_char_list;
+ if (pat[1] == '-' && pat[2] == '\0') {
+ res.error = "Unfinished character range";
+ res.matched = neg;
+ return res;
}
+ if (pat[1] == '-') {
+ unsigned char e1 = (unsigned char)pat[0];
+ unsigned char c = (unsigned char)*str;
+ unsigned char e2 = (unsigned char)pat[2];
+ if ((e1 <= c && c <= e2)
+ || (e2 <= c && c <= e1))
+ goto end_of_char_list;
+ pat += 2;
+ }
+ pat++;
+ goto next_char_in_list;
+
+ end_of_char_list:
if (neg && *pat != ']' && *pat != '\0')
- return false;
+ goto no_match;
while (*pat != ']' && *pat != '\0')
pat++;
if (*pat == '\0')
pat--;
- goto thisCharOK;
+ continue;
}
- /*
- * A backslash in the pattern matches the character following
- * it exactly.
- */
- if (*pat == '\\') {
+ if (*pat == '\\') /* match the next character exactly */
pat++;
- if (*pat == '\0')
- return false;
+ if (*pat != *str) {
+ if (asterisk && str == fixed_str) {
+ while (*str != '\0' && *str != *pat)
+ str++;
+ fixed_str = str;
+ goto match_fixed_length;
+ }
+ goto no_match;
}
+ }
- if (*pat != *str)
- return false;
-
- thisCharOK:
- pat++;
- str++;
+ if (*pat == '*') {
+ asterisk = true;
+ while (*pat == '*')
+ pat++;
+ if (*pat == '\0') {
+ res.matched = true;
+ return res;
+ }
+ fixed_str = str;
+ fixed_pat = pat;
+ goto match_fixed_length;
}
+ if (asterisk && *str != '\0') {
+ fixed_str += strlen(str);
+ goto match_fixed_length;
+ }
+ res.matched = *str == '\0';
+ return res;
+
+no_match:
+ if (!asterisk)
+ return res;
+ fixed_str++;
+ goto match_fixed_length;
+}
+
+void
+Str_Intern_Init(void)
+{
+ HashTable_Init(&interned_strings);
+}
+
+void
+Str_Intern_End(void)
+{
+#ifdef CLEANUP
+ HashTable_Done(&interned_strings);
+#endif
+}
+
+/* Return a canonical instance of str, with unlimited lifetime. */
+const char *
+Str_Intern(const char *str)
+{
+ return HashTable_CreateEntry(&interned_strings, str, NULL)->key;
}
diff --git a/contrib/bmake/str.h b/contrib/bmake/str.h
index ce0bb5ad82bc..6bdfbf4d497f 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.19 2024/01/05 21:56:55 rillig Exp $ */
/*
Copyright (c) 2021 Roland Illig <rillig@NetBSD.org>
@@ -39,12 +39,6 @@ typedef struct FStr {
void *freeIt;
} FStr;
-/* A modifiable string that may need to be freed after use. */
-typedef struct MFStr {
- char *str;
- void *freeIt;
-} MFStr;
-
/* A read-only range of a character array, NOT null-terminated. */
typedef struct Substring {
const char *start;
@@ -60,7 +54,6 @@ typedef struct LazyBuf {
size_t len;
size_t cap;
const char *expected;
- void *freeIt;
} LazyBuf;
/* The result of splitting a string into words. */
@@ -77,28 +70,30 @@ typedef struct SubstringWords {
void *freeIt;
} SubstringWords;
+typedef struct StrMatchResult {
+ const char *error;
+ bool matched;
+} StrMatchResult;
-MAKE_INLINE FStr
-FStr_Init(const char *str, void *freeIt)
-{
- FStr fstr;
- fstr.str = str;
- fstr.freeIt = freeIt;
- return fstr;
-}
/* Return a string that is the sole owner of str. */
MAKE_INLINE FStr
FStr_InitOwn(char *str)
{
- return FStr_Init(str, str);
+ FStr fstr;
+ fstr.str = str;
+ fstr.freeIt = str;
+ return fstr;
}
/* Return a string that refers to the shared str. */
MAKE_INLINE FStr
FStr_InitRefer(const char *str)
{
- return FStr_Init(str, NULL);
+ FStr fstr;
+ fstr.str = str;
+ fstr.freeIt = NULL;
+ return fstr;
}
MAKE_INLINE void
@@ -112,40 +107,6 @@ FStr_Done(FStr *fstr)
}
-MAKE_INLINE MFStr
-MFStr_Init(char *str, void *freeIt)
-{
- MFStr mfstr;
- mfstr.str = str;
- mfstr.freeIt = freeIt;
- return mfstr;
-}
-
-/* Return a string that is the sole owner of str. */
-MAKE_INLINE MFStr
-MFStr_InitOwn(char *str)
-{
- return MFStr_Init(str, str);
-}
-
-/* Return a string that refers to the shared str. */
-MAKE_INLINE MFStr
-MFStr_InitRefer(char *str)
-{
- return MFStr_Init(str, NULL);
-}
-
-MAKE_INLINE void
-MFStr_Done(MFStr *mfstr)
-{
- free(mfstr->freeIt);
-#ifdef CLEANUP
- mfstr->str = NULL;
- mfstr->freeIt = NULL;
-#endif
-}
-
-
MAKE_STATIC Substring
Substring_Init(const char *start, const char *end)
{
@@ -182,12 +143,12 @@ Substring_Equals(Substring sub, const char *str)
memcmp(sub.start, str, len) == 0;
}
-MAKE_STATIC Substring
-Substring_Sub(Substring sub, size_t start, size_t end)
+MAKE_INLINE bool
+Substring_Eq(Substring sub, Substring str)
{
- assert(start <= Substring_Length(sub));
- assert(end <= Substring_Length(sub));
- return Substring_Init(sub.start + start, sub.start + end);
+ size_t len = Substring_Length(sub);
+ return len == Substring_Length(str) &&
+ memcmp(sub.start, str.start, len) == 0;
}
MAKE_STATIC bool
@@ -226,7 +187,7 @@ Substring_SkipFirst(Substring sub, char ch)
}
MAKE_STATIC const char *
-Substring_LastIndex(Substring sub, char ch)
+Substring_FindLast(Substring sub, char ch)
{
const char *p;
@@ -266,13 +227,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
@@ -307,21 +267,15 @@ LazyBuf_AddStr(LazyBuf *buf, const char *str)
LazyBuf_Add(buf, *p);
}
-MAKE_STATIC void
-LazyBuf_AddBytesBetween(LazyBuf *buf, const char *start, const char *end)
+MAKE_INLINE void
+LazyBuf_AddSubstring(LazyBuf *buf, Substring sub)
{
const char *p;
- for (p = start; p != end; p++)
+ for (p = sub.start; p != sub.end; p++)
LazyBuf_Add(buf, *p);
}
-MAKE_INLINE void
-LazyBuf_AddSubstring(LazyBuf *buf, Substring sub)
-{
- LazyBuf_AddBytesBetween(buf, sub.start, sub.end);
-}
-
MAKE_STATIC Substring
LazyBuf_Get(const LazyBuf *buf)
{
@@ -329,6 +283,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 +312,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);
@@ -363,4 +330,8 @@ SubstringWords_Free(SubstringWords w)
char *str_concat2(const char *, const char *);
char *str_concat3(const char *, const char *, const char *);
-bool Str_Match(const char *, const char *);
+StrMatchResult Str_Match(const char *, const char *);
+
+void Str_Intern_Init(void);
+void Str_Intern_End(void);
+const char *Str_Intern(const char *);
diff --git a/contrib/bmake/strlcpy.c b/contrib/bmake/strlcpy.c
index b59b2f4ba5a6..111a2aa13435 100644
--- a/contrib/bmake/strlcpy.c
+++ b/contrib/bmake/strlcpy.c
@@ -17,9 +17,8 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
+#include "make.h"
+
#ifndef HAVE_STRLCPY
#include <sys/cdefs.h>
diff --git a/contrib/bmake/suff.c b/contrib/bmake/suff.c
index b2c926a2b8bc..de00cc4e8581 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.378 2024/02/07 06:43:02 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -115,7 +115,7 @@
#include "dir.h"
/* "@(#)suff.c 8.4 (Berkeley) 3/21/94" */
-MAKE_RCSID("$NetBSD: suff.c,v 1.350 2021/04/04 10:05:08 rillig Exp $");
+MAKE_RCSID("$NetBSD: suff.c,v 1.378 2024/02/07 06:43:02 rillig Exp $");
typedef List SuffixList;
typedef ListNode SuffixListNode;
@@ -142,21 +142,25 @@ static GNodeList transforms = LST_INIT;
*/
static int sNum = 0;
-typedef enum SuffixFlags {
- SUFF_NONE = 0,
-
+/*
+ * A suffix such as ".c" or ".o" that may be 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,43 +170,19 @@ 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.
- *
- * XXX: These lists are used nowhere, they are just appended to, for
- * no apparent reason. They do have the side effect of increasing
- * refCount though. */
- SuffixListList ref;
} Suffix;
/*
@@ -217,20 +197,26 @@ typedef struct Suffix {
typedef struct Candidate {
/* The file or node to look for. */
char *file;
- /* The prefix from which file was formed.
- * Its memory is shared among all candidates. */
+ /*
+ * The prefix from which file was formed. Its memory is shared among
+ * all candidates.
+ */
char *prefix;
/* The suffix on the file. */
Suffix *suff;
- /* The candidate that can be made from this,
- * or NULL for the top-level candidate. */
+ /*
+ * The candidate that can be made from this, or NULL for the
+ * top-level candidate.
+ */
struct Candidate *parent;
/* The node describing the file. */
GNode *node;
- /* Count of existing children, only used for memory management, so we
- * don't free this candidate too early or too late. */
+ /*
+ * Count of existing children, only used for memory management, so we
+ * don't free this candidate too early or too late.
+ */
int numChildren;
#ifdef DEBUG_SRC
CandidateList childrenList;
@@ -250,7 +236,7 @@ typedef struct CandidateSearcher {
/* TODO: Document the difference between nullSuff and emptySuff. */
-/* The NULL suffix for this run */
+/* The NULL suffix is used when a file has no known suffix */
static Suffix *nullSuff;
/* The empty suffix required for POSIX single-suffix transformation rules */
static Suffix *emptySuff;
@@ -378,7 +364,6 @@ SuffixList_Unref(SuffixList *list, Suffix *suff)
}
}
-/* Free up all memory associated with the given suffix structure. */
static void
Suffix_Free(Suffix *suff)
{
@@ -396,7 +381,6 @@ Suffix_Free(Suffix *suff)
suff->name, suff->refCount);
#endif
- Lst_Done(&suff->ref);
Lst_Done(&suff->children);
Lst_Done(&suff->parents);
SearchPath_Free(suff->searchPath);
@@ -405,12 +389,6 @@ Suffix_Free(Suffix *suff)
free(suff);
}
-static void
-SuffFree(void *p)
-{
- Suffix_Free(p);
-}
-
/* Remove the suffix from the list, and free if it is otherwise unused. */
static void
SuffixList_Remove(SuffixList *list, Suffix *suff)
@@ -420,7 +398,7 @@ SuffixList_Remove(SuffixList *list, Suffix *suff)
/* XXX: can lead to suff->refCount == -1 */
SuffixList_Unref(&sufflist, suff);
DEBUG1(SUFF, "Removing suffix \"%s\"\n", suff->name);
- SuffFree(suff);
+ Suffix_Free(suff);
}
}
@@ -444,12 +422,10 @@ SuffixList_Insert(SuffixList *list, Suffix *suff)
DEBUG2(SUFF, "inserting \"%s\" (%d) at end of list\n",
suff->name, suff->sNum);
Lst_Append(list, Suffix_Ref(suff));
- Lst_Append(&suff->ref, list);
} else if (listSuff->sNum != suff->sNum) {
DEBUG4(SUFF, "inserting \"%s\" (%d) before \"%s\" (%d)\n",
suff->name, suff->sNum, listSuff->name, listSuff->sNum);
Lst_InsertBefore(list, ln, Suffix_Ref(suff));
- Lst_Append(&suff->ref, list);
} else {
DEBUG2(SUFF, "\"%s\" (%d) is already there\n",
suff->name, suff->sNum);
@@ -473,9 +449,10 @@ Suffix_New(const char *name)
suff->searchPath = SearchPath_New();
Lst_Init(&suff->children);
Lst_Init(&suff->parents);
- Lst_Init(&suff->ref);
suff->sNum = sNum++;
- suff->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;
@@ -498,11 +475,13 @@ Suff_ClearSuffixes(void)
Lst_Init(&sufflist);
sNum = 0;
if (nullSuff != NULL)
- SuffFree(nullSuff);
+ Suffix_Free(nullSuff);
emptySuff = nullSuff = Suffix_New("");
SearchPath_AddAll(nullSuff->searchPath, &dirSearchPath);
- nullSuff->flags = SUFF_NULL;
+ nullSuff->include = false;
+ nullSuff->library = false;
+ nullSuff->isNull = true;
}
/*
@@ -619,6 +598,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;
}
@@ -697,7 +677,9 @@ RebuildGraph(GNode *transform, Suffix *suff)
size_t nameLen = strlen(name);
const char *toName;
- /* See if it is a transformation from this suffix to another suffix. */
+ /*
+ * See if it is a transformation from this suffix to another suffix.
+ */
toName = StrTrimPrefix(suff->name, name);
if (toName != NULL) {
Suffix *to = FindSuffixByName(toName);
@@ -707,7 +689,9 @@ RebuildGraph(GNode *transform, Suffix *suff)
}
}
- /* See if it is a transformation from another suffix to this suffix. */
+ /*
+ * See if it is a transformation from another suffix to this suffix.
+ */
toName = Suffix_TrimSuffix(suff, nameLen, name + nameLen);
if (toName != NULL) {
Suffix *from = FindSuffixByNameLen(name,
@@ -729,17 +713,15 @@ RebuildGraph(GNode *transform, Suffix *suff)
* true iff a new main target has been selected.
*/
static bool
-UpdateTarget(GNode *target, GNode **inout_main, Suffix *suff,
- bool *inout_removedMain)
+UpdateTarget(GNode *target, Suffix *suff, bool *inout_removedMain)
{
Suffix *srcSuff, *targSuff;
char *ptr;
- if (*inout_main == NULL && *inout_removedMain &&
- !(target->type & OP_NOTARGET)) {
+ if (mainNode == NULL && *inout_removedMain &&
+ GNode_IsMainCandidate(target)) {
DEBUG1(MAKE, "Setting main node to \"%s\"\n", target->name);
- *inout_main = target;
- Targ_SetMain(target);
+ mainNode = target;
/*
* XXX: Why could it be a good idea to return true here?
* The main task of this function is to turn ordinary nodes
@@ -777,13 +759,12 @@ UpdateTarget(GNode *target, GNode **inout_main, Suffix *suff,
return false;
if (ParseTransform(target->name, &srcSuff, &targSuff)) {
- if (*inout_main == target) {
+ if (mainNode == target) {
DEBUG1(MAKE,
"Setting main node from \"%s\" back to null\n",
target->name);
*inout_removedMain = true;
- *inout_main = NULL;
- Targ_SetMain(NULL);
+ mainNode = NULL;
}
Lst_Done(&target->children);
Lst_Init(&target->children);
@@ -807,33 +788,21 @@ UpdateTarget(GNode *target, GNode **inout_main, Suffix *suff,
* suffix rules.
*/
static void
-UpdateTargets(GNode **inout_main, Suffix *suff)
+UpdateTargets(Suffix *suff)
{
bool removedMain = false;
GNodeListNode *ln;
for (ln = Targ_List()->first; ln != NULL; ln = ln->next) {
GNode *gn = ln->datum;
- if (UpdateTarget(gn, inout_main, suff, &removedMain))
+ if (UpdateTarget(gn, suff, &removedMain))
break;
}
}
-/*
- * Add the suffix to the end of the list of known suffixes.
- * Should we restructure the suffix graph? Make doesn't.
- *
- * A GNode is created for the suffix (XXX: this sounds completely wrong) and
- * a Suffix structure is created and added to the suffixes list unless the
- * suffix was already known.
- * The mainNode passed can be modified if a target mutated into a
- * transform and that target happened to be the main target.
- *
- * Input:
- * name the name of the suffix to add
- */
+/* Add the suffix to the end of the list of known suffixes. */
void
-Suff_AddSuffix(const char *name, GNode **inout_main)
+Suff_AddSuffix(const char *name)
{
GNodeListNode *ln;
@@ -845,7 +814,7 @@ Suff_AddSuffix(const char *name, GNode **inout_main)
Lst_Append(&sufflist, suff);
DEBUG1(SUFF, "Adding suffix \"%s\"\n", suff->name);
- UpdateTargets(inout_main, suff);
+ UpdateTargets(suff);
/*
* Look for any existing transformations from or to this suffix.
@@ -857,9 +826,9 @@ Suff_AddSuffix(const char *name, GNode **inout_main)
/* Return the search path for the given suffix, or NULL. */
SearchPath *
-Suff_GetPath(const char *sname)
+Suff_GetPath(const char *name)
{
- Suffix *suff = FindSuffixByName(sname);
+ Suffix *suff = FindSuffixByName(name);
return suff != NULL ? suff->searchPath : NULL;
}
@@ -887,15 +856,11 @@ Suff_ExtendPaths(void)
for (ln = sufflist.first; ln != NULL; ln = ln->next) {
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);
} else {
SearchPath_Free(suff->searchPath);
@@ -926,7 +891,7 @@ Suff_AddInclude(const char *suffName)
{
Suffix *suff = FindSuffixByName(suffName);
if (suff != NULL)
- suff->flags |= SUFF_INCLUDE;
+ suff->include = true;
}
/*
@@ -940,7 +905,7 @@ Suff_AddLib(const char *suffName)
{
Suffix *suff = FindSuffixByName(suffName);
if (suff != NULL)
- suff->flags |= SUFF_LIBRARY;
+ suff->library = true;
}
/********** Implicit Source Search Functions *********/
@@ -1017,7 +982,7 @@ Candidate_New(char *name, char *prefix, Suffix *suff, Candidate *parent,
/*ARGSUSED*/
static void
CandidateList_Add(CandidateList *list, char *srcName, Candidate *targ,
- Suffix *suff, const char *debug_tag)
+ Suffix *suff, const char *debug_tag MAKE_ATTR_UNUSED)
{
Candidate *cand = Candidate_New(srcName, targ->prefix, suff, targ,
NULL);
@@ -1043,7 +1008,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
@@ -1202,7 +1167,9 @@ FindCmds(Candidate *targ, CandidateSearcher *cs)
base = str_basename(sgn->name);
if (strncmp(base, targ->prefix, prefLen) != 0)
continue;
- /* The node matches the prefix, see if it has a known suffix. */
+ /*
+ * The node matches the prefix, see if it has a known suffix.
+ */
suff = FindSuffixByName(base + prefLen);
if (suff == NULL)
continue;
@@ -1243,9 +1210,7 @@ ExpandWildcards(GNodeListNode *cln, GNode *pgn)
if (!Dir_HasWildcards(cgn->name))
return;
- /*
- * Expand the word along the chosen path
- */
+ /* Expand the word along the chosen path. */
Lst_Init(&expansions);
SearchPath_Expand(Suff_FindPath(cgn), cgn->name, &expansions);
@@ -1254,12 +1219,12 @@ ExpandWildcards(GNodeListNode *cln, GNode *pgn)
/*
* Fetch next expansion off the list and find its GNode
*/
- char *cp = Lst_Dequeue(&expansions);
+ char *name = Lst_Dequeue(&expansions);
- DEBUG1(SUFF, "%s...", cp);
- gn = Targ_GetNode(cp);
+ DEBUG1(SUFF, "%s...", name);
+ gn = Targ_GetNode(name);
- /* Add gn to the parents child list before the original child */
+ /* Insert gn before the original child. */
Lst_InsertBefore(&pgn->children, cln, gn);
Lst_Append(&gn->parents, pgn);
pgn->unmade++;
@@ -1270,8 +1235,8 @@ ExpandWildcards(GNodeListNode *cln, GNode *pgn)
DEBUG0(SUFF, "\n");
/*
- * Now the source is expanded, remove it from the list of children to
- * keep it from being processed.
+ * Now that the source is expanded, remove it from the list of
+ * children, to keep it from being processed.
*/
pgn->unmade--;
Lst_Remove(&pgn->children, cln);
@@ -1283,59 +1248,56 @@ ExpandWildcards(GNodeListNode *cln, GNode *pgn)
* add those nodes to the members list.
*
* Unfortunately, we can't use Str_Words because it doesn't understand about
- * variable expressions with spaces in them.
+ * expressions with spaces in them.
*/
static void
-ExpandChildrenRegular(char *cp, GNode *pgn, GNodeList *members)
+ExpandChildrenRegular(char *p, GNode *pgn, GNodeList *members)
{
char *start;
- pp_skip_hspace(&cp);
- start = cp;
- while (*cp != '\0') {
- if (*cp == ' ' || *cp == '\t') {
+ pp_skip_hspace(&p);
+ start = p;
+ while (*p != '\0') {
+ if (*p == ' ' || *p == '\t') {
GNode *gn;
/*
* White-space -- terminate element, find the node,
* add it, skip any further spaces.
*/
- *cp++ = '\0';
+ *p++ = '\0';
gn = Targ_GetNode(start);
Lst_Append(members, gn);
- pp_skip_hspace(&cp);
+ pp_skip_hspace(&p);
/* Continue at the next non-space. */
- start = cp;
- } else if (*cp == '$') {
- /* Skip over the variable expression. */
- const char *nested_p = cp;
- FStr junk;
-
- (void)Var_Parse(&nested_p, pgn, VARE_PARSE_ONLY, &junk);
+ start = p;
+ } else if (*p == '$') {
+ /* Skip over the expression. */
+ const char *nested_p = p;
+ FStr junk = Var_Parse(&nested_p, pgn, VARE_PARSE_ONLY);
/* TODO: handle errors */
if (junk.str == var_Error) {
Parse_Error(PARSE_FATAL,
- "Malformed variable expression at \"%s\"",
- cp);
- cp++;
+ "Malformed expression at \"%s\"", p);
+ p++;
} else {
- cp += nested_p - cp;
+ p += nested_p - p;
}
FStr_Done(&junk);
- } else if (cp[0] == '\\' && cp[1] != '\0') {
+ } else if (p[0] == '\\' && p[1] != '\0') {
/* Escaped something -- skip over it. */
/*
* XXX: In other places, escaping at this syntactical
* position is done by a '$', not a '\'. The '\' is
* only used in variable modifiers.
*/
- cp += 2;
+ p += 2;
} else {
- cp++;
+ p++;
}
}
- if (cp != start) {
+ if (p != start) {
/*
* Stuff left over -- add it to the list too
*/
@@ -1345,7 +1307,7 @@ ExpandChildrenRegular(char *cp, GNode *pgn, GNodeList *members)
}
/*
- * Expand the names of any children of a given node that contain variable
+ * Expand the names of any children of a given node that contain
* expressions or file wildcards into actual targets.
*
* The expanded node is removed from the parent's list of children, and the
@@ -1359,7 +1321,7 @@ static void
ExpandChildren(GNodeListNode *cln, GNode *pgn)
{
GNode *cgn = cln->datum;
- char *cp; /* Expanded value */
+ char *expanded;
if (!Lst_IsEmpty(&cgn->order_pred) || !Lst_IsEmpty(&cgn->order_succ))
/* It is all too hard to process the result of .ORDER */
@@ -1381,7 +1343,7 @@ ExpandChildren(GNodeListNode *cln, GNode *pgn)
}
DEBUG1(SUFF, "Expanding \"%s\"...", cgn->name);
- (void)Var_Subst(cgn->name, pgn, VARE_UNDEFERR, &cp);
+ expanded = Var_Subst(cgn->name, pgn, VARE_UNDEFERR);
/* TODO: handle errors */
{
@@ -1393,39 +1355,32 @@ ExpandChildren(GNodeListNode *cln, GNode *pgn)
* call on the Arch module to find the nodes for us,
* expanding variables in the parent's scope.
*/
- char *p = cp;
- (void)Arch_ParseArchive(&p, &members, pgn);
+ char *ap = expanded;
+ (void)Arch_ParseArchive(&ap, &members, pgn);
} else {
- ExpandChildrenRegular(cp, pgn, &members);
+ ExpandChildrenRegular(expanded, pgn, &members);
}
- /*
- * Add all elements of the members list to the parent node.
- */
+ /* Add all members to the parent node. */
while (!Lst_IsEmpty(&members)) {
GNode *gn = Lst_Dequeue(&members);
DEBUG1(SUFF, "%s...", gn->name);
- /*
- * Add gn to the parents child list before the
- * original child.
- */
Lst_InsertBefore(&pgn->children, cln, gn);
Lst_Append(&gn->parents, pgn);
pgn->unmade++;
- /* Expand wildcards on new node */
ExpandWildcards(cln->prev, pgn);
}
Lst_Done(&members);
- free(cp);
+ free(expanded);
}
DEBUG0(SUFF, "\n");
/*
- * Now the source is expanded, remove it from the list of children to
- * keep it from being processed.
+ * The source is expanded now, so remove it from the list of children,
+ * to keep it from being processed.
*/
pgn->unmade--;
Lst_Remove(&pgn->children, cln);
@@ -1444,16 +1399,10 @@ ExpandAllChildren(GNode *gn)
}
/*
- * Find a path along which to expand the node.
- *
- * If the node has a known suffix, use that path.
- * If it has no known suffix, use the default system search path.
- *
- * Input:
- * gn Node being examined
+ * Find a path along which to search or expand the node.
*
- * Results:
- * The appropriate path to search for the GNode.
+ * If the node has a known suffix, use that path,
+ * otherwise use the default system search path.
*/
SearchPath *
Suff_FindPath(GNode *gn)
@@ -1527,7 +1476,7 @@ ApplyTransform(GNode *tgn, GNode *sgn, Suffix *tsuff, Suffix *ssuff)
/* Apply the rule. */
Make_HandleUse(gn, tgn);
- /* Deal with wildcards and variables in any acquired sources. */
+ /* Deal with wildcards and expressions in any acquired sources. */
ln = ln != NULL ? ln->next : NULL;
while (ln != NULL) {
GNodeListNode *nln = ln->next;
@@ -1554,7 +1503,7 @@ ApplyTransform(GNode *tgn, GNode *sgn, Suffix *tsuff, Suffix *ssuff)
static void
ExpandMember(GNode *gn, const char *eoarch, GNode *mem, Suffix *memSuff)
{
- GNodeListNode *ln;
+ SuffixListNode *ln;
size_t nameLen = (size_t)(eoarch - gn->name);
/* Use first matching suffix... */
@@ -1563,7 +1512,6 @@ ExpandMember(GNode *gn, const char *eoarch, GNode *mem, Suffix *memSuff)
break;
if (ln != NULL) {
- /* Got one -- apply it */
Suffix *suff = ln->datum;
if (!ApplyTransform(gn, mem, suff, memSuff)) {
DEBUG2(SUFF, "\tNo transformation from %s -> %s\n",
@@ -1577,9 +1525,6 @@ static void FindDeps(GNode *, CandidateSearcher *);
/*
* Locate dependencies for an OP_ARCHV node.
*
- * Input:
- * gn Node for which to locate dependencies
- *
* Side Effects:
* Same as Suff_FindDeps
*/
@@ -1593,7 +1538,7 @@ FindDepsArchive(GNode *gn, CandidateSearcher *cs)
const char *name; /* Start of member's name */
/*
- * The node is an archive(member) pair. so we must find a
+ * The node is an 'archive(member)' pair, so we must find a
* suffix for both of them.
*/
eoarch = strchr(gn->name, '(');
@@ -1772,8 +1717,7 @@ FindDepsRegularPath(GNode *gn, Candidate *targ)
free(gn->path);
gn->path = Dir_FindFile(gn->name,
- (targ == NULL ? &dirSearchPath :
- targ->suff->searchPath));
+ targ == NULL ? &dirSearchPath : targ->suff->searchPath);
if (gn->path == NULL)
return;
@@ -1918,7 +1862,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;
/*
@@ -1968,8 +1912,7 @@ FindDepsRegular(GNode *gn, CandidateSearcher *cs)
if (targ->node == NULL)
targ->node = Targ_GetNode(targ->file);
- ApplyTransform(targ->node, src->node,
- targ->suff, src->suff);
+ ApplyTransform(targ->node, src->node, targ->suff, src->suff);
if (targ->node != gn) {
/*
@@ -2067,9 +2010,6 @@ FindDeps(GNode *gn, CandidateSearcher *cs)
*
* Need to handle the changing of the null suffix gracefully so the old
* transformation rules don't just go away.
- *
- * Input:
- * name Name of null suffix
*/
void
Suff_SetNull(const char *name)
@@ -2077,14 +2017,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;
}
@@ -2101,47 +2041,57 @@ Suff_Init(void)
Suff_ClearSuffixes();
}
-
/* Clean up the suffixes module. */
void
Suff_End(void)
{
#ifdef CLEANUP
- Lst_DoneCall(&sufflist, SuffFree);
- Lst_DoneCall(&suffClean, SuffFree);
+ SuffixListNode *ln;
+
+ for (ln = sufflist.first; ln != NULL; ln = ln->next)
+ Suffix_Free(ln->datum);
+ Lst_Done(&sufflist);
+ for (ln = suffClean.first; ln != NULL; ln = ln->next)
+ Suffix_Free(ln->datum);
+ Lst_Done(&suffClean);
if (nullSuff != NULL)
- SuffFree(nullSuff);
+ Suffix_Free(nullSuff);
Lst_Done(&transforms);
#endif
}
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_Init(&buf);
+ 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 +2127,20 @@ Suff_PrintAll(void)
PrintTransformation(ln->datum);
}
}
+
+char *
+Suff_NamesStr(void)
+{
+ Buffer buf;
+ SuffixListNode *ln;
+ Suffix *suff;
+
+ Buf_Init(&buf);
+ 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..35745cd21fe0 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.181 2024/04/27 17:33:47 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -78,10 +78,8 @@
*
* Targ_List Return the list of all targets so far.
*
- * GNode_New Create a new GNode for the passed target
- * (string). The node is *not* placed in the
- * hash table, though all its fields are
- * initialized.
+ * GNode_New Create a new GNode with the given name, don't add it
+ * to allNodes.
*
* Targ_FindNode Find the node, or return NULL.
*
@@ -93,9 +91,6 @@
* Targ_FindList Given a list of names, find nodes for all
* of them, creating them as necessary.
*
- * Targ_Precious Return true if the target is precious and
- * should not be removed if we are interrupted.
- *
* Targ_Propagate Propagate information between related nodes.
* Should be called after the makefiles are parsed
* but before any action is taken.
@@ -103,8 +98,7 @@
* Debugging:
* Targ_PrintGraph
* Print out the entire graph, all variables and
- * statistics for the directory cache. Should print
- * something for suffixes, too, but...
+ * statistics for the directory cache.
*/
#include <time.h>
@@ -113,7 +107,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.181 2024/04/27 17:33:47 rillig Exp $");
/*
* All target nodes that appeared on the left-hand side of one of the
@@ -125,7 +119,7 @@ static HashTable allTargetsByName;
#ifdef CLEANUP
static GNodeList allNodes = LST_INIT;
-static void GNode_Free(void *);
+static void GNode_Free(GNode *);
#endif
void
@@ -137,11 +131,16 @@ Targ_Init(void)
void
Targ_End(void)
{
+#ifdef CLEANUP
+ GNodeListNode *ln;
+#endif
Targ_Stats();
#ifdef CLEANUP
Lst_Done(&allTargets);
HashTable_Done(&allTargetsByName);
- Lst_DoneCall(&allNodes, GNode_Free);
+ for (ln = allNodes.first; ln != NULL; ln = ln->next)
+ GNode_Free(ln->datum);
+ Lst_Done(&allNodes);
#endif
}
@@ -165,17 +164,17 @@ Targ_List(void)
/*
* Create a new graph node, but don't register it anywhere.
*
- * Graph nodes that appear on the left-hand side of a dependency line such
+ * Graph nodes that occur on the left-hand side of a dependency line such
* as "target: source" are called targets. XXX: In some cases (like the
- * .ALLTARGETS variable), all nodes are called targets as well, even if they
- * never appear on the left-hand side. This is a mistake.
+ * .ALLTARGETS variable), other nodes are called targets as well, even if
+ * they never occur on the left-hand side of a dependency line.
*
* Typical names for graph nodes are:
- * "src.c" (an ordinary file)
- * "clean" (a .PHONY target)
- * ".END" (a special hook target)
- * "-lm" (a library)
- * "libc.a(isspace.o)" (an archive member)
+ * "src.c" an ordinary file
+ * "clean" a .PHONY target
+ * ".END" a special hook target
+ * "-lm" a library
+ * "libm.a(sin.o)" an archive member
*/
GNode *
GNode_New(const char *name)
@@ -187,7 +186,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;
@@ -207,6 +206,7 @@ GNode_New(const char *name)
gn->suffix = NULL;
gn->fname = NULL;
gn->lineno = 0;
+ gn->exit_status = 0;
#ifdef CLEANUP
Lst_Append(&allNodes, gn);
@@ -217,10 +217,8 @@ GNode_New(const char *name)
#ifdef CLEANUP
static void
-GNode_Free(void *gnp)
+GNode_Free(GNode *gn)
{
- GNode *gn = gnp;
-
free(gn->name);
free(gn->uname);
free(gn->path);
@@ -248,9 +246,9 @@ GNode_Free(void *gnp)
*
* XXX: The GNodes that are only used as variable scopes (SCOPE_CMD,
* SCOPE_GLOBAL, SCOPE_INTERNAL) are not freed at all (see Var_End,
- * where they are not mentioned). These might be freed at all, if
- * their variable values are indeed not used anywhere else (see
- * Trace_Init for the only suspicious use).
+ * where they are not mentioned). These may be freed if their
+ * variable values are indeed not used anywhere else (see Trace_Init
+ * for the only suspicious use).
*/
HashTable_Done(&gn->vars);
@@ -309,7 +307,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;
}
@@ -346,27 +344,6 @@ Targ_FindList(GNodeList *gns, StringList *names)
}
}
-/* See if the given target is precious. */
-bool
-Targ_Precious(const GNode *gn)
-{
- /* XXX: Why are '::' targets precious? */
- return allPrecious || gn->type & (OP_PRECIOUS | OP_DOUBLEDEP);
-}
-
-/*
- * The main target to be made; only for debugging output.
- * See mainNode in parse.c for the definitive source.
- */
-static GNode *mainTarg;
-
-/* Remember the main target to make; only used for debugging. */
-void
-Targ_SetMain(GNode *gn)
-{
- mainTarg = gn;
-}
-
static void
PrintNodeNames(GNodeList *gnodes)
{
@@ -416,36 +393,38 @@ 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_USEBEFORE, false, "USEBEFORE" },
+ { 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,20 +459,34 @@ 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))
return;
debug_printf("#\n");
- if (gn == mainTarg)
+ if (gn == mainNode)
debug_printf("# *** MAIN TARGET ***\n");
if (pass >= 2) {
@@ -541,7 +534,6 @@ Targ_PrintNodes(GNodeList *gnodes, int pass)
Targ_PrintNode(ln->datum, pass);
}
-/* Print only those targets that are just a source. */
static void
PrintOnlySources(void)
{
@@ -611,7 +603,7 @@ Targ_Propagate(void)
for (cln = gn->cohorts.first; cln != NULL; cln = cln->next) {
GNode *cohort = cln->datum;
- cohort->type |= type & ~OP_OPMASK;
+ cohort->type |= type & (unsigned)~OP_OPMASK;
}
}
}
diff --git a/contrib/bmake/trace.c b/contrib/bmake/trace.c
index 840c7995e5c1..9674cec7becb 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.33 2023/03/28 14:39:31 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.33 2023/03/28 14:39:31 rillig Exp $");
static FILE *trfile;
static pid_t trpid;
-const char *trwd;
+static const char *trwd;
-static const char *evname[] = {
+static const char evname[][4] = {
"BEG",
"END",
"ERR",
@@ -69,8 +69,10 @@ Trace_Init(const char *pathname)
if (pathname != NULL) {
FStr curDir;
trpid = getpid();
- /* XXX: This variable may get overwritten later, which
- * would make trwd point to undefined behavior. */
+ /*
+ * XXX: This variable may get overwritten later, which would
+ * make trwd point to undefined behavior.
+ */
curDir = Var_Value(SCOPE_GLOBAL, ".CURDIR");
trwd = curDir.str;
@@ -88,10 +90,17 @@ Trace_Log(TrEvent event, Job *job)
gettimeofday(&rightnow, NULL);
+#if __STDC_VERSION__ >= 199901L
fprintf(trfile, "%lld.%06ld %d %s %d %s",
(long long)rightnow.tv_sec, (long)rightnow.tv_usec,
jobTokensRunning,
evname[event], trpid, trwd);
+#else
+ fprintf(trfile, "%ld.%06ld %d %s %d %s",
+ (long)rightnow.tv_sec, (long)rightnow.tv_usec,
+ jobTokensRunning,
+ evname[event], trpid, trwd);
+#endif
if (job != NULL) {
char flags[4];
diff --git a/contrib/bmake/unit-tests/Makefile b/contrib/bmake/unit-tests/Makefile
index 784223a56652..5960a0621ddd 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.216 2024/04/30 16:42:50 sjg Exp $
#
-# $NetBSD: Makefile,v 1.279 2021/06/16 09:39:48 rillig Exp $
+# $NetBSD: Makefile,v 1.344 2024/04/30 16:41:32 sjg Exp $
#
# Unit tests for make(1)
#
@@ -25,10 +25,6 @@
# named makefile (*.mk), with its own set of expected results (*.exp),
# and it should be added to the TESTS list.
#
-# A few *.mk files are helper files for other tests (such as include-sub.mk)
-# and are thus not added to TESTS. Such files must be ignored in
-# src/tests/usr.bin/make/t_make.sh.
-#
.MAIN: all
@@ -36,6 +32,21 @@
.MAKE.OS?= ${uname -s:L:sh}
.MAKE.UID?= ${id -u:L:sh}
+# for many tests we need a TMPDIR that will not collide
+# with other users.
+.if ${.OBJDIR} != ${.CURDIR}
+# easy
+TMPDIR:= ${.OBJDIR}/tmp
+.elif defined(TMPDIR)
+TMPDIR:= ${TMPDIR}/uid${.MAKE.UID}
+.else
+TMPDIR:= /tmp/uid${.MAKE.UID}
+.endif
+# make sure it exists
+.if !exist(${TMPDIR})
+_!= mkdir -p ${TMPDIR}
+.endif
+
# Each test is in a sub-makefile.
# Keep the list sorted.
# Any test that is commented out must be ignored in
@@ -83,7 +94,6 @@ TESTS+= cond-token-plain
TESTS+= cond-token-string
TESTS+= cond-token-var
TESTS+= cond-undef-lint
-TESTS+= cond1
TESTS+= counter
TESTS+= counter-append
TESTS+= dep
@@ -91,8 +101,10 @@ TESTS+= dep-colon
TESTS+= dep-colon-bug-cross-file
TESTS+= dep-double-colon
TESTS+= dep-double-colon-indep
+TESTS+= dep-duplicate
TESTS+= dep-exclam
TESTS+= dep-none
+TESTS+= dep-op-missing
TESTS+= dep-percent
TESTS+= dep-var
TESTS+= dep-wildcards
@@ -140,9 +152,11 @@ TESTS+= deptgt-order
TESTS+= deptgt-path
TESTS+= deptgt-path-suffix
TESTS+= deptgt-phony
+TESTS+= deptgt-posix
TESTS+= deptgt-precious
TESTS+= deptgt-shell
TESTS+= deptgt-silent
+TESTS+= deptgt-silent-jobs
TESTS+= deptgt-stale
TESTS+= deptgt-suffixes
TESTS+= dir
@@ -164,9 +178,12 @@ TESTS+= directive-export-impl
TESTS+= directive-export-gmake
TESTS+= directive-export-literal
TESTS+= directive-for
+TESTS+= directive-for-break
+TESTS+= directive-for-empty
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
@@ -178,6 +195,7 @@ TESTS+= directive-ifndef
TESTS+= directive-ifnmake
TESTS+= directive-include
TESTS+= directive-include-fatal
+TESTS+= directive-include-guard
TESTS+= directive-info
TESTS+= directive-misspellings
TESTS+= directive-sinclude
@@ -188,21 +206,18 @@ TESTS+= directive-warning
TESTS+= dollar
TESTS+= doterror
TESTS+= dotwait
-TESTS+= envfirst
TESTS+= error
TESTS+= # escape # broken by reverting POSIX changes
TESTS+= export
TESTS+= export-all
TESTS+= export-env
TESTS+= export-variants
-TESTS+= forloop
-TESTS+= forsubst
TESTS+= gnode-submake
TESTS+= hanoi-include
TESTS+= impsrc
TESTS+= include-main
TESTS+= job-flags
-#TESTS+= job-output-long-lines
+TESTS+= job-output-long-lines
TESTS+= job-output-null
TESTS+= jobs-empty-commands
TESTS+= jobs-empty-commands-error
@@ -213,10 +228,7 @@ TESTS+= lint
TESTS+= make-exported
TESTS+= meta-cmd-cmp
TESTS+= moderrs
-TESTS+= modmatch
TESTS+= modmisc
-TESTS+= modts
-TESTS+= modword
.if ${.MAKE.UID} > 0
TESTS+= objdir-writable
.endif
@@ -237,7 +249,7 @@ TESTS+= opt-debug-graph1
TESTS+= opt-debug-graph2
TESTS+= opt-debug-graph3
TESTS+= opt-debug-hash
-#TESTS+= opt-debug-jobs
+TESTS+= opt-debug-jobs
TESTS+= opt-debug-lint
TESTS+= opt-debug-loud
TESTS+= opt-debug-meta
@@ -258,6 +270,7 @@ TESTS+= opt-jobs
TESTS+= opt-jobs-internal
TESTS+= opt-jobs-no-action
TESTS+= opt-keep-going
+TESTS+= opt-keep-going-indirect
TESTS+= opt-keep-going-multiple
TESTS+= opt-m-include-dir
TESTS+= opt-no-action
@@ -272,10 +285,12 @@ TESTS+= opt-touch-jobs
TESTS+= opt-tracefile
TESTS+= opt-var-expanded
TESTS+= opt-var-literal
+TESTS+= opt-version
TESTS+= opt-warnings-as-errors
TESTS+= opt-where-am-i
TESTS+= opt-x-reduce-exported
TESTS+= order
+TESTS+= parse
TESTS+= parse-var
TESTS+= phony-end
TESTS+= posix
@@ -313,17 +328,19 @@ TESTS+= suff-transform-debug
TESTS+= suff-transform-endless
TESTS+= suff-transform-expand
TESTS+= suff-transform-select
+TESTS+= suff-use
TESTS+= sunshcmd
TESTS+= ternary
TESTS+= unexport
TESTS+= unexport-env
TESTS+= use-inference
-TESTS+= var-class
-TESTS+= var-class-cmdline
-TESTS+= var-class-env
-TESTS+= var-class-global
-TESTS+= var-class-local
-TESTS+= var-class-local-legacy
+TESTS+= var-readonly
+TESTS+= var-scope
+TESTS+= var-scope-cmdline
+TESTS+= var-scope-env
+TESTS+= var-scope-global
+TESTS+= var-scope-local
+TESTS+= var-scope-local-legacy
TESTS+= var-eval-short
TESTS+= var-op
TESTS+= var-op-append
@@ -339,6 +356,7 @@ TESTS+= varfind
TESTS+= varmisc
TESTS+= varmod
TESTS+= varmod-assign
+TESTS+= varmod-assign-shell
TESTS+= varmod-defined
TESTS+= varmod-edge
TESTS+= varmod-exclam-shell
@@ -351,13 +369,17 @@ 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-mtime
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
@@ -398,12 +420,20 @@ TESTS+= varname-dot-make-makefiles
TESTS+= varname-dot-make-meta-bailiwick
TESTS+= varname-dot-make-meta-created
TESTS+= varname-dot-make-meta-files
+.if ${.MAKE.PATH_FILEMON:Uno:Nktrace:N/dev*} == "" && ${TMPDIR:N/tmp*:N/var/tmp*} != ""
+# these tests will not work if TMPDIR is or is a subdir of
+# /tmp or /var/tmp
+.if ${.MAKE.PATH_FILEMON:N/dev/*} != "" || exists(${.MAKE.PATH_FILEMON})
TESTS+= varname-dot-make-meta-ignore_filter
TESTS+= varname-dot-make-meta-ignore_paths
TESTS+= varname-dot-make-meta-ignore_patterns
+TESTS+= varname-dot-make-path_filemon
+.else
+.warning Skipping tests that require ${.MAKE.PATH_FILEMON}
+.endif
+.endif
TESTS+= varname-dot-make-meta-prefix
TESTS+= varname-dot-make-mode
-TESTS+= varname-dot-make-path_filemon
TESTS+= varname-dot-make-pid
TESTS+= varname-dot-make-ppid
TESTS+= varname-dot-make-save_dollars
@@ -415,6 +445,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
@@ -428,14 +459,64 @@ TESTS+= varparse-dynamic
TESTS+= varparse-errors
TESTS+= varparse-mod
TESTS+= varparse-undef-partial
-TESTS+= varquote
+
+# some shells have quirks
+_shell := ${.SHELL:tA:T}
+.if ${_shell} == "dash"
+# dash fails -x output
+BROKEN_TESTS+= opt-debug-x-trace
+.elif ${_shell} == "ksh"
+BROKEN_TESTS+= sh-flags
+.endif
+
+.if ${UTC_1:Uno} == ""
+# this will not work if UTC_1 is set empty
+BROKEN_TESTS+= varmod-localtime
+.endif
+
+.if ${.MAKE.OS:NDarwin} == ""
+BROKEN_TESTS+= shell-ksh
+.endif
+
+.if ${.MAKE.OS:NIRIX*} == ""
+BROKEN_TESTS+= \
+ cmd-interrupt \
+ deptgt-interrupt \
+ job-output-null \
+ opt-chdir \
+ opt-debug-x-trace \
+ sh-leading-hyphen \
+
+.endif
+
+.if ${.MAKE.OS} == "SCO_SV"
+BROKEN_TESTS+= \
+ opt-debug-graph[23] \
+ varmod-localtime \
+ varmod-to-separator \
+
+.if ${_shell} == "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
@@ -453,18 +534,20 @@ TESTS+= varquote
# Additional environment variables for some of the tests.
# The base environment is -i PATH="$PATH".
ENV.depsrc-optional+= TZ=UTC
-ENV.envfirst= FROM_ENV=value-from-env
+ENV.deptgt-phony+= MAKESYSPATH=.
+ENV.directive-undef= ENV_VAR=env-value
+ENV.opt-env= FROM_ENV=value-from-env
+ENV.opt-m-include-dir= ${MAKEOBJDIR:DMAKEOBJDIR=${MAKEOBJDIR}}
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.
# If possible, write ".MAKEFLAGS: -dv" in the test .mk file instead of
# settings FLAGS.test=-dv here, since that is closer to the test code.
FLAGS.cond-func-make= via-cmdline
-FLAGS.directive-ifmake= first second
FLAGS.doterror= # none, especially not -k
FLAGS.jobs-error-indirect= # none, especially not -k
FLAGS.jobs-error-nested= # none, especially not -k
@@ -472,10 +555,18 @@ FLAGS.jobs-error-nested-make= # none, especially not -k
FLAGS.varname-empty= -dv '$${:U}=cmdline-u' '=cmdline-plain'
# Some tests need extra postprocessing.
-SED_CMDS.dir= ${:D remove output from -DCLEANUP mode }
-SED_CMDS.dir+= -e '/^OpenDirs_Done:/d'
-SED_CMDS.dir+= -e '/^CachedDir /d'
+SED_CMDS.deptgt-phony= ${STD_SED_CMDS.dd}
+SED_CMDS.dir= ${STD_SED_CMDS.dd}
+SED_CMDS.directive-include-guard= \
+ -e '/\.MAKEFLAGS/d' \
+ -e '/^Parsing line/d' \
+ -e '/^SetFilenameVars:/d' \
+ -e '/^ParseDependency/d' \
+ -e '/^ParseEOF:/d'
SED_CMDS.export= -e '/^[^=_A-Za-z0-9]*=/d'
+.if ${.MAKE.OS:NCygwin} == ""
+SED_CMDS.export+= -e '/^WINDIR=/d' -e '/^SYSTEMROOT=/d'
+.endif
SED_CMDS.export-all= ${SED_CMDS.export}
SED_CMDS.export-env= ${SED_CMDS.export}
SED_CMDS.cmdline= -e 's,uid${.MAKE.UID}/,,'
@@ -490,10 +581,17 @@ 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,'
+
+# meta line numbers can vary based on filemon implementation
+SED_CMDS.meta-ignore= -e 's,\(\.meta:\) [1-9][0-9]*:,\1 <line>:,'
SED_CMDS.opt-debug-graph1= ${STD_SED_CMDS.dg1}
SED_CMDS.opt-debug-graph2= ${STD_SED_CMDS.dg2}
SED_CMDS.opt-debug-graph3= ${STD_SED_CMDS.dg3}
+SED_CMDS.opt-debug-hash= -e 's,\(numEntries\)=[1-9][0-9],\1=<entries>,'
SED_CMDS.opt-debug-jobs= -e 's,([0-9][0-9]*),(<pid>),'
SED_CMDS.opt-debug-jobs+= -e 's,pid [0-9][0-9]*,pid <pid>,'
SED_CMDS.opt-debug-jobs+= -e 's,Process [0-9][0-9]*,Process <pid>,'
@@ -504,6 +602,7 @@ SED_CMDS.opt-debug-jobs+= -e 's,^\(.Command: <shell>\) -q,\1,'
SED_CMDS.opt-debug-lint+= ${STD_SED_CMDS.regex}
SED_CMDS.opt-jobs-no-action= ${STD_SED_CMDS.hide-from-output}
SED_CMDS.opt-no-action-runflags= ${STD_SED_CMDS.hide-from-output}
+SED_CMDS.opt-where-am-i= -e '/usr.obj/d'
# For Compat_RunCommand, useShell == false.
SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,<not found: ...>,'
# For Compat_RunCommand, useShell == true.
@@ -511,25 +610,33 @@ 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.sh-leading-hyphen= ${STD_SED_CMDS.shell}
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-mtime+= -e "s,\(.*\)': .*,\1': <ENOENT>,"
SED_CMDS.varmod-subst-regex+= ${STD_SED_CMDS.regex}
+SED_CMDS.varparse-errors+= ${STD_SED_CMDS.timestamp}
+SED_CMDS.varname-dot-make-meta-ignore_filter+= ${SED_CMDS.meta-ignore}
+SED_CMDS.varname-dot-make-meta-ignore_paths+= ${SED_CMDS.meta-ignore}
+SED_CMDS.varname-dot-make-meta-ignore_patterns+= ${SED_CMDS.meta-ignore}
SED_CMDS.varname-dot-parsedir= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
SED_CMDS.varname-dot-parsefile= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
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 .SYSPATH:L:@v@-e '/\\$v/d'@}
# Some tests need an additional round of postprocessing.
+POSTPROC.depsrc-wait= sed -e '/^---/d' -e 's,^\(: Making 3[abc]\)[123]$$,\1,'
POSTPROC.deptgt-suffixes= awk '/^\#\*\*\* Suffixes/,/^never-stop/'
POSTPROC.gnode-submake= awk '/Input graph/, /^$$/'
+POSTPROC.varname-dot-make-mode= sed 's,^\(: Making [abc]\)[123]$$,\1,'
# Some tests reuse other tests, which makes them unnecessarily fragile.
export-all.rawout: export.mk
@@ -540,16 +647,24 @@ unexport-env.rawout: export.mk
# Some standard sed commands, to be used in the SED_CMDS above.
+# In tests that use the debugging option -dd, ignore debugging output that is
+# only logged in -DCLEANUP mode.
+STD_SED_CMDS.dd= -e '/^OpenDirs_Done:/d'
+STD_SED_CMDS.dd+= -e '/^CachedDir /d'
+STD_SED_CMDS.dd+= -e 's, ${DEFSYSPATH:U/usr/share/mk} , <defsyspath> ,'
+
# 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 '/^\#.*\/mk/d'
STD_SED_CMDS.dg1+= -e 's, ${DEFSYSPATH:U/usr/share/mk}$$, <defsyspath>,'
STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE *=\) .*,\1 <details omitted>,'
STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE\.[A-Z_]* *=\) .*,\1 <details omitted>,'
+STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE\.JOBS\.C *=\) .*,\1 <details omitted>,'
STD_SED_CMDS.dg1+= -e 's,^\(MACHINE[_ARCH]* *=\) .*,\1 <details omitted>,'
STD_SED_CMDS.dg1+= -e 's,^\(MAKE *=\) .*,\1 <details omitted>,'
STD_SED_CMDS.dg1+= -e 's,^\(\.SHELL *=\) .*,\1 <details omitted>,'
+STD_SED_CMDS.dg1+= -e '/\.SYSPATH/d'
STD_SED_CMDS.dg2= ${STD_SED_CMDS.dg1}
STD_SED_CMDS.dg2+= -e 's,\(last modified\) ..:..:.. ... ..\, ....,\1 <timestamp>,'
@@ -593,15 +708,24 @@ STD_SED_CMDS.hide-from-output= \
# bash 5.1.0 bash: line 1: /nonexistent: No such file or directory
# dash dash: 1: cannot open /nonexistent: No such file
#
+STD_SED_CMDS.shell+= -e 's,^${.SHELL},${.SHELL:T},'
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.shell+= -e 's,: command not found,: not found,'
+
+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= \
-e 's,\(Regex compilation error:\).*,\1 (details omitted),'
+# Normalize timestamps from ':gmtime' or ':localtime' to '<timestamp>'.
+# See STD_SED_CMDS.dg2 for timestamps from the debug log.
+STD_SED_CMDS.timestamp= \
+ -e 's,[A-Z][a-z][a-z] [A-Z][a-z][a-z] [ 0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] [12][0-9][0-9][0-9],<timestamp>,'
+
# End of the configuration helpers section.
.-include "Makefile.inc"
@@ -633,43 +757,36 @@ TOOL_TR?= tr
TOOL_DIFF?= diff
DIFF_FLAGS?= -u
-.if defined(.PARSEDIR)
# ensure consistent results from sort(1)
LC_ALL= C
LANG= C
.export LANG LC_ALL
-.endif
.if ${.MAKE.MODE:Unormal:Mmeta} != ""
# we don't need the noise
_MKMSG_TEST= :
.endif
-
-# for many tests we need a TMPDIR that will not collide
-# with other users.
-.if ${.OBJDIR} != ${.CURDIR}
-# easy
-TMPDIR:= ${.OBJDIR}/tmp
-.elif defined(TMPDIR)
-TMPDIR:= ${TMPDIR}/uid${.MAKE.UID}
-.else
-TMPDIR:= /tmp/uid${.MAKE.UID}
-.endif
-# make sure it exists
-.if !exist(${TMPDIR})
-x!= echo; mkdir -p ${TMPDIR}
+# Some Linux systems such as Fedora have deprecated egrep in favor of grep -E.
+.if ${.MAKE.OS:NLinux} == ""
+EGREP= grep -E
.endif
-
-MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc
+# Keep the classical definition for all other systems. Just as the bmake code
+# is kept compatible with C90, the tests are kept compatible with systems that
+# are several decades old and don't follow modern POSIX standards.
+EGREP?= egrep
+
+MAKE_TEST_ENV= EGREP="${EGREP}"
+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"
-LIMIT_RESOURCES?= ulimit -v 200000
+LIMIT_RESOURCES?= ulimit -v 300000
.endif
LIMIT_RESOURCES?= :
-# Each test is run in a sub-make, to keep the tests for interfering with
+# Each test is run in a sub-make, to keep the tests from interfering with
# each other, and because they use different environment variables and
# command line options.
.SUFFIXES: .mk .rawout .out
@@ -687,23 +804,38 @@ LIMIT_RESOURCES?= :
echo $$status > ${.TARGET:R}.status
@mv ${.TARGET}.tmp ${.TARGET}
-# Postprocess the test output so that the results can be compared.
+# Postprocess the test output to make the output platform-independent.
#
+# Replace anything after 'stopped in' with unit-tests
+_SED_CMDS+= -e '/stopped/s, /.*, unit-tests,'
+# Allow the test files to be placed anywhere.
+_SED_CMDS+= -e 's,\(\.PARSEDIR}\) = `'"/[^']*'"',\1 = <some-dir>,'
+_SED_CMDS+= -e 's,\(\.INCLUDEDFROMDIR}\) = `'"/[^']*'"',\1 = <some-dir>,'
+_SED_CMDS+= -e 's,${TMPDIR},<tmpdir>,g' -e 's,${TMPDIR:tA},<tmpdir>,g'
+# canonicalize ${.OBJDIR} and ${.CURDIR}
+_SED_CMDS+= -e 's,${.CURDIR},<curdir>,g'
+.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' -e 's,${.OBJDIR:tA},<curdir>,g'
+.endif
# always pretend .MAKE was called 'make'
_SED_CMDS+= -e 's,^${TEST_MAKE:T:S,.,\\.,g}[][0-9]*:,make:,'
_SED_CMDS+= -e 's,${TEST_MAKE:S,.,\\.,g},make,'
-_SED_CMDS+= -e 's,${TEST_MAKE:T:S,.,\\.,g}[][0-9]* warning,make warning,'
_SED_CMDS+= -e 's,^usage: ${TEST_MAKE:T:S,.,\\.,g} ,usage: make ,'
-# replace anything after 'stopped in' with unit-tests
-_SED_CMDS+= -e '/stopped/s, /.*, unit-tests,'
-_SED_CMDS+= -e 's,${TMPDIR},TMPDIR,g'
-# strip ${.CURDIR}/ from the output
-_SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g'
+_SED_CMDS+= -e 's,${TEST_MAKE:T:S,.,\\.,g}\(\[[1-9][0-9]*\]:\),make\1,'
+_SED_CMDS+= -e 's,<curdir>/,,g'
_SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'
-# on AT&T derrived systems; false exits 255 not 1
+_SED_CMDS+= -e '/MAKE_VERSION/d'
+_SED_CMDS+= -e '/EGREP=/d'
+
+# 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..30049eaa7c26 100644
--- a/contrib/bmake/unit-tests/Makefile.config.in
+++ b/contrib/bmake/unit-tests/Makefile.config.in
@@ -1,4 +1,7 @@
-# $Id: Makefile.config.in,v 1.1 2018/12/30 17:14:24 sjg Exp $
+# $Id: Makefile.config.in,v 1.4 2022/09/09 18:44:56 sjg Exp $
srcdir= @srcdir@
+EGREP= @egrep@
+TOOL_DIFF?= @diff@
DIFF_FLAGS?= @diff_u@
+UTC_1= @UTC_1@
diff --git a/contrib/bmake/unit-tests/archive.exp b/contrib/bmake/unit-tests/archive.exp
index 645add4f5899..5cf847388544 100644
--- a/contrib/bmake/unit-tests/archive.exp
+++ b/contrib/bmake/unit-tests/archive.exp
@@ -25,4 +25,12 @@ depend-on-existing-member
Making remove-archive
rm -f libprog.a
+begin library
+Examining libbad.a...up-to-date.
+Examining -lbad...up-to-date.
+Examining libgood.a...library...up-to-date.
+Examining -lgood...library...up-to-date.
+Examining library...nonexistent....PHONY node...out-of-date.
+Examining .END...nonexistent...nonexistent and no sources...out-of-date.
+end library
exit status 0
diff --git a/contrib/bmake/unit-tests/archive.mk b/contrib/bmake/unit-tests/archive.mk
index 2cd43a99e9ad..eef64396f419 100644
--- a/contrib/bmake/unit-tests/archive.mk
+++ b/contrib/bmake/unit-tests/archive.mk
@@ -1,4 +1,4 @@
-# $NetBSD: archive.mk,v 1.12 2021/04/09 14:42:00 christos Exp $
+# $NetBSD: archive.mk,v 1.13 2024/04/27 20:23:22 rillig Exp $
#
# Very basic demonstration of handling archives, based on the description
# in PSD.doc/tutorial.ms.
@@ -24,6 +24,12 @@ all:
@${MAKE} -f ${MAKEFILE} depend-on-existing-member
@${MAKE} -f ${MAKEFILE} depend-on-nonexistent-member
@${MAKE} -f ${MAKEFILE} remove-archive
+ @${MAKE} -f ${MAKEFILE} set-up-library
+ @${MAKE} -f ${MAKEFILE} -dm library 2>&1 \
+ | sed -n '/^Examining/p' \
+ | sed 's,\.\.\.modified[^.]*,,'
+ @${MAKE} -f ${MAKEFILE} tear-down-library
+
create-archive: ${ARCHIVE} pre post
@@ -58,3 +64,28 @@ pre: .USEBEFORE
@echo Making ${.TARGET} ${.OODATE:C,.+,out-of-date,W} ${.OODATE:O}
post: .USE
@echo
+
+
+set-up-library: .PHONY
+ @echo "member" > member.txt
+ @echo "not a library" > libbad.a
+ @ar cr libgood.a member.txt
+ @echo "begin library"
+
+.if make(library)
+.SUFFIXES: .a
+.LIBS: .a
+.endif
+# The two lines for libgood contain the word "library", the two lines for
+# libbad don't.
+#
+# expect: Examining libbad.a...up-to-date.
+# expect: Examining -lbad...up-to-date.
+# expect: Examining libgood.a...library...up-to-date.
+# expect: Examining -lgood...library...up-to-date.
+library: .PHONY libbad.a -lbad libgood.a -lgood
+ : Making ${.TARGET} from ${.ALLSRC}
+
+tear-down-library: .PHONY
+ @echo "end library"
+ @rm member.txt libbad.a libgood.a
diff --git a/contrib/bmake/unit-tests/cmd-errors-jobs.exp b/contrib/bmake/unit-tests/cmd-errors-jobs.exp
index 9ed0557975b3..c8e483a9609b 100644
--- a/contrib/bmake/unit-tests/cmd-errors-jobs.exp
+++ b/contrib/bmake/unit-tests/cmd-errors-jobs.exp
@@ -1,9 +1,9 @@
-: undefined eol
-make: Unclosed variable "UNCLOSED"
-: unclosed-variable
-make: Unclosed variable expression (expecting '}') for "UNCLOSED"
-: unclosed-modifier
-make: Unknown modifier "Z"
-: unknown-modifier eol
-: end eol
+: undefined--eol
+make: in target "unclosed-expression": Unclosed variable "UNCLOSED"
+: unclosed-expression-
+make: Unclosed expression, expecting '}' for "UNCLOSED"
+: unclosed-modifier-
+make: in target "unknown-modifier": while evaluating variable "UNKNOWN": Unknown modifier "Z"
+: unknown-modifier--eol
+: end-eol
exit status 0
diff --git a/contrib/bmake/unit-tests/cmd-errors-jobs.mk b/contrib/bmake/unit-tests/cmd-errors-jobs.mk
index 8462a2e3497e..7a82c0b416e8 100644
--- a/contrib/bmake/unit-tests/cmd-errors-jobs.mk
+++ b/contrib/bmake/unit-tests/cmd-errors-jobs.mk
@@ -1,32 +1,39 @@
-# $NetBSD: cmd-errors-jobs.mk,v 1.1 2020/12/27 05:11:40 rillig Exp $
+# $NetBSD: cmd-errors-jobs.mk,v 1.4 2024/04/23 22:51:28 rillig Exp $
#
-# Demonstrate how errors in variable expansions affect whether the commands
+# Demonstrate how errors in expressions affect whether the commands
# are actually executed in jobs mode.
.MAKEFLAGS: -j1
-all: undefined unclosed-variable unclosed-modifier unknown-modifier end
+all: undefined unclosed-expression unclosed-modifier unknown-modifier end
-# Undefined variables are not an error. They expand to empty strings.
+# Undefined variables in expressions are not an error. They expand to empty
+# strings.
+# expect: : undefined--eol
undefined:
- : $@ ${UNDEFINED} eol
+ : $@-${UNDEFINED}-eol
-# XXX: As of 2020-11-01, this command is executed even though it contains
-# parse errors.
-unclosed-variable:
- : $@ ${UNCLOSED
+# XXX: This command is executed even though it contains parse errors.
+# expect: make: in target "unclosed-expression": Unclosed variable "UNCLOSED"
+# expect: : unclosed-expression-
+unclosed-expression:
+ : $@-${UNCLOSED
-# XXX: As of 2020-11-01, this command is executed even though it contains
-# parse errors.
+# XXX: This command is executed even though it contains parse errors.
+# expect: make: Unclosed expression, expecting '}' for "UNCLOSED"
+# expect: : unclosed-modifier-
unclosed-modifier:
- : $@ ${UNCLOSED:
+ : $@-${UNCLOSED:
-# XXX: As of 2020-11-01, this command is executed even though it contains
-# parse errors.
+# XXX: This command is executed even though it contains parse errors.
+# expect: make: in target "unknown-modifier": while evaluating variable "UNKNOWN": Unknown modifier "Z"
+# expect: : unknown-modifier--eol
unknown-modifier:
- : $@ ${UNKNOWN:Z} eol
+ : $@-${UNKNOWN:Z}-eol
+# expect: : end-eol
end:
- : $@ eol
+ : $@-eol
-# XXX: As of 2020-11-02, despite the parse errors, the exit status is 0.
+# XXX: Despite the parse errors, the exit status is 0.
+# expect: exit status 0
diff --git a/contrib/bmake/unit-tests/cmd-errors-lint.exp b/contrib/bmake/unit-tests/cmd-errors-lint.exp
index 90b63bbcb08e..d489c6be57c9 100644
--- a/contrib/bmake/unit-tests/cmd-errors-lint.exp
+++ b/contrib/bmake/unit-tests/cmd-errors-lint.exp
@@ -1,9 +1,9 @@
: undefined
-make: Unclosed variable "UNCLOSED"
-: unclosed-variable
-make: Unclosed variable expression (expecting '}') for "UNCLOSED"
+make: in target "unclosed-expression": Unclosed variable "UNCLOSED"
+: unclosed-expression
+make: Unclosed expression, expecting '}' for "UNCLOSED"
: unclosed-modifier
-make: Unknown modifier "Z"
+make: in target "unknown-modifier": while evaluating variable "UNKNOWN": Unknown modifier "Z"
: unknown-modifier
: end
exit status 2
diff --git a/contrib/bmake/unit-tests/cmd-errors-lint.mk b/contrib/bmake/unit-tests/cmd-errors-lint.mk
index 371e12af0f4f..2e7d537f5f1f 100644
--- a/contrib/bmake/unit-tests/cmd-errors-lint.mk
+++ b/contrib/bmake/unit-tests/cmd-errors-lint.mk
@@ -1,20 +1,21 @@
-# $NetBSD: cmd-errors-lint.mk,v 1.1 2020/11/02 20:43:27 rillig Exp $
+# $NetBSD: cmd-errors-lint.mk,v 1.2 2024/04/23 22:51:28 rillig Exp $
#
-# Demonstrate how errors in variable expansions affect whether the commands
+# Demonstrate how errors in expressions affect whether the commands
# are actually executed.
.MAKEFLAGS: -dL
-all: undefined unclosed-variable unclosed-modifier unknown-modifier end
+all: undefined unclosed-expression unclosed-modifier unknown-modifier end
-# Undefined variables are not an error. They expand to empty strings.
+# Undefined variables in expressions are not an error. They expand to empty
+# strings.
undefined:
: $@ ${UNDEFINED}
# XXX: As of 2020-11-01, this obvious syntax error is not detected.
# XXX: As of 2020-11-01, this command is executed even though it contains
# parse errors.
-unclosed-variable:
+unclosed-expression:
: $@ ${UNCLOSED
# XXX: As of 2020-11-01, this obvious syntax error is not detected.
diff --git a/contrib/bmake/unit-tests/cmd-errors.exp b/contrib/bmake/unit-tests/cmd-errors.exp
index 9ed0557975b3..c8e483a9609b 100644
--- a/contrib/bmake/unit-tests/cmd-errors.exp
+++ b/contrib/bmake/unit-tests/cmd-errors.exp
@@ -1,9 +1,9 @@
-: undefined eol
-make: Unclosed variable "UNCLOSED"
-: unclosed-variable
-make: Unclosed variable expression (expecting '}') for "UNCLOSED"
-: unclosed-modifier
-make: Unknown modifier "Z"
-: unknown-modifier eol
-: end eol
+: undefined--eol
+make: in target "unclosed-expression": Unclosed variable "UNCLOSED"
+: unclosed-expression-
+make: Unclosed expression, expecting '}' for "UNCLOSED"
+: unclosed-modifier-
+make: in target "unknown-modifier": while evaluating variable "UNKNOWN": Unknown modifier "Z"
+: unknown-modifier--eol
+: end-eol
exit status 0
diff --git a/contrib/bmake/unit-tests/cmd-errors.mk b/contrib/bmake/unit-tests/cmd-errors.mk
index 356fe1a3e4a2..d1125d444fcd 100644
--- a/contrib/bmake/unit-tests/cmd-errors.mk
+++ b/contrib/bmake/unit-tests/cmd-errors.mk
@@ -1,30 +1,31 @@
-# $NetBSD: cmd-errors.mk,v 1.4 2020/12/27 05:11:40 rillig Exp $
+# $NetBSD: cmd-errors.mk,v 1.6 2024/04/23 22:51:28 rillig Exp $
#
-# Demonstrate how errors in variable expansions affect whether the commands
+# Demonstrate how errors in expressions affect whether the commands
# are actually executed in compat mode.
-all: undefined unclosed-variable unclosed-modifier unknown-modifier end
+all: undefined unclosed-expression unclosed-modifier unknown-modifier end
-# Undefined variables are not an error. They expand to empty strings.
+# Undefined variables in expressions are not an error. They expand to empty
+# strings.
undefined:
- : $@ ${UNDEFINED} eol
+ : $@-${UNDEFINED}-eol
# XXX: As of 2020-11-01, this command is executed even though it contains
# parse errors.
-unclosed-variable:
- : $@ ${UNCLOSED
+unclosed-expression:
+ : $@-${UNCLOSED
# XXX: As of 2020-11-01, this command is executed even though it contains
# parse errors.
unclosed-modifier:
- : $@ ${UNCLOSED:
+ : $@-${UNCLOSED:
# XXX: As of 2020-11-01, this command is executed even though it contains
# parse errors.
unknown-modifier:
- : $@ ${UNKNOWN:Z} eol
+ : $@-${UNKNOWN:Z}-eol
end:
- : $@ eol
+ : $@-eol
# XXX: As of 2020-11-02, despite the parse errors, the exit status is 0.
diff --git a/contrib/bmake/unit-tests/cmd-interrupt.exp b/contrib/bmake/unit-tests/cmd-interrupt.exp
index 91f4439e7bea..242db1d9507d 100755
--- a/contrib/bmake/unit-tests/cmd-interrupt.exp
+++ b/contrib/bmake/unit-tests/cmd-interrupt.exp
@@ -2,7 +2,6 @@
make: *** cmd-interrupt-ordinary removed
interrupt-ordinary: ok
> cmd-interrupt-phony
-make: *** cmd-interrupt-phony removed
interrupt-phony: ok
> cmd-interrupt-precious
interrupt-precious: ok
diff --git a/contrib/bmake/unit-tests/cmd-interrupt.mk b/contrib/bmake/unit-tests/cmd-interrupt.mk
index fa0d85fc9063..140651b55c62 100755
--- a/contrib/bmake/unit-tests/cmd-interrupt.mk
+++ b/contrib/bmake/unit-tests/cmd-interrupt.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cmd-interrupt.mk,v 1.3 2020/11/15 14:07:53 rillig Exp $
+# $NetBSD: cmd-interrupt.mk,v 1.4 2023/03/18 22:20:12 sjg Exp $
#
# Tests for interrupting a command.
#
@@ -30,7 +30,7 @@ interrupt-ordinary:
interrupt-phony: .PHONY
@${.MAKE} ${MAKEFLAGS} -f ${MAKEFILE} cmd-interrupt-phony || true
# The ././ is necessary to work around the file cache.
- @echo ${.TARGET}: ${exists(././cmd-interrupt-phony) :? error : ok }
+ @echo ${.TARGET}: ${exists(././cmd-interrupt-phony) :? ok : error }
interrupt-precious: .PRECIOUS
@${.MAKE} ${MAKEFLAGS} -f ${MAKEFILE} cmd-interrupt-precious || true
diff --git a/contrib/bmake/unit-tests/cmdline-undefined.exp b/contrib/bmake/unit-tests/cmdline-undefined.exp
index 977ceee6dbf5..197f6da60131 100644
--- a/contrib/bmake/unit-tests/cmdline-undefined.exp
+++ b/contrib/bmake/unit-tests/cmdline-undefined.exp
@@ -1,17 +1,17 @@
The = assignment operator
-make: "cmdline-undefined.mk" line 29: From the command line: Undefined is .
-make: "cmdline-undefined.mk" line 30: From .MAKEFLAGS '=': Undefined is .
-make: "cmdline-undefined.mk" line 31: From .MAKEFLAGS ':=': Undefined is .
-make: "cmdline-undefined.mk" line 35: From the command line: Undefined is now defined.
-make: "cmdline-undefined.mk" line 36: From .MAKEFLAGS '=': Undefined is now defined.
-make: "cmdline-undefined.mk" line 37: From .MAKEFLAGS ':=': Undefined is now defined.
+make: "cmdline-undefined.mk" line 31: From the command line: Undefined is .
+make: "cmdline-undefined.mk" line 34: From .MAKEFLAGS '=': Undefined is .
+make: "cmdline-undefined.mk" line 37: From .MAKEFLAGS ':=': Undefined is .
+make: "cmdline-undefined.mk" line 43: From the command line: Undefined is now defined.
+make: "cmdline-undefined.mk" line 46: From .MAKEFLAGS '=': Undefined is now defined.
+make: "cmdline-undefined.mk" line 49: From .MAKEFLAGS ':=': Undefined is now defined.
The := assignment operator
-make: "cmdline-undefined.mk" line 29: From the command line: Undefined is .
-make: "cmdline-undefined.mk" line 30: From .MAKEFLAGS '=': Undefined is .
-make: "cmdline-undefined.mk" line 31: From .MAKEFLAGS ':=': Undefined is .
-make: "cmdline-undefined.mk" line 35: From the command line: Undefined is now defined.
-make: "cmdline-undefined.mk" line 36: From .MAKEFLAGS '=': Undefined is now defined.
-make: "cmdline-undefined.mk" line 37: From .MAKEFLAGS ':=': Undefined is now defined.
+make: "cmdline-undefined.mk" line 31: From the command line: Undefined is .
+make: "cmdline-undefined.mk" line 34: From .MAKEFLAGS '=': Undefined is .
+make: "cmdline-undefined.mk" line 37: From .MAKEFLAGS ':=': Undefined is .
+make: "cmdline-undefined.mk" line 43: From the command line: Undefined is now defined.
+make: "cmdline-undefined.mk" line 46: From .MAKEFLAGS '=': Undefined is now defined.
+make: "cmdline-undefined.mk" line 49: From .MAKEFLAGS ':=': Undefined is now defined.
exit status 0
diff --git a/contrib/bmake/unit-tests/cmdline-undefined.mk b/contrib/bmake/unit-tests/cmdline-undefined.mk
index 5a3375cbbfb8..e7c0400ad1e1 100644
--- a/contrib/bmake/unit-tests/cmdline-undefined.mk
+++ b/contrib/bmake/unit-tests/cmdline-undefined.mk
@@ -1,6 +1,6 @@
-# $NetBSD: cmdline-undefined.mk,v 1.2 2020/11/04 04:49:33 rillig Exp $
+# $NetBSD: cmdline-undefined.mk,v 1.5 2024/04/23 22:51:28 rillig Exp $
#
-# Tests for undefined variable expressions in the command line.
+# Tests for undefined variables in expressions in the command line.
all:
# When the command line is parsed, variable assignments using the
@@ -26,14 +26,26 @@ all:
.MAKEFLAGS: MAKEFLAGS_ASSIGN='Undefined is $${UNDEFINED}.'
.MAKEFLAGS: MAKEFLAGS_SUBST:='Undefined is $${UNDEFINED}.'
+# expect+2: From the command line: Undefined is .
+# expect+1: From the command line: Undefined is .
.info From the command line: ${CMDLINE}
+# expect+2: From .MAKEFLAGS '=': Undefined is .
+# expect+1: From .MAKEFLAGS '=': Undefined is .
.info From .MAKEFLAGS '=': ${MAKEFLAGS_ASSIGN}
+# expect+2: From .MAKEFLAGS ':=': Undefined is .
+# expect+1: From .MAKEFLAGS ':=': Undefined is .
.info From .MAKEFLAGS ':=': ${MAKEFLAGS_SUBST}
UNDEFINED?= now defined
+# expect+2: From the command line: Undefined is now defined.
+# expect+1: From the command line: Undefined is now defined.
.info From the command line: ${CMDLINE}
+# expect+2: From .MAKEFLAGS '=': Undefined is now defined.
+# expect+1: From .MAKEFLAGS '=': Undefined is now defined.
.info From .MAKEFLAGS '=': ${MAKEFLAGS_ASSIGN}
+# expect+2: From .MAKEFLAGS ':=': Undefined is now defined.
+# expect+1: From .MAKEFLAGS ':=': Undefined is now defined.
.info From .MAKEFLAGS ':=': ${MAKEFLAGS_SUBST}
print-undefined:
diff --git a/contrib/bmake/unit-tests/cmdline.exp b/contrib/bmake/unit-tests/cmdline.exp
index 596281ab0a1f..5700da3295fb 100644
--- a/contrib/bmake/unit-tests/cmdline.exp
+++ b/contrib/bmake/unit-tests/cmdline.exp
@@ -1,5 +1,8 @@
makeobjdir-direct:
-show-objdir: TMPDIR/6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5
+show-objdir: <tmpdir>/6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5
makeobjdir-indirect:
-show-objdir: TMPDIR/a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45/
+show-objdir: <tmpdir>/a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45/
+space-and-comment:
+value # no comment $
+value # no comment $
exit status 0
diff --git a/contrib/bmake/unit-tests/cmdline.mk b/contrib/bmake/unit-tests/cmdline.mk
index f82e7f967ef8..40a0d7ccddbb 100644
--- a/contrib/bmake/unit-tests/cmdline.mk
+++ b/contrib/bmake/unit-tests/cmdline.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cmdline.mk,v 1.3 2021/02/06 18:26:03 sjg Exp $
+# $NetBSD: cmdline.mk,v 1.5 2024/04/23 22:51:28 rillig Exp $
#
# Tests for command line parsing and related special variables.
@@ -11,6 +11,7 @@ DIR12= ${TMPBASE}/${SUB1}/${SUB2}
all: prepare-dirs
all: makeobjdir-direct makeobjdir-indirect
+all: space-and-comment
prepare-dirs:
@rm -rf ${DIR2} ${DIR12}
@@ -23,7 +24,7 @@ makeobjdir-direct:
@${MAKE_CMD} MAKEOBJDIR=${DIR2} show-objdir
# The .OBJDIR can be set via the MAKEOBJDIR command line variable,
-# and that variable could even contain the usual modifiers.
+# and expressions based on that variable can contain the usual modifiers.
# Since the .OBJDIR=MAKEOBJDIR assignment happens very early,
# the SUB2 variable in the modifier is not defined yet and is therefore empty.
# The SUB1 in the resulting path comes from the environment variable TMPBASE,
@@ -34,3 +35,24 @@ makeobjdir-indirect:
show-objdir:
@echo $@: ${.OBJDIR:Q}
+
+
+# Variable assignments in the command line are handled differently from
+# variable assignments in makefiles. In the command line, trailing whitespace
+# is preserved, and the '#' does not start a comment. This is because the
+# low-level parsing from ParseRawLine does not take place.
+#
+# Preserving '#' and trailing whitespace has the benefit that when passing
+# such values to sub-makes via MAKEFLAGS, no special encoding is needed.
+# Leading whitespace in the variable value is discarded though, which makes
+# the behavior inconsistent.
+space-and-comment: .PHONY
+ @echo $@:
+
+ @env -i \
+ ${MAKE} -r -f /dev/null ' VAR= value # no comment ' -v VAR \
+ | sed 's,$$,$$,'
+
+ @env -i MAKEFLAGS="' VAR= value # no comment '" \
+ ${MAKE} -r -f /dev/null -v VAR \
+ | sed 's,$$,$$,'
diff --git a/contrib/bmake/unit-tests/comment.mk b/contrib/bmake/unit-tests/comment.mk
index d4fb041104a7..fea0f0b3d817 100644
--- a/contrib/bmake/unit-tests/comment.mk
+++ b/contrib/bmake/unit-tests/comment.mk
@@ -1,4 +1,4 @@
-# $NetBSD: comment.mk,v 1.3 2020/11/15 14:07:53 rillig Exp $
+# $NetBSD: comment.mk,v 1.7 2024/04/23 22:51:28 rillig Exp $
#
# Demonstrate how comments are written in makefiles.
@@ -15,13 +15,15 @@ on and on.
# Comments can be indented with spaces, but that is rather unusual.
# Comments can be indented with a tab.
- # These are not shell commands, they are just makefile comments.
+ # Since parse.c 1.127 from 2007-01-01, these are not shell commands,
+ # they are just makefile comments. Before that commit, these comments
+ # triggered the error message "Unassociated shell command".
.if 1 # There can be comments after conditions.
.endif # And after the closing directive.
VAR= # This comment makes the variable value empty.
- # ParseGetLine removes any whitespace before the
+ # ParseRawLine removes any whitespace before the
# comment.
.if ${VAR} != ""
. error
@@ -51,9 +53,9 @@ VAR= \# # Both in the assignment.
. error
.endif
-# Since 2012-03-24 the variable modifier :[#] does not need to be escaped.
-# To keep the parsing code simple, any "[#" does not start a comment, even
-# outside of a variable expression.
+# Since 2012-03-24 the modifier :[#] does not need to be escaped.
+# To keep the parsing code simple, the "#" in "[#" does not start a comment,
+# regardless of the syntactical context it appears in.
WORDS= ${VAR:[#]} [#
.if ${WORDS} != "1 [#"
. error
diff --git a/contrib/bmake/unit-tests/compat-error.mk b/contrib/bmake/unit-tests/compat-error.mk
index 4cbc48d4b6bb..bcfeb14ac408 100644
--- a/contrib/bmake/unit-tests/compat-error.mk
+++ b/contrib/bmake/unit-tests/compat-error.mk
@@ -1,17 +1,24 @@
-# $NetBSD: compat-error.mk,v 1.3 2020/12/13 19:33:53 rillig Exp $
+# $NetBSD: compat-error.mk,v 1.5 2022/05/08 06:51:27 rillig Exp $
#
# Test detailed error handling in compat mode.
#
-# Until 2020-12-13, .ERROR_TARGET was success3, which was wrong.
-# Since compat.c 1.215 from 2020-12-13, it is 'fail1', which is the first
-# failed top-level target. XXX: Even better would be if .ERROR_TARGET were
-# the smallest target that caused the build to fail, even if it were a
-# sub-sub-sub-dependency of a top-level target.
+# Make several targets that alternately succeed and fail.
#
-# XXX: As of 2020-12-13, .ERROR_CMD is empty, which is wrong.
+# The first failing top-level target is recorded in '.ERROR_TARGET'. While
+# this information may give a hint as to which target failed, it would be more
+# useful at that point to know the actual target that failed, or the complete
+# chain from root cause to top-level target.
+#
+# Historic bugs
+# Before compat.c 1.215 from 2020-12-13, '.ERROR_TARGET' was 'success3',
+# which was obviously wrong.
+#
+# Bugs
+# As of 2020-12-13, '.ERROR_CMD' is empty, which does not provide any
+# insight into the command that actually failed.
#
# See also:
-# Compat_Run
+# Compat_MakeAll
#
# The commit that added the NULL command to gn->commands:
# CVS: 1994.06.06.22.45.??
@@ -20,10 +27,10 @@
# 2020: LstNode_SetNull(cmdNode);
#
# The commit that skipped NULL commands for .ERROR_CMD:
-# CVS: 2016.08.11.19.53.??
+# CVS: 2016.08.11.19.53.17
# Git: 58b23478b7353d46457089e726b07a49197388e4
-.MAKEFLAGS: success1 fail1 success2 fail2 success3
+.MAKEFLAGS: -k success1 fail1 success2 fail2 success3
success1 success2 success3:
: Making ${.TARGET} out of nothing.
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-eq.exp b/contrib/bmake/unit-tests/cond-cmp-numeric-eq.exp
index 72d3d935755b..059007f00c84 100644
--- a/contrib/bmake/unit-tests/cond-cmp-numeric-eq.exp
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-eq.exp
@@ -1,5 +1,5 @@
-make: "cond-cmp-numeric-eq.mk" line 67: Malformed conditional (!(12345 = 12345))
-make: "cond-cmp-numeric-eq.mk" line 74: Malformed conditional (!(12345 === 12345))
+make: "cond-cmp-numeric-eq.mk" line 68: Malformed conditional (!(12345 = 12345))
+make: "cond-cmp-numeric-eq.mk" line 76: Malformed conditional (!(12345 === 12345))
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-eq.mk b/contrib/bmake/unit-tests/cond-cmp-numeric-eq.mk
index c6b39876e75e..1d0ade26087e 100755
--- a/contrib/bmake/unit-tests/cond-cmp-numeric-eq.mk
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-eq.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-cmp-numeric-eq.mk,v 1.5 2020/11/08 21:47:59 rillig Exp $
+# $NetBSD: cond-cmp-numeric-eq.mk,v 1.7 2023/09/07 05:36:33 rillig Exp $
#
# Tests for numeric comparisons with the == operator in .if conditions.
@@ -40,7 +40,7 @@
. error
.endif
-# As of 2020-08-23, numeric comparison is implemented as parsing both sides
+# Numeric comparison works by parsing both sides
# as double, and then performing a normal comparison. The range of double is
# typically 16 or 17 significant digits, therefore these two numbers seem to
# be equal.
@@ -64,6 +64,7 @@
.endif
# There is no = operator for numbers.
+# expect+1: Malformed conditional (!(12345 = 12345))
.if !(12345 = 12345)
. error
.else
@@ -71,11 +72,9 @@
.endif
# There is no === operator for numbers either.
+# expect+1: Malformed conditional (!(12345 === 12345))
.if !(12345 === 12345)
. error
.else
. error
.endif
-
-all:
- @:;
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-ge.mk b/contrib/bmake/unit-tests/cond-cmp-numeric-ge.mk
index e64be7f0c1a8..62975f950c2a 100755
--- a/contrib/bmake/unit-tests/cond-cmp-numeric-ge.mk
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-ge.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-cmp-numeric-ge.mk,v 1.2 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: cond-cmp-numeric-ge.mk,v 1.3 2023/09/07 05:36:33 rillig Exp $
#
# Tests for numeric comparisons with the >= operator in .if conditions.
@@ -62,7 +62,7 @@
. error
.endif
-# As of 2020-08-23, numeric comparison is implemented as parsing both sides
+# Numeric comparison works by parsing both sides
# as double, and then performing a normal comparison. The range of double is
# typically 16 or 17 significant digits, therefore these two numbers seem to
# be equal.
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-gt.mk b/contrib/bmake/unit-tests/cond-cmp-numeric-gt.mk
index 1cdcc9891d6f..6134f7daf3fe 100755
--- a/contrib/bmake/unit-tests/cond-cmp-numeric-gt.mk
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-gt.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-cmp-numeric-gt.mk,v 1.2 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: cond-cmp-numeric-gt.mk,v 1.3 2023/09/07 05:36:33 rillig Exp $
#
# Tests for numeric comparisons with the > operator in .if conditions.
@@ -61,11 +61,11 @@
. error
.endif
-# As of 2020-08-23, numeric comparison is implemented as parsing both sides
+# Numeric comparison works by parsing both sides
# as double, and then performing a normal comparison. The range of double is
# typically 16 or 17 significant digits, therefore these two numbers seem to
# be equal.
-.if 1.000000000000000001 > 1.000000000000000002
+.if 1.000000000000000002 > 1.000000000000000001
. error
.endif
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-le.mk b/contrib/bmake/unit-tests/cond-cmp-numeric-le.mk
index 05f5e8dba312..231db76ba618 100755
--- a/contrib/bmake/unit-tests/cond-cmp-numeric-le.mk
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-le.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-cmp-numeric-le.mk,v 1.2 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: cond-cmp-numeric-le.mk,v 1.3 2023/09/07 05:36:33 rillig Exp $
#
# Tests for numeric comparisons with the <= operator in .if conditions.
@@ -62,7 +62,7 @@
. error
.endif
-# As of 2020-08-23, numeric comparison is implemented as parsing both sides
+# Numeric comparison works by parsing both sides
# as double, and then performing a normal comparison. The range of double is
# typically 16 or 17 significant digits, therefore these two numbers seem to
# be equal.
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-lt.mk b/contrib/bmake/unit-tests/cond-cmp-numeric-lt.mk
index b0dddd591543..9529fa9ff0eb 100755
--- a/contrib/bmake/unit-tests/cond-cmp-numeric-lt.mk
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-lt.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-cmp-numeric-lt.mk,v 1.2 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: cond-cmp-numeric-lt.mk,v 1.3 2023/09/07 05:36:33 rillig Exp $
#
# Tests for numeric comparisons with the < operator in .if conditions.
@@ -61,7 +61,7 @@
. error
.endif
-# As of 2020-08-23, numeric comparison is implemented as parsing both sides
+# Numeric comparison works by parsing both sides
# as double, and then performing a normal comparison. The range of double is
# typically 16 or 17 significant digits, therefore these two numbers seem to
# be equal.
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric-ne.mk b/contrib/bmake/unit-tests/cond-cmp-numeric-ne.mk
index 0a366a905a21..e311d21f0a54 100755
--- a/contrib/bmake/unit-tests/cond-cmp-numeric-ne.mk
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric-ne.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-cmp-numeric-ne.mk,v 1.2 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: cond-cmp-numeric-ne.mk,v 1.3 2023/09/07 05:36:33 rillig Exp $
#
# Tests for numeric comparisons with the != operator in .if conditions.
@@ -37,7 +37,7 @@
. error
.endif
-# As of 2020-08-23, numeric comparison is implemented as parsing both sides
+# Numeric comparison works by parsing both sides
# as double, and then performing a normal comparison. The range of double is
# typically 16 or 17 significant digits, therefore these two numbers seem to
# be equal.
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric.exp b/contrib/bmake/unit-tests/cond-cmp-numeric.exp
index 4a97b6879e7a..69a8a1e4fca0 100644
--- a/contrib/bmake/unit-tests/cond-cmp-numeric.exp
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric.exp
@@ -1,11 +1,15 @@
CondParser_Eval: !(${:UINF} > 1e100)
-make: "cond-cmp-numeric.mk" line 11: String comparison operator must be either == or !=
+make: "cond-cmp-numeric.mk" line 15: Comparison with '>' requires both operands 'INF' and '1e100' to be numeric
CondParser_Eval: ${:UNaN} > NaN
-make: "cond-cmp-numeric.mk" line 16: String comparison operator must be either == or !=
+make: "cond-cmp-numeric.mk" line 21: Comparison with '>' requires both operands 'NaN' and 'NaN' to be numeric
CondParser_Eval: !(${:UNaN} == NaN)
-lhs = "NaN", rhs = "NaN", op = ==
+Comparing "NaN" == "NaN"
CondParser_Eval: 123 ! 123
-make: "cond-cmp-numeric.mk" line 34: Malformed conditional (123 ! 123)
+make: "cond-cmp-numeric.mk" line 38: Malformed conditional (123 ! 123)
+CondParser_Eval: ${:U 123} < 124
+Comparing 123.000000 < 124.000000
+CondParser_Eval: ${:U123 } < 124
+make: "cond-cmp-numeric.mk" line 54: Comparison with '<' requires both operands '123 ' and '124' to be numeric
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..e025b99b27cd 100644
--- a/contrib/bmake/unit-tests/cond-cmp-numeric.mk
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric.mk
@@ -1,6 +1,9 @@
-# $NetBSD: cond-cmp-numeric.mk,v 1.4 2020/11/08 22:56:16 rillig Exp $
+# $NetBSD: cond-cmp-numeric.mk,v 1.7 2023/03/04 08:07:29 rillig Exp $
#
# Tests for numeric comparisons in .if conditions.
+#
+# See also:
+# cond-token-number.mk
.MAKEFLAGS: -dc
@@ -8,11 +11,13 @@
# Even if strtod(3) parses "INF" as +Infinity, make does not accept this
# since it is not really a number; see TryParseNumber.
+# expect+1: Comparison with '>' requires both operands 'INF' and '1e100' to be numeric
.if !(${:UINF} > 1e100)
. error
.endif
# Neither is NaN a number; see TryParseNumber.
+# expect+1: Comparison with '>' requires both operands 'NaN' and 'NaN' to be numeric
.if ${:UNaN} > NaN
. error
.endif
@@ -29,13 +34,27 @@
# whether the operator is valid, leaving the rest of the work to the
# evaluation functions EvalCompareNum and EvalCompareStr. Ensure that this
# parse error is properly reported.
-#
-# XXX: The warning message does not mention the actual operator.
+# expect+1: Malformed conditional (123 ! 123)
.if 123 ! 123
. error
.else
. 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: Comparison with '<' requires both operands '123 ' and '124' to be numeric
+.if ${:U123 } < 124
+. error
+.else
+. error
+.endif
+
all:
- @:;
diff --git a/contrib/bmake/unit-tests/cond-cmp-string.exp b/contrib/bmake/unit-tests/cond-cmp-string.exp
index a10341ed2121..e0aabfdadca4 100644
--- a/contrib/bmake/unit-tests/cond-cmp-string.exp
+++ b/contrib/bmake/unit-tests/cond-cmp-string.exp
@@ -1,11 +1,11 @@
-make: "cond-cmp-string.mk" line 18: Malformed conditional (str != str)
-make: "cond-cmp-string.mk" line 42: Malformed conditional ("string" != "str""ing")
-make: "cond-cmp-string.mk" line 49: Malformed conditional (!("value" = "value"))
-make: "cond-cmp-string.mk" line 56: Malformed conditional (!("value" === "value"))
-make: "cond-cmp-string.mk" line 113: String comparison operator must be either == or !=
-make: "cond-cmp-string.mk" line 120: String comparison operator must be either == or !=
-make: "cond-cmp-string.mk" line 127: String comparison operator must be either == or !=
-make: "cond-cmp-string.mk" line 134: String comparison operator must be either == or !=
+make: "cond-cmp-string.mk" line 19: Malformed conditional (str != str)
+make: "cond-cmp-string.mk" line 44: Malformed conditional ("string" != "str""ing")
+make: "cond-cmp-string.mk" line 52: Malformed conditional (!("value" = "value"))
+make: "cond-cmp-string.mk" line 60: Malformed conditional (!("value" === "value"))
+make: "cond-cmp-string.mk" line 118: Comparison with '<' requires both operands 'string' and 'string' to be numeric
+make: "cond-cmp-string.mk" line 126: Comparison with '<=' requires both operands 'string' and 'string' to be numeric
+make: "cond-cmp-string.mk" line 134: Comparison with '>' requires both operands 'string' and 'string' to be numeric
+make: "cond-cmp-string.mk" line 142: Comparison with '>=' requires both operands 'string' and 'string' to be numeric
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-cmp-string.mk b/contrib/bmake/unit-tests/cond-cmp-string.mk
index 9f3e731b2eb0..e3346e08b5a0 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.19 2024/04/23 22:51:28 rillig Exp $
#
# Tests for string comparisons in .if conditions.
@@ -15,18 +15,19 @@
# The left-hand side of the comparison must be enclosed in quotes.
# This one is not enclosed in quotes and thus generates an error message.
+# expect+1: Malformed conditional (str != str)
.if str != str
. error
.endif
-# The left-hand side of the comparison requires that any variable expression
-# is defined.
+# An expression that occurs on the left-hand side of the comparison must be
+# defined.
#
# The variable named "" is never defined, nevertheless it can be used as a
-# starting point for variable expressions. Applying the :U modifier to such
-# an undefined expression turns it into a defined expression.
+# starting point for an expression. 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
@@ -39,6 +40,7 @@
# It is not possible to concatenate two string literals to form a single
# string. In C, Python and the shell this is possible, but not in make.
+# expect+1: Malformed conditional ("string" != "str""ing")
.if "string" != "str""ing"
. error
.else
@@ -46,6 +48,7 @@
.endif
# There is no = operator for strings.
+# expect+1: Malformed conditional (!("value" = "value"))
.if !("value" = "value")
. error
.else
@@ -53,20 +56,22 @@
.endif
# There is no === operator for strings either.
+# expect+1: Malformed conditional (!("value" === "value"))
.if !("value" === "value")
. error
.else
. error
.endif
-# A variable expression can be enclosed in double quotes.
+# An expression can be enclosed in double quotes.
.if ${:Uword} != "${:Uword}"
. error
.endif
# Between 2003-01-01 (maybe even earlier) and 2020-10-30, adding one of the
-# characters " \t!=><" directly after a variable expression resulted in a
-# "Malformed conditional", even though the string was well-formed.
+# characters " \t!=><" directly after an expression in a string literal
+# resulted in a "Malformed conditional", even though the string was
+# well-formed.
.if ${:Uword } != "${:Uword} "
. error
.endif
@@ -85,13 +90,12 @@
. error
.endif
-# Adding another variable expression to the string literal works though.
+# Adding another expression to the string literal works though.
.if ${:Uword} != "${:Uwo}${:Urd}"
. error
.endif
-# Adding a space at the beginning of the quoted variable expression works
-# though.
+# Adding a space at the beginning of the quoted expression works though.
.if ${:U word } != " ${:Uword} "
. error
.endif
@@ -110,6 +114,7 @@
.endif
# Strings cannot be compared relationally, only for equality.
+# expect+1: Comparison with '<' requires both operands 'string' and 'string' to be numeric
.if "string" < "string"
. error
.else
@@ -117,6 +122,7 @@
.endif
# Strings cannot be compared relationally, only for equality.
+# expect+1: Comparison with '<=' requires both operands 'string' and 'string' to be numeric
.if "string" <= "string"
. error
.else
@@ -124,6 +130,7 @@
.endif
# Strings cannot be compared relationally, only for equality.
+# expect+1: Comparison with '>' requires both operands 'string' and 'string' to be numeric
.if "string" > "string"
. error
.else
@@ -131,8 +138,17 @@
.endif
# Strings cannot be compared relationally, only for equality.
+# expect+1: Comparison with '>=' requires both operands 'string' and 'string' to be numeric
.if "string" >= "string"
. error
.else
. error
.endif
+
+# Two expressions with different values compare unequal.
+VAR1= value1
+VAR2= value2
+.if ${VAR1} != ${VAR2}
+.else
+. error
+.endif
diff --git a/contrib/bmake/unit-tests/cond-cmp-unary.exp b/contrib/bmake/unit-tests/cond-cmp-unary.exp
index 89f90dc1651f..c5b800122eb9 100755
--- a/contrib/bmake/unit-tests/cond-cmp-unary.exp
+++ b/contrib/bmake/unit-tests/cond-cmp-unary.exp
@@ -1,2 +1,2 @@
-make: "cond-cmp-unary.mk" line 53: This is only reached because of a bug in EvalNotEmpty.
+make: "cond-cmp-unary.mk" line 54: This is only reached because of a bug in EvalTruthy.
exit status 0
diff --git a/contrib/bmake/unit-tests/cond-cmp-unary.mk b/contrib/bmake/unit-tests/cond-cmp-unary.mk
index 168de0f30e3f..80626a279358 100755
--- a/contrib/bmake/unit-tests/cond-cmp-unary.mk
+++ b/contrib/bmake/unit-tests/cond-cmp-unary.mk
@@ -1,11 +1,11 @@
-# $NetBSD: cond-cmp-unary.mk,v 1.2 2020/11/11 07:30:11 rillig Exp $
+# $NetBSD: cond-cmp-unary.mk,v 1.6 2023/11/19 21:47:52 rillig Exp $
#
# Tests for unary comparisons in .if conditions, that is, comparisons with
# a single operand. If the operand is a number, it is compared to zero,
# if it is a string, it is tested for emptiness.
-# The number 0 evaluates to false.
-.if 0
+# The number 0 in all its various representations evaluates to false.
+.if 0 || 0.0 || 0e0 || 0.0e0 || 0.0e10
. error
.endif
@@ -24,35 +24,52 @@
. error
.endif
-# The empty string may come from a variable expression.
+# The empty string may come from an expression.
#
-# XXX: As of 2020-11-11, this empty string is interpreted "as a number" in
-# EvalNotEmpty, which is plain wrong. The bug is in TryParseNumber.
+# XXX: As of 2023-06-01, this empty string is interpreted "as a number" in
+# EvalTruthy, which is plain wrong. The bug is in TryParseNumber.
.if ${:U}
. error
.endif
-# A variable expression that is not surrounded by quotes is interpreted
+# An expression that is not surrounded by quotes is interpreted
# as a number if possible, otherwise as a string.
.if ${:U0}
. error
.endif
-# A non-zero number from a variable expression evaluates to true.
+# A non-zero number from an expression evaluates to true.
.if !${:U12345}
. error
.endif
# A string of whitespace should evaluate to false.
#
-# XXX: As of 2020-11-11, the implementation in EvalNotEmpty does not skip
+# XXX: As of 2023-06-01, the implementation in EvalTruthy does not skip
# whitespace before testing for the end. This was probably an oversight in
# a commit from 1992-04-15 saying "A variable is empty when it just contains
# spaces".
.if ${:U }
-. info This is only reached because of a bug in EvalNotEmpty.
+# expect+1: This is only reached because of a bug in EvalTruthy.
+. info This is only reached because of a bug in EvalTruthy.
.else
. error
.endif
+# The condition '${VAR:M*}' is almost equivalent to '${VAR:M*} != ""'. The
+# only case where they differ is for a single word whose numeric value is zero.
+.if ${:U0:M*}
+. error
+.endif
+.if ${:U0:M*} == ""
+. error
+.endif
+# Multiple words cannot be parsed as a single number, thus evaluating to true.
+.if !${:U0 0:M*}
+. error
+.endif
+.if ${:U0 0:M*} == ""
+. error
+.endif
+
all: # nothing
diff --git a/contrib/bmake/unit-tests/cond-eof.exp b/contrib/bmake/unit-tests/cond-eof.exp
index 3b1e6eb1f056..58a74d854d91 100644
--- a/contrib/bmake/unit-tests/cond-eof.exp
+++ b/contrib/bmake/unit-tests/cond-eof.exp
@@ -1,9 +1,6 @@
-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: "cond-eof.mk" line 17: Malformed conditional (0 ${SIDE_EFFECT} ${SIDE_EFFECT2})
+make: "cond-eof.mk" line 20: Malformed conditional (1 ${SIDE_EFFECT} ${SIDE_EFFECT2})
+make: "cond-eof.mk" line 23: Malformed conditional ((0) ${SIDE_EFFECT} ${SIDE_EFFECT2})
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-eof.mk b/contrib/bmake/unit-tests/cond-eof.mk
index 08f432bc4593..a5f7ce95f89e 100644
--- a/contrib/bmake/unit-tests/cond-eof.mk
+++ b/contrib/bmake/unit-tests/cond-eof.mk
@@ -1,20 +1,24 @@
-# $NetBSD: cond-eof.mk,v 1.2 2020/12/14 20:28:09 rillig Exp $
+# $NetBSD: cond-eof.mk,v 1.6 2023/11/19 21:47:52 rillig Exp $
#
-# Tests for parsing conditions, especially the end of such conditions, which
-# are represented as the token TOK_EOF.
+# Tests for parsing the end of '.if' conditions, which are represented as the
+# token TOK_EOF.
+
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 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.
+# expect+1: Malformed conditional (0 ${SIDE_EFFECT} ${SIDE_EFFECT2})
.if 0 ${SIDE_EFFECT} ${SIDE_EFFECT2}
.endif
+# expect+1: Malformed conditional (1 ${SIDE_EFFECT} ${SIDE_EFFECT2})
.if 1 ${SIDE_EFFECT} ${SIDE_EFFECT2}
.endif
+# expect+1: Malformed conditional ((0) ${SIDE_EFFECT} ${SIDE_EFFECT2})
.if (0) ${SIDE_EFFECT} ${SIDE_EFFECT2}
.endif
diff --git a/contrib/bmake/unit-tests/cond-func-defined.exp b/contrib/bmake/unit-tests/cond-func-defined.exp
index 878f56de2ecc..1d4243f9eddd 100644
--- a/contrib/bmake/unit-tests/cond-func-defined.exp
+++ b/contrib/bmake/unit-tests/cond-func-defined.exp
@@ -1,8 +1,5 @@
-make: "cond-func-defined.mk" line 23: Missing closing parenthesis for defined()
-make: "cond-func-defined.mk" line 33: Missing closing parenthesis for defined()
-make: "cond-func-defined.mk" line 45: In .for loops, variable expressions for the loop variables are
-make: "cond-func-defined.mk" line 46: substituted at evaluation time. There is no actual variable
-make: "cond-func-defined.mk" line 47: involved, even if it feels like it.
+make: "cond-func-defined.mk" line 24: Missing closing parenthesis for defined()
+make: "cond-func-defined.mk" line 34: Missing closing parenthesis for defined()
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-func-defined.mk b/contrib/bmake/unit-tests/cond-func-defined.mk
index 2aa49ccbf147..6b24182c11c1 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.12 2024/04/23 22:51:28 rillig Exp $
#
# Tests for the defined() function in .if conditions.
@@ -20,16 +20,17 @@ ${:UA B}= variable name with spaces
.endif
# The argument of a function must not directly contain whitespace.
+# expect+1: Missing closing parenthesis for defined()
.if !defined(A B)
. error
.endif
-# If necessary, the whitespace can be generated by a variable expression.
+# If necessary, the whitespace can be generated by an expression.
.if !defined(${:UA B})
. error
.endif
-# Parse error: missing closing parenthesis; see ParseFuncArg.
+# expect+1: Missing closing parenthesis for defined()
.if defined(DEF
. error
.else
@@ -42,11 +43,16 @@ ${:UA B}= variable name with spaces
. if defined(var)
. error
. else
-. info In .for loops, variable expressions for the loop variables are
-. info substituted at evaluation time. There is no actual variable
-. info involved, even if it feels like it.
+# In .for loops, expressions based on the loop variables are substituted at
+# evaluation time. There is no actual variable involved, even if the code in
+# the makefiles looks like it.
. endif
.endfor
-all:
- @:;
+# Neither of the conditions is true. Before July 2020, the right-hand
+# condition was evaluated even though it was irrelevant.
+.if defined(UNDEF) && ${UNDEF:Mx} != ""
+. error
+.endif
+
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/cond-func-empty.exp b/contrib/bmake/unit-tests/cond-func-empty.exp
index 77a4edd47f49..1d955124d1c6 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 168: Unclosed variable "WORD"
+make: "cond-func-empty.mk" line 168: 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..057b175a7693 100644
--- a/contrib/bmake/unit-tests/cond-func-empty.mk
+++ b/contrib/bmake/unit-tests/cond-func-empty.mk
@@ -1,18 +1,19 @@
-# $NetBSD: cond-func-empty.mk,v 1.14 2021/04/11 13:35:56 rillig Exp $
+# $NetBSD: cond-func-empty.mk,v 1.24 2023/12/19 19:33:40 rillig Exp $
#
-# Tests for the empty() function in .if conditions, which tests a variable
+# Tests for the empty() function in .if conditions, which tests an
# expression for emptiness.
#
-# Note that the argument in the parentheses is indeed a variable name,
-# optionally followed by variable modifiers.
+# Note that the argument in the parentheses is a variable name, not an
+# expression. That name may be followed by ':...' modifiers.
#
.undef UNDEF
EMPTY= # empty
SPACE= ${:U }
+ZERO= 0
WORD= word
-# An undefined variable is empty.
+# An undefined variable counts as empty.
.if !empty(UNDEF)
. error
.endif
@@ -24,15 +25,13 @@ WORD= word
. error
.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.
+# The :S modifier replaces the empty value with an actual word. After
+# applying the :S modifier to the expression, its value is 'empty', so it is
+# no longer empty, but it is still based on an undefined variable. There are
+# a few modifiers that turn an undefined expression into a defined expression,
+# among them :U and :D, but not :S. Therefore, at the end of evaluating the
+# expression, the expression is still undefined, so its final value becomes an
+# empty string.
#
# XXX: This is hard to explain to someone who doesn't know these
# implementation details.
@@ -41,23 +40,25 @@ 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
+# When an expression is based on an undefined variable, its modifiers interact
+# in sometimes surprising ways. Applying the :S modifier to the undefined
+# expression makes its value non-empty, but doesn't change that the expression
+# is based on an undefined variable. The :U modifier that follows only looks
+# at the definedness state 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
-# the :U modifier is used in this expression.
+# Since the variable was undefined to begin with, the fallback value from the
+# :U modifier is used in this expression, instead of keeping the 'value' from
+# the :S modifier.
#
.if ${UNDEF:S,^$,value,W:Ufallback} != "fallback"
. error
@@ -78,12 +79,24 @@ WORD= word
. error
.endif
-# The empty variable named "" gets a fallback value of " ", which counts as
-# empty.
+# The variable ZERO has the numeric value 0, but is not empty. This is a
+# subtle difference between using either 'empty(ZERO)' or the expression
+# '${ZERO}' in a condition.
+.if empty(ZERO)
+. error
+.elif ${ZERO}
+. error
+.elif ${ZERO} == ""
+. error
+.endif
+
+# 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,20 +105,22 @@ 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
-# returned as an empty string.
+# that variable is indeed undefined. Since CondParser_FuncCallEmpty calls
+# Var_Parse without VARE_UNDEFERR, the value of the undefined variable ""
+# would be returned as an empty string.
${:U }= space
.if empty( )
. error
.endif
-# The value of the following expression is " word", which is not empty.
+# The value of the following expression is " word", which is not empty. To be
+# empty, _all_ characters in the expression value have to be whitespace, not
+# only the first.
.if empty(:U word)
. error
.endif
-# The :L modifier creates a variable expression that has the same value as
+# The :L modifier creates an expression that has the same value as
# its name, which both are "VAR" in this case. The value is therefore not
# empty.
.if empty(VAR:L)
@@ -123,19 +138,19 @@ ${:U }= space
. error
.endif
-# Ensure that variable expressions that appear as part of the argument are
-# properly parsed. Typical use cases for this are .for loops, which are
-# expanded to exactly these ${:U} expressions.
+# Ensure that expressions that appear as part of the function call
+# argument are properly parsed. Typical use cases for this are .for loops,
+# which are expanded to exactly these ${:U} expressions.
#
-# 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.
+# The argument expands to "WORD", and that variable is defined at the
+# beginning of this file. The surrounding 'W' and 'D' 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
-# There may be spaces at the outside of the parentheses.
+# There may be spaces outside the parentheses.
# Spaces inside the parentheses are interpreted as part of the variable name.
.if ! empty ( WORD )
. error
@@ -148,37 +163,60 @@ ${:U WORD }= variable name with spaces
. error
.endif
-# Parse error: missing closing parenthesis.
+# expect+2: Unclosed variable "WORD"
+# expect+1: Malformed conditional (empty(WORD)
.if empty(WORD
. error
.else
. 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. 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.
-#
-# The variable expression was expanded though, and this was wrong. The
-# expansion was done without VARE_WANTRES (called VARF_WANTRES back
-# then) though. This had the effect that the ${:U1} from the value of VARNAME
+# "VARNAME${:U2}", but without expanding any nested expression, in
+# this case the ${:U2}. The expression '${:U2}' was replaced with an empty
+# string, the resulting variable name was thus "VARNAME". This conceptually
+# wrong variable name should have been discarded quickly after parsing it, to
+# prevent it from doing any harm.
+#
+# The expression was expanded, and this was wrong. The
+# expansion was done without VARE_WANTRES (called VARF_WANTRES back then)
+# though. This had the effect that the ${:U1} from the value of VARNAME
# expanded to an empty string. This in turn created the seemingly recursive
# definition VARNAME=${VARNAME}, and that definition was never meant to be
# expanded.
#
-# This was fixed by expanding nested variable expressions in the variable name
+# This was fixed by expanding nested expressions in the variable name
# only if the flag VARE_WANTRES is given.
VARNAME= ${VARNAME${:U1}}
.if defined(VARNAME${:U2}) && !empty(VARNAME${:U2})
.endif
-all:
- @:;
+
+# If the word 'empty' is not followed by '(', it is not a function call but an
+# ordinary bare word. This bare word is interpreted as 'defined(empty)', and
+# since there is no variable named 'empty', the condition evaluates to false.
+.if empty
+. error
+.endif
+
+empty= # defined but empty
+.if empty
+.else
+. error
+.endif
diff --git a/contrib/bmake/unit-tests/cond-func-exists.mk b/contrib/bmake/unit-tests/cond-func-exists.mk
index 48d7e727dc3f..40228cd44902 100644
--- a/contrib/bmake/unit-tests/cond-func-exists.mk
+++ b/contrib/bmake/unit-tests/cond-func-exists.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-func-exists.mk,v 1.6 2020/11/30 20:12:29 rillig Exp $
+# $NetBSD: cond-func-exists.mk,v 1.7 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the exists() function in .if conditions.
@@ -17,7 +17,7 @@
.endif
# The only way to escape characters that would otherwise influence the parser
-# is to enclose them in a variable expression. For function arguments,
+# is to enclose them in an expression. For function arguments,
# neither the backslash nor the dollar sign act as escape character.
.if exists(\.)
. error
@@ -27,7 +27,7 @@
. error
.endif
-# The argument to the function can have several variable expressions.
+# The argument to the function can have several expressions.
# See cond-func.mk for the characters that cannot be used directly.
.if !exists(${.PARSEDIR}/${.PARSEFILE})
. error
diff --git a/contrib/bmake/unit-tests/cond-func-make.exp b/contrib/bmake/unit-tests/cond-func-make.exp
index 922203b72cbf..04ef6e29fee1 100644
--- a/contrib/bmake/unit-tests/cond-func-make.exp
+++ b/contrib/bmake/unit-tests/cond-func-make.exp
@@ -1,3 +1,4 @@
+make: "cond-func-make.mk" line 24: warning: Unfinished character list in pattern argument '[' to function 'make'
: via-cmdline
: via-dot-makeflags
exit status 0
diff --git a/contrib/bmake/unit-tests/cond-func-make.mk b/contrib/bmake/unit-tests/cond-func-make.mk
index d75b69bcf98f..15bc9f04d4e0 100644
--- a/contrib/bmake/unit-tests/cond-func-make.mk
+++ b/contrib/bmake/unit-tests/cond-func-make.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-func-make.mk,v 1.3 2020/09/25 20:11:06 rillig Exp $
+# $NetBSD: cond-func-make.mk,v 1.5 2023/06/23 04:56:54 rillig Exp $
#
# Tests for the make() function in .if conditions, which tests whether
# the argument has been passed as a target via the command line or later
@@ -20,5 +20,10 @@
. error
.endif
+# expect+1: warning: Unfinished character list in pattern argument '[' to function 'make'
+.if make([)
+. error
+.endif
+
via-cmdline via-dot-makeflags:
: $@
diff --git a/contrib/bmake/unit-tests/cond-func.exp b/contrib/bmake/unit-tests/cond-func.exp
index 855b9e5210fd..eeaa89445887 100644
--- a/contrib/bmake/unit-tests/cond-func.exp
+++ b/contrib/bmake/unit-tests/cond-func.exp
@@ -1,12 +1,12 @@
-make: "cond-func.mk" line 36: Missing closing parenthesis for defined()
-make: "cond-func.mk" line 51: Missing closing parenthesis for defined()
-make: "cond-func.mk" line 54: Missing closing parenthesis for defined()
-make: "cond-func.mk" line 94: The empty variable is never defined.
-make: "cond-func.mk" line 102: A plain function name is parsed as !empty(...).
-make: "cond-func.mk" line 109: A plain function name is parsed as !empty(...).
-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 37: Missing closing parenthesis for defined()
+make: "cond-func.mk" line 53: Missing closing parenthesis for defined()
+make: "cond-func.mk" line 57: Missing closing parenthesis for defined()
+make: "cond-func.mk" line 98: The empty variable is never defined.
+make: "cond-func.mk" line 108: A plain function name is parsed as defined(...).
+make: "cond-func.mk" line 116: A plain function name is parsed as defined(...).
+make: "cond-func.mk" line 127: Symbols may start with a function name.
+make: "cond-func.mk" line 133: Symbols may start with a function name.
+make: "cond-func.mk" line 139: 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-func.mk b/contrib/bmake/unit-tests/cond-func.mk
index 4ff43b72ef88..aabd31b4db46 100644
--- a/contrib/bmake/unit-tests/cond-func.mk
+++ b/contrib/bmake/unit-tests/cond-func.mk
@@ -1,12 +1,12 @@
-# $NetBSD: cond-func.mk,v 1.9 2020/11/15 14:07:53 rillig Exp $
+# $NetBSD: cond-func.mk,v 1.14 2023/11/19 21:47:52 rillig Exp $
#
# Tests for those parts of the functions in .if conditions that are common
# among several functions.
#
-# The below test uses the function defined(...) since it has no side-effects,
-# the other functions (except empty(...)) would work equally well. The
-# function empty is special because it uses a different parsing algorithm for
-# its argument.
+# The below test uses the 'defined' function since it has no side-effects.
+# The other functions would work equally well, except for 'empty', which
+# parses its argument differently from the other functions.
+#
DEF= defined
${:UA B}= variable name with spaces
@@ -33,11 +33,12 @@ ${VARNAME_UNBALANCED_BRACES}= variable name with unbalanced braces
.endif
# The argument of a function must not directly contain whitespace.
+# expect+1: Missing closing parenthesis for defined()
.if !defined(A B)
. error
.endif
-# If necessary, the whitespace can be generated by a variable expression.
+# If necessary, the whitespace can be generated by an expression.
.if !defined(${:UA B})
. error
.endif
@@ -48,9 +49,11 @@ ${VARNAME_UNBALANCED_BRACES}= variable name with unbalanced braces
#
# It's not entirely clear why these characters are forbidden.
# The most plausible reason seems to be typo detection.
+# expect+1: Missing closing parenthesis for defined()
.if !defined(A&B)
. error
.endif
+# expect+1: Missing closing parenthesis for defined()
.if !defined(A|B)
. error
.endif
@@ -74,7 +77,7 @@ ${VARNAME_UNBALANCED_BRACES}= variable name with unbalanced braces
# There may be spaces around the operators and parentheses, and even
# inside the parentheses. The spaces inside the parentheses are not
-# allowed for the empty() function (see cond-func-empty.mk), therefore
+# allowed for the 'empty' function (see cond-func-empty.mk), therefore
# they are typically omitted for the other functions as well.
.if ! defined ( DEF )
. error
@@ -91,22 +94,26 @@ ${VARNAME_UNBALANCED_BRACES}= variable name with unbalanced braces
.if defined()
. error
.else
+# expect+1: The empty variable is never defined.
. info The empty variable is never defined.
.endif
-# The plain word 'defined' is interpreted as '!empty(defined)'.
+# The plain word 'defined' is interpreted as 'defined(defined)', see
+# CondParser_ComparisonOrLeaf.
# That variable is not defined (yet).
.if defined
. error
.else
-. info A plain function name is parsed as !empty(...).
+# expect+1: A plain function name is parsed as defined(...).
+. info A plain function name is parsed as defined(...).
.endif
-# If a variable named 'defined' is actually defined and not empty, the plain
-# symbol 'defined' evaluates to true.
-defined= non-empty
+# If a variable named 'defined' is actually defined, the bare word 'defined'
+# is interpreted as 'defined(defined)', and the condition evaluates to true.
+defined= # defined but empty
.if defined
-. info A plain function name is parsed as !empty(...).
+# expect+1: A plain function name is parsed as defined(...).
+. info A plain function name is parsed as defined(...).
.else
. error
.endif
@@ -116,22 +123,21 @@ defined= non-empty
.if defined-var
. error
.else
+# expect+1: Symbols may start with a function name.
. info Symbols may start with a function name.
.endif
-defined-var= non-empty
+defined-var= # defined but empty
.if defined-var
+# expect+1: Symbols may start with a function name.
. info Symbols may start with a function name.
.else
. error
.endif
-# Missing closing parenthesis when parsing the function argument.
+# expect+1: Missing closing parenthesis for defined()
.if defined(
. error
.else
. error
.endif
-
-all:
- @:;
diff --git a/contrib/bmake/unit-tests/cond-late.exp b/contrib/bmake/unit-tests/cond-late.exp
index e179e8c74cc4..703677da634c 100644
--- a/contrib/bmake/unit-tests/cond-late.exp
+++ b/contrib/bmake/unit-tests/cond-late.exp
@@ -1,4 +1,4 @@
-make: Bad conditional expression ' != "no"' in ' != "no"?:'
+make: Bad conditional expression ' != "no"' before '?:'
yes
no
exit status 0
diff --git a/contrib/bmake/unit-tests/cond-late.mk b/contrib/bmake/unit-tests/cond-late.mk
index 4df3df2cf1d4..8e3d41f60001 100644
--- a/contrib/bmake/unit-tests/cond-late.mk
+++ b/contrib/bmake/unit-tests/cond-late.mk
@@ -1,11 +1,12 @@
-# $NetBSD: cond-late.mk,v 1.3 2020/11/15 14:07:53 rillig Exp $
+# $NetBSD: cond-late.mk,v 1.6 2023/12/10 20:12:28 rillig Exp $
#
-# Using the :? modifier, variable expressions can contain conditional
+# Using the :? modifier, expressions can contain conditional
# expressions that are evaluated late, at expansion time.
#
-# Any variables appearing in these
-# conditions are expanded before parsing the condition. This is
-# different from many other places.
+# Any expressions appearing in these conditions are expanded before parsing
+# the condition. This is different from conditions in .if directives, where
+# expressions are evaluated individually and only as far as necessary, see
+# cond-short.mk.
#
# Because of this, variables that are used in these lazy conditions
# should not contain double-quotes, or the parser will probably fail.
@@ -22,10 +23,14 @@ COND.false= "yes" != "yes"
# If the order of evaluation were to change to first parse the condition
# and then expand the variables, the output would change from the
# current "yes no" to "yes yes", since both variables are non-empty.
+# expect: yes
+# expect: no
cond-literal:
@echo ${ ${COND.true} :?yes:no}
@echo ${ ${COND.false} :?yes:no}
-VAR+= ${${UNDEF} != "no":?:}
+VAR= ${${UNDEF} != "no":?:}
+# expect-reset
+# expect: make: Bad conditional expression ' != "no"' before '?:'
.if empty(VAR:Mpattern)
.endif
diff --git a/contrib/bmake/unit-tests/cond-op-and-lint.exp b/contrib/bmake/unit-tests/cond-op-and-lint.exp
index 8817fd0d658b..0f2cdb0028f9 100644
--- a/contrib/bmake/unit-tests/cond-op-and-lint.exp
+++ b/contrib/bmake/unit-tests/cond-op-and-lint.exp
@@ -1,4 +1,4 @@
-make: "cond-op-and-lint.mk" line 9: Unknown operator '&'
+make: "cond-op-and-lint.mk" line 10: Unknown operator '&'
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-op-and-lint.mk b/contrib/bmake/unit-tests/cond-op-and-lint.mk
index 6262339016f5..bac4566314b0 100644
--- a/contrib/bmake/unit-tests/cond-op-and-lint.mk
+++ b/contrib/bmake/unit-tests/cond-op-and-lint.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-op-and-lint.mk,v 1.1 2020/11/08 23:54:28 rillig Exp $
+# $NetBSD: cond-op-and-lint.mk,v 1.2 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the && operator in .if conditions, in lint mode.
@@ -6,6 +6,7 @@
# The '&' operator is not allowed in lint mode.
# It is not used in practice anyway.
+# expect+1: Unknown operator '&'
.if 0 & 0
. error
.else
diff --git a/contrib/bmake/unit-tests/cond-op-and.exp b/contrib/bmake/unit-tests/cond-op-and.exp
index 173b6861a98b..6d5a14b211cc 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 37: Malformed conditional (0 || (${DEF} && ${UNDEF}))
+make: "cond-op-and.mk" line 42: Malformed conditional (0 || (${UNDEF} && ${UNDEF}))
+make: "cond-op-and.mk" line 45: Malformed conditional (0 || (!${UNDEF} && ${UNDEF}))
+make: "cond-op-and.mk" line 75: 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..19a0672ba44e 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.9 2023/12/17 09:44:00 rillig Exp $
#
# Tests for the && operator in .if conditions.
@@ -18,11 +18,42 @@
. 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
+# expect+1: Malformed conditional (0 || (${DEF} && ${UNDEF}))
+.if 0 || (${DEF} && ${UNDEF})
+.endif
+.if 0 || (!${DEF} && ${UNDEF})
+.endif
+# expect+1: Malformed conditional (0 || (${UNDEF} && ${UNDEF}))
+.if 0 || (${UNDEF} && ${UNDEF})
+.endif
+# expect+1: Malformed conditional (0 || (!${UNDEF} && ${UNDEF}))
+.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.
@@ -40,9 +71,14 @@
.endif
# There is no operator &&&.
+# expect+1: Malformed conditional (0 &&& 0)
.if 0 &&& 0
. error
.endif
-all:
- @:;
+# The '&&' operator must be preceded by whitespace, otherwise it becomes part
+# of the preceding bare word. The condition is parsed as '"1&&" != "" && 1'.
+.if 1&& && 1
+.else
+. error
+.endif
diff --git a/contrib/bmake/unit-tests/cond-op-not.exp b/contrib/bmake/unit-tests/cond-op-not.exp
index 440670ca7249..fcdceee9af21 100644
--- a/contrib/bmake/unit-tests/cond-op-not.exp
+++ b/contrib/bmake/unit-tests/cond-op-not.exp
@@ -1,9 +1,9 @@
-make: "cond-op-not.mk" line 29: Not empty evaluates to true.
-make: "cond-op-not.mk" line 37: Not space evaluates to false.
-make: "cond-op-not.mk" line 41: Not 0 evaluates to true.
-make: "cond-op-not.mk" line 49: Not 1 evaluates to false.
-make: "cond-op-not.mk" line 55: Not word evaluates to false.
-make: "cond-op-not.mk" line 59: Malformed conditional (!)
+make: "cond-op-not.mk" line 30: Not empty evaluates to true.
+make: "cond-op-not.mk" line 39: Not space evaluates to false.
+make: "cond-op-not.mk" line 44: Not 0 evaluates to true.
+make: "cond-op-not.mk" line 53: Not 1 evaluates to false.
+make: "cond-op-not.mk" line 60: Not word evaluates to false.
+make: "cond-op-not.mk" line 65: Malformed conditional (!)
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-op-not.mk b/contrib/bmake/unit-tests/cond-op-not.mk
index ffd5bc89e4bf..28f835fa3cbd 100644
--- a/contrib/bmake/unit-tests/cond-op-not.mk
+++ b/contrib/bmake/unit-tests/cond-op-not.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-op-not.mk,v 1.7 2021/01/19 17:49:13 rillig Exp $
+# $NetBSD: cond-op-not.mk,v 1.8 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the ! operator in .if conditions, which negates its argument.
@@ -26,6 +26,7 @@
.endif
.if !${:U}
+# expect+1: Not empty evaluates to true.
. info Not empty evaluates to true.
.else
. info Not empty evaluates to false.
@@ -34,10 +35,12 @@
.if !${:U }
. info Not space evaluates to true.
.else
+# expect+1: Not space evaluates to false.
. info Not space evaluates to false.
.endif
.if !${:U0}
+# expect+1: Not 0 evaluates to true.
. info Not 0 evaluates to true.
.else
. info Not 0 evaluates to false.
@@ -46,16 +49,19 @@
.if !${:U1}
. info Not 1 evaluates to true.
.else
+# expect+1: Not 1 evaluates to false.
. info Not 1 evaluates to false.
.endif
.if !${:Uword}
. info Not word evaluates to true.
.else
+# expect+1: Not word evaluates to false.
. info Not word evaluates to false.
.endif
# A single exclamation mark is a parse error.
+# expect+1: Malformed conditional (!)
.if !
. error
.else
diff --git a/contrib/bmake/unit-tests/cond-op-or-lint.exp b/contrib/bmake/unit-tests/cond-op-or-lint.exp
index 8abae99b6c4c..0fb1d0dae166 100644
--- a/contrib/bmake/unit-tests/cond-op-or-lint.exp
+++ b/contrib/bmake/unit-tests/cond-op-or-lint.exp
@@ -1,4 +1,4 @@
-make: "cond-op-or-lint.mk" line 9: Unknown operator '|'
+make: "cond-op-or-lint.mk" line 10: Unknown operator '|'
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-op-or-lint.mk b/contrib/bmake/unit-tests/cond-op-or-lint.mk
index aa29e9a6c2f2..9ece9d5c9af6 100644
--- a/contrib/bmake/unit-tests/cond-op-or-lint.mk
+++ b/contrib/bmake/unit-tests/cond-op-or-lint.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-op-or-lint.mk,v 1.1 2020/11/08 23:54:28 rillig Exp $
+# $NetBSD: cond-op-or-lint.mk,v 1.2 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the || operator in .if conditions, in lint mode.
@@ -6,6 +6,7 @@
# The '|' operator is not allowed in lint mode.
# It is not used in practice anyway.
+# expect+1: Unknown operator '|'
.if 0 | 0
. error
.else
diff --git a/contrib/bmake/unit-tests/cond-op-or.exp b/contrib/bmake/unit-tests/cond-op-or.exp
index 7888a475e3e4..b10bc4bf7c52 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 47: Malformed conditional (1 && (!${DEF} || ${UNDEF}))
+make: "cond-op-or.mk" line 50: Malformed conditional (1 && (${UNDEF} || ${UNDEF}))
+make: "cond-op-or.mk" line 53: Malformed conditional (1 && (!${UNDEF} || ${UNDEF}))
+make: "cond-op-or.mk" line 75: 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..165408f3c130 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.11 2023/12/17 09:44:00 rillig Exp $
#
# Tests for the || operator in .if conditions.
@@ -18,11 +18,42 @@
. 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
+# expect+1: Malformed conditional (1 && (!${DEF} || ${UNDEF}))
+.if 1 && (!${DEF} || ${UNDEF})
+.endif
+# expect+1: Malformed conditional (1 && (${UNDEF} || ${UNDEF}))
+.if 1 && (${UNDEF} || ${UNDEF})
+.endif
+# expect+1: Malformed conditional (1 && (!${UNDEF} || ${UNDEF}))
+.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.
@@ -40,9 +71,14 @@
.endif
# There is no operator |||.
+# expect+1: Malformed conditional (0 ||| 0)
.if 0 ||| 0
. error
.endif
-all:
- @:;
+# The '||' operator must be preceded by whitespace, otherwise it becomes part
+# of the preceding bare word. The condition is parsed as '"1||" != "" || 0'.
+.if 1|| || 0
+.else
+. error
+.endif
diff --git a/contrib/bmake/unit-tests/cond-op-parentheses.exp b/contrib/bmake/unit-tests/cond-op-parentheses.exp
index b44093304100..1daad92b2682 100644
--- a/contrib/bmake/unit-tests/cond-op-parentheses.exp
+++ b/contrib/bmake/unit-tests/cond-op-parentheses.exp
@@ -1,6 +1,7 @@
-make: "cond-op-parentheses.mk" line 13: Parentheses can be nested at least to depth 112.
-make: "cond-op-parentheses.mk" line 19: Malformed conditional (()
-make: "cond-op-parentheses.mk" line 29: Malformed conditional ())
+make: "cond-op-parentheses.mk" line 22: Comparison with '>' requires both operands '3' and '(2' to be numeric
+make: "cond-op-parentheses.mk" line 25: Malformed conditional ((3) > 2)
+make: "cond-op-parentheses.mk" line 44: Malformed conditional (()
+make: "cond-op-parentheses.mk" line 58: Malformed conditional ())
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-op-parentheses.mk b/contrib/bmake/unit-tests/cond-op-parentheses.mk
index ca288cad5826..b6c9bd3c0e9d 100644
--- a/contrib/bmake/unit-tests/cond-op-parentheses.mk
+++ b/contrib/bmake/unit-tests/cond-op-parentheses.mk
@@ -1,8 +1,29 @@
-# $NetBSD: cond-op-parentheses.mk,v 1.4 2021/01/19 17:49:13 rillig Exp $
+# $NetBSD: cond-op-parentheses.mk,v 1.7 2023/06/01 20:56:35 rillig Exp $
#
-# Tests for parentheses in .if conditions.
+# Tests for parentheses in .if conditions, which group expressions to override
+# the precedence of the operators '!', '&&' and '||'. Parentheses cannot be
+# used to form arithmetic expressions such as '(3+4)' though.
-# TODO: Implementation
+# Contrary to the C family of programming languages, the outermost condition
+# does not have to be enclosed in parentheses.
+.if defined(VAR)
+. error
+.elif !1
+. error
+.endif
+
+# Parentheses cannot enclose numbers as there is no need for it. Make does
+# not implement any arithmetic functions in its condition parser. If
+# absolutely necessary, use expr(1).
+#
+# XXX: It's inconsistent that the right operand has unbalanced parentheses.
+#
+# expect+1: Comparison with '>' requires both operands '3' and '(2' to be numeric
+.if 3 > (2)
+.endif
+# expect+1: Malformed conditional ((3) > 2)
+.if (3) > 2
+.endif
# Test for deeply nested conditions.
.if (((((((((((((((((((((((((((((((((((((((((((((((((((((((( \
@@ -10,12 +31,16 @@
1 \
)))))))))))))))))))))))))))))))))))))))))))))))))))))))) \
))))))))))))))))))))))))))))))))))))))))))))))))))))))))
-. info Parentheses can be nested at least to depth 112.
+# Parentheses can be nested at least to depth 112. There is nothing special
+# about this number though, much higher numbers work as well, at least on
+# NetBSD. The actual limit depends on the allowed call stack depth for C code
+# of the platform. Anyway, 112 should be enough for all practical purposes.
.else
. error
.endif
# An unbalanced opening parenthesis is a parse error.
+# expect+1: Malformed conditional (()
.if (
. error
.else
@@ -24,8 +49,12 @@
# An unbalanced closing parenthesis is a parse error.
#
-# As of 2021-01-19, CondParser_Term returned TOK_RPAREN even though this
-# function promised to only ever return TOK_TRUE, TOK_FALSE or TOK_ERROR.
+# Before cond.c 1.237 from 2021-01-19, CondParser_Term returned TOK_RPAREN
+# even though the documentation of that function promised to only ever return
+# TOK_TRUE, TOK_FALSE or TOK_ERROR. In cond.c 1.241, the return type of that
+# function was changed to a properly restricted enum type, to prevent this bug
+# from occurring again.
+# expect+1: Malformed conditional ())
.if )
. error
.else
diff --git a/contrib/bmake/unit-tests/cond-op.exp b/contrib/bmake/unit-tests/cond-op.exp
index 28e8d48e2697..33bab484a6c8 100644
--- a/contrib/bmake/unit-tests/cond-op.exp
+++ b/contrib/bmake/unit-tests/cond-op.exp
@@ -1,20 +1,21 @@
-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 51: Malformed conditional ("!word" == !word)
+make: "cond-op.mk" line 72: Malformed conditional (0 ${ERR::=evaluated})
+make: "cond-op.mk" line 77: A misplaced expression after 0 is not evaluated.
+make: "cond-op.mk" line 82: Malformed conditional (1 ${ERR::=evaluated})
+make: "cond-op.mk" line 87: A misplaced expression after 1 is not evaluated.
+make: "cond-op.mk" line 93: A B C => (A || B) && C A || B && C A || (B && C)
+make: "cond-op.mk" line 108: 0 0 0 => 0 0 0
+make: "cond-op.mk" line 108: 0 0 1 => 0 0 0
+make: "cond-op.mk" line 108: 0 1 0 => 0 0 0
+make: "cond-op.mk" line 108: 0 1 1 => 1 1 1
+make: "cond-op.mk" line 108: 1 0 0 => 0 1 1
+make: "cond-op.mk" line 108: 1 0 1 => 1 1 1
+make: "cond-op.mk" line 108: 1 1 0 => 0 1 1
+make: "cond-op.mk" line 108: 1 1 1 => 1 1 1
+make: "cond-op.mk" line 120: Malformed conditional (1 &&)
+make: "cond-op.mk" line 129: Malformed conditional (0 &&)
+make: "cond-op.mk" line 138: Malformed conditional (1 ||)
+make: "cond-op.mk" line 148: 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..6371e4db0594 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.16 2023/06/01 20:56:35 rillig Exp $
#
# Tests for operators like &&, ||, ! in .if conditions.
#
@@ -47,6 +47,7 @@
# appear unquoted. If any, it must be enclosed in quotes.
# In any case, it is not interpreted as a negation of an unquoted string.
# See CondParser_String.
+# expect+1: Malformed conditional ("!word" == !word)
.if "!word" == !word
. error
.endif
@@ -58,31 +59,37 @@
. 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.
-#
-# 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.
+# 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.
+.undef ERR
+# expect+1: Malformed conditional (0 ${ERR::=evaluated})
.if 0 ${ERR::=evaluated}
. error
.endif
-.if ${ERR:Uundefined} == evaluated
-. info After detecting a parse error, the rest is evaluated.
+.if ${ERR:Uundefined} == undefined
+# expect+1: A misplaced expression after 0 is not evaluated.
+. info A misplaced expression after 0 is not evaluated.
+.endif
+
+.undef ERR
+# expect+1: Malformed conditional (1 ${ERR::=evaluated})
+.if 1 ${ERR::=evaluated}
+. error
+.endif
+.if ${ERR:Uundefined} == undefined
+# expect+1: A misplaced expression after 1 is not evaluated.
+. info A misplaced expression after 1 is not evaluated.
.endif
-# Just in case that parsing should ever stop on the first error.
-.info Parsing continues until here.
# Demonstration that '&&' has higher precedence than '||'.
+# expect+1: A B C => (A || B) && C A || B && C A || (B && C)
.info A B C => (A || B) && C A || B && C A || (B && C)
.for a in 0 1
. for b in 0 1
@@ -90,6 +97,14 @@
. for r1 in ${ ($a || $b) && $c :?1:0}
. for r2 in ${ $a || $b && $c :?1:0}
. for r3 in ${ $a || ($b && $c) :?1:0}
+# expect+8: 0 0 0 => 0 0 0
+# expect+7: 0 0 1 => 0 0 0
+# expect+6: 0 1 0 => 0 0 0
+# expect+5: 0 1 1 => 1 1 1
+# expect+4: 1 0 0 => 0 1 1
+# expect+3: 1 0 1 => 1 1 1
+# expect+2: 1 1 0 => 0 1 1
+# expect+1: 1 1 1 => 1 1 1
. info $a $b $c => ${r1} ${r2} ${r3}
. endfor
. endfor
@@ -101,6 +116,7 @@
# This condition is obviously malformed. It is properly detected and also
# was properly detected before 2021-01-19, but only because the left hand
# side of the '&&' evaluated to true.
+# expect+1: Malformed conditional (1 &&)
.if 1 &&
. error
.else
@@ -109,6 +125,7 @@
# This obviously malformed condition was not detected as such before cond.c
# 1.238 from 2021-01-19.
+# expect+1: Malformed conditional (0 &&)
.if 0 &&
. error
.else
@@ -117,6 +134,7 @@
# This obviously malformed condition was not detected as such before cond.c
# 1.238 from 2021-01-19.
+# expect+1: Malformed conditional (1 ||)
.if 1 ||
. error
.else
@@ -126,6 +144,7 @@
# This condition is obviously malformed. It is properly detected and also
# was properly detected before 2021-01-19, but only because the left hand
# side of the '||' evaluated to false.
+# expect+1: Malformed conditional (0 ||)
.if 0 ||
. error
.else
diff --git a/contrib/bmake/unit-tests/cond-short.exp b/contrib/bmake/unit-tests/cond-short.exp
index 2865dcb6ef33..44681b57ebc1 100644
--- a/contrib/bmake/unit-tests/cond-short.exp
+++ b/contrib/bmake/unit-tests/cond-short.exp
@@ -7,10 +7,7 @@ expected M pattern
expected or
expected or exists
expected or empty
-defined(V42) && ${V42} > 0: Ok
-defined(V66) && ( "${iV2}" < ${V42} ): Ok
-1 || ${iV1} < ${V42}: Ok
-1 || ${iV2:U2} < ${V42}: Ok
-0 || ${iV1} <= ${V42}: Ok
-0 || ${iV2:U2} < ${V42}: Ok
-exit status 0
+make: "cond-short.mk" line 231: Comparison with '<' requires both operands '' and '42' to be numeric
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/cond-short.mk b/contrib/bmake/unit-tests/cond-short.mk
index 113c3fd08fed..bcdf372ca6e6 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.23 2023/11/19 22:32:44 rillig Exp $
#
# Demonstrates that in conditions, the right-hand side of an && or ||
# is only evaluated if it can actually influence the result.
@@ -9,10 +9,23 @@
# Before 2020-06-28, the right-hand side of an && or || operator was always
# evaluated, which was wrong. In cond.c 1.69 and var.c 1.197 on 2015-10-11,
# Var_Parse got a new parameter named 'wantit'. Since then it would have been
-# possible to skip evaluation of irrelevant variable expressions and only
+# possible to skip evaluation of irrelevant expressions and only
# parse them. They were still evaluated though, the only difference to
-# relevant variable expressions was that in the irrelevant variable
-# expressions, undefined variables were allowed.
+# relevant expressions was that in the irrelevant
+# expressions, undefined variables were allowed. This allowed for conditions
+# like 'defined(VAR) && ${VAR:S,from,to,} != ""', which no longer produced an
+# error message 'Malformed conditional', but the irrelevant expression was
+# still evaluated.
+#
+# 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 did 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
@@ -109,7 +122,9 @@ VAR= # empty again, for the following tests
.if 0 || empty(${echo "expected or empty" 1>&2 :L:sh})
.endif
-# Unreachable nested conditions are skipped completely as well.
+# Unreachable nested conditions are skipped completely as well. These skipped
+# lines may even contain syntax errors. This allows to skip syntactically
+# incompatible new features in older versions of make.
.if 0
. if ${echo "unexpected nested and" 1>&2 :L:sh}
@@ -120,28 +135,32 @@ VAR= # empty again, for the following tests
.elif ${echo "unexpected nested or" 1>&2 :L:sh}
.endif
-# make sure these do not cause complaint
-#.MAKEFLAGS: -dc
-# TODO: Rewrite this whole section and check all the conditions and variables.
-# Several of the assumptions are probably wrong here.
-# TODO: replace 'x=' with '.info' or '.error'.
-V42= 42
-iV1= ${V42}
-iV2= ${V66}
+NUMBER= 42
+INDIR_NUMBER= ${NUMBER}
+INDIR_UNDEF= ${UNDEF}
-.if defined(V42) && ${V42} > 0
-x= Ok
+.if defined(NUMBER) && ${NUMBER} > 0
.else
-x= Fail
+. error
.endif
-x!= echo 'defined(V42) && $${V42} > 0: $x' >&2; echo
-# With cond.c 1.76 from 2020-07-03, the following condition triggered a
-# warning: "String comparison operator should be either == or !=".
-# This was because the variable expression ${iV2} was defined, but the
-# contained variable V66 was undefined. The left-hand side of the comparison
-# therefore evaluated to the string "${V66}", which is obviously not a number.
+# Starting with var.c 1.226 from from 2020-07-02, the following condition
+# triggered a warning: "String comparison operator should be either == or !=".
+#
+# The left-hand side of the '&&' evaluated to false, which should have made
+# the right-hand side irrelevant.
+#
+# On the right-hand side of the '&&', the expression ${INDIR_UNDEF} was
+# defined and had the value '${UNDEF}', but the nested variable UNDEF was
+# undefined. The right hand side "${INDIR_UNDEF}" still needed to be parsed,
+# and in parse-only mode, the "value" of the parsed expression was the
+# uninterpreted variable value, in this case '${UNDEF}'. And even though the
+# right hand side of the '&&' should have been irrelevant, the two sides of
+# the comparison were still parsed and evaluated. Comparing these two values
+# numerically was not possible since the string '${UNDEF}' is not a number,
+# so the comparison fell back to string comparison, which then complained
+# about the '>' operator.
#
# This was fixed in cond.c 1.79 from 2020-07-09 by not evaluating irrelevant
# comparisons. Instead, they are only parsed and then discarded.
@@ -149,59 +168,94 @@ x!= echo 'defined(V42) && $${V42} > 0: $x' >&2; echo
# At that time, there was not enough debug logging to see the details in the
# -dA log. To actually see it, add debug logging at the beginning and end of
# Var_Parse.
-.if defined(V66) && ( ${iV2} < ${V42} )
-x= Fail
-.else
-x= Ok
+.if defined(UNDEF) && ${INDIR_UNDEF} < ${NUMBER}
+. error
+.endif
+# Adding a ':U' modifier to the irrelevant expression didn't help, as that
+# expression was only parsed, not evaluated. The resulting literal string
+# '${INDIR_UNDEF:U2}' was not numeric either, for the same reason as above.
+.if defined(UNDEF) && ${INDIR_UNDEF:U2} < ${NUMBER}
+. error
.endif
-# XXX: This condition doesn't match the one above. The quotes are missing
-# above. This is a crucial detail since without quotes, the variable
-# expression ${iV2} evaluates to "${V66}", and with quotes, it evaluates to ""
-# since undefined variables are allowed and expand to an empty string.
-x!= echo 'defined(V66) && ( "$${iV2}" < $${V42} ): $x' >&2; echo
-.if 1 || ${iV1} < ${V42}
-x= Ok
-.else
-x= Fail
+
+# Since cond.c 1.76 from 2020.06.28 and before var.c 1.225 from 2020.07.01,
+# the following snippet resulted in the error message 'Variable VAR is
+# recursive'. The condition '0' evaluated to false, which made the right-hand
+# side of the '&&' irrelevant. Back then, irrelevant condition parts were
+# still evaluated, but in "irrelevant mode", which allowed undefined variables
+# to occur in expressions. In this mode, the variable name 'VAR' was
+# unnecessarily evaluated, resulting in the expression '${VAR${:U1}}'. In
+# this expression, the variable name was 'VAR${:U1}', and of this variable
+# name, only the fixed part 'VAR' was evaluated, without the part '${:U1}'.
+# This partial evaluation led to the wrong error message about 'VAR' being
+# recursive.
+VAR= ${VAR${:U1}}
+.if 0 && !empty(VAR)
.endif
-x!= echo '1 || $${iV1} < $${V42}: $x' >&2; echo
-# With cond.c 1.76 from 2020-07-03, the following condition triggered a
-# warning: "String comparison operator should be either == or !=".
-# This was because the variable expression ${iV2} was defined, but the
-# contained variable V66 was undefined. The left-hand side of the comparison
-# therefore evaluated to the string "${V66}", which is obviously not a number.
+
+# Enclosing the expression in double quotes changes how that expression is
+# evaluated. In irrelevant expressions that are enclosed in double quotes,
+# expressions based on undefined variables are allowed and evaluate to an
+# empty string.
#
-# This was fixed in cond.c 1.79 from 2020-07-09 by not evaluating irrelevant
-# comparisons. Instead, they are only parsed and then discarded.
+# The manual page stated from at least 1993 on that irrelevant conditions were
+# not evaluated, but that was wrong. These conditions were evaluated, the
+# only difference was that undefined variables in them didn't trigger an
+# error. Since numeric conditions are quite rare, this subtle difference
+# didn't catch much attention, as most other conditions such as pattern
+# matches or equality comparisons worked fine and never produced error
+# messages.
+.if defined(UNDEF) && "${INDIR_UNDEF}" < ${NUMBER}
+. error
+.endif
+
+# Since the condition is relevant, the indirect undefined variable is
+# evaluated as usual, resolving nested undefined expressions to an empty
+# string.
#
-# At that time, there was not enough debug logging to see the details in the
-# -dA log. To actually see it, add debug logging at the beginning and end of
-# Var_Parse.
-.if 1 || ${iV2:U2} < ${V42}
-x= Ok
+# Comparing an empty string numerically is not possible, however, make has an
+# ugly hack in TryParseNumber that treats an empty string as a valid numerical
+# value, thus hiding bugs in the makefile.
+.if ${INDIR_UNDEF} < ${NUMBER}
+# only due to the ugly hack
+.else
+. error
+.endif
+
+# Due to the quotes around the left-hand side of the '<', the operand is
+# marked as a string, thus preventing a numerical comparison.
+#
+# expect+1: Comparison with '<' requires both operands '' and '42' to be numeric
+.if "${INDIR_UNDEF}" < ${NUMBER}
+. info yes
.else
-x= Fail
+. info no
.endif
-x!= echo '1 || $${iV2:U2} < $${V42}: $x' >&2; echo
-# the same expressions are fine when the lhs is expanded
-# ${iV1} expands to 42
-.if 0 || ${iV1} <= ${V42}
-x= Ok
+# The right-hand side of '||' is irrelevant and thus not evaluated.
+.if 1 || ${INDIR_NUMBER} < ${NUMBER}
.else
-x= Fail
+. error
+.endif
+
+# The right-hand side of '||' is relevant and thus evaluated normally.
+.if 0 || ${INDIR_NUMBER} < ${NUMBER}
+. error
.endif
-x!= echo '0 || $${iV1} <= $${V42}: $x' >&2; echo
-# ${iV2:U2} expands to 2
-.if 0 || ${iV2:U2} < ${V42}
-x= Ok
+# The right-hand side of '||' evaluates to an empty string, as the variable
+# 'INDIR_UNDEF' is defined, therefore the modifier ':U2' has no effect.
+# Comparing an empty string numerically is not possible, however, make has an
+# ugly hack in TryParseNumber that treats an empty string as a valid numerical
+# value, thus hiding bugs in the makefile.
+.if 0 || ${INDIR_UNDEF:U2} < ${NUMBER}
+# only due to the ugly hack
.else
-x= Fail
+. error
.endif
-x!= echo '0 || $${iV2:U2} < $${V42}: $x' >&2; echo
+
# The right-hand side of the '&&' is irrelevant since the left-hand side
# already evaluates to false. Before cond.c 1.79 from 2020-07-09, it was
@@ -211,4 +265,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
+# evaluated to true (see CondParser_FuncCall and CondParser_FuncCallEmpty), an
+# irrelevant comparison evaluated 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-number.exp b/contrib/bmake/unit-tests/cond-token-number.exp
index b5bfaf95d575..d05833b4e01b 100644
--- a/contrib/bmake/unit-tests/cond-token-number.exp
+++ b/contrib/bmake/unit-tests/cond-token-number.exp
@@ -1,8 +1,7 @@
-make: "cond-token-number.mk" line 15: Malformed conditional (-0)
-make: "cond-token-number.mk" line 25: Malformed conditional (+0)
-make: "cond-token-number.mk" line 35: Malformed conditional (!-1)
-make: "cond-token-number.mk" line 45: Malformed conditional (!+1)
-make: "cond-token-number.mk" line 80: End of the tests.
+make: "cond-token-number.mk" line 16: Malformed conditional (-0)
+make: "cond-token-number.mk" line 27: Malformed conditional (+0)
+make: "cond-token-number.mk" line 38: Malformed conditional (!-1)
+make: "cond-token-number.mk" line 49: Malformed conditional (!+1)
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-token-number.mk b/contrib/bmake/unit-tests/cond-token-number.mk
index 93e2646a60eb..7e73f8b76f94 100644
--- a/contrib/bmake/unit-tests/cond-token-number.mk
+++ b/contrib/bmake/unit-tests/cond-token-number.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-token-number.mk,v 1.5 2020/11/15 14:58:14 rillig Exp $
+# $NetBSD: cond-token-number.mk,v 1.10 2023/11/19 21:47:52 rillig Exp $
#
# Tests for number tokens in .if conditions.
#
@@ -12,6 +12,7 @@
# accepted by the condition parser.
#
# See the ch_isdigit call in CondParser_String.
+# expect+1: Malformed conditional (-0)
.if -0
. error
.else
@@ -22,6 +23,7 @@
# accepted by the condition parser.
#
# See the ch_isdigit call in CondParser_String.
+# expect+1: Malformed conditional (+0)
.if +0
. error
.else
@@ -32,6 +34,7 @@
# accepted by the condition parser.
#
# See the ch_isdigit call in CondParser_String.
+# expect+1: Malformed conditional (!-1)
.if !-1
. error
.else
@@ -42,19 +45,20 @@
# accepted by the condition parser.
#
# See the ch_isdigit call in CondParser_String.
+# expect+1: Malformed conditional (!+1)
.if !+1
. error
.else
. error
.endif
-# When the number comes from a variable expression though, it may be signed.
+# When the number comes from an expression though, it may be signed.
# XXX: This is inconsistent.
.if ${:U+0}
. error
.endif
-# When the number comes from a variable expression though, it may be signed.
+# When the number comes from an expression though, it may be signed.
# XXX: This is inconsistent.
.if !${:U+1}
. error
@@ -69,14 +73,37 @@
. error
.endif
-# This is not a hexadecimal number, even though it has an x.
-# It is interpreted as a string instead, effectively meaning defined(3x4).
+# This is not a hexadecimal number, even though it has an x. It is
+# interpreted as a string instead. In a plain '.if', such a token evaluates
+# to true if it is non-empty. In other '.if' directives, such a token is
+# evaluated by either FuncDefined or FuncMake.
.if 3x4
.else
. error
.endif
-# Ensure that parsing continues until here.
-.info End of the tests.
+# Make can do radix conversion from hex.
+HEX= dead
+.if 0x${HEX} == 57005
+.else
+. error
+.endif
+
+# Very small numbers round to 0.
+.if 12345e-400
+. error
+.endif
+.if 12345e-200
+.else
+. error
+.endif
+
+# Very large numbers round up to infinity on IEEE 754 implementations, or to
+# the largest representable number (VAX); in particular, make does not fall
+# back to checking whether a variable of that name is defined.
+.if 12345e400
+.else
+. error
+.endif
-all: # nothing
+all:
diff --git a/contrib/bmake/unit-tests/cond-token-plain.exp b/contrib/bmake/unit-tests/cond-token-plain.exp
index 24cfa6bcbc82..8044f3bac826 100644
--- a/contrib/bmake/unit-tests/cond-token-plain.exp
+++ b/contrib/bmake/unit-tests/cond-token-plain.exp
@@ -1,54 +1,63 @@
CondParser_Eval: ${:Uvalue} != value
-lhs = "value", rhs = "value", op = !=
+Comparing "value" != "value"
CondParser_Eval: ${:U} != "
-lhs = "", rhs = "", op = !=
+Comparing "" != ""
CondParser_Eval: ${:U#hash} != "#hash"
-lhs = "#hash", rhs = "#hash", op = !=
+Comparing "#hash" != "#hash"
CondParser_Eval: ${:U\\} != "\\
-lhs = "\", rhs = "\", op = !=
+Comparing "\" != "\"
CondParser_Eval: ${:U#hash} != #hash
-lhs = "#hash", rhs = "#hash", op = !=
+Comparing "#hash" != "#hash"
CondParser_Eval: 0 # This is treated as a comment, but why?
-CondParser_Eval: ${0 # comment :?yes:no} != no
-CondParser_Eval: 0 # comment
-lhs = "no", rhs = "no", op = !=
-CondParser_Eval: ${1 # comment :?yes:no} != yes
-CondParser_Eval: 1 # comment
-lhs = "yes", rhs = "yes", op = !=
+CondParser_Eval: ${0 # comment:?yes:no} != no
+CondParser_Eval: 0 # comment
+Comparing "no" != "no"
+CondParser_Eval: ${1 # comment:?yes:no} != yes
+CondParser_Eval: 1 # comment
+Comparing "yes" != "yes"
CondParser_Eval: ${UNDEF:Uundefined}!=undefined
-lhs = "undefined", rhs = "undefined", op = !=
+Comparing "undefined" != "undefined"
CondParser_Eval: ${UNDEF:U12345}>12345
-lhs = 12345.000000, rhs = 12345.000000, op = >
+Comparing 12345.000000 > 12345.000000
CondParser_Eval: ${UNDEF:U12345}<12345
-lhs = 12345.000000, rhs = 12345.000000, op = <
+Comparing 12345.000000 < 12345.000000
CondParser_Eval: (${UNDEF:U0})||0
CondParser_Eval: ${:Uvar}&&name != "var&&name"
-lhs = "var&&name", rhs = "var&&name", op = !=
+Comparing "var&&name" != "var&&name"
CondParser_Eval: ${:Uvar}||name != "var||name"
-lhs = "var||name", rhs = "var||name", op = !=
+Comparing "var||name" != "var||name"
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 113: 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 121: 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 130: 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.
+make: "cond-token-plain.mk" line 139: Numbers can be composed from literals and expressions.
CondParser_Eval: 0${:Ux01}
-make: "cond-token-plain.mk" line 134: Numbers can be composed from literals and variable expressions.
+make: "cond-token-plain.mk" line 144: Numbers can be composed from literals and expressions.
CondParser_Eval: "" ==
-make: "cond-token-plain.mk" line 140: Missing right-hand-side of operator '=='
+make: "cond-token-plain.mk" line 151: Missing right-hand side of operator '=='
CondParser_Eval: == ""
-make: "cond-token-plain.mk" line 148: Malformed conditional (== "")
+make: "cond-token-plain.mk" line 160: Malformed conditional (== "")
CondParser_Eval: \\
-make: "cond-token-plain.mk" line 163: The variable '\\' is not defined.
+make: "cond-token-plain.mk" line 176: 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 182: Now the variable '\\' is defined.
CondParser_Eval: "unquoted\"quoted" != unquoted"quoted
-lhs = "unquoted"quoted", rhs = "unquoted"quoted", op = !=
+Comparing "unquoted"quoted" != "unquoted"quoted"
CondParser_Eval: $$$$$$$$ != ""
+make: "cond-token-plain.mk" line 197: Malformed conditional ($$$$$$$$ != "")
+CondParser_Eval: left == right
+make: "cond-token-plain.mk" line 206: Malformed conditional (left == right)
+CondParser_Eval: ${0:?:} || left == right
+CondParser_Eval: 0
+make: "cond-token-plain.mk" line 212: Malformed conditional (${0:?:} || left == right)
+CondParser_Eval: left == right || ${0:?:}
+make: "cond-token-plain.mk" line 217: Malformed conditional (left == right || ${0:?:})
+make: "cond-token-plain.mk" line 236: Malformed conditional (VAR.${IF_COUNT::+=1} != "")
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..79fcc771a855 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.19 2023/11/19 21:47:52 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.
@@ -59,10 +63,10 @@
# anybody really use this? This is neither documented nor obvious since
# the '#' is escaped. It's much clearer to write a comment in the line
# above the condition.
-.if ${0 \# comment :?yes:no} != no
+.if ${0 \# comment:?yes:no} != no
. error
.endif
-.if ${1 \# comment :?yes:no} != yes
+.if ${1 \# comment:?yes:no} != yes
. error
.endif
@@ -85,7 +89,7 @@
# a coincidence that the '!' is both used in the '!=' comparison operator
# as well as for negating a comparison result.
#
-# The boolean operators '&' and '|' don't terminate a comparison operand.
+# The characters '&' and '|' are part of the comparison operand.
.if ${:Uvar}&&name != "var&&name"
. error
.endif
@@ -93,24 +97,27 @@
. error
.endif
-# A bare word may appear alone in a condition, without any comparison
-# operator. It is implicitly converted into defined(bare).
+# A bare word may occur alone in a condition, without any comparison
+# operator. It is interpreted as the function call 'defined(bare)'.
.if bare
. error
.else
+# expect+1: A bare word is treated like defined(...), and the variable 'bare' is not defined.
. info A bare word is treated like defined(...), and the variable $\
'bare' is not defined.
.endif
VAR= defined
.if VAR
+# expect+1: A bare word is treated like defined(...).
. info A bare word is treated like defined(...).
.else
. error
.endif
-# Bare words may be intermixed with variable expressions.
+# Bare words may be intermixed with expressions.
.if V${:UA}R
+# expect+1: ok
. info ok
.else
. error
@@ -119,6 +126,7 @@ VAR= defined
# In bare words, even undefined variables are allowed. Without the bare
# words, undefined variables are not allowed. That feels inconsistent.
.if V${UNDEF}AR
+# expect+1: Undefined variables in bare words expand to an empty string.
. info Undefined variables in bare words expand to an empty string.
.else
. error
@@ -127,16 +135,19 @@ VAR= defined
.if 0${:Ux00}
. error
.else
-. info Numbers can be composed from literals and variable expressions.
+# expect+1: Numbers can be composed from literals and expressions.
+. info Numbers can be composed from literals and expressions.
.endif
.if 0${:Ux01}
-. info Numbers can be composed from literals and variable expressions.
+# expect+1: Numbers can be composed from literals and expressions.
+. info Numbers can be composed from literals and expressions.
.else
. error
.endif
# If the right-hand side is missing, it's a parse error.
+# expect+1: Missing right-hand side of operator '=='
.if "" ==
. error
.else
@@ -145,6 +156,7 @@ VAR= defined
# If the left-hand side is missing, it's a parse error as well, but without
# a specific error message.
+# expect+1: Malformed conditional (== "")
.if == ""
. error
.else
@@ -152,7 +164,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
@@ -160,11 +172,13 @@ VAR= defined
.if \\
. error
.else
+# expect+1: The variable '\\' is not defined.
. info The variable '\\' is not defined.
.endif
${:U\\\\}= backslash
.if \\
+# expect+1: Now the variable '\\' is defined.
. info Now the variable '\\' is defined.
.else
. error
@@ -179,14 +193,75 @@ ${:U\\\\}= backslash
# FIXME: In CondParser_String, Var_Parse returns var_Error without a
# corresponding error message.
+# expect+1: Malformed conditional ($$$$$$$$ != "")
.if $$$$$$$$ != ""
. error
.else
. 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, an expression containing the
+# modifier ':?:' allowed unquoted string literals for the rest of the
+# condition. This was an unintended implementation mistake.
+# expect+1: Malformed conditional (${0:?:} || left == right)
+.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".
-all:
- @:;
+.MAKEFLAGS: -d0
+
+
+# As of cond.c 1.320 from 2021-12-30, the code in CondParser_ComparisonOrLeaf
+# looks suspicious of evaluating the expression twice: first for parsing a
+# bare word and second for parsing the left-hand side of a comparison.
+#
+# In '.if' directives, the left-hand side of a comparison must not be a bare
+# word though, and this keeps CondParser_Leaf from evaluating the expression
+# for the second time. The right-hand side of a comparison may be a bare
+# word, but that side has no risk of being parsed more than once.
+#
+# expect+1: Malformed conditional (VAR.${IF_COUNT::+=1} != "")
+.if VAR.${IF_COUNT::+=1} != ""
+. error
+.else
+. error
+.endif
+.if ${IF_COUNT} != "1"
+. error
+.endif
+
+# A different situation is when CondParser.leftUnquotedOK is true. This
+# situation arises in expressions of the form ${cond:?yes:no}. As of
+# 2021-12-30, the condition in such an expression is evaluated before parsing
+# the condition, see varmod-ifelse.mk. To pass an expression to the
+# condition parser, it needs to be escaped. This rarely happens in practice,
+# in most cases the conditions are simple enough that it doesn't matter
+# whether the condition is first evaluated and then parsed, or vice versa.
+# A half-baked attempt at hiding this implementation detail is
+# CondParser.leftUnquotedOK, but that is a rather leaky abstraction.
+
+#.MAKEFLAGS: -dcv
+COND= VAR.$${MOD_COUNT::+=1}
+.if ${${COND} == "VAR.":?yes:no} != "yes"
+. error
+.endif
+
+# The value "1 1" demonstrates that the expression ${MOD_COUNT::+=1} was
+# evaluated twice. In practice, expressions that occur in conditions do not
+# have side effects, making this problem rather academic, but it is there.
+.if ${MOD_COUNT} != "1 1"
+. error
+.endif
+#.MAKEFLAGS: -d0
diff --git a/contrib/bmake/unit-tests/cond-token-string.exp b/contrib/bmake/unit-tests/cond-token-string.exp
index 45f9993457d3..4fbafc5e1986 100644
--- a/contrib/bmake/unit-tests/cond-token-string.exp
+++ b/contrib/bmake/unit-tests/cond-token-string.exp
@@ -1,18 +1,21 @@
-make: "cond-token-string.mk" line 13: Unknown modifier "Z"
-make: "cond-token-string.mk" line 13: Malformed conditional ("" != "${:Uvalue:Z}")
-make: "cond-token-string.mk" line 22: xvalue is not defined.
-make: "cond-token-string.mk" line 28: Malformed conditional (x${:Uvalue} == "")
-make: "cond-token-string.mk" line 37: Expected.
+make: "cond-token-string.mk" line 15: while evaluating "${:Uvalue:Z}"": Unknown modifier "Z"
+make: "cond-token-string.mk" line 15: Malformed conditional ("" != "${:Uvalue:Z}")
+make: "cond-token-string.mk" line 25: xvalue is not defined.
+make: "cond-token-string.mk" line 32: Malformed conditional (x${:Uvalue} == "")
+make: "cond-token-string.mk" line 42: Expected.
CondParser_Eval: "UNDEF"
-make: "cond-token-string.mk" line 46: The string literal "UNDEF" is not empty.
+make: "cond-token-string.mk" line 52: The string literal "UNDEF" is not empty.
CondParser_Eval: " "
-make: "cond-token-string.mk" line 55: The string literal " " is not empty, even though it consists of whitespace only.
+make: "cond-token-string.mk" line 61: The string literal " " is not empty, even though it consists of whitespace only.
CondParser_Eval: "${UNDEF}"
-make: "cond-token-string.mk" line 64: An undefined variable in quotes expands to an empty string, which then evaluates to false.
+make: "cond-token-string.mk" line 71: An undefined variable in quotes expands to an empty string, which then evaluates to false.
CondParser_Eval: "${:Uvalue}"
-make: "cond-token-string.mk" line 68: A nonempty variable expression evaluates to true.
+make: "cond-token-string.mk" line 77: A nonempty expression evaluates to true.
CondParser_Eval: "${:U}"
-make: "cond-token-string.mk" line 76: An empty variable evaluates to false.
+make: "cond-token-string.mk" line 86: An empty variable evaluates to false.
+CondParser_Eval: ("${VAR}")
+CondParser_Eval: "quoted" == quoted
+Comparing "quoted" == "quoted"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-token-string.mk b/contrib/bmake/unit-tests/cond-token-string.mk
index a92d3a896116..edc9936b7d53 100644
--- a/contrib/bmake/unit-tests/cond-token-string.mk
+++ b/contrib/bmake/unit-tests/cond-token-string.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-token-string.mk,v 1.4 2021/01/21 00:38:28 rillig Exp $
+# $NetBSD: cond-token-string.mk,v 1.10 2024/04/20 10:18:55 rillig Exp $
#
# Tests for quoted string literals in .if conditions.
#
@@ -9,7 +9,9 @@
# TODO: Implementation
# Cover the code in CondParser_String that frees the memory after parsing
-# a variable expression based on an undefined variable.
+# an expression based on an undefined variable.
+# expect+2: Malformed conditional ("" != "${:Uvalue:Z}")
+# expect+1: while evaluating "${:Uvalue:Z}"": Unknown modifier "Z"
.if "" != "${:Uvalue:Z}"
. error
.else
@@ -19,12 +21,14 @@
.if x${:Uvalue}
. error
.else
+# expect+1: xvalue is not defined.
. info xvalue is not defined.
.endif
# The 'x' produces a "Malformed conditional" since the left-hand side of a
-# comparison in an .if directive must be either a variable expression, a
+# comparison in an .if directive must be either an expression, a
# quoted string literal or a number that starts with a digit.
+# expect+1: Malformed conditional (x${:Uvalue} == "")
.if x${:Uvalue} == ""
. error
.else
@@ -34,6 +38,7 @@
# In plain words, a '\' can be used to escape any character, just as in
# double-quoted string literals. See CondParser_String.
.if \x${:Uvalue} == "xvalue"
+# expect+1: Expected.
. info Expected.
.else
. error
@@ -43,6 +48,7 @@
# A string in quotes is checked whether it is not empty.
.if "UNDEF"
+# expect+1: The string literal "UNDEF" is not empty.
. info The string literal "UNDEF" is not empty.
.else
. error
@@ -51,6 +57,7 @@
# A space is not empty as well.
# This differs from many other places where whitespace is trimmed.
.if " "
+# expect+1: The string literal " " is not empty, even though it consists of whitespace only.
. info The string literal " " is not empty, even though it consists of $\
whitespace only.
.else
@@ -60,12 +67,14 @@
.if "${UNDEF}"
. error
.else
+# expect+1: An undefined variable in quotes expands to an empty string, which then evaluates to false.
. info An undefined variable in quotes expands to an empty string, which $\
then evaluates to false.
.endif
.if "${:Uvalue}"
-. info A nonempty variable expression evaluates to true.
+# expect+1: A nonempty expression evaluates to true.
+. info A nonempty expression evaluates to true.
.else
. error
.endif
@@ -73,10 +82,28 @@
.if "${:U}"
. error
.else
+# expect+1: An empty variable evaluates to false.
. info An empty variable evaluates to false.
.endif
+# A non-empty string evaluates to true, no matter if it's a literal string or
+# if it contains expressions. The parentheses are not necessary for
+# the parser, in this case their only purpose is to make the code harder to
+# read for humans.
+VAR= value
+.if ("${VAR}")
+.else
+. error
+.endif
+
+# In the conditions in .if directives, the left-hand side of a comparison must
+# be enclosed in quotes. The right-hand side does not need to be enclosed in
+# quotes.
+.if "quoted" == quoted
+.else
+. error
+.endif
+
.MAKEFLAGS: -d0
-all:
- @:;
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/cond-token-var.exp b/contrib/bmake/unit-tests/cond-token-var.exp
index fcd92d12a3da..d84ca56e981b 100644
--- a/contrib/bmake/unit-tests/cond-token-var.exp
+++ b/contrib/bmake/unit-tests/cond-token-var.exp
@@ -1,7 +1,7 @@
-make: "cond-token-var.mk" line 20: ok
-make: "cond-token-var.mk" line 27: Malformed conditional (${UNDEF} == ${DEF})
-make: "cond-token-var.mk" line 33: Malformed conditional (${DEF} == ${UNDEF})
-make: "cond-token-var.mk" line 42: Malformed conditional (${UNDEF})
+make: "cond-token-var.mk" line 21: ok
+make: "cond-token-var.mk" line 28: Malformed conditional (${UNDEF} == ${DEF})
+make: "cond-token-var.mk" line 34: Malformed conditional (${DEF} == ${UNDEF})
+make: "cond-token-var.mk" line 44: Malformed conditional (${UNDEF})
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-token-var.mk b/contrib/bmake/unit-tests/cond-token-var.mk
index 168c63c46ac1..c6471756a3dd 100644
--- a/contrib/bmake/unit-tests/cond-token-var.mk
+++ b/contrib/bmake/unit-tests/cond-token-var.mk
@@ -1,11 +1,11 @@
-# $NetBSD: cond-token-var.mk,v 1.6 2021/04/25 21:05:38 rillig Exp $
+# $NetBSD: cond-token-var.mk,v 1.8 2023/11/19 21:47:52 rillig Exp $
#
-# Tests for variable expressions in .if conditions.
+# Tests for expressions in .if conditions.
#
-# Note the fine distinction between a variable and a variable expression.
-# A variable has a name and a value. To access the value, one writes a
-# variable expression of the form ${VAR}. This is a simple variable
-# expression. Variable expressions can get more complicated by adding
+# Note the fine distinction between a variable and an expression.
+# A variable has a name and a value. To access the value, one writes an
+# expression of the form ${VAR}. This is a simple
+# expression. Expressions can get more complicated by adding
# variable modifiers such as in ${VAR:Mpattern}.
#
# XXX: Strictly speaking, variable modifiers should be called expression
@@ -17,19 +17,20 @@ DEF= defined
# A defined variable may appear on either side of the comparison.
.if ${DEF} == ${DEF}
+# expect+1: ok
. info ok
.else
. error
.endif
# A variable that appears on the left-hand side must be defined.
-# The following line thus generates a parse error.
+# expect+1: Malformed conditional (${UNDEF} == ${DEF})
.if ${UNDEF} == ${DEF}
. error
.endif
# A variable that appears on the right-hand side must be defined.
-# The following line thus generates a parse error.
+# expect+1: Malformed conditional (${DEF} == ${UNDEF})
.if ${DEF} == ${UNDEF}
. error
.endif
@@ -39,6 +40,7 @@ DEF= defined
.endif
# An undefined variable on its own generates a parse error.
+# expect+1: Malformed conditional (${UNDEF})
.if ${UNDEF}
.endif
@@ -47,7 +49,7 @@ DEF= defined
.if ${UNDEF:U}
.endif
-# If the value of the variable expression is a number, it is compared against
+# If the value of the expression is a number, it is compared against
# zero.
.if ${:U0}
. error
@@ -56,7 +58,7 @@ DEF= defined
. error
.endif
-# If the value of the variable expression is not a number, any non-empty
+# If the value of the expression is not a number, any non-empty
# value evaluates to true, even if there is only whitespace.
.if ${:U}
. error
diff --git a/contrib/bmake/unit-tests/cond-undef-lint.exp b/contrib/bmake/unit-tests/cond-undef-lint.exp
index 2c4feb0376ff..efab46113f67 100755
--- a/contrib/bmake/unit-tests/cond-undef-lint.exp
+++ b/contrib/bmake/unit-tests/cond-undef-lint.exp
@@ -1,7 +1,10 @@
-make: "cond-undef-lint.mk" line 23: Variable "UNDEF" is undefined
-make: "cond-undef-lint.mk" line 38: Variable "UNDEF" is undefined
-make: "cond-undef-lint.mk" line 38: Variable "VAR." is undefined
-make: "cond-undef-lint.mk" line 49: Variable "VAR.defined" is undefined
+make: "cond-undef-lint.mk" line 25: Variable "UNDEF" is undefined
+make: "cond-undef-lint.mk" line 25: Malformed conditional (${UNDEF})
+make: "cond-undef-lint.mk" line 43: Variable "UNDEF" is undefined
+make: "cond-undef-lint.mk" line 43: Variable "VAR." is undefined
+make: "cond-undef-lint.mk" line 43: Malformed conditional (${VAR.${UNDEF}})
+make: "cond-undef-lint.mk" line 56: Variable "VAR.defined" is undefined
+make: "cond-undef-lint.mk" line 56: Malformed conditional (${VAR.${DEF}})
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-undef-lint.mk b/contrib/bmake/unit-tests/cond-undef-lint.mk
index 9dfd1bd53252..6fd353dc60e2 100755
--- a/contrib/bmake/unit-tests/cond-undef-lint.mk
+++ b/contrib/bmake/unit-tests/cond-undef-lint.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-undef-lint.mk,v 1.3 2020/11/15 14:58:14 rillig Exp $
+# $NetBSD: cond-undef-lint.mk,v 1.4 2023/06/01 20:56:35 rillig Exp $
#
# Tests for defined and undefined variables in .if conditions, in lint mode.
#
@@ -20,6 +20,8 @@ DEF= defined
.endif
# Since the condition fails to evaluate, neither of the branches is taken.
+# expect+2: Malformed conditional (${UNDEF})
+# expect+1: Variable "UNDEF" is undefined
.if ${UNDEF}
. error
.else
@@ -35,6 +37,9 @@ DEF= defined
#
# TODO: Suppress the error message "Variable VAR. is undefined". That part
# of the expression must not be evaluated at all.
+# expect+3: Variable "UNDEF" is undefined
+# expect+2: Variable "VAR." is undefined
+# expect+1: Malformed conditional (${VAR.${UNDEF}})
.if ${VAR.${UNDEF}}
. error
.else
@@ -46,6 +51,8 @@ DEF= defined
# TODO: This pattern looks a lot like CFLAGS.${OPSYS}, which is at least
# debatable. Or would any practical use of CFLAGS.${OPSYS} be via an indirect
# expression, as in the next example?
+# expect+2: Variable "VAR.defined" is undefined
+# expect+1: Malformed conditional (${VAR.${DEF}})
.if ${VAR.${DEF}}
. error
.else
diff --git a/contrib/bmake/unit-tests/cond1.exp b/contrib/bmake/unit-tests/cond1.exp
deleted file mode 100644
index 8b65d782524d..000000000000
--- a/contrib/bmake/unit-tests/cond1.exp
+++ /dev/null
@@ -1,23 +0,0 @@
-make: "cond1.mk" line 80: warning: extra else
-make: "cond1.mk" line 90: warning: extra else
-2 is prime
-A='other' B='unknown' C='clever' o='no,no'
-Passed:
- var
- ("var")
- (var != var)
- var != var
- !((var != var) && defined(name))
- var == quoted
-
-1 is not prime
-2 is prime
-3 is prime
-4 is not prime
-5 is prime
-
-make: String comparison operator must be either == or !=
-make: Bad conditional expression '"0" > 0' in '"0" > 0?OK:No'
-
-OK
-exit status 0
diff --git a/contrib/bmake/unit-tests/cond1.mk b/contrib/bmake/unit-tests/cond1.mk
deleted file mode 100644
index 53908c2dacf1..000000000000
--- a/contrib/bmake/unit-tests/cond1.mk
+++ /dev/null
@@ -1,114 +0,0 @@
-# $NetBSD: cond1.mk,v 1.3 2020/11/15 14:58:14 rillig Exp $
-
-# TODO: Convert these tests into tutorial form.
-# TODO: Split these tests by topic.
-# TODO: Use better variable names and expression values that actually express
-# the intended behavior. uname(1) has nothing to do with conditions.
-
-# hard code these!
-TEST_UNAME_S= NetBSD
-TEST_UNAME_M= sparc
-TEST_MACHINE= i386
-
-.if ${TEST_UNAME_S}
-Ok=var,
-.endif
-.if ("${TEST_UNAME_S}")
-Ok+=(\"var\"),
-.endif
-.if (${TEST_UNAME_M} != ${TEST_MACHINE})
-Ok+=(var != var),
-.endif
-.if ${TEST_UNAME_M} != ${TEST_MACHINE}
-Ok+= var != var,
-.endif
-.if !((${TEST_UNAME_M} != ${TEST_MACHINE}) && defined(X))
-Ok+= !((var != var) && defined(name)),
-.endif
-# from bsd.obj.mk
-MKOBJ?=no
-.if ${MKOBJ} == "no"
-o= no
-Ok+= var == "quoted",
-.else
-.if defined(notMAKEOBJDIRPREFIX) || defined(norMAKEOBJDIR)
-.if defined(notMAKEOBJDIRPREFIX)
-o=${MAKEOBJDIRPREFIX}${__curdir}
-.else
-o= ${MAKEOBJDIR}
-.endif
-.endif
-o= o
-.endif
-
-# repeat the above to check we get the same result
-.if ${MKOBJ} == "no"
-o2= no
-.else
-.if defined(notMAKEOBJDIRPREFIX) || defined(norMAKEOBJDIR)
-.if defined(notMAKEOBJDIRPREFIX)
-o2=${MAKEOBJDIRPREFIX}${__curdir}
-.else
-o2= ${MAKEOBJDIR}
-.endif
-.endif
-o2= o
-.endif
-
-PRIMES=2 3 5 7 11
-NUMBERS=1 2 3 4 5
-
-n=2
-.if ${PRIMES:M$n} == ""
-X=not
-.else
-X=
-.endif
-
-.if ${MACHINE_ARCH} == no-such
-A=one
-.else
-.if ${MACHINE_ARCH} == not-this
-.if ${MACHINE_ARCH} == something-else
-A=unlikely
-.else
-A=no
-.endif
-.endif
-A=other
-# We expect an extra else warning - we're not skipping here
-.else
-A=this should be an error
-.endif
-
-.if $X != ""
-.if $X == not
-B=one
-.else
-B=other
-# We expect an extra else warning - we are skipping here
-.else
-B=this should be an error
-.endif
-.else
-B=unknown
-.endif
-
-.if "quoted" == quoted
-C=clever
-.else
-C=dim
-.endif
-
-.if defined(nosuch) && ${nosuch:Mx} != ""
-# this should not happen
-.info nosuch is x
-.endif
-
-all:
- @echo "$n is $X prime"
- @echo "A='$A' B='$B' C='$C' o='$o,${o2}'"
- @echo "Passed:${.newline} ${Ok:S/,/${.newline}/}"
- @echo "${NUMBERS:@n@$n is ${("${PRIMES:M$n}" == ""):?not:} prime${.newline}@}"
- @echo "${"${DoNotQuoteHere:U0}" > 0:?OK:No}"
- @echo "${${NoSuchNumber:U42} > 0:?OK:No}"
diff --git a/contrib/bmake/unit-tests/dep-colon-bug-cross-file.exp b/contrib/bmake/unit-tests/dep-colon-bug-cross-file.exp
index 855b575c48bc..a8ef9d0707af 100644
--- a/contrib/bmake/unit-tests/dep-colon-bug-cross-file.exp
+++ b/contrib/bmake/unit-tests/dep-colon-bug-cross-file.exp
@@ -1,4 +1,4 @@
-make: "dep-colon-bug-cross-file.mk" line 31: warning: duplicate script for target "all" ignored
-make: "dep-colon-bug-cross-file.mk" line 40: warning: using previous script for "all" defined here
+make: "dep-colon-bug-cross-file.mk" line 32: warning: duplicate script for target "all" ignored
+make: "dep-colon-bug-cross-file.mk" line 42: warning: using previous script for "all" defined here
: pass 1
exit status 0
diff --git a/contrib/bmake/unit-tests/dep-colon-bug-cross-file.mk b/contrib/bmake/unit-tests/dep-colon-bug-cross-file.mk
index 57fbf478163c..930358af2871 100644
--- a/contrib/bmake/unit-tests/dep-colon-bug-cross-file.mk
+++ b/contrib/bmake/unit-tests/dep-colon-bug-cross-file.mk
@@ -1,4 +1,4 @@
-# $NetBSD: dep-colon-bug-cross-file.mk,v 1.4 2020/09/27 09:53:41 rillig Exp $
+# $NetBSD: dep-colon-bug-cross-file.mk,v 1.5 2023/06/01 20:56:35 rillig Exp $
#
# Until 2020-09-25, the very last dependency group of a top-level makefile
# was not finished properly. This made it possible to add further commands
@@ -28,6 +28,7 @@ PASS?= 1
.if ${PASS} == 2
all:
+# expect+1: warning: duplicate script for target "all" ignored
: pass 2
.endif
@@ -37,5 +38,6 @@ PASS= 2
.MAKEFLAGS: -f ${.PARSEDIR:q}/${.PARSEFILE:q}
all:
+# expect+1: warning: using previous script for "all" defined here
: pass 1
.endif
diff --git a/contrib/bmake/unit-tests/dep-duplicate.exp b/contrib/bmake/unit-tests/dep-duplicate.exp
new file mode 100644
index 000000000000..039145f8fd97
--- /dev/null
+++ b/contrib/bmake/unit-tests/dep-duplicate.exp
@@ -0,0 +1,4 @@
+make: "dep-duplicate.inc" line 1: warning: duplicate script for target "all" ignored
+make: "dep-duplicate.main" line 3: warning: using previous script for "all" defined here
+main-output
+exit status 0
diff --git a/contrib/bmake/unit-tests/dep-duplicate.mk b/contrib/bmake/unit-tests/dep-duplicate.mk
new file mode 100644
index 000000000000..6f64ba1c1981
--- /dev/null
+++ b/contrib/bmake/unit-tests/dep-duplicate.mk
@@ -0,0 +1,27 @@
+# $NetBSD: dep-duplicate.mk,v 1.3 2022/01/20 19:24:53 rillig Exp $
+#
+# Test for a target whose commands are defined twice. This generates a
+# warning, not an error, so ensure that the correct commands are kept.
+#
+# Also ensure that the diagnostics mention the correct file in case of
+# included files. Since parse.c 1.231 from 2018-12-22 and before parse.c
+# 1.653 from 2022-01-20, the wrong filename had been printed if the file of
+# the first commands section was included by its relative path.
+
+all: .PHONY
+ @exec > dep-duplicate.main; \
+ echo '# empty line 1'; \
+ echo '# empty line 2'; \
+ echo 'all:; @echo main-output'; \
+ echo '.include "dep-duplicate.inc"'
+
+ @exec > dep-duplicate.inc; \
+ echo 'all:; @echo inc-output'
+
+ # The main file must be specified using a relative path, just like the
+ # default 'makefile' or 'Makefile', to produce the same result when
+ # run via ATF or 'make test'.
+ @${MAKE} -r -f dep-duplicate.main
+
+ @rm -f dep-duplicate.main
+ @rm -f dep-duplicate.inc
diff --git a/contrib/bmake/unit-tests/dep-op-missing.exp b/contrib/bmake/unit-tests/dep-op-missing.exp
new file mode 100644
index 000000000000..9b42c5080122
--- /dev/null
+++ b/contrib/bmake/unit-tests/dep-op-missing.exp
@@ -0,0 +1,4 @@
+make: "dep-op-missing.tmp" line 1: Invalid line 'target'
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 0
diff --git a/contrib/bmake/unit-tests/dep-op-missing.mk b/contrib/bmake/unit-tests/dep-op-missing.mk
new file mode 100644
index 000000000000..2079f3c19871
--- /dev/null
+++ b/contrib/bmake/unit-tests/dep-op-missing.mk
@@ -0,0 +1,13 @@
+# $NetBSD: dep-op-missing.mk,v 1.1 2021/12/14 00:02:57 rillig Exp $
+#
+# Test for a missing dependency operator, in a line with trailing whitespace.
+
+# Before parse.c 1.578 from 2021-12-14, there was some unreachable error
+# handling code in ParseDependencyOp. This test tried to reach it and failed.
+# To reach that point, there would have to be trailing whitespace in the line,
+# but that is removed in an early stage of parsing.
+
+all: .PHONY
+ @printf 'target ' > dep-op-missing.tmp
+ @${MAKE} -r -f dep-op-missing.tmp || exit 0
+ @rm dep-op-missing.tmp
diff --git a/contrib/bmake/unit-tests/dep-var.exp b/contrib/bmake/unit-tests/dep-var.exp
index d32aca455ceb..cc229d32e6d4 100755
--- a/contrib/bmake/unit-tests/dep-var.exp
+++ b/contrib/bmake/unit-tests/dep-var.exp
@@ -1,4 +1,28 @@
-make: Malformed variable expression at "$)"
+Var_Parse: ${UNDEF1} (eval-defined)
+Global: .ALLTARGETS = all
+Global: .ALLTARGETS = all ${DEF2}
+Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b
+Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3}
+Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1
+Global: INDIRECT_1 = 2-$${INDIRECT_2}-2
+Global: INDIRECT_2 = 3-$${INDIRECT_3}-3
+Global: INDIRECT_3 = indirect
+Global: UNDEF1 = undef1
+Global: DEF2 = def2
+Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$)
+Var_Parse: ${:U\$)}: (eval-defined)
+Evaluating modifier ${:U...} on value "" (eval-defined, undefined)
+Result of ${:U\$)} is "$)" (eval-defined, defined)
+Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$) undef1
+Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$) undef1 def2
+Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$) undef1 def2 a-def2-b
+Var_Parse: $INDIRECT_2-2-1 $): (parse-only)
+Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$) undef1 def2 a-def2-b 1-2-$INDIRECT_2-2-1
+Var_Parse: $): (parse-only)
+Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$) undef1 def2 a-def2-b 1-2-$INDIRECT_2-2-1 $)
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
+make: Malformed expression at "$)"
def2
a-def2-b
1-2-NDIRECT_2-2-1
diff --git a/contrib/bmake/unit-tests/dep-var.mk b/contrib/bmake/unit-tests/dep-var.mk
index 4503424e31ab..8c1636bbdec2 100755
--- a/contrib/bmake/unit-tests/dep-var.mk
+++ b/contrib/bmake/unit-tests/dep-var.mk
@@ -1,17 +1,19 @@
-# $NetBSD: dep-var.mk,v 1.6 2021/04/04 10:13:09 rillig Exp $
+# $NetBSD: dep-var.mk,v 1.11 2023/12/19 19:33:40 rillig Exp $
#
# Tests for variable references in dependency declarations.
#
# Uh oh, this feels so strange that probably nobody uses it. But it seems to
# be the only way to reach the lower half of SuffExpandChildren.
-# XXX: The -dv log says:
-# Var_Parse: ${UNDEF1} with VARE_UNDEFERR|VARE_WANTRES
-# but no error message is generated for this line.
-# The variable expression ${UNDEF1} simply expands to an empty string.
+.MAKEFLAGS: -dv
+
+# expect: Var_Parse: ${UNDEF1} (eval-defined)
+# Even though undefined expressions should lead to errors, no error message is
+# generated for this line. The expression ${UNDEF1} simply expands
+# to an empty string.
all: ${UNDEF1}
-# Using a double dollar in order to circumvent immediate variable expansion
+# Using a double dollar in order to circumvent immediate expression expansion
# feels like unintended behavior. At least the manual page says nothing at
# all about defined or undefined variables in dependency lines.
#
@@ -20,10 +22,10 @@ all: ${UNDEF1}
all: $${DEF2} a-$${DEF2}-b
# This variable is not defined at all.
-# XXX: The -dv log says:
-# Var_Parse: ${UNDEF3} with VARE_UNDEFERR|VARE_WANTRES
+# XXX: The -dv log says later when expanding the sources of 'all':
+# Var_Parse: ${UNDEF3} (eval-defined)
# but no error message is generated for this line, just like for UNDEF1.
-# The variable expression ${UNDEF3} simply expands to an empty string.
+# The expression ${UNDEF3} simply expands to an empty string.
all: $${UNDEF3}
# Try out how many levels of indirection are really expanded in dependency
@@ -61,7 +63,7 @@ INDIRECT_3= indirect
UNDEF1= undef1
DEF2= def2
-# Cover the code in SuffExpandChildren that deals with malformed variable
+# Cover the code in SuffExpandChildren that deals with malformed
# expressions.
#
# This seems to be an edge case that never happens in practice, and it would
@@ -81,8 +83,14 @@ all: $$$$)
# Since 2020-09-13, this generates a parse error in lint mode (-dL), but not
# in normal mode since ParseDependency does not handle any errors after
# calling Var_Parse.
+# expect: Var_Parse: ${:U\$)}: (eval-defined)
+# expect: Var_Parse: $INDIRECT_2-2-1 $): (parse-only)
+# expect: Var_Parse: $): (parse-only)
undef1 def2 a-def2-b 1-2-$$INDIRECT_2-2-1 ${:U\$)}:
@echo ${.TARGET:Q}
-# XXX: Why is the exit status still 0, even though Parse_Error is called
-# with PARSE_FATAL in SuffExpandChildren?
+.MAKEFLAGS: -d0
+
+# XXX: The exit status is still 0, even though Parse_Error is called with
+# PARSE_FATAL in SuffExpandChildren. The exit status is only affected by
+# parse errors when they occur in the parsing phase, see Parse_File.
diff --git a/contrib/bmake/unit-tests/dep-wildcards.exp b/contrib/bmake/unit-tests/dep-wildcards.exp
index fb8a44e2c80a..45eafb5d2693 100644
--- a/contrib/bmake/unit-tests/dep-wildcards.exp
+++ b/contrib/bmake/unit-tests/dep-wildcards.exp
@@ -2,8 +2,10 @@ dep-colon-bug-cross-file.mk
dep-colon.mk
dep-double-colon-indep.mk
dep-double-colon.mk
+dep-duplicate.mk
dep-exclam.mk
dep-none.mk
+dep-op-missing.mk
dep-percent.mk
dep-var.mk
dep-wildcards.mk
diff --git a/contrib/bmake/unit-tests/dep-wildcards.mk b/contrib/bmake/unit-tests/dep-wildcards.mk
index 781b149f5a70..b3e2f14a07c3 100644
--- a/contrib/bmake/unit-tests/dep-wildcards.mk
+++ b/contrib/bmake/unit-tests/dep-wildcards.mk
@@ -1,4 +1,4 @@
-# $NetBSD: dep-wildcards.mk,v 1.3 2020/09/08 05:33:05 rillig Exp $
+# $NetBSD: dep-wildcards.mk,v 1.4 2023/06/21 12:27:50 rillig Exp $
#
# Tests for wildcards such as *.c in dependency declarations.
@@ -7,3 +7,9 @@ all: ${.PARSEDIR}/dep-*.mk
# The :O is necessary since the result of the dependency resolution
# does not order the directory entries itself.
@printf '%s\n' ${.ALLSRC:T:O}
+
+# This is not a wildcard rule as implemented by GNU make, as those rules would
+# use '%' instead of '*'. Instead, the pattern '*.target' is a file pattern
+# in the current working directory. As there are no such files, the target
+# list becomes empty, and the source pattern '*.source' is not even expanded.
+*.target: *.source
diff --git a/contrib/bmake/unit-tests/dep.exp b/contrib/bmake/unit-tests/dep.exp
index 39a9383953dd..6b7f0fabb12b 100644
--- a/contrib/bmake/unit-tests/dep.exp
+++ b/contrib/bmake/unit-tests/dep.exp
@@ -1 +1,5 @@
-exit status 0
+make: "dep.mk" line 11: Inconsistent operator for only-colon
+make: "dep.mk" line 13: Inconsistent operator for only-colon
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/dep.mk b/contrib/bmake/unit-tests/dep.mk
index b2463dfc6458..53fadc789b13 100644
--- a/contrib/bmake/unit-tests/dep.mk
+++ b/contrib/bmake/unit-tests/dep.mk
@@ -1,8 +1,30 @@
-# $NetBSD: dep.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: dep.mk,v 1.4 2023/06/01 07:27:30 rillig Exp $
#
# Tests for dependency declarations, such as "target: sources".
-# TODO: Implementation
+.MAIN: all
+
+# As soon as a target is defined using one of the dependency operators, it is
+# restricted to this dependency operator and cannot use the others anymore.
+only-colon:
+# expect+1: Inconsistent operator for only-colon
+only-colon!
+# expect+1: Inconsistent operator for only-colon
+only-colon::
+# Ensure that the target still has the original operator. If it hadn't, there
+# would be another error message.
+only-colon:
+
+
+# Before parse.c 1.158 from 2009-10-07, the parser broke dependency lines at
+# the first ';', without parsing expressions as such. It interpreted the
+# first ';' as the separator between the dependency and its commands, and the
+# '^' as a shell command.
+all: for-subst
+.for file in ${.PARSEFILE}
+for-subst: ${file:S;^;./;g}
+ @echo ".for with :S;... OK"
+.endfor
+
all:
- @:;
diff --git a/contrib/bmake/unit-tests/depsrc-end.mk b/contrib/bmake/unit-tests/depsrc-end.mk
index eb7543d5dfad..1bfe50d98620 100644
--- a/contrib/bmake/unit-tests/depsrc-end.mk
+++ b/contrib/bmake/unit-tests/depsrc-end.mk
@@ -1,6 +1,6 @@
-# $NetBSD: depsrc-end.mk,v 1.1 2020/10/23 19:23:01 rillig Exp $
+# $NetBSD: depsrc-end.mk,v 1.2 2024/04/27 20:41:32 rillig Exp $
#
-# Demonstrate the edge case that .BEGIN depends on .END, which sounds a bit
+# Demonstrate an edge case in which .BEGIN depends on .END, which sounds a bit
# paradox but works since these special nodes are not in the dependency
# hierarchy where the cycles are detected.
diff --git a/contrib/bmake/unit-tests/depsrc-ignore.exp b/contrib/bmake/unit-tests/depsrc-ignore.exp
index 162f10ddc17b..608671f58ed8 100644
--- a/contrib/bmake/unit-tests/depsrc-ignore.exp
+++ b/contrib/bmake/unit-tests/depsrc-ignore.exp
@@ -1,8 +1,8 @@
ignore-errors begin
false ignore-errors
+*** Error code 1 (ignored)
ignore-errors end
all begin
-*** Error code 1 (ignored)
false all
*** Error code 1 (continuing)
diff --git a/contrib/bmake/unit-tests/depsrc-meta.exp b/contrib/bmake/unit-tests/depsrc-meta.exp
index 77e27582f7da..6f17dcf0ba8a 100644
--- a/contrib/bmake/unit-tests/depsrc-meta.exp
+++ b/contrib/bmake/unit-tests/depsrc-meta.exp
@@ -2,4 +2,6 @@ Skipping meta for actual-test: no commands
Skipping meta for .END: .SPECIAL
Targets from meta mode:
| TARGET depsrc-meta-target
+Targets from meta mode in jobs mode:
+| TARGET depsrc-meta-target
exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-meta.mk b/contrib/bmake/unit-tests/depsrc-meta.mk
index d41aad9a9c96..7cd5fdf265ab 100644
--- a/contrib/bmake/unit-tests/depsrc-meta.mk
+++ b/contrib/bmake/unit-tests/depsrc-meta.mk
@@ -1,31 +1,30 @@
-# $NetBSD: depsrc-meta.mk,v 1.4 2020/11/27 08:39:07 rillig Exp $
+# $NetBSD: depsrc-meta.mk,v 1.7 2022/03/02 19:32:15 sjg Exp $
#
# Tests for the special source .META in dependency declarations.
# TODO: Implementation
# TODO: Explanation
-.if make(actual-test)
+.MAIN: all
+.if make(actual-test)
.MAKEFLAGS: -dM
-.MAKE.MODE= meta curDirOk=true
+.MAKE.MODE= meta curDirOk=true nofilemon
+.endif
actual-test: depsrc-meta-target
depsrc-meta-target: .META
@> ${.TARGET}-file
@rm -f ${.TARGET}-file
-.elif make(check-results)
-
check-results:
- @echo 'Targets from meta mode:'
+ @echo 'Targets from meta mode${.MAKE.JOBS:D in jobs mode}:'
@awk '/^TARGET/ { print "| " $$0 }' depsrc-meta-target.meta
@rm depsrc-meta-target.meta
-.else
-
all:
- @${MAKE} -f ${MAKEFILE} actual-test
- @${MAKE} -f ${MAKEFILE} check-results
+ @${MAKE} -r -f ${MAKEFILE} actual-test
+ @${MAKE} -r -f ${MAKEFILE} check-results
-.endif
+ @${MAKE} -r -f ${MAKEFILE} actual-test -j1
+ @${MAKE} -r -f ${MAKEFILE} check-results -j1
diff --git a/contrib/bmake/unit-tests/depsrc-nopath.exp b/contrib/bmake/unit-tests/depsrc-nopath.exp
index 39a9383953dd..01295d8df7f5 100644
--- a/contrib/bmake/unit-tests/depsrc-nopath.exp
+++ b/contrib/bmake/unit-tests/depsrc-nopath.exp
@@ -1 +1,3 @@
+: Making test-regular from depsrc-nopath.dir/regular.file
+: Making test-nopath from nopath.file
exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-nopath.mk b/contrib/bmake/unit-tests/depsrc-nopath.mk
index 052c6f10db66..8d9ce93c16b9 100644
--- a/contrib/bmake/unit-tests/depsrc-nopath.mk
+++ b/contrib/bmake/unit-tests/depsrc-nopath.mk
@@ -1,8 +1,27 @@
-# $NetBSD: depsrc-nopath.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: depsrc-nopath.mk,v 1.3 2024/04/27 20:41:32 rillig Exp $
#
# Tests for the special source .NOPATH in dependency declarations.
-# TODO: Implementation
+.if !target(test-*)
+_!= rm -rf depsrc-nopath.dir
+_!= mkdir depsrc-nopath.dir
+_!= touch depsrc-nopath.dir/regular.file
+_!= touch depsrc-nopath.dir/nopath.file
+.endif
all:
- @:;
+ @${MAKE} -f ${MAKEFILE} test-regular
+ @${MAKE} -f ${MAKEFILE} test-nopath || echo "should have failed"
+ @rm -rf depsrc-nopath.dir
+
+.PATH: depsrc-nopath.dir
+
+test-regular: regular.file
+ : Making ${.TARGET} from ${.ALLSRC}
+test-nopath: nopath.file
+ : Making ${.TARGET} from ${.ALLSRC}
+
+nopath.file: .NOPATH
+
+# expect: : Making test-regular from depsrc-nopath.dir/regular.file
+# expect: : Making test-nopath from nopath.file
diff --git a/contrib/bmake/unit-tests/depsrc-phony.mk b/contrib/bmake/unit-tests/depsrc-phony.mk
index c41efac369a8..9df1eb570ab4 100644
--- a/contrib/bmake/unit-tests/depsrc-phony.mk
+++ b/contrib/bmake/unit-tests/depsrc-phony.mk
@@ -1,9 +1,10 @@
-# $NetBSD: depsrc-phony.mk,v 1.3 2020/09/05 15:57:12 rillig Exp $
+# $NetBSD: depsrc-phony.mk,v 1.4 2024/04/27 20:41:32 rillig Exp $
#
# Tests for the special source .PHONY in dependency declarations,
# which executes the commands for the target even if a file of the same
# name exists and would be considered up to date.
# Without the .PHONY, this target would be "up to date".
+# expect: : depsrc-phony.mk is made.
${MAKEFILE}: .PHONY
: ${.TARGET:T} is made.
diff --git a/contrib/bmake/unit-tests/depsrc-use.exp b/contrib/bmake/unit-tests/depsrc-use.exp
index c9810bda462d..d31b9e70eb30 100644
--- a/contrib/bmake/unit-tests/depsrc-use.exp
+++ b/contrib/bmake/unit-tests/depsrc-use.exp
@@ -2,5 +2,9 @@ first 1
first 2
second 1
second 2
+first-first 1
+first-first 2
+first-second 1
+first-second 2
directly
exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-use.mk b/contrib/bmake/unit-tests/depsrc-use.mk
index 17836cd39e23..ea2cf25bfb6a 100644
--- a/contrib/bmake/unit-tests/depsrc-use.mk
+++ b/contrib/bmake/unit-tests/depsrc-use.mk
@@ -1,13 +1,28 @@
-# $NetBSD: depsrc-use.mk,v 1.4 2020/08/22 12:30:57 rillig Exp $
+# $NetBSD: depsrc-use.mk,v 1.6 2022/04/18 14:38:24 rillig Exp $
#
# Tests for the special source .USE in dependency declarations,
# which allows to append common commands to other targets.
+#
+# See also:
+# .USEBEFORE
+# depsrc-usebefore.mk
+
+# Before make.h 1.280 from 2021-12-28, a .USEBEFORE target was accidentally
+# regarded as a candidate for the main target. On the other hand, a .USE
+# target was not.
+not-a-main-candidate: .USE
all: action directly
-first: .USE
+first: .USE first-first first-second
@echo first 1 # Using ${.TARGET} here would expand to "action"
@echo first 2
+first-first: .USE
+ @echo first-first 1
+ @echo first-first 2
+first-second: .USE
+ @echo first-second 1
+ @echo first-second 2
second: .USE
@echo second 1
@@ -17,7 +32,7 @@ second: .USE
# This may happen as the result of expanding a .for loop.
empty: .USE
-# It's possible but uncommon to directly make a .USEBEFORE target.
+# It's possible but uncommon to directly make a .USE target.
directly: .USE
@echo directly
diff --git a/contrib/bmake/unit-tests/depsrc-usebefore.exp b/contrib/bmake/unit-tests/depsrc-usebefore.exp
index c9810bda462d..d84299b66275 100644
--- a/contrib/bmake/unit-tests/depsrc-usebefore.exp
+++ b/contrib/bmake/unit-tests/depsrc-usebefore.exp
@@ -1,6 +1,42 @@
-first 1
-first 2
-second 1
-second 2
-directly
+after-2-before-2 1
+after-2-before-2 2
+after-2-before-1 1
+after-2-before-1 2
+after-1-before-2 1
+after-1-before-2 2
+after-1-before-1 1
+after-1-before-1 2
+before-2-before-2 1
+before-2-before-2 2
+before-2-before-1 1
+before-2-before-1 2
+before-1-before-2 1
+before-1-before-2 2
+before-1-before-1 1
+before-1-before-1 2
+before-2 1
+before-2 2
+before-1 1
+before-1 2
+after-1 1
+after-1 2
+after-2 1
+after-2 2
+before-1-after-1 1
+before-1-after-1 2
+before-1-after-2 1
+before-1-after-2 2
+before-2-after-1 1
+before-2-after-1 2
+before-2-after-2 1
+before-2-after-2 2
+after-1-after-1 1
+after-1-after-1 2
+after-1-after-2 1
+after-1-after-2 2
+after-2-after-1 1
+after-2-after-1 2
+after-2-after-2 1
+after-2-after-2 2
+`directly' is up to date.
exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-usebefore.mk b/contrib/bmake/unit-tests/depsrc-usebefore.mk
index 001cfb0d71c1..672fb7144856 100644
--- a/contrib/bmake/unit-tests/depsrc-usebefore.mk
+++ b/contrib/bmake/unit-tests/depsrc-usebefore.mk
@@ -1,28 +1,115 @@
-# $NetBSD: depsrc-usebefore.mk,v 1.6 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: depsrc-usebefore.mk,v 1.9 2022/04/18 14:41:42 rillig Exp $
#
# Tests for the special source .USEBEFORE in dependency declarations,
# which allows to prepend common commands to other targets.
#
+# If a target depends on several .USE or .USEBEFORE nodes, the commands get
+# appended or prepended in declaration order. For .USE nodes, this is the
+# expected order, for .USEBEFORE nodes the order is somewhat reversed, and for
+# .USE or .USEBEFORE nodes that depend on other .USE or .USEBEFORE nodes, it
+# gets even more complicated.
+#
# See also:
# .USE
# depsrc-use.mk
-all: action directly
+# Before make.h 1.280 from 2021-12-28, a .USEBEFORE target was accidentally
+# regarded as a candidate for the main target. On the other hand, a .USE
+# target was not.
+not-a-main-candidate: .USEBEFORE
+
+all:
+ @${MAKE} -r -f ${MAKEFILE} ordering
+ @${MAKE} -r -f ${MAKEFILE} directly
+
+ordering: before-1 before-2 after-1 after-2
+
+before-1: .USEBEFORE before-1-before-1 before-1-before-2 before-1-after-1 before-1-after-2
+ @echo before-1 1
+ @echo before-1 2
+
+before-1-before-1: .USEBEFORE
+ @echo before-1-before-1 1
+ @echo before-1-before-1 2
+
+before-1-before-2: .USEBEFORE
+ @echo before-1-before-2 1
+ @echo before-1-before-2 2
+
+before-1-after-1: .USE
+ @echo before-1-after-1 1
+ @echo before-1-after-1 2
+
+before-1-after-2: .USE
+ @echo before-1-after-2 1
+ @echo before-1-after-2 2
+
+before-2: .USEBEFORE before-2-before-1 before-2-before-2 before-2-after-1 before-2-after-2
+ @echo before-2 1
+ @echo before-2 2
+
+before-2-before-1: .USEBEFORE
+ @echo before-2-before-1 1
+ @echo before-2-before-1 2
+
+before-2-before-2: .USEBEFORE
+ @echo before-2-before-2 1
+ @echo before-2-before-2 2
-first: .USEBEFORE
- @echo first 1 # Using ${.TARGET} here would expand to "action"
- @echo first 2 # Using ${.TARGET} here would expand to "action"
+before-2-after-1: .USE
+ @echo before-2-after-1 1
+ @echo before-2-after-1 2
-second: .USEBEFORE
- @echo second 1
- @echo second 2
+before-2-after-2: .USE
+ @echo before-2-after-2 1
+ @echo before-2-after-2 2
+
+after-1: .USE after-1-before-1 after-1-before-2 after-1-after-1 after-1-after-2
+ @echo after-1 1
+ @echo after-1 2
+
+after-1-before-1: .USEBEFORE
+ @echo after-1-before-1 1
+ @echo after-1-before-1 2
+
+after-1-before-2: .USEBEFORE
+ @echo after-1-before-2 1
+ @echo after-1-before-2 2
+
+after-1-after-1: .USE
+ @echo after-1-after-1 1
+ @echo after-1-after-1 2
+
+after-1-after-2: .USE
+ @echo after-1-after-2 1
+ @echo after-1-after-2 2
+
+after-2: .USE after-2-before-1 after-2-before-2 after-2-after-1 after-2-after-2
+ @echo after-2 1
+ @echo after-2 2
+
+after-2-before-1: .USEBEFORE
+ @echo after-2-before-1 1
+ @echo after-2-before-1 2
+
+after-2-before-2: .USEBEFORE
+ @echo after-2-before-2 1
+ @echo after-2-before-2 2
+
+after-2-after-1: .USE
+ @echo after-2-after-1 1
+ @echo after-2-after-1 2
+
+after-2-after-2: .USE
+ @echo after-2-after-2 1
+ @echo after-2-after-2 2
# It is possible but uncommon to have a .USEBEFORE target with no commands.
# This may happen as the result of expanding a .for loop.
empty: .USEBEFORE
-# It is possible but uncommon to directly make a .USEBEFORE target.
+# It is technically possible to directly make a .USEBEFORE target, but it
+# doesn't make sense since GNode_IsOODate considers such a target to always be
+# up to date.
directly: .USEBEFORE
@echo directly
-
-action: second first empty
diff --git a/contrib/bmake/unit-tests/depsrc-wait.exp b/contrib/bmake/unit-tests/depsrc-wait.exp
index d1a60fbaa6e6..36bcb0678151 100644
--- a/contrib/bmake/unit-tests/depsrc-wait.exp
+++ b/contrib/bmake/unit-tests/depsrc-wait.exp
@@ -1,13 +1,18 @@
---- a ---
echo a
a
---- b1 ---
echo b1
b1
---- b ---
echo b
b
---- x ---
echo x
x
+: Making 3a
+: Making 3a
+: Making 3a
+: Making 3b
+: Making 3b
+: Making 3b
+: Making 3c
+: Making 3c
+: Making 3c
exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc-wait.mk b/contrib/bmake/unit-tests/depsrc-wait.mk
index 95b0ea96e0a5..ab974d47c29d 100644
--- a/contrib/bmake/unit-tests/depsrc-wait.mk
+++ b/contrib/bmake/unit-tests/depsrc-wait.mk
@@ -1,9 +1,15 @@
-# $NetBSD: depsrc-wait.mk,v 1.3 2020/09/07 18:40:32 rillig Exp $
+# $NetBSD: depsrc-wait.mk,v 1.4 2022/05/07 17:49:47 rillig Exp $
#
# Tests for the special source .WAIT in dependency declarations,
# which adds a sequence point between the nodes to its left and the nodes
# to its right.
+all: .PHONY
+ @${MAKE} -r -f ${MAKEFILE} x
+ @${MAKE} -r -f ${MAKEFILE} three-by-three
+
+
+.if make(x)
# Even though the build could run massively parallel, the .WAIT imposes a
# strict ordering in this example, which forces the targets to be made in
# exactly this order.
@@ -19,3 +25,17 @@ b: b1
echo b
b1:
echo b1
+.endif
+
+
+# There are 3 groups of 3 targets, with .WAIT barriers in between. Each of
+# these groups has to be made completely before starting the next group.
+# See Makefile, POSTPROC for the postprocessing that takes place.
+.if make(three-by-three)
+.MAKEFLAGS: -j5
+.MAKE.MODE+= randomize-targets
+
+three-by-three: .WAIT 3a1 3a2 3a3 .WAIT 3b1 3b2 3b3 .WAIT 3c1 3c2 3c3 .WAIT
+3a1 3a2 3a3 3b1 3b2 3b3 3c1 3c2 3c3:
+ : Making ${.TARGET}
+.endif
diff --git a/contrib/bmake/unit-tests/depsrc.exp b/contrib/bmake/unit-tests/depsrc.exp
index 06165e6f9ac4..147ea8b24371 100644
--- a/contrib/bmake/unit-tests/depsrc.exp
+++ b/contrib/bmake/unit-tests/depsrc.exp
@@ -1,4 +1,5 @@
: 'Undefined variables are expanded directly in the dependency'
: 'declaration. They are not preserved and maybe expanded later.'
: 'This is in contrast to local variables such as ${.TARGET}.'
+: Making .UNKNOWN from nothing.
exit status 0
diff --git a/contrib/bmake/unit-tests/depsrc.mk b/contrib/bmake/unit-tests/depsrc.mk
index ab9d04c1d3a4..4e5752c97184 100644
--- a/contrib/bmake/unit-tests/depsrc.mk
+++ b/contrib/bmake/unit-tests/depsrc.mk
@@ -1,7 +1,7 @@
-# $NetBSD: depsrc.mk,v 1.4 2020/12/22 19:38:44 rillig Exp $
+# $NetBSD: depsrc.mk,v 1.5 2021/12/13 23:38:54 rillig Exp $
#
# Tests for special sources (those starting with a dot, followed by
-# uppercase letters) in dependency declarations, such as .PHONY.
+# uppercase letters) in dependency declarations, such as '.PHONY'.
# TODO: Implementation
@@ -14,13 +14,19 @@ target: .PHONY source-${DEFINED_LATER}
DEFINED_LATER= later
#
source-: .PHONY
+ # This section applies.
: 'Undefined variables are expanded directly in the dependency'
: 'declaration. They are not preserved and maybe expanded later.'
: 'This is in contrast to local variables such as $${.TARGET}.'
source-later: .PHONY
+ # This section doesn't apply.
: 'Undefined variables are tried to be expanded in a dependency'
: 'declaration. If that fails because the variable is undefined,'
: 'the expression is preserved and tried to be expanded later.'
-all:
- @:;
+# Sources that look like keywords but are not known are interpreted as
+# ordinary sources.
+target: .UNKNOWN
+
+.UNKNOWN:
+ : Making ${.TARGET} from ${.ALLSRC:S,^$,nothing,W}.
diff --git a/contrib/bmake/unit-tests/deptgt-begin.exp b/contrib/bmake/unit-tests/deptgt-begin.exp
index abc80afe9964..db4c7d7dfa13 100644
--- a/contrib/bmake/unit-tests/deptgt-begin.exp
+++ b/contrib/bmake/unit-tests/deptgt-begin.exp
@@ -1,5 +1,5 @@
-make: "deptgt-begin.mk" line 17: warning: duplicate script for target ".BEGIN" ignored
-make: "deptgt-begin.mk" line 8: warning: using previous script for ".BEGIN" defined here
+make: "deptgt-begin.mk" line 19: warning: duplicate script for target ".BEGIN" ignored
+make: "deptgt-begin.mk" line 9: warning: using previous script for ".BEGIN" defined here
: parse time
: Making before-begin before .BEGIN.
: .BEGIN
diff --git a/contrib/bmake/unit-tests/deptgt-begin.mk b/contrib/bmake/unit-tests/deptgt-begin.mk
index b71d78f371ed..8b9842641a2d 100644
--- a/contrib/bmake/unit-tests/deptgt-begin.mk
+++ b/contrib/bmake/unit-tests/deptgt-begin.mk
@@ -1,9 +1,10 @@
-# $NetBSD: deptgt-begin.mk,v 1.5 2020/11/15 22:28:08 rillig Exp $
+# $NetBSD: deptgt-begin.mk,v 1.7 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the special target .BEGIN in dependency declarations,
# which is a container for commands that are run before any other
# commands from the shell lines.
+# expect+2: warning: using previous script for ".BEGIN" defined here
.BEGIN:
: $@
@@ -13,6 +14,7 @@
# add its commands after this.
#
# There are several ways to resolve this situation, which are detailed below.
+# expect+2: warning: duplicate script for target ".BEGIN" ignored
.BEGIN:
: Making another $@.
@@ -25,8 +27,8 @@ before-begin: .PHONY .NOTMAIN
# Another way is to define a custom target and make that a .USE dependency.
# For the .BEGIN target, .USE dependencies do not work though, since in
-# Compat_Run, the .USE and .USEBEFORE nodes are expanded right after the
-# .BEGIN target has been run, which is too late.
+# Compat_MakeAll, the .USE and .USEBEFORE nodes are expanded right after the
+# .BEGIN target has been made, which is too late.
.BEGIN: use
use: .USE .NOTMAIN
: Making $@ from a .USE dependency.
@@ -35,8 +37,8 @@ use: .USE .NOTMAIN
# .BEGIN target.
#
# For the .BEGIN target, .USEBEFORE dependencies do not work though, since in
-# Compat_Run, the .USE and .USEBEFORE nodes are expanded right after the
-# .BEGIN target has been run, which is too late.
+# Compat_MakeAll, the .USE and .USEBEFORE nodes are expanded right after the
+# .BEGIN target has been made, which is too late.
.BEGIN: use-before
use-before: .USEBEFORE .NOTMAIN
: Making $@ from a .USEBEFORE dependency.
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-delete_on_error.exp b/contrib/bmake/unit-tests/deptgt-delete_on_error.exp
index 9d9f1dc3e5ec..e60aa01351e2 100644
--- a/contrib/bmake/unit-tests/deptgt-delete_on_error.exp
+++ b/contrib/bmake/unit-tests/deptgt-delete_on_error.exp
@@ -7,10 +7,8 @@ make: *** deptgt-delete_on_error-regular removed
make: *** deptgt-delete_on_error-regular-delete removed
> deptgt-delete_on_error-phony; false
*** Error code 1 (continuing)
-make: *** deptgt-delete_on_error-phony removed
> deptgt-delete_on_error-phony-delete; false
*** Error code 1 (continuing)
-make: *** deptgt-delete_on_error-phony-delete removed
> deptgt-delete_on_error-precious; false
*** Error code 1 (continuing)
> deptgt-delete_on_error-precious-delete; false
@@ -18,6 +16,7 @@ make: *** deptgt-delete_on_error-phony-delete removed
Stop.
make: stopped in unit-tests
+*** Error code 1 (ignored)
Parallel mode
> deptgt-delete_on_error-regular; false
@@ -47,5 +46,4 @@ make: stopped in unit-tests
make: stopped in unit-tests
*** Error code 1 (ignored)
-*** Error code 1 (ignored)
exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-delete_on_error.mk b/contrib/bmake/unit-tests/deptgt-delete_on_error.mk
index e6d0610f4672..2309e67c01a5 100644
--- a/contrib/bmake/unit-tests/deptgt-delete_on_error.mk
+++ b/contrib/bmake/unit-tests/deptgt-delete_on_error.mk
@@ -1,4 +1,4 @@
- # $NetBSD: deptgt-delete_on_error.mk,v 1.3 2020/10/25 21:31:00 rillig Exp $
+# $NetBSD: deptgt-delete_on_error.mk,v 1.4 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the special target .DELETE_ON_ERROR in dependency declarations,
# which controls whether the target is deleted if a shell command fails or
diff --git a/contrib/bmake/unit-tests/deptgt-end-fail-indirect.mk b/contrib/bmake/unit-tests/deptgt-end-fail-indirect.mk
index 29346b8321fe..dc921bcfda2c 100644
--- a/contrib/bmake/unit-tests/deptgt-end-fail-indirect.mk
+++ b/contrib/bmake/unit-tests/deptgt-end-fail-indirect.mk
@@ -1,10 +1,10 @@
-# $NetBSD: deptgt-end-fail-indirect.mk,v 1.2 2020/12/06 21:22:04 rillig Exp $
+# $NetBSD: deptgt-end-fail-indirect.mk,v 1.3 2022/05/07 08:01:20 rillig Exp $
#
# Tests for an error in a dependency of the .END node.
#
# Before 2020-11-25, an error in the .END target did not print the "Stop."
# and exited with status 0. The cause for this was a missing condition in
-# Compat_Run in the handling of the .END node.
+# Compat_MakeAll in the handling of the .END node.
all:
: $@
diff --git a/contrib/bmake/unit-tests/deptgt-end-fail.mk b/contrib/bmake/unit-tests/deptgt-end-fail.mk
index 57cdc7da8679..c77127f26091 100644
--- a/contrib/bmake/unit-tests/deptgt-end-fail.mk
+++ b/contrib/bmake/unit-tests/deptgt-end-fail.mk
@@ -1,11 +1,11 @@
-# $NetBSD: deptgt-end-fail.mk,v 1.6 2020/12/07 01:04:07 rillig Exp $
+# $NetBSD: deptgt-end-fail.mk,v 1.7 2022/05/07 08:01:20 rillig Exp $
#
# Tests for an errors in the main target, its dependencies,
# the .END node and its dependencies.
#
# Before 2020-11-25, an error in the .END target did not print the "Stop.",
# even though this was intended. The cause for this was a missing condition
-# in Compat_Run, in the code handling the .END node.
+# in Compat_MakeAll, in the code handling the .END node.
test: .PHONY
diff --git a/contrib/bmake/unit-tests/deptgt-error.exp b/contrib/bmake/unit-tests/deptgt-error.exp
index 39a9383953dd..48e2f90954cf 100644
--- a/contrib/bmake/unit-tests/deptgt-error.exp
+++ b/contrib/bmake/unit-tests/deptgt-error.exp
@@ -1 +1,9 @@
-exit status 0
+false fails
+*** Error code 1 (continuing)
+
+Stop.
+make: stopped in unit-tests
+ERROR_INFO='This information is printed on 'errors'.'
+Making sub-error as prerequisite.
+Making .ERROR out of nothing.
+exit status 1
diff --git a/contrib/bmake/unit-tests/deptgt-error.mk b/contrib/bmake/unit-tests/deptgt-error.mk
index 5d515b95afc3..67f94e6999f7 100644
--- a/contrib/bmake/unit-tests/deptgt-error.mk
+++ b/contrib/bmake/unit-tests/deptgt-error.mk
@@ -1,9 +1,21 @@
-# $NetBSD: deptgt-error.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: deptgt-error.mk,v 1.4 2022/01/22 21:50:41 rillig Exp $
#
# Tests for the special target .ERROR in dependency declarations, which
-# collects commands that are run when another target fails.
+# is made when another target fails.
-# TODO: Implementation
+all: .PHONY
+ false fails
-all:
- @:;
+.ERROR:
+ @echo 'Making ${.TARGET} out of nothing.'
+
+.ERROR: sub-error
+sub-error: .PHONY
+ @echo 'Making ${.TARGET} as prerequisite.'
+
+# Before making the '.ERROR' target, these variable values are printed.
+MAKE_PRINT_VAR_ON_ERROR= ERROR_INFO
+
+# Use single quotes to demonstrate that the output is only informational, it
+# does not use any established escaping mechanism.
+ERROR_INFO= This information is ${:Uprinted} on 'errors'.
diff --git a/contrib/bmake/unit-tests/deptgt-ignore.exp b/contrib/bmake/unit-tests/deptgt-ignore.exp
index 39a9383953dd..2aa1311c8ff7 100644
--- a/contrib/bmake/unit-tests/deptgt-ignore.exp
+++ b/contrib/bmake/unit-tests/deptgt-ignore.exp
@@ -1 +1,11 @@
-exit status 0
+error-failed before
+*** Error code 1 (continuing)
+error-ignored before
+*** Error code 1 (ignored)
+error-ignored after
+Making depends-on-ignored from error-ignored.
+`all' not remade because of errors.
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/deptgt-ignore.mk b/contrib/bmake/unit-tests/deptgt-ignore.mk
index 49c14d2cfd43..a0191847e69f 100644
--- a/contrib/bmake/unit-tests/deptgt-ignore.mk
+++ b/contrib/bmake/unit-tests/deptgt-ignore.mk
@@ -1,9 +1,31 @@
-# $NetBSD: deptgt-ignore.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: deptgt-ignore.mk,v 1.4 2022/01/22 21:50:41 rillig Exp $
#
# Tests for the special target .IGNORE in dependency declarations, which
# does not stop if a command from this target exits with a non-zero status.
+#
+# This test only applies to compatibility mode. In jobs mode such as with
+# '-j1', all commands for a single target are bundled into a single shell
+# program, which is a different implementation technique, the .IGNORE applies
+# there as well.
+
+.MAKEFLAGS: -d0 # force stdout to be unbuffered
+
+all: depends-on-failed depends-on-ignored
+.PHONY: all depends-on-failed depends-on-ignored error-failed error-ignored
+
+error-failed error-ignored:
+ @echo '${.TARGET} before'
+ @false
+ @echo '${.TARGET} after'
+
+depends-on-failed: error-failed
+ @echo 'Making ${.TARGET} from ${.ALLSRC}.'
+depends-on-ignored: error-ignored
+ @echo 'Making ${.TARGET} from ${.ALLSRC}.'
-# TODO: Implementation
+# Even though the command 'false' in the middle fails, the remaining commands
+# are still run. After that, the target is marked made, so targets depending
+# on the target with the ignored commands are made.
+.IGNORE: error-ignored
-all:
- @:;
+#.MAKEFLAGS: -dg2
diff --git a/contrib/bmake/unit-tests/deptgt-interrupt.exp b/contrib/bmake/unit-tests/deptgt-interrupt.exp
index 39a9383953dd..e59bdc7d7c41 100644
--- a/contrib/bmake/unit-tests/deptgt-interrupt.exp
+++ b/contrib/bmake/unit-tests/deptgt-interrupt.exp
@@ -1 +1,2 @@
-exit status 0
+Ctrl-C
+exit status 130
diff --git a/contrib/bmake/unit-tests/deptgt-interrupt.mk b/contrib/bmake/unit-tests/deptgt-interrupt.mk
index d94009a52e05..9a3b4d9e81c9 100644
--- a/contrib/bmake/unit-tests/deptgt-interrupt.mk
+++ b/contrib/bmake/unit-tests/deptgt-interrupt.mk
@@ -1,10 +1,11 @@
-# $NetBSD: deptgt-interrupt.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: deptgt-interrupt.mk,v 1.4 2022/01/22 21:50:41 rillig Exp $
#
# Tests for the special target .INTERRUPT in dependency declarations, which
# collects commands to be run when make is interrupted while building another
# target.
-# TODO: Implementation
-
all:
- @:;
+ @kill -INT ${.MAKE.PID}
+
+.INTERRUPT:
+ @echo 'Ctrl-C'
diff --git a/contrib/bmake/unit-tests/deptgt-main.exp b/contrib/bmake/unit-tests/deptgt-main.exp
index 39a9383953dd..40c9a2c3cb8f 100644
--- a/contrib/bmake/unit-tests/deptgt-main.exp
+++ b/contrib/bmake/unit-tests/deptgt-main.exp
@@ -1 +1,2 @@
+This target real-main is the one that is made.
exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-main.mk b/contrib/bmake/unit-tests/deptgt-main.mk
index 84d05dc25ed6..184b6f3f73bb 100644
--- a/contrib/bmake/unit-tests/deptgt-main.mk
+++ b/contrib/bmake/unit-tests/deptgt-main.mk
@@ -1,10 +1,29 @@
-# $NetBSD: deptgt-main.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: deptgt-main.mk,v 1.4 2022/01/23 21:48:59 rillig Exp $
#
# Tests for the special target .MAIN in dependency declarations, which defines
# the main target. This main target is built if no target has been specified
# on the command line or via MAKEFLAGS.
-# TODO: Implementation
+# The first target becomes the main target by default. It can be overridden
+# though.
+all: .PHONY
+ @echo 'This target is not made.'
-all:
- @:;
+# This target is not the first to be defined, but it lists '.MAIN' as one of
+# its sources. The word '.MAIN' only has a special meaning when it appears as
+# a _target_ in a dependency declaration, not as a _source_. It is thus
+# ignored.
+depsrc-main: .PHONY .MAIN
+ @echo 'This target is not made either.'
+
+# This target is the first to be marked with '.MAIN', so it replaces the
+# previous main target, which was 'all'.
+.MAIN: real-main
+real-main: .PHONY
+ @echo 'This target ${.TARGET} is the one that is made.'
+
+# This target is marked with '.MAIN' but there already is a main target. The
+# attribute '.MAIN' is thus ignored.
+.MAIN: too-late
+too-late: .PHONY
+ @echo 'This target comes too late, there is already a .MAIN target.'
diff --git a/contrib/bmake/unit-tests/deptgt-makeflags.exp b/contrib/bmake/unit-tests/deptgt-makeflags.exp
index 11043bc5110c..ea29f76ad464 100644
--- a/contrib/bmake/unit-tests/deptgt-makeflags.exp
+++ b/contrib/bmake/unit-tests/deptgt-makeflags.exp
@@ -1,9 +1,9 @@
-Global:delete DOLLAR (not found)
+Global: ignoring delete 'DOLLAR' as it is not found
Command: DOLLAR = $$$$
Global: .MAKEOVERRIDES = VAR DOLLAR
CondParser_Eval: ${DOLLAR} != "\$\$"
Var_Parse: ${DOLLAR} != "\$\$" (eval-defined)
-lhs = "$$", rhs = "$$", op = !=
+Comparing "$$" != "$$"
Global: .MAKEFLAGS = -r -k -D VAR -D VAR -d cv -d
Global: .MAKEFLAGS = -r -k -D VAR -D VAR -d cv -d 0
make: Unterminated quoted string [make VAR=initial UNBALANCED=']
diff --git a/contrib/bmake/unit-tests/deptgt-makeflags.mk b/contrib/bmake/unit-tests/deptgt-makeflags.mk
index 0a0f410e14c4..2f8b00743e3f 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.9 2023/11/19 22:32:44 rillig Exp $
#
# Tests for the special target .MAKEFLAGS in dependency declarations,
# which adds command line options later, at parse time.
@@ -65,9 +65,9 @@
.endif
# Next try at defining another newline variable. Since whitespace around the
-# variable value is trimmed, two empty variable expressions surround the
+# variable value is trimmed, two empty expressions ${:U} surround the
# literal newline now. This prevents the newline from being skipped during
-# parsing. The ':=' assignment operator expands the empty variable
+# parsing. The ':=' assignment operator expands the empty
# expressions, leaving only the newline as the variable value.
#
# This is one of the very few ways (maybe even the only one) to inject literal
@@ -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/deptgt-notparallel.exp b/contrib/bmake/unit-tests/deptgt-notparallel.exp
index 39a9383953dd..1e4d8ad7befb 100644
--- a/contrib/bmake/unit-tests/deptgt-notparallel.exp
+++ b/contrib/bmake/unit-tests/deptgt-notparallel.exp
@@ -1 +1,9 @@
+: Making 1 out of nothing.
+: Making 2 out of nothing.
+: Making 3 out of nothing.
+: Making 4 out of nothing.
+: Making 5 out of nothing.
+: Making 6 out of nothing.
+: Making 7 out of nothing.
+: Making 8 out of nothing.
exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-notparallel.mk b/contrib/bmake/unit-tests/deptgt-notparallel.mk
index db08aa9d3558..8d32ed8f2461 100644
--- a/contrib/bmake/unit-tests/deptgt-notparallel.mk
+++ b/contrib/bmake/unit-tests/deptgt-notparallel.mk
@@ -1,8 +1,16 @@
-# $NetBSD: deptgt-notparallel.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: deptgt-notparallel.mk,v 1.3 2021/12/13 23:38:54 rillig Exp $
#
-# Tests for the special target .NOTPARALLEL in dependency declarations.
+# Tests for the special target .NOTPARALLEL in dependency declarations, which
+# prevents the job module from doing anything in parallel, by setting the
+# maximum jobs to 1. This only applies to the current make, it is not
+# exported to submakes.
-# TODO: Implementation
+.MAKEFLAGS: -j4
-all:
- @:;
+# Set opts.maxJobs back to 1. Without this line, the output would be in
+# random order, interleaved with separators like '--- 1 ---'.
+.NOTPARALLEL:
+
+all: 1 2 3 4 5 6 7 8
+1 2 3 4 5 6 7 8: .PHONY
+ : Making ${.TARGET} out of nothing.
diff --git a/contrib/bmake/unit-tests/deptgt-order.exp b/contrib/bmake/unit-tests/deptgt-order.exp
index 5f7dde0ac69d..ecbf03fcc572 100644
--- a/contrib/bmake/unit-tests/deptgt-order.exp
+++ b/contrib/bmake/unit-tests/deptgt-order.exp
@@ -1,3 +1,10 @@
+Parsing line 15: .ORDER: three one
+ParseDependency(.ORDER: three one)
+# .ORDER forces 'three' to be made before 'one'
+# three, unmade, type OP_DEPENDS|OP_PHONY|OP_HAS_COMMANDS, flags none
+# one, unmade, type OP_DEPENDS|OP_PHONY, flags none
+Parsing line 16: .MAKEFLAGS: -d0
+ParseDependency(.MAKEFLAGS: -d0)
: 'Making two out of one.'
: 'Making three out of two.'
: 'Making all out of three.'
diff --git a/contrib/bmake/unit-tests/deptgt-order.mk b/contrib/bmake/unit-tests/deptgt-order.mk
index f241331ae1e1..88f5958425dd 100644
--- a/contrib/bmake/unit-tests/deptgt-order.mk
+++ b/contrib/bmake/unit-tests/deptgt-order.mk
@@ -1,4 +1,4 @@
-# $NetBSD: deptgt-order.mk,v 1.3 2021/06/17 15:25:33 rillig Exp $
+# $NetBSD: deptgt-order.mk,v 1.4 2021/12/13 23:38:54 rillig Exp $
#
# Tests for the special target .ORDER in dependency declarations.
@@ -11,7 +11,9 @@ three: two
# This .ORDER creates a circular dependency since 'three' depends on 'one'
# but 'one' is supposed to be built after 'three'.
+.MAKEFLAGS: -dp
.ORDER: three one
+.MAKEFLAGS: -d0
# XXX: The circular dependency should be detected here.
all: three
diff --git a/contrib/bmake/unit-tests/deptgt-path-suffix.exp b/contrib/bmake/unit-tests/deptgt-path-suffix.exp
index 39a9383953dd..228a29851f48 100644
--- a/contrib/bmake/unit-tests/deptgt-path-suffix.exp
+++ b/contrib/bmake/unit-tests/deptgt-path-suffix.exp
@@ -1 +1,4 @@
-exit status 0
+make: "deptgt-path-suffix.mk" line 8: Suffix '.c' not defined (yet)
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/deptgt-path-suffix.mk b/contrib/bmake/unit-tests/deptgt-path-suffix.mk
index 3a7e697bc748..494a076a5520 100644
--- a/contrib/bmake/unit-tests/deptgt-path-suffix.mk
+++ b/contrib/bmake/unit-tests/deptgt-path-suffix.mk
@@ -1,8 +1,16 @@
-# $NetBSD: deptgt-path-suffix.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: deptgt-path-suffix.mk,v 1.3 2021/12/13 23:38:54 rillig Exp $
#
# Tests for the special target .PATH.suffix in dependency declarations.
# TODO: Implementation
+# expect+1: Suffix '.c' not defined (yet)
+.PATH.c: ..
+
+.SUFFIXES: .c
+
+# Now the suffix is defined, and the path is recorded.
+.PATH.c: ..
+
all:
@:;
diff --git a/contrib/bmake/unit-tests/deptgt-phony.exp b/contrib/bmake/unit-tests/deptgt-phony.exp
index 39a9383953dd..e943091e4cef 100644
--- a/contrib/bmake/unit-tests/deptgt-phony.exp
+++ b/contrib/bmake/unit-tests/deptgt-phony.exp
@@ -1 +1,42 @@
+Expanding "depsrc-phony-pr-15164-*-wildcard"...
+Expanding "deptgt-phony-pr-15164-*-wildcard"...
+Searching for .depend ...
+ failed.
+Searching for .depend ...[dot last]...
+ . ...
+ failed.
+Wildcard expanding "all"...
+Searching for all ...
+ failed.
+Found 'all' as '(not found)'
+SuffFindDeps "all"
+ No known suffix on all. Using .NULL suffix
+adding suffix rules
+Wildcard expanding "depsrc-phony-pr-15164-*-wildcard"...
+Expanding "depsrc-phony-pr-15164-*-wildcard"...
+
+Wildcard expanding "deptgt-phony-pr-15164-*-wildcard"...
+Expanding "deptgt-phony-pr-15164-*-wildcard"...
+
+Searching for all ...
+ failed.
+SuffFindDeps "depsrc-phony-pr-15164"
+ No valid suffix on depsrc-phony-pr-15164
+SuffFindDeps "deptgt-phony-pr-15164"
+ No valid suffix on deptgt-phony-pr-15164
+: Making depsrc-phony-pr-15164
+: Making deptgt-phony-pr-15164
+Wildcard expanding "all"...
+Searching for all ...
+ failed.
+Found 'all' as '(not found)'
+SuffFindDeps ".END"
+ No known suffix on .END. Using .NULL suffix
+adding suffix rules
+Searching for .END ...
+ failed.
+Wildcard expanding ".END"...
+Searching for .END ...
+ failed.
+Found '.END' as '(not found)'
exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-phony.mk b/contrib/bmake/unit-tests/deptgt-phony.mk
index 7f9909fa89a2..d5616e0edd96 100644
--- a/contrib/bmake/unit-tests/deptgt-phony.mk
+++ b/contrib/bmake/unit-tests/deptgt-phony.mk
@@ -1,8 +1,31 @@
-# $NetBSD: deptgt-phony.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: deptgt-phony.mk,v 1.3 2022/02/11 23:44:18 rillig Exp $
#
# Tests for the special target .PHONY in dependency declarations.
# TODO: Implementation
all:
- @:;
+
+
+# https://gnats.netbsd.org/15164 describes that .PHONY targets are still
+# looked up in directories, even though .PHONY means that these targets do
+# _not_ correspond to actual files.
+#
+# expect: Expanding "depsrc-phony-pr-15164-*-wildcard"...
+# expect: Expanding "deptgt-phony-pr-15164-*-wildcard"...
+.MAKEFLAGS: -dds
+depsrc-phony-pr-15164: .PHONY
+ : Making ${.TARGET}
+depsrc-phony-pr-15164-*-wildcard: .PHONY
+ : Making ${.TARGET}
+
+.PHONY: deptgt-phony-pr-15164
+deptgt-phony-pr-15164:
+ : Making ${.TARGET}
+
+.PHONY: deptgt-phony-pr-15164-*-wildcard
+deptgt-phony-pr-15164-*-wildcard:
+ : Making ${.TARGET}
+
+all: depsrc-phony-pr-15164 depsrc-phony-pr-15164-*-wildcard
+all: deptgt-phony-pr-15164 deptgt-phony-pr-15164-*-wildcard
diff --git a/contrib/bmake/unit-tests/envfirst.exp b/contrib/bmake/unit-tests/deptgt-posix.exp
index 39a9383953dd..39a9383953dd 100644
--- a/contrib/bmake/unit-tests/envfirst.exp
+++ b/contrib/bmake/unit-tests/deptgt-posix.exp
diff --git a/contrib/bmake/unit-tests/deptgt-posix.mk b/contrib/bmake/unit-tests/deptgt-posix.mk
new file mode 100644
index 000000000000..bf29dbfbd627
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-posix.mk
@@ -0,0 +1,123 @@
+# $NetBSD: deptgt-posix.mk,v 1.4 2022/05/07 21:24:52 rillig Exp $
+#
+# Tests for the special target '.POSIX', which enables POSIX mode.
+#
+# As of 2022-04-18, when parsing the dependency line '.POSIX', the variable
+# '%POSIX' is defined and <posix.mk> is included, if it exists. Other than
+# that, POSIX support is still incomplete, the exact set of supported features
+# needs to be cross-checked with the POSIX specification.
+#
+# At the point of '.POSIX:', <sys.mk> has been loaded already, unless the
+# option '-r' was given. This means that an implementation of <posix.mk> must
+# work both with and without the system rules from <sys.mk> being in effect.
+#
+# Implementation note: this test needs to run isolated from the usual tests
+# directory to prevent unit-tests/posix.mk from interfering with the posix.mk
+# from the system directory that this test uses; since at least 1997, the
+# directive '.include <file>' has been looking in the current directory first
+# before searching the file in the system search path, as described in
+# https://gnats.netbsd.org/15163.
+#
+# See also:
+# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html
+
+TESTTMP= ${TMPDIR:U/tmp}/make.test.deptgt-posix
+SYSDIR= ${TESTTMP}/sysdir
+MAIN_MK= ${TESTTMP}/main.mk
+INCLUDED_MK= ${TESTTMP}/included.mk
+
+all: .PHONY
+.SILENT:
+
+set-up-sysdir: .USEBEFORE
+ mkdir -p ${SYSDIR}
+ printf '%s\n' > ${SYSDIR}/sys.mk \
+ 'CC=sys-cc' \
+ 'SEEN_SYS_MK=yes'
+ printf '%s\n' > ${SYSDIR}/posix.mk \
+ 'CC=posix-cc'
+
+check-is-posix: .USE
+ printf '%s\n' >> ${MAIN_MK} \
+ '.if $${CC} != "posix-cc"' \
+ '. error' \
+ '.endif' \
+ '.if $${%POSIX} != "1003.2"' \
+ '. error' \
+ '.endif' \
+ 'all: .PHONY'
+
+check-not-posix: .USE
+ printf '%s\n' >> ${MAIN_MK} \
+ '.if $${CC} != "sys-cc"' \
+ '. error' \
+ '.endif' \
+ '.if defined(%POSIX)' \
+ '. error' \
+ '.endif' \
+ 'all: .PHONY'
+
+check-not-seen-sys-mk: .USE
+ printf '%s\n' >> ${MAIN_MK} \
+ '.if defined(SEEN_SYS_MK)' \
+ '. error' \
+ '.endif'
+
+run: .USE
+ (cd "${TESTTMP}" && MAKEFLAGS=${MAKEFLAGS.${.TARGET}:Q} ${MAKE} \
+ -m "${SYSDIR}" -f ${MAIN_MK:T})
+ rm -rf ${TESTTMP}
+
+# If the main makefile has a '.for' loop as its first non-comment line, a
+# strict reading of POSIX 2018 makes the makefile non-conforming.
+all: after-for
+after-for: .PHONY set-up-sysdir check-not-posix run
+ printf '%s\n' > ${MAIN_MK} \
+ '# comment' \
+ '' \
+ '.for i in once' \
+ '.POSIX:' \
+ '.endfor'
+
+# If the main makefile has an '.if' conditional as its first non-comment line,
+# a strict reading of POSIX 2018 makes the makefile non-conforming.
+all: after-if
+after-if: .PHONY set-up-sysdir check-not-posix run
+ printf '%s\n' > ${MAIN_MK} \
+ '# comment' \
+ '' \
+ '.if 1' \
+ '.POSIX:' \
+ '.endif'
+
+# If the main makefile first includes another makefile and that included
+# makefile tries to switch to POSIX mode, that's too late.
+all: in-included-file
+in-included-file: .PHONY set-up-sysdir check-not-posix run
+ printf 'include included.mk\n' > ${MAIN_MK}
+ printf '.POSIX:\n' > ${INCLUDED_MK}
+
+# If the main makefile switches to POSIX mode in its very first line, before
+# and comment lines or empty lines, that works.
+all: in-first-line
+in-first-line: .PHONY set-up-sysdir check-is-posix run
+ printf '%s\n' > ${MAIN_MK} \
+ '.POSIX:'
+
+# The only allowed lines before switching to POSIX mode are comment lines.
+# POSIX defines comment lines as "blank lines, empty lines, and lines with
+# <number-sign> ('#') as the first character".
+all: after-comment-lines
+after-comment-lines: .PHONY set-up-sysdir check-is-posix run
+ printf '%s\n' > ${MAIN_MK} \
+ '# comment' \
+ '' \
+ '.POSIX:'
+
+# Running make with the option '-r' skips the builtin rules from <sys.mk>.
+# In that mode, '.POSIX:' just loads <posix.mk>, which works as well.
+MAKEFLAGS.no-builtins= -r
+all: no-builtins
+no-builtins: .PHONY set-up-sysdir check-is-posix check-not-seen-sys-mk run
+ printf '%s\n' > ${MAIN_MK} \
+ '.POSIX:'
diff --git a/contrib/bmake/unit-tests/deptgt-silent-jobs.exp b/contrib/bmake/unit-tests/deptgt-silent-jobs.exp
new file mode 100644
index 000000000000..4d8a73b36139
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-silent-jobs.exp
@@ -0,0 +1,7 @@
+compat: testing 1
+compat: testing 2
+compat: testing 3
+jobs: testing 1
+jobs: testing 2
+jobs: testing 3
+exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-silent-jobs.mk b/contrib/bmake/unit-tests/deptgt-silent-jobs.mk
new file mode 100644
index 000000000000..aed85932281b
--- /dev/null
+++ b/contrib/bmake/unit-tests/deptgt-silent-jobs.mk
@@ -0,0 +1,34 @@
+# $NetBSD: deptgt-silent-jobs.mk,v 1.3 2022/04/15 09:33:20 rillig Exp $
+#
+# Ensure that the special dependency target '.SILENT' only affects the amount
+# of output, but not the kind of error handling.
+#
+# History:
+# In job.c 1.83 from 2003.12.20.00.18.22, in an attempt to fix
+# https://gnats.netbsd.org/18573, commands that suppressed error
+# handling were output in jobs mode, even when the global '.SILENT'
+# was set. This was fixed in job.c 1.452 from 2022-02-12.
+#
+# See also:
+# https://gnats.netbsd.org/45356
+
+all: compat jobs
+.PHONY: all compat jobs test
+
+.SILENT:
+test:
+ @echo '${VARIANT}: testing 1'
+ -echo '${VARIANT}: testing 2'
+ echo '${VARIANT}: testing 3'
+
+# expect: compat: testing 1
+# expect: compat: testing 2
+# expect: compat: testing 3
+compat:
+ @${MAKE} -r -f ${MAKEFILE} test VARIANT=compat
+
+# expect: jobs: testing 1
+# expect: jobs: testing 2
+# expect: jobs: testing 3
+jobs:
+ @${MAKE} -r -f ${MAKEFILE} test VARIANT=jobs -j1
diff --git a/contrib/bmake/unit-tests/deptgt.exp b/contrib/bmake/unit-tests/deptgt.exp
index bdac2aee3e6c..90213fe44fb5 100644
--- a/contrib/bmake/unit-tests/deptgt.exp
+++ b/contrib/bmake/unit-tests/deptgt.exp
@@ -1,14 +1,17 @@
-make: "deptgt.mk" line 10: warning: Extra target ignored
-make: "deptgt.mk" line 28: Unassociated shell command ": command3 # parse error, since targets == NULL"
-ParseReadLine (34): '${:U}: empty-source'
+make: "deptgt.mk" line 11: warning: Extra target '.PHONY' ignored
+make: "deptgt.mk" line 30: Unassociated shell command ": command3 # parse error, since targets == NULL"
+Parsing line 36: ${:U}: empty-source
ParseDependency(: empty-source)
-ParseReadLine (35): ' : command for empty targets list'
-ParseReadLine (36): ': empty-source'
+Parsing line 37: : command for empty targets list
+Parsing line 38: : empty-source
ParseDependency(: empty-source)
-ParseReadLine (37): ' : command for empty targets list'
-ParseReadLine (38): '.MAKEFLAGS: -d0'
+Parsing line 39: : command for empty targets list
+Parsing line 40: .MAKEFLAGS: -d0
ParseDependency(.MAKEFLAGS: -d0)
-make: "deptgt.mk" line 46: Unknown modifier "Z"
+make: "deptgt.mk" line 49: while evaluating "${:U:Z}:": Unknown modifier "Z"
+make: "deptgt.mk" line 52: warning: Extra target 'ordinary' ignored
+make: "deptgt.mk" line 55: warning: Extra target (ordinary) ignored
+make: "deptgt.mk" line 58: warning: Special and mundane targets don't mix. Mundane ones ignored
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/deptgt.mk b/contrib/bmake/unit-tests/deptgt.mk
index 15d7e59aeced..eb948918abb7 100644
--- a/contrib/bmake/unit-tests/deptgt.mk
+++ b/contrib/bmake/unit-tests/deptgt.mk
@@ -1,4 +1,4 @@
-# $NetBSD: deptgt.mk,v 1.11 2021/04/04 10:13:09 rillig Exp $
+# $NetBSD: deptgt.mk,v 1.17 2024/04/20 10:18:55 rillig Exp $
#
# Tests for special targets like .BEGIN or .SUFFIXES in dependency
# declarations.
@@ -7,6 +7,7 @@
# Just in case anyone tries to compile several special targets in a single
# dependency line: That doesn't work, and make immediately rejects it.
+# expect+1: warning: Extra target '.PHONY' ignored
.SUFFIXES .PHONY: .c.o
# The following lines demonstrate how 'targets' is set and reset during
@@ -25,10 +26,11 @@ target1 target2: sources # targets := [target1, target2]
: command1 # targets == [target1, target2]
: command2 # targets == [target1, target2]
VAR=value # targets := NULL
+# expect+1: Unassociated shell command ": command3 # parse error, since targets == NULL"
: command3 # parse error, since targets == NULL
# In a dependency declaration, the list of targets can be empty.
-# It doesn't matter whether the empty string is generated by a variable
+# It doesn't matter whether the empty string is generated by an
# expression or whether it is just omitted.
.MAKEFLAGS: -dp
${:U}: empty-source
@@ -43,7 +45,17 @@ ${:U}: empty-source
# expansion would be to use the variable modifier '::=' to modify the
# targets. This in turn would be such an extreme and unreliable edge case
# that nobody uses it.
+# expect+1: while evaluating "${:U:Z}:": Unknown modifier "Z"
$$$$$$$${:U:Z}:
+# expect+1: warning: Extra target 'ordinary' ignored
+.END ordinary:
+
+# expect+1: warning: Extra target (ordinary) ignored
+.PATH ordinary:
+
+# expect+1: warning: Special and mundane targets don't mix. Mundane ones ignored
+ordinary .PATH:
+
all:
@:;
diff --git a/contrib/bmake/unit-tests/dir.mk b/contrib/bmake/unit-tests/dir.mk
index 36fe2baf978c..956285393489 100644
--- a/contrib/bmake/unit-tests/dir.mk
+++ b/contrib/bmake/unit-tests/dir.mk
@@ -1,8 +1,10 @@
-# $NetBSD: dir.mk,v 1.9 2021/01/23 10:48:49 rillig Exp $
+# $NetBSD: dir.mk,v 1.11 2023/12/19 19:33:40 rillig Exp $
#
# Tests for dir.c.
-.MAKEFLAGS: -m / # hide /usr/share/mk from the debug log
+# hide /usr/share/mk from the debug log
+.SYSPATH:
+.SYSPATH: /
# Dependency lines may use braces for expansion.
# See DirExpandCurly for the implementation.
@@ -65,7 +67,7 @@ fetch fetch-post extract extract-post:
# The expansions may have duplicates.
# When the source of the dependency line is expanded later, each of the
-# expanded words will be the same.
+# expanded words resolves to the same node.
all: dup-{1,1,1,1,1,1,1}
dup-1:
diff --git a/contrib/bmake/unit-tests/directive-dinclude.exp b/contrib/bmake/unit-tests/directive-dinclude.exp
index 39a9383953dd..8f71e42c0515 100755
--- a/contrib/bmake/unit-tests/directive-dinclude.exp
+++ b/contrib/bmake/unit-tests/directive-dinclude.exp
@@ -1 +1,4 @@
-exit status 0
+make: "directive-dinclude-error.inc" line 1: Invalid line 'syntax error'
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/directive-dinclude.mk b/contrib/bmake/unit-tests/directive-dinclude.mk
index 94fa5fb4429b..7fccff413cd6 100755
--- a/contrib/bmake/unit-tests/directive-dinclude.mk
+++ b/contrib/bmake/unit-tests/directive-dinclude.mk
@@ -1,9 +1,24 @@
-# $NetBSD: directive-dinclude.mk,v 1.1 2020/09/13 09:20:23 rillig Exp $
+# $NetBSD: directive-dinclude.mk,v 1.3 2023/08/19 10:52:13 rillig Exp $
#
# Tests for the .dinclude directive, which includes another file,
-# typically named .depend.
+# silently skipping it if it cannot be opened. This is primarily used for
+# including '.depend' files, that's where the 'd' comes from.
+#
+# The 'silently skipping' only applies to the case where the file cannot be
+# opened. Parse errors and other errors are handled the same way as in the
+# other .include directives.
+
+# No complaint that there is no such file.
+.dinclude "${.CURDIR}/directive-dinclude-nonexistent.inc"
+
+# No complaint either, even though the operating system error is ENOTDIR, not
+# ENOENT.
+.dinclude "${MAKEFILE}/subdir"
-# TODO: Implementation
+# Errors that are not related to opening the file are still reported.
+# expect: make: "directive-dinclude-error.inc" line 1: Invalid line 'syntax error'
+_!= echo 'syntax error' > directive-dinclude-error.inc
+.dinclude "${.CURDIR}/directive-dinclude-error.inc"
+_!= rm directive-dinclude-error.inc
-all:
- @:;
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/directive-elif.exp b/contrib/bmake/unit-tests/directive-elif.exp
index 6856494023d7..15dd6bf1430b 100644
--- a/contrib/bmake/unit-tests/directive-elif.exp
+++ b/contrib/bmake/unit-tests/directive-elif.exp
@@ -1,21 +1,21 @@
-make: "directive-elif.mk" line 47: Unknown directive "elsif"
-make: "directive-elif.mk" line 52: This branch is taken.
-make: "directive-elif.mk" line 60: Unknown directive "elsif"
-make: "directive-elif.mk" line 63: This branch is taken.
-make: "directive-elif.mk" line 69: This branch is taken.
-make: "directive-elif.mk" line 89: Unknown directive "elsif"
-make: "directive-elif.mk" line 90: This misspelling is detected.
-make: "directive-elif.mk" line 91: This branch is taken because of the .else.
-make: "directive-elif.mk" line 109: What happens on misspelling in a skipped branch?
-make: "directive-elif.mk" line 119: else
-make: "directive-elif.mk" line 122: What happens on misspelling in a taken branch?
-make: "directive-elif.mk" line 124: 1-then
-make: "directive-elif.mk" line 125: Unknown directive "elsif"
-make: "directive-elif.mk" line 126: 1-elsif
-make: "directive-elif.mk" line 127: Unknown directive "elsif"
-make: "directive-elif.mk" line 128: 2-elsif
-make: "directive-elif.mk" line 134: if-less elif
-make: "directive-elif.mk" line 139: warning: extra elif
+make: "directive-elif.mk" line 48: Unknown directive "elsif"
+make: "directive-elif.mk" line 54: This branch is taken.
+make: "directive-elif.mk" line 62: Unknown directive "elsif"
+make: "directive-elif.mk" line 66: This branch is taken.
+make: "directive-elif.mk" line 73: This branch is taken.
+make: "directive-elif.mk" line 94: Unknown directive "elsif"
+make: "directive-elif.mk" line 96: This misspelling is detected.
+make: "directive-elif.mk" line 98: This branch is taken because of the .else.
+make: "directive-elif.mk" line 117: What happens on misspelling in a skipped branch?
+make: "directive-elif.mk" line 128: else
+make: "directive-elif.mk" line 132: What happens on misspelling in a taken branch?
+make: "directive-elif.mk" line 135: 1-then
+make: "directive-elif.mk" line 137: Unknown directive "elsif"
+make: "directive-elif.mk" line 139: 1-elsif
+make: "directive-elif.mk" line 141: Unknown directive "elsif"
+make: "directive-elif.mk" line 143: 2-elsif
+make: "directive-elif.mk" line 149: if-less elif
+make: "directive-elif.mk" line 154: warning: extra elif
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/directive-elif.mk b/contrib/bmake/unit-tests/directive-elif.mk
index e7b89beec4e9..d6500cc1e872 100644
--- a/contrib/bmake/unit-tests/directive-elif.mk
+++ b/contrib/bmake/unit-tests/directive-elif.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-elif.mk,v 1.7 2020/12/19 19:49:01 rillig Exp $
+# $NetBSD: directive-elif.mk,v 1.8 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the .elif directive.
#
@@ -44,28 +44,32 @@
# Misspelling '.elsif' below an .if branch that is taken.
.if 1
# This misspelling is in an active branch and is therefore detected.
+# expect+1: Unknown directive "elsif"
.elsif 0
# The only thing that make detects here is a misspelled directive, make
# doesn't recognize that it was meant to be a conditional directive.
# Therefore the branch continues here, even though the '.elsif' condition
# evaluates to false.
+# expect+1: This branch is taken.
. info This branch is taken.
.endif
# Misspelling '.elsif' below an .if branch that is taken.
.if 1
-# As of 2020-12-19, the misspelling is in an active branch and is therefore
-# detected.
+# The misspelling is in an active branch and is therefore detected.
+# expect+1: Unknown directive "elsif"
.elsif 1
# Since both conditions evaluate to true, this branch is taken no matter
# whether make detects a misspelling or not.
+# expect+1: This branch is taken.
. info This branch is taken.
.endif
# Misspelling '.elsif' in a skipped branch below a branch that was taken.
.if 1
+# expect+1: This branch is taken.
. info This branch is taken.
.elif 0
. info This branch is not taken.
@@ -86,8 +90,11 @@
# Misspelling '.elsif' in an .else branch that is taken.
.if 0
.else
+# expect+1: Unknown directive "elsif"
.elsif 1
+# expect+1: This misspelling is detected.
. info This misspelling is detected.
+# expect+1: This branch is taken because of the .else.
. info This branch is taken because of the .else.
.endif
@@ -106,6 +113,7 @@
.endif
+# expect+1: What happens on misspelling in a skipped branch?
.info What happens on misspelling in a skipped branch?
.if 0
. info 0-then
@@ -116,26 +124,33 @@
. info XXX: This misspelling is not detected.
. info 2-elsif
.else
+# expect+1: else
. info else
.endif
+# expect+1: What happens on misspelling in a taken branch?
.info What happens on misspelling in a taken branch?
.if 1
+# expect+1: 1-then
. info 1-then
+# expect+1: Unknown directive "elsif"
.elsif 1
+# expect+1: 1-elsif
. info 1-elsif
+# expect+1: Unknown directive "elsif"
.elsif 2
+# expect+1: 2-elsif
. info 2-elsif
.else
. info else
.endif
-# Expect: "if-less elif"
+# expect+1: if-less elif
.elif 0
.if 1
.else
-# Expect: "warning: extra elif"
+# expect+1: warning: extra elif
.elif
.endif
diff --git a/contrib/bmake/unit-tests/directive-elifdef.mk b/contrib/bmake/unit-tests/directive-elifdef.mk
index f960c1513e8e..e835bccc0d40 100644
--- a/contrib/bmake/unit-tests/directive-elifdef.mk
+++ b/contrib/bmake/unit-tests/directive-elifdef.mk
@@ -1,8 +1,21 @@
-# $NetBSD: directive-elifdef.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: directive-elifdef.mk,v 1.4 2022/02/09 21:09:24 rillig Exp $
#
-# Tests for the .elifdef directive.
+# Tests for the .elifdef directive, which is seldom used. Instead of writing
+# '.elifdef VAR', the usual form is the more general '.elif defined(VAR)'.
-# TODO: Implementation
+# At this point, VAR is not defined, so the condition evaluates to false.
+.if 0
+.elifdef VAR
+. error
+.endif
+
+VAR= # defined
+
+# At this point, VAR is defined, so the condition evaluates to true.
+.if 0
+.elifdef VAR
+.else
+. error
+.endif
all:
- @:;
diff --git a/contrib/bmake/unit-tests/directive-elifndef.mk b/contrib/bmake/unit-tests/directive-elifndef.mk
index 19bb66c11b01..87aaf2fdd9ac 100644
--- a/contrib/bmake/unit-tests/directive-elifndef.mk
+++ b/contrib/bmake/unit-tests/directive-elifndef.mk
@@ -1,8 +1,23 @@
-# $NetBSD: directive-elifndef.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: directive-elifndef.mk,v 1.3 2022/01/22 21:50:41 rillig Exp $
#
-# Tests for the .elifndef directive.
+# Tests for the .elifndef directive, which is an obscure form of writing the
+# more usual '.elif !defined(VAR)'.
-# TODO: Implementation
+# At this point, VAR is not yet defined, and due to the 'n' in 'elifndef' the
+# condition evaluates to true.
+.if 0
+.elifndef VAR && VAR || VAR
+.else
+. error
+.endif
+
+VAR= # defined
+
+# At this point, VAR is defined, and due to the 'n' in 'elifndef' the
+# condition evaluates to false.
+.if 0
+.elifndef VAR && VAR || VAR
+. error
+.endif
all:
- @:;
diff --git a/contrib/bmake/unit-tests/directive-else.exp b/contrib/bmake/unit-tests/directive-else.exp
index 138e893ffa88..992b60e9308b 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 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 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 14: The .else directive does not take arguments
+make: "directive-else.mk" line 16: ok
+make: "directive-else.mk" line 21: ok
+make: "directive-else.mk" line 23: The .else directive does not take arguments
+make: "directive-else.mk" line 29: if-less else
+make: "directive-else.mk" line 36: ok
+make: "directive-else.mk" line 38: warning: extra else
+make: "directive-else.mk" line 51: 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-else.mk b/contrib/bmake/unit-tests/directive-else.mk
index 794057110ef7..cda671907217 100644
--- a/contrib/bmake/unit-tests/directive-else.mk
+++ b/contrib/bmake/unit-tests/directive-else.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-else.mk,v 1.7 2020/12/14 22:17:11 rillig Exp $
+# $NetBSD: directive-else.mk,v 1.9 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the .else directive.
#
@@ -10,26 +10,31 @@
.if 0
. warning must not be reached
-# The .else directive does not take any arguments.
+# expect+1: The .else directive does not take arguments
.else 123
+# expect+1: ok
. info ok
.endif
.if 1
+# expect+1: ok
. info ok
-# The .else directive does not take any arguments.
+# expect+1: The .else directive does not take arguments
.else 123
. warning must not be reached
.endif
# An .else without a corresponding .if is an error.
+# expect+1: if-less else
.else
# Accidental extra .else directives are detected too.
.if 0
. warning must not be reached
.else
+# expect+1: ok
. info ok
+# expect+1: warning: extra else
.else
. info After an extra .else, everything is skipped.
.endif
@@ -40,8 +45,9 @@
.else # comment
.endif
-# A variable expression does count as an argument, even if it is empty.
+# An expression does count as an argument, even if it is empty.
.if 0
+# expect+1: The .else directive does not take arguments
.else ${:U}
.endif
diff --git a/contrib/bmake/unit-tests/directive-endfor.exp b/contrib/bmake/unit-tests/directive-endfor.exp
index 7e243a8f67e6..8b77c0b31307 100644
--- a/contrib/bmake/unit-tests/directive-endfor.exp
+++ b/contrib/bmake/unit-tests/directive-endfor.exp
@@ -1,4 +1,4 @@
-make: "directive-endfor.mk" line 9: for-less endfor
+make: "directive-endfor.mk" line 10: for-less endfor
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/directive-endfor.mk b/contrib/bmake/unit-tests/directive-endfor.mk
index b0c37f388504..93119156fb89 100644
--- a/contrib/bmake/unit-tests/directive-endfor.mk
+++ b/contrib/bmake/unit-tests/directive-endfor.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-endfor.mk,v 1.1 2020/12/30 14:50:08 rillig Exp $
+# $NetBSD: directive-endfor.mk,v 1.2 2023/06/01 20:56:35 rillig Exp $
#
# Test for the directive .endfor, which ends a .for loop.
#
@@ -6,4 +6,5 @@
# directive-for.mk
# An .endfor without a corresponding .for is a parse error.
+# expect+1: for-less endfor
.endfor
diff --git a/contrib/bmake/unit-tests/directive-endif.exp b/contrib/bmake/unit-tests/directive-endif.exp
index 286d85244eae..c88cc97c44c5 100644
--- a/contrib/bmake/unit-tests/directive-endif.exp
+++ b/contrib/bmake/unit-tests/directive-endif.exp
@@ -1,8 +1,8 @@
-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: "directive-endif.mk" line 16: The .endif directive does not take arguments
+make: "directive-endif.mk" line 21: The .endif directive does not take arguments
+make: "directive-endif.mk" line 32: 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 44: Unknown directive "endifx"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/directive-endif.mk b/contrib/bmake/unit-tests/directive-endif.mk
index 10dd6ce22ce8..5e01382af5f2 100644
--- a/contrib/bmake/unit-tests/directive-endif.mk
+++ b/contrib/bmake/unit-tests/directive-endif.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-endif.mk,v 1.5 2020/12/14 21:56:17 rillig Exp $
+# $NetBSD: directive-endif.mk,v 1.7 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the .endif directive.
#
@@ -8,18 +8,16 @@
# See also:
# Cond_EvalLine
-# TODO: Implementation
-
.MAKEFLAGS: -dL
-# Error: .endif does not take arguments
.if 0
-# Since 2020-12-15, complain about the extra text after the 'endif'.
+# Since 2020-12-15:
+# expect+1: The .endif directive does not take arguments
.endif 0
-# Error: .endif does not take arguments
.if 1
-# Since 2020-12-15, complain about the extra text after the 'endif'.
+# Since 2020-12-15:
+# expect+1: The .endif directive does not take arguments
.endif 1
# Comments are allowed after an '.endif'.
@@ -29,21 +27,19 @@
# Only whitespace and comments are allowed after an '.endif', but nothing
# else.
.if 1
-# Since 2020-12-15, complain about the extra text after the 'endif'.
+# Since 2020-12-15:
+# expect+1: The .endif directive does not take arguments
.endif0
# Only whitespace and comments are allowed after an '.endif', but nothing
# else.
.if 1
-# Since 2020-12-15, complain about the extra text after the 'endif'.
+# Since 2020-12-15:
+# expect+1: The .endif directive does not take arguments
.endif/
-# After an '.endif', no other letter must occur. This 'endifx' is not
-# parsed as an 'endif', therefore another '.endif' must follow to balance
-# the directives.
+# After an '.endif', no other letter must occur.
.if 1
+# expect+1: Unknown directive "endifx"
.endifx
-.endif # to close the preceding '.if'
-
-all:
- @:;
+.endif # to close the preceding '.if'
diff --git a/contrib/bmake/unit-tests/directive-error.exp b/contrib/bmake/unit-tests/directive-error.exp
index bad12326a514..8ce33160e67f 100644
--- a/contrib/bmake/unit-tests/directive-error.exp
+++ b/contrib/bmake/unit-tests/directive-error.exp
@@ -1,4 +1,4 @@
-make: "directive-error.mk" line 13: message
+make: "directive-error.mk" line 14: message
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/directive-error.mk b/contrib/bmake/unit-tests/directive-error.mk
index 135db2159dd2..f35a9da3a018 100644
--- a/contrib/bmake/unit-tests/directive-error.mk
+++ b/contrib/bmake/unit-tests/directive-error.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-error.mk,v 1.5 2021/01/27 00:02:38 rillig Exp $
+# $NetBSD: directive-error.mk,v 1.6 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the .error directive, which prints an error message and exits
# immediately, unlike other "fatal" parse errors, which continue to parse
@@ -10,4 +10,5 @@
# Before parse.c 1.532 from 2021-01-27, the ".error" issued an irrelevant
# message saying "parsing warnings being treated as errors".
.MAKEFLAGS: -W
+# expect+1: message
.error message
diff --git a/contrib/bmake/unit-tests/directive-export-gmake.exp b/contrib/bmake/unit-tests/directive-export-gmake.exp
index 39a9383953dd..c37d3b2d8591 100644
--- a/contrib/bmake/unit-tests/directive-export-gmake.exp
+++ b/contrib/bmake/unit-tests/directive-export-gmake.exp
@@ -1 +1,6 @@
-exit status 0
+make: "directive-export-gmake.mk" line 71: Invalid line 'export VAR=${:U1}', expanded to 'export VAR=1'
+make: "directive-export-gmake.mk" line 85: 16:00:00
+make: "directive-export-gmake.mk" line 92: Variable/Value missing from "export"
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/directive-export-gmake.mk b/contrib/bmake/unit-tests/directive-export-gmake.mk
index d94cd9debf64..de79470bf305 100644
--- a/contrib/bmake/unit-tests/directive-export-gmake.mk
+++ b/contrib/bmake/unit-tests/directive-export-gmake.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-export-gmake.mk,v 1.3 2020/11/17 20:16:44 rillig Exp $
+# $NetBSD: directive-export-gmake.mk,v 1.9 2023/12/17 09:44:00 rillig Exp $
#
# Tests for the export directive (without leading dot), as in GNU make.
@@ -60,5 +60,47 @@ export VAR=an ${UNDEF} variable
. error
.endif
-all:
- @:;
+
+# The body of the .for loop expands to 'export VAR=${:U1}', and the 'export'
+# directive is only recognized if the line does not contain a ':', to allow
+# 'export' to be a regular target.
+.for value in 1
+# XXX: The ':' in this line is inside an expression and should thus not be
+# interpreted as a dependency operator.
+# expect+1: Invalid line 'export VAR=${:U1}', expanded to 'export VAR=1'
+export VAR=${value}
+.endfor
+
+
+# The 'export' directive expands expressions, but the expressions must not
+# contain a ':', due to the overly strict parser. The indirect expressions
+# may contain a ':', though.
+#
+# As a side effect, this test demonstrates that the 'export' directive exports
+# the environment variable immediately, other than the '.export' directive,
+# which defers that action if the variable value contains a '$'.
+INDIRECT_TZ= ${:UAmerica/Los_Angeles}
+export TZ=${INDIRECT_TZ}
+# expect+1: 16:00:00
+.info ${%T:L:localtime=86400}
+
+
+# The '=' must be present in the unexpanded line, it cannot be generated by
+# an expression.
+EQ= =
+# expect+1: Variable/Value missing from "export"
+export EQ_VAR${EQ}eq-value
+.if ${:!env!:MEQ_VAR=*}
+. error
+.endif
+
+
+# The variable name must be given directly, it is not expanded. The name of
+# the exported variable thus starts with a '$', and that name may be filtered
+# out by the platform.
+INDIRECT_NAME= I_NAME
+INDIRECT_VALUE= indirect value
+export ${INDIRECT_NAME}=${INDIRECT_VALUE}
+.if ${:!env!:MI_NAME=*}
+. error
+.endif
diff --git a/contrib/bmake/unit-tests/directive-export-impl.exp b/contrib/bmake/unit-tests/directive-export-impl.exp
index 740daa605129..7daf1f45b0cb 100644
--- a/contrib/bmake/unit-tests/directive-export-impl.exp
+++ b/contrib/bmake/unit-tests/directive-export-impl.exp
@@ -1,19 +1,21 @@
-ParseReadLine (21): 'UT_VAR= <${REF}>'
+Parsing line 21: UT_VAR= <${REF}>
Global: UT_VAR = <${REF}>
-ParseReadLine (28): '.export UT_VAR'
+Parsing line 28: .export UT_VAR
Global: .MAKE.EXPORTED = UT_VAR
-ParseReadLine (32): ': ${UT_VAR:N*}'
+Parsing line 32: : ${UT_VAR:N*}
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(: )
+Parsing line 42: .if ${:!echo "\$UT_VAR"!} != "<>"
CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<>"
Var_Parse: ${:!echo "\$UT_VAR"!} != "<>" (eval-defined)
Evaluating modifier ${:!...} on value "" (eval-defined, undefined)
Modifier part: "echo "$UT_VAR""
+Capturing the output of command "echo "$UT_VAR""
Var_Parse: ${.MAKE.EXPORTED:O:u} (eval)
Evaluating modifier ${.MAKE.EXPORTED:O} on value "UT_VAR"
Result of ${.MAKE.EXPORTED:O} is "UT_VAR"
@@ -22,21 +24,23 @@ Result of ${.MAKE.EXPORTED:u} is "UT_VAR"
Var_Parse: ${UT_VAR} (eval)
Var_Parse: ${REF}> (eval)
Result of ${:!echo "\$UT_VAR"!} is "<>" (eval-defined, defined)
-lhs = "<>", rhs = "<>", op = !=
-ParseReadLine (50): ': ${UT_VAR:N*}'
+Comparing "<>" != "<>"
+Parsing line 50: : ${UT_VAR:N*}
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'
+Parsing line 54: REF= defined
Global: REF = defined
+Parsing line 58: .if ${:!echo "\$UT_VAR"!} != "<defined>"
CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<defined>"
Var_Parse: ${:!echo "\$UT_VAR"!} != "<defined>" (eval-defined)
Evaluating modifier ${:!...} on value "" (eval-defined, undefined)
Modifier part: "echo "$UT_VAR""
+Capturing the output of command "echo "$UT_VAR""
Var_Parse: ${.MAKE.EXPORTED:O:u} (eval)
Evaluating modifier ${.MAKE.EXPORTED:O} on value "UT_VAR"
Result of ${.MAKE.EXPORTED:O} is "UT_VAR"
@@ -45,11 +49,11 @@ Result of ${.MAKE.EXPORTED:u} is "UT_VAR"
Var_Parse: ${UT_VAR} (eval)
Var_Parse: ${REF}> (eval)
Result of ${:!echo "\$UT_VAR"!} is "<defined>" (eval-defined, defined)
-lhs = "<defined>", rhs = "<defined>", op = !=
-ParseReadLine (62): 'all:'
+Comparing "<defined>" != "<defined>"
+Parsing line 62: all:
ParseDependency(all:)
Global: .ALLTARGETS = all
-ParseReadLine (63): '.MAKEFLAGS: -d0'
+Parsing line 63: .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/directive-export.exp b/contrib/bmake/unit-tests/directive-export.exp
index 39a9383953dd..774a814570e3 100644
--- a/contrib/bmake/unit-tests/directive-export.exp
+++ b/contrib/bmake/unit-tests/directive-export.exp
@@ -1 +1,4 @@
+make: "directive-export.mk" line 56: 00:00:00
+make: "directive-export.mk" line 61: 00:00:00
+make: "directive-export.mk" line 64: 16:00:00
exit status 0
diff --git a/contrib/bmake/unit-tests/directive-export.mk b/contrib/bmake/unit-tests/directive-export.mk
index 942d4b371bbd..08109814bcfd 100644
--- a/contrib/bmake/unit-tests/directive-export.mk
+++ b/contrib/bmake/unit-tests/directive-export.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-export.mk,v 1.8 2021/02/16 19:01:18 rillig Exp $
+# $NetBSD: directive-export.mk,v 1.10 2023/11/19 09:45:19 rillig Exp $
#
# Tests for the .export directive.
#
@@ -35,10 +35,33 @@ VAR= value $$ ${INDIRECT}
.export ${:U}
-# Trigger the "This isn't going to end well" in ExportVarEnv.
+# Before a child process is started, whether for the '!=' assignment operator
+# or for the ':sh' modifier, all variables that were marked for being exported
+# are expanded and then exported. If expanding such a variable requires
+# running a child command, the marked-as-exported variables would need to be
+# exported first, ending in an endless loop. To avoid this endless loop,
+# don't export the variables while preparing a child process, see
+# ExportVarEnv.
EMPTY_SHELL= ${:sh}
.export EMPTY_SHELL # only marked for export at this point
_!= :;: # Force the variable to be actually exported.
+# If the '.export' directive exports a variable whose value contains a '$',
+# the actual export action is deferred until a subprocess is started, assuming
+# that only subprocesses access the environment variables. The ':localtime'
+# modifier depends on the 'TZ' environment variable, without any subprocess.
+export TZ=${UTC}
+# expect+1: 00:00:00
+.info ${%T:L:localtime=86400}
+INDIRECT_TZ= ${:UAmerica/Los_Angeles}
+TZ= ${INDIRECT_TZ}
+.export TZ
+# expect+1: 00:00:00
+.info ${%T:L:localtime=86400}
+_!= echo 'force exporting the environment variables'
+# expect+1: 16:00:00
+.info ${%T:L:localtime=86400}
+
+
all:
diff --git a/contrib/bmake/unit-tests/directive-for-break.exp b/contrib/bmake/unit-tests/directive-for-break.exp
new file mode 100644
index 000000000000..64941448141c
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-for-break.exp
@@ -0,0 +1,5 @@
+make: "directive-for-break.mk" line 45: break outside of for loop
+make: "directive-for-break.mk" line 65: The .break 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-for-break.mk b/contrib/bmake/unit-tests/directive-for-break.mk
new file mode 100644
index 000000000000..d9290d38c215
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-for-break.mk
@@ -0,0 +1,66 @@
+# $NetBSD: directive-for-break.mk,v 1.5 2023/06/01 20:56:35 rillig Exp $
+#
+# Tests for .break in .for loops, which immediately terminates processing of
+# the surrounding .for loop.
+
+
+# .break terminates the loop early.
+# This is usually done within a conditional.
+.for i in 1 2 3 4 5 6 7 8
+. if $i == 3
+I= $i
+. break
+I= unreached
+. endif
+.endfor
+.if $I != "3"
+. error
+.endif
+
+
+# The .break only breaks out of the immediately surrounding .for loop, any
+# other .for loops are continued normally.
+.for outer in o1 o2 o3
+. for inner in i1 i2 i3
+. if ${outer} == o2 && ${inner} == i2
+. break
+. endif
+COMBINED+= ${outer}-${inner}
+. endfor
+.endfor
+# Only o2-i2 and o2-i3 are missing.
+.if ${COMBINED} != "o1-i1 o1-i2 o1-i3 o2-i1 o3-i1 o3-i2 o3-i3"
+. error
+.endif
+
+
+# A .break outside the context of a .for loop is an error.
+.if $I == 0
+# No parse error, even though the .break occurs outside a .for loop, since
+# lines from inactive branches are only parsed as far as necessary to see
+# whether they belong to an .if/.elif/.else/.endif chain.
+. break
+.else
+# expect+1: break outside of for loop
+. break
+.endif
+
+
+# Since cond.c 1.335 from 2022-09-02 and before cond.c 1.338 from 2022-09-23,
+# the following paragraph generated the wrong error message '4294967294 open
+# conditionals'.
+.if 1
+. if 2
+. for var in value
+. if 3
+. break
+. endif
+. endfor
+. endif
+.endif
+
+
+.for i in 1
+# expect+1: The .break directive does not take arguments
+. break 1
+.endfor
diff --git a/contrib/bmake/unit-tests/directive-for-empty.exp b/contrib/bmake/unit-tests/directive-for-empty.exp
new file mode 100644
index 000000000000..5cc3ac846b36
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-for-empty.exp
@@ -0,0 +1,27 @@
+make: "directive-for-empty.mk" line 22: 2
+make: "directive-for-empty.mk" line 38: Missing argument for ".error"
+make: "directive-for-empty.mk" line 38: Missing argument for ".error"
+make: "directive-for-empty.mk" line 38: Missing argument for ".error"
+For: end for 1
+For: loop body with i = value:
+# The identifier 'empty' can only be used in conditions such as .if, .ifdef or
+# .elif. In other lines the string 'empty(' must be preserved.
+CPPFLAGS+= -Dmessage="empty(i)"
+# There may be whitespace between 'empty' and '('.
+.if ! empty (i)
+. error
+.endif
+# Even in conditions, the string 'empty(' is not always a function call, it
+# can occur in a string literal as well.
+.if "empty\(i)" != "empty(i)"
+. error
+.endif
+# In comments like 'empty(i)', the text must be preserved as well.
+#
+# Conditions, including function calls to 'empty', can not only occur in
+# condition directives, they can also occur in the modifier ':?', see
+# varmod-ifelse.mk.
+CPPFLAGS+= -Dmacro="${empty(i):?empty:not-empty}"
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/directive-for-empty.mk b/contrib/bmake/unit-tests/directive-for-empty.mk
new file mode 100644
index 000000000000..1c4cb0f1ad27
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-for-empty.mk
@@ -0,0 +1,124 @@
+# $NetBSD: directive-for-empty.mk,v 1.3 2023/11/19 21:47:52 rillig Exp $
+#
+# Tests for .for loops containing conditions of the form 'empty(var:...)'.
+#
+# When a .for loop is expanded, expressions in the body of the loop
+# are replaced with expressions containing the variable values. This
+# replacement is a bit naive but covers most of the practical cases. The one
+# popular exception is the condition 'empty(var:Modifiers)', which does not
+# look like an expression and is thus not replaced.
+#
+# See also:
+# https://gnats.netbsd.org/43821
+
+
+# In the body of the .for loop, the expression '${i:M*2*}' is replaced with
+# '${:U11:M*2*}', '${:U12:M*2*}', '${:U13:M*2*}', one after another. This
+# replacement creates the impression that .for variables were real variables,
+# when in fact they aren't.
+.for i in 11 12 13
+. if ${i:M*2*}
+# expect+1: 2
+.info 2
+. endif
+.endfor
+
+
+# In conditions, the function call to 'empty' does not look like an
+# expression, therefore it is not replaced. Since there is no global variable
+# named 'i', this expression makes for a leaky abstraction. If the .for
+# variables were real variables, calling 'empty' would work on them as well.
+.for i in 11 12 13
+# Asking for an empty iteration variable does not make sense as the .for loop
+# splits the iteration items into words, and such a word cannot be empty.
+. if empty(i)
+# expect+3: Missing argument for ".error"
+# expect+2: Missing argument for ".error"
+# expect+1: Missing argument for ".error"
+. error # due to the leaky abstraction
+. endif
+# The typical way of using 'empty' with variables from .for loops is pattern
+# matching using the modifiers ':M' or ':N'.
+. if !empty(i:M*2*)
+. if ${i} != "12"
+. error
+. endif
+. endif
+.endfor
+
+
+# The idea of replacing every occurrences of 'empty(i' in the body of a .for
+# loop would be naive and require many special cases, as there are many cases
+# that need to be considered when deciding whether the token 'empty' is a
+# function call or not, as demonstrated by the following examples. For
+# expressions like '${i:Modifiers}', this is simpler as a single
+# dollar almost always starts an expression. For counterexamples and
+# edge cases, see directive-for-escape.mk. Adding another such tricky detail
+# is out of the question.
+.MAKEFLAGS: -df
+.for i in value
+# The identifier 'empty' can only be used in conditions such as .if, .ifdef or
+# .elif. In other lines the string 'empty(' must be preserved.
+CPPFLAGS+= -Dmessage="empty(i)"
+# There may be whitespace between 'empty' and '('.
+.if ! empty (i)
+. error
+.endif
+# Even in conditions, the string 'empty(' is not always a function call, it
+# can occur in a string literal as well.
+.if "empty\(i)" != "empty(i)"
+. error
+.endif
+# In comments like 'empty(i)', the text must be preserved as well.
+#
+# Conditions, including function calls to 'empty', can not only occur in
+# condition directives, they can also occur in the modifier ':?', see
+# varmod-ifelse.mk.
+CPPFLAGS+= -Dmacro="${empty(i):?empty:not-empty}"
+.endfor
+.MAKEFLAGS: -d0
+
+
+# An idea to work around the above problems is to collect the variables from
+# the .for loops in a separate scope. To match the current behavior, there
+# has to be one scope per included file. There may be .for loops using the
+# same variable name in files that include each other:
+#
+# outer.mk: .for i in outer
+# . info $i # outer
+# . include "inner.mk"
+# inner.mk: . info $i # (undefined)
+# . for i in inner
+# . info $i # inner
+# . endfor
+# . info $i # (undefined)
+# outer.mk: . info $i # outer
+# .endfor
+#
+# This might be regarded another leaky abstraction, but it is in fact useful
+# that variables from .for loops can only affect expressions in the current
+# file. If variables from .for loops were implemented as global variables,
+# they might interact between files.
+#
+# To emulate this exact behavior for the function 'empty', each file in the
+# stack of included files needs its own scope that is independent from the
+# other files.
+#
+# Another tricky detail are nested .for loops in a single file that use the
+# same variable name. These are generally avoided by developers, as they
+# would be difficult to understand for humans as well. Technically, they are
+# possible though. Assuming there are two nested .for loops, both using the
+# variable 'i'. When the inner .for loop ends, the inner 'i' needs to be
+# removed from the scope, which would need to make the outer 'i' visible
+# again. This would suggest to use one variable scope per .for loop.
+#
+# Using a separate scope has the benefit that Var_Parse already allows for
+# a custom scope to be passed as parameter. This would have another side
+# effect though. There are several modifiers that actually modify variables,
+# and these modifications happen in the scope that is passed to Var_Parse.
+# This would mean that the combination of a .for variable and the modifiers
+# '::=', '::+=', '::?=', '::!=' and ':_' would lead to different behavior than
+# before.
+
+# TODO: Add code that demonstrates the current interaction between variables
+# from .for loops and the modifiers mentioned above.
diff --git a/contrib/bmake/unit-tests/directive-for-errors.exp b/contrib/bmake/unit-tests/directive-for-errors.exp
index da5eee473ec2..9c1aa5c0b1ed 100644
--- a/contrib/bmake/unit-tests/directive-for-errors.exp
+++ b/contrib/bmake/unit-tests/directive-for-errors.exp
@@ -1,22 +1,17 @@
-make: "directive-for-errors.mk" line 7: Unknown directive "fori"
-make: "directive-for-errors.mk" line 8: warning:
-make: "directive-for-errors.mk" line 9: for-less endfor
-make: "directive-for-errors.mk" line 19: Unknown directive "for"
-make: "directive-for-errors.mk" line 20: warning:
-make: "directive-for-errors.mk" line 21: for-less endfor
-make: "directive-for-errors.mk" line 37: Dollar $ 1 1 and backslash 2 2 2.
-make: "directive-for-errors.mk" line 37: Dollar $ 3 3 and backslash 4 4 4.
-make: "directive-for-errors.mk" line 43: no iteration variables in for
-make: "directive-for-errors.mk" line 47: warning: Should not be reached.
-make: "directive-for-errors.mk" line 48: for-less endfor
-make: "directive-for-errors.mk" line 53: Wrong number of words (5) in .for substitution list with 3 variables
-make: "directive-for-errors.mk" line 64: missing `in' in for
-make: "directive-for-errors.mk" line 66: warning: Should not be reached.
-make: "directive-for-errors.mk" line 67: for-less endfor
-make: "directive-for-errors.mk" line 73: Unknown modifier "Z"
-make: "directive-for-errors.mk" line 74: warning: Should not be reached.
-make: "directive-for-errors.mk" line 74: warning: Should not be reached.
-make: "directive-for-errors.mk" line 74: warning: Should not be reached.
+make: "directive-for-errors.mk" line 9: Unknown directive "fori"
+make: "directive-for-errors.mk" line 10: warning: <>
+make: "directive-for-errors.mk" line 11: for-less endfor
+make: "directive-for-errors.mk" line 25: Unknown directive "for"
+make: "directive-for-errors.mk" line 26: warning: <>
+make: "directive-for-errors.mk" line 27: for-less endfor
+make: "directive-for-errors.mk" line 44: invalid character '$' in .for loop variable name
+make: "directive-for-errors.mk" line 52: no iteration variables in for
+make: "directive-for-errors.mk" line 64: Wrong number of words (5) in .for substitution list with 3 variables
+make: "directive-for-errors.mk" line 78: missing `in' in for
+make: "directive-for-errors.mk" line 89: while evaluating "${:U3:Z} 4": Unknown modifier "Z"
+make: "directive-for-errors.mk" line 90: warning: Should not be reached.
+make: "directive-for-errors.mk" line 90: warning: Should not be reached.
+make: "directive-for-errors.mk" line 90: warning: Should not be reached.
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/directive-for-errors.mk b/contrib/bmake/unit-tests/directive-for-errors.mk
index 602ecbf32e4e..1bd4f31d383a 100644
--- a/contrib/bmake/unit-tests/directive-for-errors.mk
+++ b/contrib/bmake/unit-tests/directive-for-errors.mk
@@ -1,45 +1,54 @@
-# $NetBSD: directive-for-errors.mk,v 1.3 2021/04/04 10:13:09 rillig Exp $
+# $NetBSD: directive-for-errors.mk,v 1.10 2024/04/20 10:18:55 rillig Exp $
#
# Tests for error handling in .for loops.
+
# A .for directive must be followed by whitespace, everything else results
# in a parse error.
+# expect+1: Unknown directive "fori"
.fori in 1 2 3
-. warning ${i}
+. warning <${i}>
.endfor
+# expect-2: warning: <>
+# expect-2: for-less endfor
+
# A slash is not whitespace, therefore this is not parsed as a .for loop.
#
# XXX: The error message is misleading though. As of 2020-12-31, it says
-# "Unknown directive "for"", but that directive is actually known. This is
+# 'Unknown directive "for"', but that directive is actually known. This is
# because ForEval does not detect the .for loop as such, so parsing
# continues in ParseLine > ParseDependencyLine > ParseDependency >
# ParseDependencyTargets > ParseErrorNoDependency, and there the directive
# name is parsed a bit differently.
+# expect+1: Unknown directive "for"
.for/i in 1 2 3
-. warning ${i}
+. warning <${i}>
.endfor
+# expect-2: warning: <>
+# expect-2: for-less endfor
-# As of 2020-12-31, the variable name can be an arbitrary word, it just needs
-# to be separated by whitespace. Even '$' and '\' are valid variable names,
-# which is not useful in practice.
-#
-# The '$$' is not replaced with the values '1' or '3' from the .for loop,
-# instead it is kept as-is, and when the .info directive expands its argument,
-# each '$$' gets replaced with a single '$'. The "long variable expression"
-# ${$} gets replaced though, even though this would be a parse error everywhere
-# outside a .for loop.
+
+# Before for.c 1.173 from 2023-05-08, the variable name could be an arbitrary
+# word, it only needed to be separated by whitespace. Even '$' and '\' were
+# valid variable names, which was not useful in practice.
#
-# The '\' on the other hand is treated as a normal variable name.
+# The '$$' was not replaced with the values '1' or '3' from the .for loop,
+# instead it was kept as-is, and when the .info directive expanded its
+# argument, each '$$' got replaced with a single '$'. The "long
+# expression" ${$} got replaced though, even though this would be a parse
+# error everywhere outside a .for loop.
${:U\$}= dollar # see whether the "variable" '$' is local
${:U\\}= backslash # see whether the "variable" '\' is local
+# expect+1: invalid character '$' in .for loop variable name
.for $ \ in 1 2 3 4
. info Dollar $$ ${$} $($) and backslash $\ ${\} $(\).
.endfor
# If there are no variables, there is no point in expanding the .for loop
-# since this would end up in an endless loop, each time consuming 0 of the
-# 3 values.
+# since this would end up in an endless loop, consuming 0 of the 3 values in
+# each iteration.
+# expect+1: no iteration variables in for
.for in 1 2 3
# XXX: This should not be reached. It should be skipped, as already done
# when the number of values is not a multiple of the number of variables,
@@ -47,29 +56,39 @@ ${:U\\}= backslash # see whether the "variable" '\' is local
. warning Should not be reached.
.endfor
+
# There are 3 variables and 5 values. These 5 values cannot be split evenly
# among the variables, therefore the loop is not expanded at all, it is
-# rather skipped.
+# skipped instead.
+# expect+1: Wrong number of words (5) in .for substitution list with 3 variables
.for a b c in 1 2 3 4 5
. warning Should not be reached.
.endfor
+
# The list of values after the 'in' may be empty, no matter if this emptiness
-# comes from an empty expansion or even from a syntactically empty line.
+# comes from an expanded expression or from a syntactically empty line.
.for i in
. info Would be reached if there were items to loop over.
.endfor
+
# A missing 'in' should parse the .for loop but skip the body.
-.for i : k
+# expect+1: missing `in' in for
+.for i over k
# XXX: As of 2020-12-31, this line is reached once.
. warning Should not be reached.
.endfor
+
# A malformed modifier should be detected and skip the body of the loop.
#
# XXX: As of 2020-12-31, Var_Subst doesn't report any errors, therefore
# the loop body is expanded as if no error had happened.
+# expect+1: while evaluating "${:U3:Z} 4": Unknown modifier "Z"
.for i in 1 2 ${:U3:Z} 4
. warning Should not be reached.
.endfor
+# expect-2: warning: Should not be reached.
+# expect-3: warning: Should not be reached.
+# expect-4: warning: Should not be reached.
diff --git a/contrib/bmake/unit-tests/directive-for-escape.exp b/contrib/bmake/unit-tests/directive-for-escape.exp
index 59d4c2324f15..1f4305185d65 100644
--- a/contrib/bmake/unit-tests/directive-for-escape.exp
+++ b/contrib/bmake/unit-tests/directive-for-escape.exp
@@ -1,57 +1,76 @@
For: end for 1
-For: loop body:
+For: loop body with chars = !"#$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~:
. info ${:U!"#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~}
-make: Unclosed variable expression, expecting '}' for modifier "U!"" of variable "" with value "!""
+make: Unclosed expression, expecting '}' for modifier "U!"" of variable "" with value "!""
make: "directive-for-escape.mk" line 19: !"
For: end for 1
-For: loop body:
+For: loop body with chars = !"\\#$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~:
. info ${:U!"\\\\#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~}
-make: Unclosed variable expression, expecting '}' for modifier "U!"\\\\" of variable "" with value "!"\\"
-make: "directive-for-escape.mk" line 29: !"\\
+make: Unclosed expression, expecting '}' for modifier "U!"\\\\" of variable "" with value "!"\\"
+make: "directive-for-escape.mk" line 30: !"\\
For: end for 1
-For: loop body:
+For: loop body with i = $:
. info ${:U\$}
-make: "directive-for-escape.mk" line 41: $
-For: loop body:
+make: "directive-for-escape.mk" line 45: $
+For: loop body with i = ${V}:
. info ${:U${V}}
-make: "directive-for-escape.mk" line 41: value
-For: loop body:
+make: "directive-for-escape.mk" line 45: value
+For: loop body with i = ${V:=-with-modifier}:
. info ${:U${V:=-with-modifier}}
-make: "directive-for-escape.mk" line 41: value-with-modifier
-For: loop body:
+make: "directive-for-escape.mk" line 45: value-with-modifier
+For: loop body with i = $(V):
. info ${:U$(V)}
-make: "directive-for-escape.mk" line 41: value
-For: loop body:
+make: "directive-for-escape.mk" line 45: value
+For: loop body with i = $(V:=-with-modifier):
. info ${:U$(V:=-with-modifier)}
-make: "directive-for-escape.mk" line 41: value-with-modifier
+make: "directive-for-escape.mk" line 45: value-with-modifier
For: end for 1
-For: loop body:
+For: loop body with i = $:
+. info ${:U\$}
+make: "directive-for-escape.mk" line 60: $
+For: loop body with i = ${V}:
+. info ${:U${V}}
+make: "directive-for-escape.mk" line 60: value
+For: loop body with i = ${V:=-with-modifier}:
+. info ${:U${V:=-with-modifier}}
+make: "directive-for-escape.mk" line 60: value-with-modifier
+For: loop body with i = $(V):
+. info ${:U$(V)}
+make: "directive-for-escape.mk" line 60: value
+For: loop body with i = $(V:=-with-modifier):
+. info ${:U$(V:=-with-modifier)}
+make: "directive-for-escape.mk" line 60: value-with-modifier
+For: end for 1
+For: loop body with i = ${UNDEF:U\$\$:
+# ${:U\${UNDEF\:U\\$\\$}
+For: loop body with i = {{}}:
+# ${:U{{\}\}}
+For: loop body with i = end}:
+# ${:Uend\}}
+For: end for 1
+For: loop body with i = ${UNDEF:U\$\$:
. info ${:U\${UNDEF\:U\\$\\$}
-make: "directive-for-escape.mk" line 55: ${UNDEF:U\$
-For: loop body:
+make: "directive-for-escape.mk" line 115: ${UNDEF:U\backslash$
+For: loop body with i = {{}}:
. info ${:U{{\}\}}
-make: "directive-for-escape.mk" line 55: {{}}
-For: loop body:
+make: "directive-for-escape.mk" line 115: {{}}
+For: loop body with i = end}:
. info ${:Uend\}}
-make: "directive-for-escape.mk" line 55: end}
+make: "directive-for-escape.mk" line 115: end}
For: end for 1
-For: loop body:
+For: loop body with i = begin<${UNDEF:Ufallback:N{{{}}}}>end:
. info ${:Ubegin<${UNDEF:Ufallback:N{{{}}}}>end}
-make: "directive-for-escape.mk" line 67: begin<fallback>end
+make: "directive-for-escape.mk" line 136: begin<fallback>end
For: end for 1
-For: loop body:
+For: loop body with i = $:
. info ${:U\$}
-make: "directive-for-escape.mk" line 75: $
+make: "directive-for-escape.mk" line 145: $
+make: "directive-for-escape.mk" line 154: invalid character ':' in .for loop variable name
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 164: invalid character '}' in .for loop variable name
For: end for 1
-For: loop body:
-. info ${:Ureplaced}
-make: "directive-for-escape.mk" line 93: replaced
For: end for 1
-For: loop body:
+For: loop body with i = inner:
. info . $$i: ${:Uinner}
. info . $${i}: ${:Uinner}
. info . $${i:M*}: ${:Uinner:M*}
@@ -62,14 +81,90 @@ 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 173: . $i: inner
+make: "directive-for-escape.mk" line 174: . ${i}: inner
+make: "directive-for-escape.mk" line 175: . ${i:M*}: inner
+make: "directive-for-escape.mk" line 176: . $(i): inner
+make: "directive-for-escape.mk" line 177: . $(i:M*): inner
+make: "directive-for-escape.mk" line 178: . ${i${:U}}: outer
+make: "directive-for-escape.mk" line 179: . ${i\}}: inner}
+make: "directive-for-escape.mk" line 180: . ${i2}: two
+make: "directive-for-escape.mk" line 181: . ${i,}: comma
+make: "directive-for-escape.mk" line 182: . adjacent: innerinnerinnerinner
+make: "directive-for-escape.mk" line 201: invalid character '$' in .for loop variable name
+For: end for 1
+make: "directive-for-escape.mk" line 213: eight and no cents.
+For: end for 1
+make: "directive-for-escape.mk" line 226: newline in .for value
+make: "directive-for-escape.mk" line 226: newline in .for value
+For: loop body with i = "
+":
+. info short: ${:U" "}
+. info long: ${:U" "}
+make: "directive-for-escape.mk" line 227: short: " "
+make: "directive-for-escape.mk" line 228: long: " "
+For: end for 1
+For: loop body with i = "
+":
+Parsing line 244: .for i in "${.newline}"
+For: end for 1
+Parse_PushInput: .for loop in directive-for-escape.mk, line 244
+make: "directive-for-escape.mk" line 244: newline in .for value
+ in .for loop from directive-for-escape.mk:244 with i = "
+"
+For: loop body with i = "
+":
+: ${:U" "}
+SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `directive-for-escape.mk'
+Parsing line 245: : ${:U" "}
+ParseDependency(: " ")
+ParseEOF: returning to file directive-for-escape.mk, line 247
+SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `directive-for-escape.mk'
+Parsing line 247: .MAKEFLAGS: -d0
+ParseDependency(.MAKEFLAGS: -d0)
+For: end for 1
+For: loop body with i = #:
+# ${:U#}
+For: loop body with i = \\#:
+# ${:U\\\\#}
+For: end for 1
+For: loop body with i = $:
+# ${:U\$}
+For: loop body with i = $i:
+# ${:U$i}
+For: loop body with i = $(i):
+# ${:U$(i)}
+For: loop body with i = ${i}:
+# ${:U${i}}
+For: loop body with i = $$:
+# ${:U$$}
+For: loop body with i = $$$$:
+# ${:U$$$$}
+For: loop body with i = ${:U\$\$}:
+# ${:U${:U\$\$}}
+For: end for 1
+For: loop body with i = ${.TARGET}:
+# ${:U${.TARGET}}
+For: loop body with i = ${.TARGET}:
+# ${:U${.TARGET}}
+For: loop body with i = $${.TARGET}:
+# ${:U$${.TARGET\}}
+For: loop body with i = $${.TARGET}:
+# ${:U$${.TARGET\}}
+For: end for 1
+For: loop body with i = (((:
+# ${:U(((}
+For: loop body with i = {{{:
+# ${:U{{{}
+For: loop body with i = ))):
+# ${:U)))}
+For: loop body with i = }}}:
+# ${:U\}\}\}}
+For: end for 1
+For: loop body with , = 1:
+# $$i $i
+# VAR= $$i $i ${a:S,from${:U1}to,}
+VAR= $$i $i ${a:S,from${:U1}to,}
+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..16df5b1db4e3 100644
--- a/contrib/bmake/unit-tests/directive-for-escape.mk
+++ b/contrib/bmake/unit-tests/directive-for-escape.mk
@@ -1,9 +1,8 @@
-# $NetBSD: directive-for-escape.mk,v 1.7 2021/02/15 07:58:19 rillig Exp $
+# $NetBSD: directive-for-escape.mk,v 1.23 2023/11/19 22:32:44 rillig Exp $
#
# Test escaping of special characters in the iteration values of a .for loop.
# These values get expanded later using the :U variable modifier, and this
-# escaping and unescaping must pass all characters and strings effectively
-# unmodified.
+# escaping and unescaping must pass all characters and strings unmodified.
.MAKEFLAGS: -df
@@ -12,12 +11,14 @@
# This could be considered a bug.
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
+# expect-2: !"
# As of 2020-12-31, using 2 backslashes before be '#' would treat the '#'
# as comment character. Using 3 backslashes doesn't help either since
@@ -28,44 +29,113 @@ ASCII.2020-12-31= !"\\\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~
.for chars in ${ASCII.2020-12-31}
. info ${chars}
.endfor
+# expect-2: !"\\
-# 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
+# expect: . info ${:U\$}
+# expect-3: $
+# expect: . info ${:U${V}}
+# expect-5: value
+# expect: . info ${:U${V:=-with-modifier}}
+# expect-7: value-with-modifier
+# expect: . info ${:U$(V)}
+# expect-9: value
+# expect: . info ${:U$(V:=-with-modifier)}
+# expect-11: value-with-modifier
+#
+# Providing the loop items directly has the same effect.
+.for i in $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier)
+. info $i
+.endfor
+# expect: . info ${:U\$}
+# expect-3: $
+# expect: . info ${:U${V}}
+# expect-5: value
+# expect-6: value-with-modifier
+# expect-7: value
+# expect-8: value-with-modifier
-# 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.
+# The value of the variable VALUES is not meant to be an expression.
# Instead, it is meant to represent literal text, the only escaping mechanism
# being that each '$' is written as '$$'.
+VALUES= $${UNDEF:U\$$\$$ {{}} end}
#
# The .for loop splits ${VALUES} into 3 words, at the space characters, since
-# these are not escaped.
-VALUES= $${UNDEF:U\$$\$$ {{}} end}
-# XXX: Where in the code does the '\$\$' get converted into a single '\$'?
+# the '$$' is an ordinary character and the spaces are not escaped.
+# Word 1 is '${UNDEF:U\$\$'
+# Word 2 is '{{}}'
+# Word 3 is 'end}'
+#
+# Each of these words is now inserted in the body of the .for loop.
+.for i in ${VALUES}
+# $i
+.endfor
+#
+# When these words are injected into the body of the .for loop, each inside a
+# '${:U...}' expression, the result is:
+#
+# expect: For: loop body with i = ${UNDEF:U\$\$:
+# expect: # ${:U\${UNDEF\:U\\$\\$}
+# expect: For: loop body with i = {{}}:
+# expect: # ${:U{{\}\}}
+# expect: For: loop body with i = end}:
+# expect: # ${:Uend\}}
+# expect: For: end for 1
+#
+# The first of these expressions is the most interesting one, due to its many
+# special characters. This expression is properly balanced:
+#
+# Text Meaning Explanation
+# \$ $ escaped
+# { { ordinary text
+# UNDEF UNDEF ordinary text
+# \: : escaped
+# U U ordinary text
+# \\ \ escaped
+# $\ (expr) an expression, the variable name is '\'
+# \$ $ escaped
+#
+# To make the expression '$\' visible, define it to an actual word:
+${:U\\}= backslash
.for i in ${VALUES}
. info $i
.endfor
+#
+# expect-3: ${UNDEF:U\backslash$
+# expect-4: {{}}
+# expect-5: end}
+#
+# FIXME: There was no expression '$\' in the original text of the variable
+# 'VALUES', that's a surprise in the parser.
+
-# 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 not the job of ExprLen to parse an expression, it is naive to
+# expect ExprLen to get all the details right in just a few lines of code.
# Each variable modifier has its own inconsistent way of parsing nested
-# variable expressions, braces and parentheses. (Compare ':M', ':S', and
+# expressions, braces and parentheses. (Compare ':M', ':S', and
# ':D' for details.) The only sensible thing to do is therefore to let
# Var_Parse do all the parsing work.
VALUES= begin<$${UNDEF:Ufallback:N{{{}}}}>end
.for i in ${VALUES}
. info $i
.endfor
+# expect-2: begin<fallback>end
# A single trailing dollar doesn't happen in practice.
# The dollar sign is correctly passed through to the body of the .for loop.
@@ -74,21 +144,23 @@ VALUES= begin<$${UNDEF:Ufallback:N{{{}}}}>end
.for i in ${:U\$}
. info ${i}
.endfor
+# expect-2: $
-# As of 2020-12-31, the name of the iteration variable can even contain
-# colons, which then affects variable expressions having this exact modifier.
-# This is clearly an unintended side effect of the implementation.
+# Before for.c 1.173 from 2023-05-08, the name of the iteration variable
+# could contain colons, which affected expressions having this exact
+# modifier. This possibility was neither intended nor documented.
NUMBERS= one two three
+# expect+1: invalid character ':' in .for loop variable name
.for NUMBERS:M*e in replaced
. info ${NUMBERS} ${NUMBERS:M*e}
.endfor
-# As of 2020-12-31, the name of the iteration variable can contain braces,
-# which gets even more surprising than colons, since it allows to replace
-# sequences of variable expressions. There is no practical use case for
-# this, though.
+# Before for.c 1.173 from 2023-05-08, the name of the iteration variable
+# could contain braces, which allowed to replace sequences of
+# expressions. This possibility was neither intended nor documented.
BASENAME= one
EXT= .c
+# expect+1: invalid character '}' in .for loop variable name
.for BASENAME}${EXT in replaced
. info ${BASENAME}${EXT}
.endfor
@@ -109,5 +181,113 @@ i,= comma
. info . $${i,}: ${i,}
. info . adjacent: $i${i}${i:M*}$i
.endfor
+# expect-11: . $i: inner
+# expect-11: . ${i}: inner
+# expect-11: . ${i:M*}: inner
+# expect-11: . $(i): inner
+# expect-11: . $(i:M*): inner
+# expect-11: . ${i${:U}}: outer
+# expect-11: . ${i\}}: inner}
+# expect-11: . ${i2}: two
+# expect-11: . ${i,}: comma
+# expect-11: . adjacent: innerinnerinnerinner
+
+# Before for.c 1.173 from 2023-05-08, the variable name could be a single '$'
+# since there was no check on valid variable names. ForLoop_SubstVarShort
+# skipped "stupid" variable names though, but ForLoop_SubstVarLong naively
+# parsed the body of the loop, substituting each '${$}' with an actual
+# '${:Udollar}'.
+# expect+1: invalid character '$' in .for loop variable name
+.for $ in dollar
+. info eight $$$$$$$$ and no cents.
+. info eight ${$}${$}${$}${$} and no cents.
+.endfor
+# Outside a .for loop, '${$}' is interpreted differently. The outer '$' starts
+# an expression. The inner '$' is followed by a '}' and is thus a
+# silent syntax error, the '$' is skipped. The variable name is thus '', and
+# since since there is never a variable named '', the whole expression '${$}'
+# evaluates to an empty string.
+closing-brace= } # guard against an
+${closing-brace}= <closing-brace> # alternative interpretation
+# expect+1: eight and no cents.
+.info eight ${$}${$}${$}${$} and no cents.
-all:
+# 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.
+#
+# The error message occurs in the line of the .for loop since that's the place
+# where the body of the .for loop is constructed, and at this point the
+# newline character gets replaced with a plain space.
+# expect+2: newline in .for value
+# expect+1: newline in .for value
+.for i in "${.newline}"
+. info short: $i
+. info long: ${i}
+.endfor
+# expect-3: short: " "
+# expect-3: long: " "
+
+# No error since the newline character is not actually used.
+.for i in "${.newline}"
+.endfor
+
+# Between for.c 1.161 from 2022-01-08 and before for.c 1.163 from 2022-01-09,
+# a newline character in a .for loop led to a crash since at the point where
+# the error message including the stack trace is printed, the body of the .for
+# loop is assembled, and at that point, ForLoop.nextItem had already been
+# advanced.
+.MAKEFLAGS: -dp
+# expect+1: newline in .for value
+.for i in "${.newline}"
+: $i
+.endfor
+.MAKEFLAGS: -d0
+
+.MAKEFLAGS: -df
+.for i in \# \\\#
+# $i
+.endfor
+
+.for i in $$ $$i $$(i) $${i} $$$$ $$$$$$$$ $${:U\$$\$$}
+# $i
+.endfor
+
+# The expression '${.TARGET}' must be preserved as it is one of the 7 built-in
+# target-local variables. See for.c 1.45 from 2009-01-14.
+.for i in ${.TARGET} $${.TARGET} $$${.TARGET} $$$${.TARGET}
+# $i
+.endfor
+# expect: # ${:U${.TARGET}}
+# XXX: Why does '$' result in the same text as '$$'?
+# expect: # ${:U${.TARGET}}
+# XXX: Why does the '$$' before the '${.TARGET}' lead to an escaped '}'?
+# expect: # ${:U$${.TARGET\}}
+# XXX: Why does '$' result in the same text as '$$'?
+# XXX: Why does the '$$' before the '${.TARGET}' lead to an escaped '}'?
+# expect: # ${:U$${.TARGET\}}
+
+.for i in ((( {{{ ))) }}}
+# $i
+.endfor
+
+
+# When generating the body of a .for loop, recognizing the expressions is done
+# using simple heuristics. These can go wrong in ambiguous cases like this.
+# The variable name ',' is unusual as it is not a pronounceable name, but the
+# same principle applies for other names as well. In this case, the text '$,'
+# is replaced with the expression '${:U1}', even though the text does not
+# represent an expression.
+.for , in 1
+# $$i $i
+# VAR= $$i $i ${a:S,from$,to,}
+VAR= $$i $i ${a:S,from$,to,}
+.endfor
+# expect: # $$i $i
+# expect: # VAR= $$i $i ${a:S,from${:U1}to,}
+# expect: VAR= $$i $i ${a:S,from${:U1}to,}
+#
+# When the above variable is evaluated, make will complain about the
+# unfinished modifier ':S', as it is missing a comma.
diff --git a/contrib/bmake/unit-tests/directive-for-generating-endif.exp b/contrib/bmake/unit-tests/directive-for-generating-endif.exp
index 9e1301abf96f..ecdeb0962202 100755
--- a/contrib/bmake/unit-tests/directive-for-generating-endif.exp
+++ b/contrib/bmake/unit-tests/directive-for-generating-endif.exp
@@ -1,7 +1,7 @@
-make: "directive-for-generating-endif.mk" line 21: if-less endif
-make: "directive-for-generating-endif.mk" line 21: if-less endif
-make: "directive-for-generating-endif.mk" line 21: if-less endif
-make: "directive-for-generating-endif.mk" line 0: 3 open conditionals
+make: "directive-for-generating-endif.mk" line 24: if-less endif
+make: "directive-for-generating-endif.mk" line 24: if-less endif
+make: "directive-for-generating-endif.mk" line 24: if-less endif
+make: "directive-for-generating-endif.mk" line 30: 3 open conditionals
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/directive-for-generating-endif.mk b/contrib/bmake/unit-tests/directive-for-generating-endif.mk
index b4d709551003..3079ad3353c2 100755
--- a/contrib/bmake/unit-tests/directive-for-generating-endif.mk
+++ b/contrib/bmake/unit-tests/directive-for-generating-endif.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-for-generating-endif.mk,v 1.1 2020/08/29 18:50:25 rillig Exp $
+# $NetBSD: directive-for-generating-endif.mk,v 1.2 2023/06/01 20:56:35 rillig Exp $
#
# Test whether a .for loop can be used to generate multiple .endif
# directives to close nested .if directives. Depending on the exact
@@ -18,8 +18,12 @@
. if 2
. if 3
.for i in 3 2 1
+# expect+3: if-less endif
+# expect+2: if-less endif
+# expect+1: if-less endif
.endif
.endfor
all:
@:;
+# expect+1: 3 open conditionals
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..f30171f4db87
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-for-if.exp
@@ -0,0 +1,8 @@
+make: "directive-for-if.mk" line 51: if-less endif
+make: "directive-for-if.mk" line 51: if-less endif
+make: "directive-for-if.mk" line 51: 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..f5a20279cc97
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-for-if.mk
@@ -0,0 +1,89 @@
+# $NetBSD: directive-for-if.mk,v 1.3 2023/11/19 21:47:52 rillig Exp $
+#
+# Test for a .for directive that contains an .if directive.
+#
+# 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"
+# expect+3: if-less endif
+# expect+2: if-less endif
+# expect+1: if-less endif
+. 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
+# 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-lines.exp b/contrib/bmake/unit-tests/directive-for-lines.exp
index 7aeaaa4a7002..857b4829083b 100644
--- a/contrib/bmake/unit-tests/directive-for-lines.exp
+++ b/contrib/bmake/unit-tests/directive-for-lines.exp
@@ -1,9 +1,9 @@
-make: "directive-for-lines.mk" line 23: expect 23
-make: "directive-for-lines.mk" line 23: expect 23
-make: "directive-for-lines.mk" line 30: expect 30
-make: "directive-for-lines.mk" line 23: expect 23
-make: "directive-for-lines.mk" line 23: expect 23
-make: "directive-for-lines.mk" line 30: expect 30
+make: "directive-for-lines.mk" line 27: expect 23
+make: "directive-for-lines.mk" line 27: expect 23
+make: "directive-for-lines.mk" line 36: expect 30
+make: "directive-for-lines.mk" line 27: expect 23
+make: "directive-for-lines.mk" line 27: expect 23
+make: "directive-for-lines.mk" line 36: expect 30
make: no target to make.
make: stopped in unit-tests
diff --git a/contrib/bmake/unit-tests/directive-for-lines.mk b/contrib/bmake/unit-tests/directive-for-lines.mk
index 96d659426882..cae4e0a38897 100644
--- a/contrib/bmake/unit-tests/directive-for-lines.mk
+++ b/contrib/bmake/unit-tests/directive-for-lines.mk
@@ -1,9 +1,9 @@
-# $NetBSD: directive-for-lines.mk,v 1.3 2020/12/19 12:40:00 rillig Exp $
+# $NetBSD: directive-for-lines.mk,v 1.5 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the line numbers that are reported in .for loops.
#
-# Between 2007-01-01 (git 4d3c468f96e1080e, parse.c 1.127) and 2020-12-19
-# (parse.c 1.494), the line numbers for the .info directives and error
+# Since parse.c 1.127 from 2007-01-01 and before parse.c 1.494 from
+# 2020-12-19, the line numbers for the .info directives and error
# messages inside .for loops had been wrong since ParseGetLine skipped empty
# lines, even when collecting the lines for the .for loop body.
@@ -20,6 +20,10 @@
VAR= \
multi-line
+# expect+4: expect 23
+# expect+3: expect 23
+# expect+2: expect 23
+# expect+1: expect 23
.info expect 23
.endfor
@@ -27,6 +31,8 @@ VAR= \
# comment \
# continued comment
+# expect+2: expect 30
+# expect+1: expect 30
.info expect 30
.endfor
diff --git a/contrib/bmake/unit-tests/directive-for-null.exp b/contrib/bmake/unit-tests/directive-for-null.exp
index 37a7d68925ed..d6198c644817 100644
--- a/contrib/bmake/unit-tests/directive-for-null.exp
+++ b/contrib/bmake/unit-tests/directive-for-null.exp
@@ -1,9 +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 3: Zero byte read from file
-make: Fatal errors encountered -- cannot continue
-make: stopped in unit-tests
-*** Error code 1 (continuing)
+*** Error code 2 (continuing)
Stop.
make: stopped in unit-tests
diff --git a/contrib/bmake/unit-tests/directive-for-null.mk b/contrib/bmake/unit-tests/directive-for-null.mk
index a374f508dd55..d3de8b598bc2 100644
--- a/contrib/bmake/unit-tests/directive-for-null.mk
+++ b/contrib/bmake/unit-tests/directive-for-null.mk
@@ -1,19 +1,13 @@
-# $NetBSD: directive-for-null.mk,v 1.1 2020/12/19 16:00:17 rillig Exp $
+# $NetBSD: directive-for-null.mk,v 1.4 2024/04/01 12:26:02 rillig Exp $
#
# Test for parsing a .for loop that accidentally contains a null byte.
#
-# As of 2020-12-19, there are 3 error messages:
-#
-# make: "(stdin)" line 2: Zero byte read from file
-# make: "(stdin)" line 2: Unexpected end of file in for loop.
-# make: "(stdin)" line 3: Zero byte read from file
-#
-# The one about "end of file" might be misleading but is due to the
-# implementation. On both errors and EOF, ParseGetLine returns NULL.
-#
-# The one about the "zero byte" in line 3 is surprising since the only
-# line that contains a null byte is line 2.
+# expect: make: "(stdin)" line 2: Zero byte read from file
all: .PHONY
- @printf '%s\n' '.for i in 1 2 3' 'VAR=value' '.endfor' | tr 'l' '\0' \
+ @printf '%s\n' \
+ '.for i in 1 2 3' \
+ 'VAR=value' \
+ '.endfor' \
+ | tr 'l' '\0' \
| ${MAKE} -f -
diff --git a/contrib/bmake/unit-tests/directive-for.exp b/contrib/bmake/unit-tests/directive-for.exp
index 4e882aad7b68..3e346dfab4ea 100755
--- a/contrib/bmake/unit-tests/directive-for.exp
+++ b/contrib/bmake/unit-tests/directive-for.exp
@@ -1,24 +1,42 @@
-make: "directive-for.mk" line 108: outer
-make: "directive-for.mk" line 133: a:\ a:\file.txt
-make: "directive-for.mk" line 133: d:\\
-make: "directive-for.mk" line 133: d:\\file.txt
-make: "directive-for.mk" line 140: ( ( (
-make: "directive-for.mk" line 140: [ [ [
-make: "directive-for.mk" line 140: { { {
-make: "directive-for.mk" line 140: ) ) )
-make: "directive-for.mk" line 140: ] ] ]
-make: "directive-for.mk" line 140: } } }
-make: "directive-for.mk" line 140: (()) (()) (())
-make: "directive-for.mk" line 140: [[]] [[]] [[]]
-make: "directive-for.mk" line 140: {{}} {{}} {{}}
-make: "directive-for.mk" line 140: )( )( )(
-make: "directive-for.mk" line 140: ][ ][ ][
-make: "directive-for.mk" line 140: }{ }{ }{
-make: "directive-for.mk" line 148: outer value value
-make: "directive-for.mk" line 148: outer "quoted" \"quoted\"
-make: "directive-for.mk" line 154: Unknown modifier "Z"
-make: "directive-for.mk" line 155: XXX: Not reached word1
-make: "directive-for.mk" line 155: XXX: Not reached word3
+make: "directive-for.mk" line 117: outer
+make: "directive-for.mk" line 135: a:\ a:\file.txt
+make: "directive-for.mk" line 135: d:\\
+make: "directive-for.mk" line 135: d:\\file.txt
+make: "directive-for.mk" line 146: ( ( (
+make: "directive-for.mk" line 146: [ [ [
+make: "directive-for.mk" line 146: { { {
+make: "directive-for.mk" line 146: ) ) )
+make: "directive-for.mk" line 146: ] ] ]
+make: "directive-for.mk" line 146: } } }
+make: "directive-for.mk" line 146: (()) (()) (())
+make: "directive-for.mk" line 146: [[]] [[]] [[]]
+make: "directive-for.mk" line 146: {{}} {{}} {{}}
+make: "directive-for.mk" line 146: )( )( )(
+make: "directive-for.mk" line 146: ][ ][ ][
+make: "directive-for.mk" line 146: }{ }{ }{
+make: "directive-for.mk" line 166: invalid character ':' in .for loop variable name
+make: "directive-for.mk" line 173: invalid character '$' in .for loop variable name
+make: "directive-for.mk" line 185: invalid character '$' in .for loop variable name
+make: "directive-for.mk" line 210: while evaluating "${:Uword2:Z}-after word3": Unknown modifier "Z"
+make: "directive-for.mk" line 211: XXX: Should not reach word1
+make: "directive-for.mk" line 211: XXX: Should not reach before--after
+make: "directive-for.mk" line 211: XXX: Should not reach word3
+make: "directive-for.mk" line 219: no iteration variables in for
+make: "directive-for.mk" line 245: 1 open conditional
+make: "directive-for.mk" line 261: for-less endfor
+make: "directive-for.mk" line 262: if-less endif
+make: "directive-for.mk" line 270: if-less endif
+For: new loop 2
+For: end for 2
+For: end for 1
+For: loop body with outer = o:
+.\
+ for inner in i
+.\
+ endfor
+For: end for 1
+For: loop body with inner = i:
+make: "directive-for.mk" line 318: newline-item=(a)
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/directive-for.mk b/contrib/bmake/unit-tests/directive-for.mk
index 153762509b7a..d777c3921556 100755
--- a/contrib/bmake/unit-tests/directive-for.mk
+++ b/contrib/bmake/unit-tests/directive-for.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-for.mk,v 1.10 2020/12/27 09:58:35 rillig Exp $
+# $NetBSD: directive-for.mk,v 1.25 2024/04/20 10:18:55 rillig Exp $
#
# Tests for the .for directive.
#
@@ -8,11 +8,13 @@
# .for _FILE_ in values
# .for .FILE. in values
# .for _f_ in values
-
-# Using the .for loop, lists of values can be produced.
-# In simple cases, the :@var@${var}@ variable modifier can be used to
-# reach the same effects.
#
+# See also:
+# varmod-loop.mk The ':@var@...@' modifier
+
+# A typical use case for a .for loop is to populate a variable with a list of
+# values depending on other variables. In simple cases, the same effect can
+# be achieved using the ':@var@${var}@' modifier.
.undef NUMBERS
.for num in 1 2 3
NUMBERS+= ${num}
@@ -21,8 +23,9 @@ NUMBERS+= ${num}
. error
.endif
+
# The .for loop also works for multiple iteration variables.
-# This is something that the variable modifier :@ cannot do.
+# This is something that the modifier :@ cannot do as easily.
.for name value in VARNAME value NAME2 value2
${name}= ${value}
.endfor
@@ -30,12 +33,12 @@ ${name}= ${value}
. error
.endif
+
# The .for loop splits the items at whitespace, taking quotes into account,
-# just like the :M or :S variable modifiers.
-#
-# Until 2012-06-03, it had split the items exactly at whitespace, without
-# taking the quotes into account. This had resulted in 10 words.
+# just like the :M or :S modifiers.
#
+# Until 2012-06-03, the .for loop had split the items exactly at whitespace,
+# without taking the quotes into account. This had resulted in 10 words.
.undef WORDS
.for var in one t\ w\ o "three three" 'four four' `five six`
WORDS+= counted
@@ -44,16 +47,19 @@ WORDS+= counted
. error
.endif
+
# In the body of the .for loop, the iteration variables can be accessed
# like normal variables, even though they are not really variables.
#
-# Instead, the expression ${var} is transformed into ${:U1}, ${:U2} and so
-# on, before the loop body is evaluated.
+# Instead, before interpreting the body of the .for loop, the body is
+# generated by replacing each expression ${var} with ${:U1}, ${:U2} and so
+# on.
#
-# A notable effect of this implementation technique is that the .for
+# A noticeable effect of this implementation technique is that the .for
# iteration variables and the normal global variables live in separate
-# namespaces and do not influence each other.
-#
+# namespaces and do not influence each other. The "scope" of the .for loop
+# variables is restricted to the current makefile, it does not reach over to
+# any included makefiles.
var= value before
var2= value before
.for var var2 in 1 2 3 4
@@ -66,9 +72,8 @@ var2= value before
.endif
# Everything from the paragraph above also applies if the loop body is
-# empty, even if there is no actual iteration since the loop items are
-# also empty.
-#
+# empty. In this particular example, the items to be iterated are empty as
+# well.
var= value before
var2= value before
.for var var2 in ${:U}
@@ -80,13 +85,15 @@ var2= value before
. warning After the .for loop, var2 must still have its original value.
.endif
-# Until 2008-12-21, the values of the iteration variables were simply
-# inserted as plain text and then parsed as usual, which made it possible
-# to achieve all kinds of strange effects.
+# Before for.c 1.39 from 2008-12-21, the values of the iteration variables
+# were simply inserted as plain text and then parsed as usual, which made it
+# possible to achieve all kinds of strange effects, such as generating '.if'
+# directives or inserting '$' characters in random places, thereby changing
+# how following '$' are interpreted.
#
-# Before that date, the .for loop expanded to:
+# Before that date, the .for loop below expanded to:
# EXPANSION+= value
-# Since that date, the .for loop expands to:
+# Since that date, the .for loop below expands to:
# EXPANSION${:U+}= value
#
EXPANSION= before
@@ -102,13 +109,16 @@ EXPANSION${plus}= value
.endif
# When the outer .for loop is expanded, it sees the expression ${i} and
-# expands it. The inner loop then has nothing more to expand.
+# expands it. The inner loop then only sees the expression ${:Uouter} and
+# has nothing more to expand.
.for i in outer
. for i in inner
+# expect+1: outer
. info ${i}
. endfor
.endfor
+
# From https://gnats.netbsd.org/29985.
#
# Until 2008-12-21, the .for loop was expanded by replacing the variable
@@ -121,39 +131,190 @@ EXPANSION${plus}= value
# like "a:\ a:\file.txt" that ended in a single backslash. Since then, the
# variable values have been replaced with expressions of the form ${:U...},
# which are not interpreted as code anymore.
-#
-# As of 2020-09-22, a comment in for.c says that it may be possible to
-# produce an "unwanted substitution", but there is no demonstration code yet.
-#
-# The above changes prevent a backslash at the end of a word from being
-# interpreted as part of the code. Because of this, the trailingBackslash
-# hack in Var_Subst is no longer needed and as of 2020-09-22, has been
-# removed.
.for path in a:\ a:\file.txt d:\\ d:\\file.txt
. info ${path}
.endfor
+# expect-2: a:\ a:\file.txt
+# expect-3: d:\\
+# expect-4: d:\\file.txt
+
# Ensure that braces and parentheses are properly escaped by the .for loop.
# Each line must print the same word 3 times.
-# See GetEscapes.
+# See ForLoop_SubstBody.
.for v in ( [ { ) ] } (()) [[]] {{}} )( ][ }{
. info $v ${v} $(v)
.endfor
+# expect-02: ( ( (
+# expect-03: [ [ [
+# expect-04: { { {
+# expect-05: ) ) )
+# expect-06: ] ] ]
+# expect-07: } } }
+# expect-08: (()) (()) (())
+# expect-09: [[]] [[]] [[]]
+# expect-10: {{}} {{}} {{}}
+# expect-11: )( )( )(
+# expect-12: ][ ][ ][
+# expect-13: }{ }{ }{
-# As of 2020-10-25, the variable names may contain arbitrary characters,
-# except for whitespace. This allows for creative side effects. Hopefully
-# nobody is misusing this "feature".
+# Before 2023-05-09, the variable names could contain arbitrary characters,
+# except for whitespace, allowing for creative side effects, as usual for
+# arbitrary code injection.
var= outer
+# expect+1: invalid character ':' in .for loop variable name
.for var:Q in value "quoted"
-. info ${var} ${var:Q} ${var:Q:Q}
+. info <${var}> <${var:Q}> <${var:Q:Q}>
+.endfor
+
+# Before 2023-05-09, when variable names could contain '$', the short
+# expression '$$' was preserved, the long expressions were substituted.
+# expect+1: invalid character '$' in .for loop variable name
+.for $ in value
+. info <$$> <${$}> <$($)>
+.endfor
+
+
+# https://gnats.netbsd.org/53146 mentions the idea of using a dynamic
+# variable name in .for loops, based on some other variable. The .for loops
+# are already tricky enough to understand in detail, even without this
+# possibility, therefore the variable names are restricted to using harmless
+# characters only.
+INDIRECT= direct
+# expect+1: invalid character '$' in .for loop variable name
+.for $(INDIRECT) in value
+# If the variable name could be chosen dynamically, the iteration variable
+# might have been 'direct', thereby expanding the expression '${direct}'.
+. info <$(INDIRECT)> <$(direct)> <$($(INDIRECT))>
+.endfor
+
+
+# Regular global variables and the "variables" from the .for loop don't
+# interfere with each other. In the following snippet, the variable 'DIRECT'
+# is used both as a global variable, as well as an iteration variable in the
+# .for loop. The expression '${INDIRECT}' refers to the global variable, not
+# to the one from the .for loop.
+DIRECT= global
+INDIRECT= ${DIRECT}
+.for DIRECT in iteration
+. if "${DIRECT} ${INDIRECT}" != "iteration global"
+. error
+. endif
.endfor
# XXX: A parse error or evaluation error in the items of the .for loop
-# should skip the whole loop. As of 2020-12-27, the loop is expanded twice.
-.for var in word1 ${:Uword2:Z} word3
-. info XXX: Not reached ${var}
+# should skip the whole loop. As of 2023-05-09, the loop is expanded as
+# usual.
+# expect+1: while evaluating "${:Uword2:Z}-after word3": Unknown modifier "Z"
+.for var in word1 before-${:Uword2:Z}-after word3
+. info XXX: Should not reach ${var}
+.endfor
+# expect-2: XXX: Should not reach word1
+# expect-3: XXX: Should not reach before--after
+# expect-4: XXX: Should not reach word3
+
+
+# An empty list of variables to the left of the 'in' is a parse error.
+.for in value # expect+0: no iteration variables in for
+. error
+.endfor
+
+# An empty list of iteration values to the right of the 'in' is accepted.
+# Unlike in the shell, it is not a parse error.
+.for var in
+. error
+.endfor
+
+# If the iteration values become empty after expanding the expressions, the
+# body of the loop is not evaluated. It is not a parse error.
+.for var in ${:U}
+. error
+.endfor
+
+
+# The loop body can be empty.
+.for var in 1 2 3
+.endfor
+
+
+# A mismatched .if inside a .for loop is detected each time when the loop body
+# is processed.
+.for var in value
+. if 0
+.endfor # expect+0: 1 open conditional
+
+# If there are no iteration values, the loop body is not processed, and the
+# check for mismatched conditionals is not performed.
+.for var in ${:U}
+. if 0
+.endfor
+
+
+# When a .for without the corresponding .endfor occurs in an inactive branch
+# of an .if, the .for directive is just skipped, it does not even need a
+# corresponding .endfor. In other words, the behavior of the parser depends
+# on the actual values of the conditions in the .if clauses.
+.if 0
+. for var in value # does not need a corresponding .endfor
+.endif
+.endfor # expect+0: for-less endfor
+.endif # expect+0: if-less endif
+
+
+# When a .for without the corresponding .endfor occurs in an active branch of
+# an .if, the parser just counts the number of .for and .endfor directives,
+# without looking at any other directives.
+.if 1
+. for var in value
+. endif # expect+0: if-less endif
+. endfor # no 'for-less endfor'
+.endif # no 'if-less endif'
+
+
+# Before for.c 1.172 from 2023-05-08, when make parsed a .for loop, it
+# assumed that there was no line continuation between the '.' and the 'for'
+# or 'endfor', as there is no practical reason to break the line at this
+# point.
+#
+# When make scanned the outer .for loop, it did not recognize the inner .for
+# loop as such and instead treated it as an unknown directive. The body of
+# the outer .for loop thus ended above the '.endfor'.
+#
+# When make scanned the inner .for loop, it did not recognize the inner
+# .endfor as such, which led to a parse error 'Unexpected end of file in .for
+# loop' from the '.endfor' line, followed by a second parse error 'for-less
+# .endfor' from the '.\\n endfor' line.
+.MAKEFLAGS: -df
+.for outer in o
+.\
+ for inner in i
+.\
+ endfor
+.endfor
+.MAKEFLAGS: -d0
+
+
+# When there is a variable definition 'scope=cmdline' from the command line
+# (which has higher precedence than global variables) and a .for loop iterates
+# over a variable of the same name, the expression '${scope}' expands to the
+# value from the .for loop. This is because when the body of the .for loop is
+# expanded, the expression '${scope}' is textually replaced with ${:Uloop}',
+# without resolving any other variable names (ForLoop_SubstBody). Later, when
+# the body of the .for loop is actually interpreted, the body text doesn't
+# contain the word 'scope' anymore.
+.MAKEFLAGS: scope=cmdline
+.for scope in loop
+. if ${scope} != "loop"
+. error
+. endif
.endfor
-all:
- @:;
+
+# Since at least 1993, iteration stops at the first newline.
+# Back then, the .newline variable didn't exist, therefore it was unlikely
+# that a newline ever occurred.
+.for var in a${.newline}b${.newline}c
+. info newline-item=(${var})
+.endfor
+# expect-2: newline-item=(a)
diff --git a/contrib/bmake/unit-tests/directive-hyphen-include.exp b/contrib/bmake/unit-tests/directive-hyphen-include.exp
index 39a9383953dd..308a444890d5 100755
--- a/contrib/bmake/unit-tests/directive-hyphen-include.exp
+++ b/contrib/bmake/unit-tests/directive-hyphen-include.exp
@@ -1 +1,4 @@
-exit status 0
+make: "directive-hyphen-include-error.inc" line 1: Invalid line 'syntax error'
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/directive-hyphen-include.mk b/contrib/bmake/unit-tests/directive-hyphen-include.mk
index 8c851be43215..de3b68ae52b3 100755
--- a/contrib/bmake/unit-tests/directive-hyphen-include.mk
+++ b/contrib/bmake/unit-tests/directive-hyphen-include.mk
@@ -1,9 +1,23 @@
-# $NetBSD: directive-hyphen-include.mk,v 1.1 2020/09/13 09:20:23 rillig Exp $
+# $NetBSD: directive-hyphen-include.mk,v 1.3 2023/08/19 10:52:13 rillig Exp $
#
# Tests for the .-include directive, which includes another file,
# silently skipping it if it cannot be opened.
+#
+# The 'silently skipping' only applies to the case where the file cannot be
+# opened. Parse errors and other errors are handled the same way as in the
+# other .include directives.
+
+# No complaint that there is no such file.
+.-include "${.CURDIR}/directive-hyphen-include-nonexistent.inc"
+
+# No complaint either, even though the operating system error is ENOTDIR, not
+# ENOENT.
+.-include "${MAKEFILE}/subdir"
-# TODO: Implementation
+# Errors that are not related to opening the file are still reported.
+# expect: make: "directive-hyphen-include-error.inc" line 1: Invalid line 'syntax error'
+_!= echo 'syntax error' > directive-hyphen-include-error.inc
+.-include "${.CURDIR}/directive-hyphen-include-error.inc"
+_!= rm directive-hyphen-include-error.inc
-all:
- @:;
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/directive-if.exp b/contrib/bmake/unit-tests/directive-if.exp
index 89a394fc0f22..34ba63d034dd 100644
--- a/contrib/bmake/unit-tests/directive-if.exp
+++ b/contrib/bmake/unit-tests/directive-if.exp
@@ -1,17 +1,18 @@
-make: "directive-if.mk" line 13: 0 evaluates to false.
-make: "directive-if.mk" line 17: 1 evaluates to true.
-make: "directive-if.mk" line 40: Unknown directive "ifx"
-make: "directive-if.mk" line 41: This is not conditional.
-make: "directive-if.mk" line 42: if-less else
-make: "directive-if.mk" line 43: This is not conditional.
-make: "directive-if.mk" line 44: if-less endif
-make: "directive-if.mk" line 47: Malformed conditional ()
-make: "directive-if.mk" line 57: Quotes in plain words are probably a mistake.
-make: "directive-if.mk" line 66: Don't do this, always put a space after a directive.
-make: "directive-if.mk" line 70: Don't do this, always put a space after a directive.
-make: "directive-if.mk" line 76: Don't do this, always put a space around comparison operators.
-make: "directive-if.mk" line 82: Don't do this, always put a space after a directive.
-make: "directive-if.mk" line 86: Don't do this, always put a space after a directive.
+make: "directive-if.mk" line 14: 0 evaluates to false.
+make: "directive-if.mk" line 19: 1 evaluates to true.
+make: "directive-if.mk" line 43: Unknown directive "ifx"
+make: "directive-if.mk" line 45: This is not conditional.
+make: "directive-if.mk" line 47: if-less else
+make: "directive-if.mk" line 49: This is not conditional.
+make: "directive-if.mk" line 51: if-less endif
+make: "directive-if.mk" line 55: Malformed conditional ()
+make: "directive-if.mk" line 66: Quotes in plain words are probably a mistake.
+make: "directive-if.mk" line 76: Don't do this, always put a space after a directive.
+make: "directive-if.mk" line 81: Don't do this, always put a space after a directive.
+make: "directive-if.mk" line 88: Don't do this, always put a space around comparison operators.
+make: "directive-if.mk" line 95: Don't do this, always put a space after a directive.
+make: "directive-if.mk" line 100: Don't do this, always put a space after a directive.
+make: "directive-if.mk" line 108: Unknown directive "ifn"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/directive-if.mk b/contrib/bmake/unit-tests/directive-if.mk
index b1ad2396b398..7a68c0041415 100644
--- a/contrib/bmake/unit-tests/directive-if.mk
+++ b/contrib/bmake/unit-tests/directive-if.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-if.mk,v 1.9 2020/12/19 22:33:11 rillig Exp $
+# $NetBSD: directive-if.mk,v 1.12 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the .if directive.
#
@@ -10,10 +10,12 @@
.if 0
. error
.else
+# expect+1: 0 evaluates to false.
. info 0 evaluates to false.
.endif
.if 1
+# expect+1: 1 evaluates to true.
. info 1 evaluates to true.
.else
. error
@@ -37,13 +39,19 @@
# longer interpreted as a variant of '.if', therefore the '.error' and '.else'
# are interpreted as ordinary directives, producing the error messages
# "if-less else" and "if-less endif".
+# expect+1: Unknown directive "ifx"
.ifx 123
+# expect+1: This is not conditional.
.info This is not conditional.
+# expect+1: if-less else
.else
+# expect+1: This is not conditional.
.info This is not conditional.
+# expect+1: if-less endif
.endif
# Missing condition.
+# expect+1: Malformed conditional ()
.if
. error
.else
@@ -54,6 +62,7 @@
# though, which are kept. The quotes need not be balanced. The next space
# ends the word, and the remaining " || 1" is parsed as "or true".
.if ${:Uplain"""""} == plain""""" || 1
+# expect+1: Quotes in plain words are probably a mistake.
. info Quotes in plain words are probably a mistake.
# XXX: Accepting quotes in plain words is probably a mistake as well.
.else
@@ -63,27 +72,37 @@
.if0
. error
.else
+# expect+1: Don't do this, always put a space after a directive.
. info Don't do this, always put a space after a directive.
.endif
.if${:U-3}
+# expect+1: Don't do this, always put a space after a directive.
. info Don't do this, always put a space after a directive.
.else
. error
.endif
.if${:U-3}>-4
+# expect+1: Don't do this, always put a space around comparison operators.
. info Don't do this, always put a space around comparison operators.
.else
. error
.endif
.if(1)
+# expect+1: Don't do this, always put a space after a directive.
. info Don't do this, always put a space after a directive.
.endif
.if!0
+# expect+1: Don't do this, always put a space after a directive.
. info Don't do this, always put a space after a directive.
.endif
-all:
+
+# The directives '.ifdef' and '.ifmake' can be negated by inserting an 'n'.
+# This doesn't work for a plain '.if' though.
+#
+# expect+1: Unknown directive "ifn"
+.ifn 0
diff --git a/contrib/bmake/unit-tests/directive-ifdef.exp b/contrib/bmake/unit-tests/directive-ifdef.exp
index 1a1358988f39..39a9383953dd 100644
--- a/contrib/bmake/unit-tests/directive-ifdef.exp
+++ b/contrib/bmake/unit-tests/directive-ifdef.exp
@@ -1,4 +1 @@
-make: "directive-ifdef.mk" line 12: Function calls in .ifdef are possible.
-make: "directive-ifdef.mk" line 23: String literals are tested for emptiness.
-make: "directive-ifdef.mk" line 27: String literals are tested for emptiness. Whitespace is non-empty.
exit status 0
diff --git a/contrib/bmake/unit-tests/directive-ifdef.mk b/contrib/bmake/unit-tests/directive-ifdef.mk
index 12f3648e8b2c..8a60fb4a2669 100644
--- a/contrib/bmake/unit-tests/directive-ifdef.mk
+++ b/contrib/bmake/unit-tests/directive-ifdef.mk
@@ -1,33 +1,51 @@
-# $NetBSD: directive-ifdef.mk,v 1.4 2021/01/21 23:03:41 rillig Exp $
+# $NetBSD: directive-ifdef.mk,v 1.5 2022/01/23 21:48:59 rillig Exp $
#
-# Tests for the .ifdef directive.
-
-# TODO: Implementation
+# Tests for the .ifdef directive, which evaluates bare words by calling
+# 'defined(word)'.
DEFINED= defined
+# There is no variable named 'UNDEF', therefore the condition evaluates to
+# false.
+.ifdef UNDEF
+. error
+.endif
+
+# There is a variable named 'DEFINED', so the condition evaluates to true.
+.ifdef DEFINED
+.else
+. error
+.endif
+
+# Since a bare word is an abbreviation for 'defined(word)', these can be
+# used to construct complex conditions.
+.ifdef UNDEF && DEFINED
+. error
+.endif
+.ifdef UNDEF || DEFINED
+.else
+. error
+.endif
+
# It looks redundant to have a call to defined() in an .ifdef, but it's
-# possible. The .ifdef only affects plain symbols, not function calls.
+# possible. The '.ifdef' only affects bare words, not function calls.
.ifdef defined(DEFINED)
-. info Function calls in .ifdef are possible.
.else
. error
.endif
-# String literals are handled the same in all variants of the .if directive.
-# They evaluate to true if they are not empty. Whitespace counts as non-empty
-# as well.
+# String literals are handled the same in all variants of the '.if' directive,
+# they evaluate to true if they are not empty, therefore this particular
+# example looks confusing and is thus not found in practice.
.ifdef ""
. error
.else
-. info String literals are tested for emptiness.
.endif
+# Whitespace counts as non-empty as well.
.ifdef " "
-. info String literals are tested for emptiness. Whitespace is non-empty.
.else
. error
.endif
-all:
- @:;
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/directive-ifmake.exp b/contrib/bmake/unit-tests/directive-ifmake.exp
index fd4bcae151a0..e607726fd87c 100644
--- a/contrib/bmake/unit-tests/directive-ifmake.exp
+++ b/contrib/bmake/unit-tests/directive-ifmake.exp
@@ -1,11 +1,15 @@
-make: "directive-ifmake.mk" line 13: ok: positive condition works
-make: "directive-ifmake.mk" line 24: ok: negation works
-make: "directive-ifmake.mk" line 33: ok: double negation works
-make: "directive-ifmake.mk" line 40: ok: both mentioned
-make: "directive-ifmake.mk" line 47: ok: only those mentioned
-make: "directive-ifmake.mk" line 57: Targets can even be added at parse time.
-make: "directive-ifmake.mk" line 75: ok
+make: "directive-ifmake.mk" line 14: ok: positive condition works
+make: "directive-ifmake.mk" line 26: ok: negation works
+make: "directive-ifmake.mk" line 36: ok: double negation works
+make: "directive-ifmake.mk" line 44: ok: both mentioned
+make: "directive-ifmake.mk" line 52: ok: only those mentioned
+make: "directive-ifmake.mk" line 63: Targets can even be added at parse time.
+make: "directive-ifmake.mk" line 82: ok
: first
: second
: late-target
-exit status 0
+make: don't know how to make !edge (continuing)
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/directive-ifmake.mk b/contrib/bmake/unit-tests/directive-ifmake.mk
index 4d49add72626..fa0a56c60030 100644
--- a/contrib/bmake/unit-tests/directive-ifmake.mk
+++ b/contrib/bmake/unit-tests/directive-ifmake.mk
@@ -1,26 +1,28 @@
-# $NetBSD: directive-ifmake.mk,v 1.8 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: directive-ifmake.mk,v 1.12 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the .ifmake directive, which provides a shortcut for asking
# whether a certain target is requested to be made from the command line.
#
# TODO: Describe why the shortcut may be useful (if it's useful at all),
-# instead of sticking to the simple '.if' only.
+# instead of using the more general '.if make(target)'.
-# The targets 'first' and 'second' are passed in on the command line.
+.MAKEFLAGS: first second
# This is the most basic form.
.ifmake first
+# expect+1: ok: positive condition works
. info ok: positive condition works
.else
. warning positive condition fails
.endif
-# The not operator works as expected.
-# An alternative interpretation were that this condition is asking whether
-# the target "!first" was requested. To distinguish this, see the next test.
+# The '!' is interpreted as 'not'. A possible alternative interpretation of
+# this condition is whether the target named "!first" was requested. To
+# distinguish these cases, see the next test.
.ifmake !first
. warning unexpected
.else
+# expect+1: ok: negation works
. info ok: negation works
.endif
@@ -30,6 +32,7 @@
# exclamation mark were part of the name instead, the name would be '!!first',
# and such a target was not requested to be made.
.ifmake !!first
+# expect+1: ok: double negation works
. info ok: double negation works
.else
. warning double negation fails
@@ -37,6 +40,7 @@
# Multiple targets can be combined using the && and || operators.
.ifmake first && second
+# expect+1: ok: both mentioned
. info ok: both mentioned
.else
. warning && does not work as expected
@@ -44,6 +48,7 @@
# Negation also works in complex conditions.
.ifmake first && !unmentioned
+# expect+1: ok: only those mentioned
. info ok: only those mentioned
.else
. warning && with ! does not work as expected
@@ -54,6 +59,7 @@
# possible to extend the targets to be made.
.MAKEFLAGS: late-target
.ifmake late-target
+# expect+1: Targets can even be added at parse time.
. info Targets can even be added at parse time.
.else
. info No, targets cannot be added at parse time anymore.
@@ -69,14 +75,40 @@
. error
.endif
-# A condition that consists of a variable expression only (without any
+# A condition that consists of an expression only (without any
# comparison operator) can be used with .if and the other .ifxxx directives.
.ifmake ${:Ufirst}
+# expect+1: ok
. info ok
.else
. error
.endif
-first second unmentioned late-target:
+# As an edge case, a target can actually be named "!first" on the command
+# line. There is no way to define a target of this name though since in a
+# dependency line, a plain '!' is interpreted as a dependency operator.
+
+.MAKEFLAGS: !edge
+.ifmake edge
+. error
+.endif
+
+# The '\!edge' in the following condition is parsed as a bare word. For such
+# a bare word, there is no escaping mechanism so the backslash passes through.
+# Since the condition function 'make' accepts a pattern instead of a plain
+# target name, the '\' is finally discarded in Str_Match.
+.ifmake \!edge
+.else
+. error
+.endif
+
+# In a dependency line, a plain '!' is interpreted as a dependency operator
+# (the other two are ':' and '::'). If the '!' is escaped by a '\', as
+# implemented in ParseDependencyTargetWord, the additional backslash is never
+# removed though. The target name thus becomes '\!edge' instead of the
+# intended '!edge'. Defining a target whose name contains a '!' will either
+# require additional tricks, or it may even be impossible.
+
+first second unmentioned late-target \!edge:
: $@
diff --git a/contrib/bmake/unit-tests/directive-ifndef.exp b/contrib/bmake/unit-tests/directive-ifndef.exp
index c653f6344429..d018e78616bc 100644
--- a/contrib/bmake/unit-tests/directive-ifndef.exp
+++ b/contrib/bmake/unit-tests/directive-ifndef.exp
@@ -1,2 +1,2 @@
-make: "directive-ifndef.mk" line 10: guarded section
+make: "directive-ifndef.mk" line 14: guarded section
exit status 0
diff --git a/contrib/bmake/unit-tests/directive-ifndef.mk b/contrib/bmake/unit-tests/directive-ifndef.mk
index bf509ef8075e..44eec55b4a87 100644
--- a/contrib/bmake/unit-tests/directive-ifndef.mk
+++ b/contrib/bmake/unit-tests/directive-ifndef.mk
@@ -1,12 +1,16 @@
-# $NetBSD: directive-ifndef.mk,v 1.6 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: directive-ifndef.mk,v 1.9 2023/10/19 18:24:33 rillig Exp $
#
# Tests for the .ifndef directive, which can be used for multiple-inclusion
# guards. In contrast to C, where #ifndef and #define nicely line up the
# macro name, there is no such syntax in make. Therefore, it is more
# common to use .if !defined(GUARD) instead.
+#
+# See also:
+# directive-include-guard.mk
.ifndef GUARD
GUARD= # defined
+# expect+1: guarded section
. info guarded section
.endif
@@ -20,5 +24,64 @@ GUARD= # defined
. info guarded section
.endif
+
+# The '.ifndef' directive can be used with multiple arguments, even negating
+# them. Since these conditions are confusing for humans, they should be
+# replaced with easier-to-understand plain '.if' directives.
+DEFINED=
+.ifndef UNDEFINED && UNDEFINED
+.else
+. error
+.endif
+.ifndef UNDEFINED && DEFINED
+. error
+.endif
+.ifndef DEFINED && DEFINED
+. error
+.endif
+.ifndef !UNDEFINED && !UNDEFINED
+. error
+.endif
+.ifndef !UNDEFINED && !DEFINED
+. error
+.endif
+.ifndef !DEFINED && !DEFINED
+.else
+. error
+.endif
+
+
+# The negation from the 'if-not-defined' directive only applies to bare words,
+# but not to numbers, quoted strings or expressions. Those are evaluated
+# without extra negation, just like in a plain '.if' directive.
+.ifndef 0
+. error
+.endif
+.ifndef 1
+.else
+. error
+.endif
+.ifndef ""
+. error
+.endif
+.ifndef "word"
+.else
+. error
+.endif
+.ifndef ${:UUNDEFINED}
+.else
+. error
+.endif
+.ifndef ${:UDEFINED}
+. error
+.endif
+.ifndef ${:U0}
+. error
+.endif
+.ifndef ${:U1}
+.else
+. error
+.endif
+
+
all:
- @:;
diff --git a/contrib/bmake/unit-tests/directive-include-fatal.exp b/contrib/bmake/unit-tests/directive-include-fatal.exp
index c8ca97a0fd5f..cae0fc97a59d 100755
--- a/contrib/bmake/unit-tests/directive-include-fatal.exp
+++ b/contrib/bmake/unit-tests/directive-include-fatal.exp
@@ -1,4 +1,4 @@
-make: "directive-include-fatal.mk" line 13: Malformed conditional (${UNDEF})
+make: "directive-include-fatal.mk" line 14: Malformed conditional (${UNDEF})
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/directive-include-fatal.mk b/contrib/bmake/unit-tests/directive-include-fatal.mk
index 6744f9e80e5c..d4ed26f2a4aa 100755
--- a/contrib/bmake/unit-tests/directive-include-fatal.mk
+++ b/contrib/bmake/unit-tests/directive-include-fatal.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-include-fatal.mk,v 1.3 2021/02/01 22:16:57 rillig Exp $
+# $NetBSD: directive-include-fatal.mk,v 1.4 2023/06/01 20:56:35 rillig Exp $
#
# Test for the .include directive combined with fatal errors.
#
@@ -10,6 +10,7 @@
# the "fatals" counter.
# Using an undefined variable in a condition generates a fatal error.
+# expect+1: Malformed conditional (${UNDEF})
.if ${UNDEF}
.endif
diff --git a/contrib/bmake/unit-tests/directive-include-guard.exp b/contrib/bmake/unit-tests/directive-include-guard.exp
new file mode 100644
index 000000000000..70d23a19fe7c
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-include-guard.exp
@@ -0,0 +1,104 @@
+Parse_PushInput: file variable-ifndef.tmp, line 1
+Skipping 'variable-ifndef.tmp' because 'VARIABLE_IFNDEF' is defined
+Parse_PushInput: file variable-ifndef-reuse.tmp, line 1
+Skipping 'variable-ifndef-reuse.tmp' because 'VARIABLE_IFNDEF' is defined
+Parse_PushInput: file variable-ifndef-zero.tmp, line 1
+Parse_PushInput: file variable-ifndef-zero.tmp, line 1
+Parse_PushInput: file variable-ifndef-one.tmp, line 1
+Parse_PushInput: file variable-ifndef-one.tmp, line 1
+Parse_PushInput: file comments.tmp, line 1
+Skipping 'comments.tmp' because 'COMMENTS' is defined
+Parse_PushInput: file variable-if.tmp, line 1
+Skipping 'variable-if.tmp' because 'VARIABLE_IF' is defined
+Parse_PushInput: file variable-if-reuse.tmp, line 1
+Skipping 'variable-if-reuse.tmp' because 'VARIABLE_IF' is defined
+Parse_PushInput: file variable-if-triple-negation.tmp, line 1
+Parse_PushInput: file variable-if-triple-negation.tmp, line 1
+Parse_PushInput: file variable-if-spaced.tmp, line 1
+Parse_PushInput: file variable-if-spaced.tmp, line 1
+Parse_PushInput: file variable-if-parenthesized.tmp, line 1
+Parse_PushInput: file variable-if-parenthesized.tmp, line 1
+Parse_PushInput: file variable-ifdef-negated.tmp, line 1
+Parse_PushInput: file variable-ifdef-negated.tmp, line 1
+Parse_PushInput: file variable-name-mismatch.tmp, line 1
+Parse_PushInput: file variable-name-mismatch.tmp, line 1
+Parse_PushInput: file variable-ifndef-parenthesized.tmp, line 1
+Parse_PushInput: file variable-ifndef-parenthesized.tmp, line 1
+Parse_PushInput: file variable-name-exclamation.tmp, line 1
+Parse_PushInput: file variable-name-exclamation.tmp, line 1
+Parse_PushInput: file variable-name-exclamation-middle.tmp, line 1
+Parse_PushInput: file variable-name-exclamation-middle.tmp, line 1
+Parse_PushInput: file variable-name-parentheses.tmp, line 1
+Parse_PushInput: file variable-name-parentheses.tmp, line 1
+Parse_PushInput: file variable-ifndef-plus.tmp, line 1
+Parse_PushInput: file variable-ifndef-plus.tmp, line 1
+Parse_PushInput: file variable-if-plus.tmp, line 1
+Parse_PushInput: file variable-if-plus.tmp, line 1
+Parse_PushInput: file variable-ifndef-indirect.tmp, line 1
+Parse_PushInput: file variable-ifndef-indirect.tmp, line 1
+Parse_PushInput: file variable-if-indirect.tmp, line 1
+Parse_PushInput: file variable-if-indirect.tmp, line 1
+Parse_PushInput: file variable-assign-indirect.tmp, line 1
+Skipping 'variable-assign-indirect.tmp' because 'VARIABLE_ASSIGN_INDIRECT' is defined
+Parse_PushInput: file variable-assign-late.tmp, line 1
+Skipping 'variable-assign-late.tmp' because 'VARIABLE_ASSIGN_LATE' is defined
+Parse_PushInput: file variable-assign-nested.tmp, line 1
+Parse_PushInput: .for loop in variable-assign-nested.tmp, line 3
+Skipping 'variable-assign-nested.tmp' because 'VARIABLE_ASSIGN_NESTED' is defined
+Parse_PushInput: file variable-already-defined.tmp, line 1
+Skipping 'variable-already-defined.tmp' because 'VARIABLE_ALREADY_DEFINED' is defined
+Parse_PushInput: file variable-defined-then-undefined.tmp, line 1
+Parse_PushInput: file variable-defined-then-undefined.tmp, line 1
+Parse_PushInput: file variable-two-times.tmp, line 1
+Parse_PushInput: file variable-two-times.tmp, line 1
+Parse_PushInput: file variable-clash.tmp, line 1
+Skipping 'variable-clash.tmp' because 'VARIABLE_IF' is defined
+Parse_PushInput: file variable-swapped.tmp, line 1
+Parse_PushInput: file variable-swapped.tmp, line 1
+Parse_PushInput: file variable-undef-between.tmp, line 1
+Parse_PushInput: file variable-undef-between.tmp, line 1
+Parse_PushInput: file variable-undef-inside.tmp, line 1
+Parse_PushInput: file variable-undef-inside.tmp, line 1
+Parse_PushInput: file variable-not-defined.tmp, line 1
+Parse_PushInput: file variable-not-defined.tmp, line 1
+Parse_PushInput: file elif.tmp, line 1
+Parse_PushInput: file elif.tmp, line 1
+Parse_PushInput: file elif-reuse.tmp, line 1
+Parse_PushInput: file elif-reuse.tmp, line 1
+Parse_PushInput: file else.tmp, line 1
+Parse_PushInput: file else.tmp, line 1
+Parse_PushInput: file else-reuse.tmp, line 1
+Parse_PushInput: file else-reuse.tmp, line 1
+Parse_PushInput: file inner-if-elif-else.tmp, line 1
+Skipping 'inner-if-elif-else.tmp' because 'INNER_IF_ELIF_ELSE' is defined
+Parse_PushInput: file target.tmp, line 1
+Skipping 'target.tmp' because '__target.tmp__' is defined
+Parse_PushInput: file target-sys.tmp, line 1
+Skipping 'target-sys.tmp' because '__<target-sys.tmp>__' is defined
+Parse_PushInput: file target-indirect.tmp, line 1
+Skipping 'target-indirect.tmp' because 'target-indirect.tmp' is defined
+Parse_PushInput: file target-indirect-PARSEFILE.tmp, line 1
+Skipping 'target-indirect-PARSEFILE.tmp' because '__target-indirect-PARSEFILE.tmp__' is defined
+Parse_PushInput: file target-indirect-PARSEFILE2.tmp, line 1
+Skipping 'target-indirect-PARSEFILE2.tmp' because '__target-indirect-PARSEFILE2.tmp__' is defined
+Parse_PushInput: file subdir/target-indirect-PARSEFILE.tmp, line 1
+Skipping 'subdir/target-indirect-PARSEFILE.tmp' because '__target-indirect-PARSEFILE.tmp__' is defined
+Parse_PushInput: file target-indirect-PARSEDIR-PARSEFILE.tmp, line 1
+Skipping 'target-indirect-PARSEDIR-PARSEFILE.tmp' because '__target-indirect-PARSEDIR-PARSEFILE.tmp__' is defined
+Parse_PushInput: file subdir/target-indirect-PARSEDIR-PARSEFILE.tmp, line 1
+Skipping 'subdir/target-indirect-PARSEDIR-PARSEFILE.tmp' because '__subdir/target-indirect-PARSEDIR-PARSEFILE.tmp__' is defined
+Parse_PushInput: file target-unguarded.tmp, line 1
+Parse_PushInput: file target-unguarded.tmp, line 1
+Parse_PushInput: file target-plus.tmp, line 1
+Parse_PushInput: file target-plus.tmp, line 1
+Parse_PushInput: file target-already-defined.tmp, line 1
+Skipping 'target-already-defined.tmp' because 'target-already-defined' is defined
+Parse_PushInput: file target-name-exclamation.tmp, line 1
+Parse_PushInput: file target-name-exclamation.tmp, line 1
+Parse_PushInput: file target-name-parenthesized.tmp, line 1
+Parse_PushInput: file target-name-parenthesized.tmp, line 1
+Parse_PushInput: file target-call-parenthesized.tmp, line 1
+Parse_PushInput: file target-call-parenthesized.tmp, line 1
+Parse_PushInput: file multiline.tmp, line 1
+Skipping 'multiline.tmp' because 'MULTILINE' is defined
+exit status 0
diff --git a/contrib/bmake/unit-tests/directive-include-guard.mk b/contrib/bmake/unit-tests/directive-include-guard.mk
new file mode 100644
index 000000000000..85c0242c2009
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-include-guard.mk
@@ -0,0 +1,638 @@
+# $NetBSD: directive-include-guard.mk,v 1.16 2023/12/17 14:07:22 rillig Exp $
+#
+# Tests for multiple-inclusion guards in makefiles.
+#
+# A file that is guarded by a multiple-inclusion guard has one of the
+# following forms:
+#
+# .ifndef GUARD_VARIABLE
+# .endif
+#
+# .if !defined(GUARD_VARIABLE)
+# .endif
+#
+# .if !target(guard-target)
+# .endif
+#
+# When such a file is included for the second or later time, and the guard
+# variable or the guard target is defined, the file is skipped completely, as
+# including it would not have any effect, not even on the special variable
+# '.MAKE.MAKEFILES', as that variable skips duplicate pathnames.
+#
+# See also:
+# https://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html
+
+# Each of the following test cases creates a temporary file named after the
+# test case and writes some lines of text to that file. That file is then
+# included twice, to see whether the second '.include' is skipped.
+
+
+# This is the canonical form of a variable-based multiple-inclusion guard.
+CASES+= variable-ifndef
+LINES.variable-ifndef= \
+ '.ifndef VARIABLE_IFNDEF' \
+ 'VARIABLE_IFNDEF=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-ifndef.tmp, line 1
+# expect: Skipping 'variable-ifndef.tmp' because 'VARIABLE_IFNDEF' is defined
+
+# A file that reuses a guard from a previous file (or whose guard is defined
+# for any other reason) is only processed once, to see whether it is guarded.
+# Its content is skipped, therefore the syntax error is not detected.
+CASES+= variable-ifndef-reuse
+LINES.variable-ifndef-reuse= \
+ '.ifndef VARIABLE_IFNDEF' \
+ 'syntax error' \
+ '.endif'
+# expect: Parse_PushInput: file variable-ifndef-reuse.tmp, line 1
+# expect: Skipping 'variable-ifndef-reuse.tmp' because 'VARIABLE_IFNDEF' is defined
+
+# The guard variable cannot be a number, as numbers are interpreted
+# differently from bare words.
+CASES+= variable-ifndef-zero
+LINES.variable-ifndef-zero= \
+ '.ifndef 0e0' \
+ 'syntax error' \
+ '.endif'
+# expect: Parse_PushInput: file variable-ifndef-zero.tmp, line 1
+# expect: Parse_PushInput: file variable-ifndef-zero.tmp, line 1
+
+# The guard variable cannot be a number, as numbers are interpreted
+# differently from bare words.
+CASES+= variable-ifndef-one
+LINES.variable-ifndef-one= \
+ '.ifndef 1' \
+ '.endif'
+# expect: Parse_PushInput: file variable-ifndef-one.tmp, line 1
+# expect: Parse_PushInput: file variable-ifndef-one.tmp, line 1
+
+# Comments and empty lines do not affect the multiple-inclusion guard.
+CASES+= comments
+LINES.comments= \
+ '\# comment' \
+ '' \
+ '.ifndef COMMENTS' \
+ '\# comment' \
+ 'COMMENTS=\#comment' \
+ '.endif' \
+ '\# comment'
+# expect: Parse_PushInput: file comments.tmp, line 1
+# expect: Skipping 'comments.tmp' because 'COMMENTS' is defined
+
+# An alternative form uses the 'defined' function. It is more verbose than
+# the canonical form but avoids the '.ifndef' directive, as that directive is
+# not commonly used.
+CASES+= variable-if
+LINES.variable-if= \
+ '.if !defined(VARIABLE_IF)' \
+ 'VARIABLE_IF=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-if.tmp, line 1
+# expect: Skipping 'variable-if.tmp' because 'VARIABLE_IF' is defined
+
+# A file that reuses a guard from a previous file (or whose guard is defined
+# for any other reason) is only processed once, to see whether it is guarded.
+# Its content is skipped, therefore the syntax error is not detected.
+CASES+= variable-if-reuse
+LINES.variable-if-reuse= \
+ '.if !defined(VARIABLE_IF)' \
+ 'syntax error' \
+ '.endif'
+# expect: Parse_PushInput: file variable-if-reuse.tmp, line 1
+# expect: Skipping 'variable-if-reuse.tmp' because 'VARIABLE_IF' is defined
+
+# Triple negation is so uncommon that it's not recognized, even though it has
+# the same effect as a single negation.
+CASES+= variable-if-triple-negation
+LINES.variable-if-triple-negation= \
+ '.if !!!defined(VARIABLE_IF_TRIPLE_NEGATION)' \
+ 'VARIABLE_IF_TRIPLE_NEGATION=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-if-triple-negation.tmp, line 1
+# expect: Parse_PushInput: file variable-if-triple-negation.tmp, line 1
+
+# If the guard variable is enclosed in spaces, it does not have an effect, as
+# that form is not common in practice.
+CASES+= variable-if-spaced
+LINES.variable-if-spaced= \
+ '.if !defined( VARIABLE_IF_SPACED )' \
+ 'VARIABLE_IF_SPACED=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-if-spaced.tmp, line 1
+# expect: Parse_PushInput: file variable-if-spaced.tmp, line 1
+
+# If the guard variable condition is enclosed in parentheses, it does not have
+# an effect, as that form is not common in practice.
+CASES+= variable-if-parenthesized
+LINES.variable-if-parenthesized= \
+ '.if (!defined(VARIABLE_IF_PARENTHESIZED))' \
+ 'VARIABLE_IF_PARENTHESIZED=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-if-parenthesized.tmp, line 1
+# expect: Parse_PushInput: file variable-if-parenthesized.tmp, line 1
+
+# A conditional other than '.if' or '.ifndef' does not guard the file, even if
+# it is otherwise equivalent to the above accepted forms.
+CASES+= variable-ifdef-negated
+LINES.variable-ifdef-negated= \
+ '.ifdef !VARIABLE_IFDEF_NEGATED' \
+ 'VARIABLE_IFDEF_NEGATED=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-ifdef-negated.tmp, line 1
+# expect: Parse_PushInput: file variable-ifdef-negated.tmp, line 1
+
+# The variable names in the '.if' and the assignment must be the same.
+CASES+= variable-name-mismatch
+LINES.variable-name-mismatch= \
+ '.ifndef VARIABLE_NAME_MISMATCH' \
+ 'VARIABLE_NAME_DIFFERENT=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-name-mismatch.tmp, line 1
+# expect: Parse_PushInput: file variable-name-mismatch.tmp, line 1
+
+# If the guard variable condition is enclosed in parentheses, it does not have
+# an effect, as that form is not common in practice.
+CASES+= variable-ifndef-parenthesized
+LINES.variable-ifndef-parenthesized= \
+ '.ifndef (VARIABLE_IFNDEF_PARENTHESIZED)' \
+ 'VARIABLE_IFNDEF_PARENTHESIZED=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-ifndef-parenthesized.tmp, line 1
+# expect: Parse_PushInput: file variable-ifndef-parenthesized.tmp, line 1
+
+# The variable name '!VARNAME' cannot be used in an '.ifndef' directive, as
+# the '!' would be a negation. It is syntactically valid in a '.if !defined'
+# condition, but this case is so uncommon that the guard mechanism doesn't
+# accept '!' in the guard variable name. Furthermore, when defining the
+# variable, the character '!' has to be escaped, to prevent it from being
+# interpreted as the '!' dependency operator.
+CASES+= variable-name-exclamation
+LINES.variable-name-exclamation= \
+ '.if !defined(!VARIABLE_NAME_EXCLAMATION)' \
+ '${:U!}VARIABLE_NAME_EXCLAMATION=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-name-exclamation.tmp, line 1
+# expect: Parse_PushInput: file variable-name-exclamation.tmp, line 1
+
+# In general, a variable name can contain a '!' in the middle, as that
+# character is interpreted as an ordinary character in conditions as well as
+# on the left side of a variable assignment. For guard variable names, the
+# '!' is not supported in any place, though.
+CASES+= variable-name-exclamation-middle
+LINES.variable-name-exclamation-middle= \
+ '.ifndef VARIABLE_NAME!MIDDLE' \
+ 'VARIABLE_NAME!MIDDLE=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-name-exclamation-middle.tmp, line 1
+# expect: Parse_PushInput: file variable-name-exclamation-middle.tmp, line 1
+
+# A variable name can contain balanced parentheses, at least in conditions and
+# on the left side of a variable assignment. There are enough places in make
+# where parentheses or braces are handled inconsistently to make this naming
+# choice a bad idea, therefore these characters are not allowed in guard
+# variable names.
+CASES+= variable-name-parentheses
+LINES.variable-name-parentheses= \
+ '.ifndef VARIABLE_NAME(&)PARENTHESES' \
+ 'VARIABLE_NAME(&)PARENTHESES=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-name-parentheses.tmp, line 1
+# expect: Parse_PushInput: file variable-name-parentheses.tmp, line 1
+
+# The guard condition must consist of only the guard variable, nothing else.
+CASES+= variable-ifndef-plus
+LINES.variable-ifndef-plus= \
+ '.ifndef VARIABLE_IFNDEF_PLUS && VARIABLE_IFNDEF_SECOND' \
+ 'VARIABLE_IFNDEF_PLUS=' \
+ 'VARIABLE_IFNDEF_SECOND=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-ifndef-plus.tmp, line 1
+# expect: Parse_PushInput: file variable-ifndef-plus.tmp, line 1
+
+# The guard condition must consist of only the guard variable, nothing else.
+CASES+= variable-if-plus
+LINES.variable-if-plus= \
+ '.if !defined(VARIABLE_IF_PLUS) && !defined(VARIABLE_IF_SECOND)' \
+ 'VARIABLE_IF_PLUS=' \
+ 'VARIABLE_IF_SECOND=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-if-plus.tmp, line 1
+# expect: Parse_PushInput: file variable-if-plus.tmp, line 1
+
+# The variable name in an '.ifndef' guard must be given directly, it must not
+# contain any '$' expression.
+CASES+= variable-ifndef-indirect
+LINES.variable-ifndef-indirect= \
+ '.ifndef $${VARIABLE_IFNDEF_INDIRECT:L}' \
+ 'VARIABLE_IFNDEF_INDIRECT=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-ifndef-indirect.tmp, line 1
+# expect: Parse_PushInput: file variable-ifndef-indirect.tmp, line 1
+
+# The variable name in an '.if' guard must be given directly, it must not
+# contain any '$' expression.
+CASES+= variable-if-indirect
+LINES.variable-if-indirect= \
+ '.if !defined($${VARIABLE_IF_INDIRECT:L})' \
+ 'VARIABLE_IF_INDIRECT=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-if-indirect.tmp, line 1
+# expect: Parse_PushInput: file variable-if-indirect.tmp, line 1
+
+# The variable name in the guard condition must only contain alphanumeric
+# characters and underscores. The place where the guard variable is defined
+# is more flexible, as long as the variable is defined at the point where the
+# file is included the next time.
+CASES+= variable-assign-indirect
+LINES.variable-assign-indirect= \
+ '.ifndef VARIABLE_ASSIGN_INDIRECT' \
+ '$${VARIABLE_ASSIGN_INDIRECT:L}=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-assign-indirect.tmp, line 1
+# expect: Skipping 'variable-assign-indirect.tmp' because 'VARIABLE_ASSIGN_INDIRECT' is defined
+
+# The time at which the guard variable is defined doesn't matter, as long as
+# it is defined at the point where the file is included the next time.
+CASES+= variable-assign-late
+LINES.variable-assign-late= \
+ '.ifndef VARIABLE_ASSIGN_LATE' \
+ 'VARIABLE_ASSIGN_LATE_OTHER=' \
+ 'VARIABLE_ASSIGN_LATE=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-assign-late.tmp, line 1
+# expect: Skipping 'variable-assign-late.tmp' because 'VARIABLE_ASSIGN_LATE' is defined
+
+# The time at which the guard variable is defined doesn't matter, as long as
+# it is defined at the point where the file is included the next time.
+CASES+= variable-assign-nested
+LINES.variable-assign-nested= \
+ '.ifndef VARIABLE_ASSIGN_NESTED' \
+ '. if 1' \
+ '. for i in once' \
+ 'VARIABLE_ASSIGN_NESTED=' \
+ '. endfor' \
+ '. endif' \
+ '.endif'
+# expect: Parse_PushInput: file variable-assign-nested.tmp, line 1
+# expect: Skipping 'variable-assign-nested.tmp' because 'VARIABLE_ASSIGN_NESTED' is defined
+
+# If the guard variable is defined before the file is included for the first
+# time, the file is considered guarded as well. In such a case, the parser
+# skips almost all lines, as they are irrelevant, but the structure of the
+# top-level '.if/.endif' conditional can be determined reliably enough to
+# decide whether the file is guarded.
+CASES+= variable-already-defined
+LINES.variable-already-defined= \
+ '.ifndef VARIABLE_ALREADY_DEFINED' \
+ 'VARIABLE_ALREADY_DEFINED=' \
+ '.endif'
+VARIABLE_ALREADY_DEFINED=
+# expect: Parse_PushInput: file variable-already-defined.tmp, line 1
+# expect: Skipping 'variable-already-defined.tmp' because 'VARIABLE_ALREADY_DEFINED' is defined
+
+# If the guard variable is defined before the file is included the first time,
+# the file is processed but its content is skipped. If that same guard
+# variable is undefined when the file is included the second time, the file is
+# processed as usual.
+CASES+= variable-defined-then-undefined
+LINES.variable-defined-then-undefined= \
+ '.ifndef VARIABLE_DEFINED_THEN_UNDEFINED' \
+ '.endif'
+VARIABLE_DEFINED_THEN_UNDEFINED=
+UNDEF_BETWEEN.variable-defined-then-undefined= \
+ VARIABLE_DEFINED_THEN_UNDEFINED
+# expect: Parse_PushInput: file variable-defined-then-undefined.tmp, line 1
+# expect: Parse_PushInput: file variable-defined-then-undefined.tmp, line 1
+
+# The whole file content must be guarded by a single '.if' conditional, not by
+# several, as each of these conditionals would require its separate guard.
+# This case is not expected to occur in practice, as the two parts would
+# rather be split into separate files.
+CASES+= variable-two-times
+LINES.variable-two-times= \
+ '.ifndef VARIABLE_TWO_TIMES_1' \
+ 'VARIABLE_TWO_TIMES_1=' \
+ '.endif' \
+ '.ifndef VARIABLE_TWO_TIMES_2' \
+ 'VARIABLE_TWO_TIMES_2=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-two-times.tmp, line 1
+# expect: Parse_PushInput: file variable-two-times.tmp, line 1
+
+# When multiple files use the same guard variable name, the optimization of
+# skipping the file affects each of these files.
+#
+# Choosing unique guard names is the responsibility of the makefile authors.
+# A typical pattern of guard variable names is '${PROJECT}_${DIR}_${FILE}_MK'.
+# System-provided files typically start the guard names with '_'.
+CASES+= variable-clash
+LINES.variable-clash= \
+ ${LINES.variable-if}
+# expect: Parse_PushInput: file variable-clash.tmp, line 1
+# expect: Skipping 'variable-clash.tmp' because 'VARIABLE_IF' is defined
+
+# The conditional must come before the assignment, otherwise the conditional
+# is useless, as it always evaluates to false.
+CASES+= variable-swapped
+LINES.variable-swapped= \
+ 'SWAPPED=' \
+ '.ifndef SWAPPED' \
+ '. error' \
+ '.endif'
+# expect: Parse_PushInput: file variable-swapped.tmp, line 1
+# expect: Parse_PushInput: file variable-swapped.tmp, line 1
+
+# If the guard variable is undefined between the first and the second time the
+# file is included, the guarded file is included again.
+CASES+= variable-undef-between
+LINES.variable-undef-between= \
+ '.ifndef VARIABLE_UNDEF_BETWEEN' \
+ 'VARIABLE_UNDEF_BETWEEN=' \
+ '.endif'
+UNDEF_BETWEEN.variable-undef-between= \
+ VARIABLE_UNDEF_BETWEEN
+# expect: Parse_PushInput: file variable-undef-between.tmp, line 1
+# expect: Parse_PushInput: file variable-undef-between.tmp, line 1
+
+# If the guard variable is undefined while the file is included the first
+# time, the guard does not have an effect, and the file is included again.
+CASES+= variable-undef-inside
+LINES.variable-undef-inside= \
+ '.ifndef VARIABLE_UNDEF_INSIDE' \
+ 'VARIABLE_UNDEF_INSIDE=' \
+ '.undef VARIABLE_UNDEF_INSIDE' \
+ '.endif'
+# expect: Parse_PushInput: file variable-undef-inside.tmp, line 1
+# expect: Parse_PushInput: file variable-undef-inside.tmp, line 1
+
+# If the file does not define the guard variable, the guard does not have an
+# effect, and the file is included again.
+CASES+= variable-not-defined
+LINES.variable-not-defined= \
+ '.ifndef VARIABLE_NOT_DEFINED' \
+ '.endif'
+# expect: Parse_PushInput: file variable-not-defined.tmp, line 1
+# expect: Parse_PushInput: file variable-not-defined.tmp, line 1
+
+# The outermost '.if' must not have an '.elif' branch.
+CASES+= elif
+LINES.elif= \
+ '.ifndef ELIF' \
+ 'ELIF=' \
+ '.elif 1' \
+ '.endif'
+# expect: Parse_PushInput: file elif.tmp, line 1
+# expect: Parse_PushInput: file elif.tmp, line 1
+
+# When a file with an '.if/.elif/.endif' conditional at the top level is
+# included, it is never optimized, as one of its branches is taken.
+CASES+= elif-reuse
+LINES.elif-reuse= \
+ '.ifndef ELIF' \
+ 'syntax error' \
+ '.elif 1' \
+ '.endif'
+# expect: Parse_PushInput: file elif-reuse.tmp, line 1
+# expect: Parse_PushInput: file elif-reuse.tmp, line 1
+
+# The outermost '.if' must not have an '.else' branch.
+CASES+= else
+LINES.else= \
+ '.ifndef ELSE' \
+ 'ELSE=' \
+ '.else' \
+ '.endif'
+# expect: Parse_PushInput: file else.tmp, line 1
+# expect: Parse_PushInput: file else.tmp, line 1
+
+# When a file with an '.if/.else/.endif' conditional at the top level is
+# included, it is never optimized, as one of its branches is taken.
+CASES+= else-reuse
+LINES.else-reuse= \
+ '.ifndef ELSE' \
+ 'syntax error' \
+ '.else' \
+ '.endif'
+# expect: Parse_PushInput: file else-reuse.tmp, line 1
+# expect: Parse_PushInput: file else-reuse.tmp, line 1
+
+# The inner '.if' directives may have an '.elif' or '.else', and it doesn't
+# matter which of their branches are taken.
+CASES+= inner-if-elif-else
+LINES.inner-if-elif-else= \
+ '.ifndef INNER_IF_ELIF_ELSE' \
+ 'INNER_IF_ELIF_ELSE=' \
+ '. if 0' \
+ '. elif 0' \
+ '. else' \
+ '. endif' \
+ '. if 0' \
+ '. elif 1' \
+ '. else' \
+ '. endif' \
+ '. if 1' \
+ '. elif 1' \
+ '. else' \
+ '. endif' \
+ '.endif'
+# expect: Parse_PushInput: file inner-if-elif-else.tmp, line 1
+# expect: Skipping 'inner-if-elif-else.tmp' because 'INNER_IF_ELIF_ELSE' is defined
+
+# The guard can also be a target instead of a variable. Using a target as a
+# guard has the benefit that a target cannot be undefined once it is defined.
+# The target should be declared '.NOTMAIN'. Since the target names are
+# usually chosen according to a pattern that doesn't interfere with real
+# target names, they don't need to be declared '.PHONY' as they don't generate
+# filesystem operations.
+CASES+= target
+LINES.target= \
+ '.if !target(__target.tmp__)' \
+ '__target.tmp__: .NOTMAIN' \
+ '.endif'
+# expect: Parse_PushInput: file target.tmp, line 1
+# expect: Skipping 'target.tmp' because '__target.tmp__' is defined
+
+# When used for system files, the target name may include '<' and '>', for
+# symmetry with the '.include <sys.mk>' directive. The characters '<' and '>'
+# are ordinary characters.
+CASES+= target-sys
+LINES.target-sys= \
+ '.if !target(__<target-sys.tmp>__)' \
+ '__<target-sys.tmp>__: .NOTMAIN' \
+ '.endif'
+# expect: Parse_PushInput: file target-sys.tmp, line 1
+# expect: Skipping 'target-sys.tmp' because '__<target-sys.tmp>__' is defined
+
+# The target name may include variable references. These references are
+# expanded as usual. Due to the current implementation, the expressions are
+# evaluated twice: Once for checking whether the condition evaluates to true,
+# and once for determining the guard name. This double evaluation should not
+# matter in practice, as guard expressions are expected to be simple,
+# deterministic and without side effects.
+CASES+= target-indirect
+LINES.target-indirect= \
+ '.if !target($${target-indirect.tmp:L})' \
+ 'target-indirect.tmp: .NOTMAIN' \
+ '.endif'
+# expect: Parse_PushInput: file target-indirect.tmp, line 1
+# expect: Skipping 'target-indirect.tmp' because 'target-indirect.tmp' is defined
+
+# A common form of guard target is __${.PARSEFILE}__. This form can only be
+# used if all files using this form have unique basenames. To get a robust
+# pattern based on the same idea, use __${.PARSEDIR}/${.PARSEFILE}__ instead.
+# This form does not work when the basename contains whitespace characters, as
+# it is not possible to define a target with whitespace, not even by cheating.
+CASES+= target-indirect-PARSEFILE
+LINES.target-indirect-PARSEFILE= \
+ '.if !target(__$${.PARSEFILE}__)' \
+ '__$${.PARSEFILE}__: .NOTMAIN' \
+ '.endif'
+# expect: Parse_PushInput: file target-indirect-PARSEFILE.tmp, line 1
+# expect: Skipping 'target-indirect-PARSEFILE.tmp' because '__target-indirect-PARSEFILE.tmp__' is defined
+
+# Two files with different basenames can both use the same syntactic pattern
+# for the target guard name, as the expressions expand to different strings.
+CASES+= target-indirect-PARSEFILE2
+LINES.target-indirect-PARSEFILE2= \
+ '.if !target(__$${.PARSEFILE}__)' \
+ '__$${.PARSEFILE}__: .NOTMAIN' \
+ '.endif'
+# expect: Parse_PushInput: file target-indirect-PARSEFILE2.tmp, line 1
+# expect: Skipping 'target-indirect-PARSEFILE2.tmp' because '__target-indirect-PARSEFILE2.tmp__' is defined
+
+# Using plain .PARSEFILE without .PARSEDIR leads to name clashes. The include
+# guard is the same as in the test case 'target-indirect-PARSEFILE', as the
+# guard name only contains the basename but not the directory name. So even
+# without defining the guard target, the file is considered guarded.
+CASES+= subdir/target-indirect-PARSEFILE
+LINES.subdir/target-indirect-PARSEFILE= \
+ '.if !target(__$${.PARSEFILE}__)' \
+ '.endif'
+# expect: Parse_PushInput: file subdir/target-indirect-PARSEFILE.tmp, line 1
+# expect: Skipping 'subdir/target-indirect-PARSEFILE.tmp' because '__target-indirect-PARSEFILE.tmp__' is defined
+
+# Another common form of guard target is __${.PARSEDIR}/${.PARSEFILE}__
+# or __${.PARSEDIR:tA}/${.PARSEFILE}__ to be truly unique.
+CASES+= target-indirect-PARSEDIR-PARSEFILE
+LINES.target-indirect-PARSEDIR-PARSEFILE= \
+ '.if !target(__$${.PARSEDIR}/$${.PARSEFILE}__)' \
+ '__$${.PARSEDIR}/$${.PARSEFILE}__: .NOTMAIN' \
+ '.endif'
+# expect: Parse_PushInput: file target-indirect-PARSEDIR-PARSEFILE.tmp, line 1
+# expect: Skipping 'target-indirect-PARSEDIR-PARSEFILE.tmp' because '__target-indirect-PARSEDIR-PARSEFILE.tmp__' is defined
+# The actual target starts with '__${.OBJDIR}/', see the .rawout file, but the
+# string '${.OBJDIR}/' gets stripped in post processing.
+
+# Using the combination of '.PARSEDIR' and '.PARSEFILE', a file in a
+# subdirectory gets a different guard target name than the previous one.
+CASES+= subdir/target-indirect-PARSEDIR-PARSEFILE
+LINES.subdir/target-indirect-PARSEDIR-PARSEFILE= \
+ '.if !target(__$${.PARSEDIR}/$${.PARSEFILE}__)' \
+ '__$${.PARSEDIR}/$${.PARSEFILE}__: .NOTMAIN' \
+ '.endif'
+# expect: Parse_PushInput: file subdir/target-indirect-PARSEDIR-PARSEFILE.tmp, line 1
+# expect: Skipping 'subdir/target-indirect-PARSEDIR-PARSEFILE.tmp' because '__subdir/target-indirect-PARSEDIR-PARSEFILE.tmp__' is defined
+# The actual target starts with '__${.OBJDIR}/', see the .rawout file, but the
+# string '${.OBJDIR}/' gets stripped in post processing.
+
+# If the guard target is not defined when including the file the next time,
+# the file is processed again.
+CASES+= target-unguarded
+LINES.target-unguarded= \
+ '.if !target(target-unguarded)' \
+ '.endif'
+# expect: Parse_PushInput: file target-unguarded.tmp, line 1
+# expect: Parse_PushInput: file target-unguarded.tmp, line 1
+
+# The guard condition must consist of only the guard target, nothing else.
+CASES+= target-plus
+LINES.target-plus= \
+ '.if !target(target-plus) && 1' \
+ 'target-plus: .NOTMAIN' \
+ '.endif'
+# expect: Parse_PushInput: file target-plus.tmp, line 1
+# expect: Parse_PushInput: file target-plus.tmp, line 1
+
+# If the guard target is defined before the file is included the first time,
+# the file is read once and then considered guarded.
+CASES+= target-already-defined
+LINES.target-already-defined= \
+ '.if !target(target-already-defined)' \
+ 'target-already-defined: .NOTMAIN' \
+ '.endif'
+target-already-defined: .NOTMAIN
+# expect: Parse_PushInput: file target-already-defined.tmp, line 1
+# expect: Skipping 'target-already-defined.tmp' because 'target-already-defined' is defined
+
+# A target name cannot contain the character '!'. In the condition, the '!'
+# is syntactically valid, but in the dependency declaration line, the '!' is
+# interpreted as the '!' dependency operator, no matter whether it occurs at
+# the beginning or in the middle of a target name. Escaping it as '${:U!}'
+# doesn't work, as the whole line is first expanded and then scanned for the
+# dependency operator. Escaping it as '\!' doesn't work either, even though
+# the '\' escapes the '!' from being a dependency operator, but when reading
+# the target name, the '\' is kept, resulting in the target name
+# '\!target-name-exclamation' instead of '!target-name-exclamation'.
+CASES+= target-name-exclamation
+LINES.target-name-exclamation= \
+ '.if !target(!target-name-exclamation)' \
+ '\!target-name-exclamation: .NOTMAIN' \
+ '.endif'
+# expect: Parse_PushInput: file target-name-exclamation.tmp, line 1
+# expect: Parse_PushInput: file target-name-exclamation.tmp, line 1
+
+# If the guard target name is enclosed in spaces, it does not have an effect,
+# as that form is not common in practice.
+CASES+= target-name-parenthesized
+LINES.target-name-parenthesized= \
+ '.if !target( target-name-parenthesized )' \
+ 'target-name-parenthesized: .NOTMAIN' \
+ '.endif'
+# expect: Parse_PushInput: file target-name-parenthesized.tmp, line 1
+# expect: Parse_PushInput: file target-name-parenthesized.tmp, line 1
+
+# If the guard target condition is enclosed in parentheses, it does not have
+# an effect, as that form is not common in practice.
+CASES+= target-call-parenthesized
+LINES.target-call-parenthesized= \
+ '.if (!target(target-call-parenthesized))' \
+ 'target-call-parenthesized: .NOTMAIN' \
+ '.endif'
+# expect: Parse_PushInput: file target-call-parenthesized.tmp, line 1
+# expect: Parse_PushInput: file target-call-parenthesized.tmp, line 1
+
+# If the '.if' or '.ifndef' directive spans more than a single line, it is
+# still recognized as a guard condition. This case is entirely uncommon, but
+# at the point where the guard condition is checked, line continuations have
+# already been converted to spaces.
+CASES+= multiline
+LINES.multiline= \
+ '.\' \
+ ' ifndef \' \
+ ' MULTILINE' \
+ 'MULTILINE=' \
+ '.endif'
+# expect: Parse_PushInput: file multiline.tmp, line 1
+# expect: Skipping 'multiline.tmp' because 'MULTILINE' is defined
+
+
+# Now run all test cases by including each of the files twice and looking at
+# the debug output. The files that properly guard against multiple inclusion
+# generate a 'Skipping' line, the others repeat the 'Parse_PushInput' line.
+#
+# Some debug output lines are suppressed in the .exp file, see ./Makefile.
+.for i in ${CASES}
+. for fname in $i.tmp
+_:= ${fname:H:N.:@dir@${:!mkdir -p ${dir}!}@}
+_!= printf '%s\n' ${LINES.$i} > ${fname}
+.MAKEFLAGS: -dp
+.include "${.CURDIR}/${fname}"
+.undef ${UNDEF_BETWEEN.$i:U}
+.include "${.CURDIR}/${fname}"
+.MAKEFLAGS: -d0
+_!= rm ${fname}
+_:= ${fname:H:N.:@dir@${:!rmdir ${dir}!}@}
+. endfor
+.endfor
+
+all:
diff --git a/contrib/bmake/unit-tests/directive-include.exp b/contrib/bmake/unit-tests/directive-include.exp
index af56eefb2b88..6454b6835edf 100755
--- a/contrib/bmake/unit-tests/directive-include.exp
+++ b/contrib/bmake/unit-tests/directive-include.exp
@@ -1,8 +1,13 @@
CondParser_Eval: ${.MAKE.MAKEFILES:T} != "${.PARSEFILE} null"
-lhs = "directive-include.mk null", rhs = "directive-include.mk null", op = !=
+Comparing "directive-include.mk null" != "directive-include.mk null"
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
+Comparing "directive-include.mk null" != "directive-include.mk null"
+make: "directive-include.mk" line 26: Could not find nonexistent.mk
+make: "directive-include.mk" line 49: Could not find "
+make: "directive-include.mk" line 56: while evaluating "${:U123:Z}.mk": Unknown modifier "Z"
+make: "directive-include.mk" line 56: Could not find nonexistent.mk
+make: "directive-include.mk" line 61: Cannot open /nonexistent
+make: "directive-include.mk" line 66: Invalid line 'include'
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..14d00600cb8f 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.14 2024/04/20 10:18:55 rillig Exp $
#
# Tests for the .include directive, which includes another file.
@@ -22,6 +22,7 @@
. error
.endif
+# expect+1: Could not find nonexistent.mk
.include "nonexistent.mk"
.include "/dev/null" # size 0
# including a directory technically succeeds, but shouldn't.
@@ -30,5 +31,59 @@
# 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= "
+# expect+1: Could not find "
+.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.
+# expect+2: Could not find nonexistent.mk
+# expect+1: while evaluating "${:U123:Z}.mk": Unknown modifier "Z"
+.include "nonexistent${:U123:Z}.mk"
+
+# The traditional include directive is seldom used.
+include /dev/null # comment
+# expect+1: Cannot open /nonexistent
+include /nonexistent # comment
+sinclude /nonexistent # comment
+include ${:U/dev/null} # comment
+include /dev/null /dev/null
+# expect+1: Invalid line 'include'
+include
+
+# XXX: trailing whitespace in diagnostic, missing quotes around filename
+### TODO: expect+1: Could not find
+# The following include directive behaves differently, depending on whether
+# the current file has a slash or is a relative filename. In the first case,
+# make opens the directory of the current file and tries to read from it,
+# resulting in the error message """ line 1: Zero byte read from file".
+# In the second case, the error message is "Could not find ", without quotes
+# or any other indicator for the empty filename at the end of the line.
+#include ${:U}
+
+
+# Since parse.c 1.612 from 2022-01-01 and before parse.c 1.620 from
+# 2022-01-07, including an empty regular file called bmake_malloc(0), which
+# may return a null pointer. On OpenBSD, this led to a segmentation fault in
+# Buf_InitSize, which assumes that bmake_malloc never returns NULL, just like
+# all other places in the code.
+_!= > directive-include-empty
+.include "${.CURDIR}/directive-include-empty"
+_!= rm directive-include-empty
+
+
all:
- @:;
diff --git a/contrib/bmake/unit-tests/directive-info.exp b/contrib/bmake/unit-tests/directive-info.exp
index 2652c191460c..397d9d31ef38 100644
--- a/contrib/bmake/unit-tests/directive-info.exp
+++ b/contrib/bmake/unit-tests/directive-info.exp
@@ -1,15 +1,15 @@
-make: "directive-info.mk" line 11: begin .info tests
-make: "directive-info.mk" line 12: Unknown directive "inf"
-make: "directive-info.mk" line 13: Missing argument for ".info"
-make: "directive-info.mk" line 14: message
-make: "directive-info.mk" line 15: indented message
-make: "directive-info.mk" line 16: Unknown directive "information"
-make: "directive-info.mk" line 17: Unknown directive "information"
-make: "directive-info.mk" line 22: Missing argument for ".info"
-make: "directive-info.mk" line 23: Missing argument for ".info"
-make: "directive-info.mk" line 26: Unknown directive "info-message"
-make: "directive-info.mk" line 27: no-target: no-source
-make: "directive-info.mk" line 36: expect line 30 for multi-line message
+make: "directive-info.mk" line 12: begin .info tests
+make: "directive-info.mk" line 14: Unknown directive "inf"
+make: "directive-info.mk" line 16: Missing argument for ".info"
+make: "directive-info.mk" line 18: message
+make: "directive-info.mk" line 20: indented message
+make: "directive-info.mk" line 22: Unknown directive "information"
+make: "directive-info.mk" line 24: Unknown directive "information"
+make: "directive-info.mk" line 30: Missing argument for ".info"
+make: "directive-info.mk" line 32: Missing argument for ".info"
+make: "directive-info.mk" line 36: Unknown directive "info-message"
+make: "directive-info.mk" line 38: no-target: no-source
+make: "directive-info.mk" line 47: expect line 35 for multi-line message
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/directive-info.mk b/contrib/bmake/unit-tests/directive-info.mk
index 5feea0cde565..ab550555d44f 100644
--- a/contrib/bmake/unit-tests/directive-info.mk
+++ b/contrib/bmake/unit-tests/directive-info.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-info.mk,v 1.8 2020/12/19 22:33:11 rillig Exp $
+# $NetBSD: directive-info.mk,v 1.11 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the .info directive.
#
@@ -8,32 +8,42 @@
# TODO: Implementation
+# expect+1: begin .info tests
.info begin .info tests
+# expect+1: Unknown directive "inf"
.inf # misspelled
-.info # "Missing argument"
+# expect+1: Missing argument for ".info"
+.info
+# expect+1: message
.info message
+# expect+1: indented message
.info indented message
+# expect+1: Unknown directive "information"
.information
+# expect+1: Unknown directive "information"
.information message # Accepted before 2020-12-13 01:07:54.
.info.man: # not a message, but possibly a suffix rule
# Even if lines would have trailing whitespace, this would be trimmed by
-# ParseGetLine.
+# ParseRawLine.
+# expect+1: Missing argument for ".info"
.info
+# expect+1: Missing argument for ".info"
.info # comment
.info: message # This is a dependency declaration.
+# expect+1: Unknown directive "info-message"
.info-message # This is an unknown directive.
+# expect+1: no-target: no-source
.info no-target: no-source # This is a .info directive, not a dependency.
# See directive.mk for more tests of this kind.
-# Since at least 2002-01-01, the line number that is used in error messages
-# and the .info directives is the number of completely read lines. For the
-# following multi-line directive, this means that the reported line number is
-# the one of the last line, not the first line.
-.info expect line 30 for\
+# Since at least 2002-01-01 and before parse.c 1.639 from 2022-01-08, the line
+# number that is used in error messages and the .info directives was the
+# number of completely read lines. For the following multi-line directive,
+# this meant that the reported line number was the one of the last line, not
+# of the first line.
+# expect+1: expect line 35 for multi-line message
+.info expect line 35 for\
multi$\
-line message
-
-all:
- @:;
diff --git a/contrib/bmake/unit-tests/directive-misspellings.exp b/contrib/bmake/unit-tests/directive-misspellings.exp
index e51d8473b305..a00615926740 100644
--- a/contrib/bmake/unit-tests/directive-misspellings.exp
+++ b/contrib/bmake/unit-tests/directive-misspellings.exp
@@ -1,45 +1,45 @@
-make: "directive-misspellings.mk" line 12: Unknown directive "dinclud"
-make: "directive-misspellings.mk" line 14: Unknown directive "dincludx"
-make: "directive-misspellings.mk" line 15: .include filename must be delimited by '"' or '<'
-make: "directive-misspellings.mk" line 17: Unknown directive "erro"
-make: "directive-misspellings.mk" line 18: Unknown directive "errox"
-make: "directive-misspellings.mk" line 22: Unknown directive "expor"
-make: "directive-misspellings.mk" line 24: Unknown directive "exporx"
-make: "directive-misspellings.mk" line 25: Unknown directive "exports"
-make: "directive-misspellings.mk" line 27: Unknown directive "export-en"
-make: "directive-misspellings.mk" line 30: Unknown directive "export-environment"
-make: "directive-misspellings.mk" line 32: Unknown directive "export-litera"
-make: "directive-misspellings.mk" line 34: Unknown directive "export-literax"
-make: "directive-misspellings.mk" line 35: Unknown directive "export-literally"
-make: "directive-misspellings.mk" line 37: Unknown directive "-includ"
-make: "directive-misspellings.mk" line 39: Unknown directive "-includx"
-make: "directive-misspellings.mk" line 40: .include filename must be delimited by '"' or '<'
-make: "directive-misspellings.mk" line 42: Unknown directive "includ"
-make: "directive-misspellings.mk" line 43: Could not find file
-make: "directive-misspellings.mk" line 44: Unknown directive "includx"
-make: "directive-misspellings.mk" line 45: .include filename must be delimited by '"' or '<'
-make: "directive-misspellings.mk" line 47: Unknown directive "inf"
-make: "directive-misspellings.mk" line 48: msg
-make: "directive-misspellings.mk" line 49: Unknown directive "infx"
-make: "directive-misspellings.mk" line 50: Unknown directive "infos"
-make: "directive-misspellings.mk" line 52: Unknown directive "sinclud"
-make: "directive-misspellings.mk" line 54: Unknown directive "sincludx"
-make: "directive-misspellings.mk" line 55: .include filename must be delimited by '"' or '<'
-make: "directive-misspellings.mk" line 57: Unknown directive "unde"
-make: "directive-misspellings.mk" line 59: Unknown directive "undex"
-make: "directive-misspellings.mk" line 60: Unknown directive "undefs"
-make: "directive-misspellings.mk" line 62: Unknown directive "unexpor"
-make: "directive-misspellings.mk" line 64: Unknown directive "unexporx"
-make: "directive-misspellings.mk" line 65: Unknown directive "unexports"
-make: "directive-misspellings.mk" line 67: Unknown directive "unexport-en"
-make: "directive-misspellings.mk" line 69: The directive .unexport-env does not take arguments
-make: "directive-misspellings.mk" line 70: Unknown directive "unexport-enx"
-make: "directive-misspellings.mk" line 71: Unknown directive "unexport-envs"
-make: "directive-misspellings.mk" line 73: Unknown directive "warn"
-make: "directive-misspellings.mk" line 74: Unknown directive "warnin"
-make: "directive-misspellings.mk" line 75: warning: msg
-make: "directive-misspellings.mk" line 76: Unknown directive "warninx"
-make: "directive-misspellings.mk" line 77: Unknown directive "warnings"
+make: "directive-misspellings.mk" line 13: Unknown directive "dinclud"
+make: "directive-misspellings.mk" line 16: Unknown directive "dincludx"
+make: "directive-misspellings.mk" line 18: .include filename must be delimited by '"' or '<'
+make: "directive-misspellings.mk" line 21: Unknown directive "erro"
+make: "directive-misspellings.mk" line 23: Unknown directive "errox"
+make: "directive-misspellings.mk" line 28: Unknown directive "expor"
+make: "directive-misspellings.mk" line 31: Unknown directive "exporx"
+make: "directive-misspellings.mk" line 33: Unknown directive "exports"
+make: "directive-misspellings.mk" line 36: Unknown directive "export-en"
+make: "directive-misspellings.mk" line 40: Unknown directive "export-environment"
+make: "directive-misspellings.mk" line 43: Unknown directive "export-litera"
+make: "directive-misspellings.mk" line 46: Unknown directive "export-literax"
+make: "directive-misspellings.mk" line 48: Unknown directive "export-literally"
+make: "directive-misspellings.mk" line 51: Unknown directive "-includ"
+make: "directive-misspellings.mk" line 54: Unknown directive "-includx"
+make: "directive-misspellings.mk" line 56: .include filename must be delimited by '"' or '<'
+make: "directive-misspellings.mk" line 59: Unknown directive "includ"
+make: "directive-misspellings.mk" line 61: Could not find file
+make: "directive-misspellings.mk" line 63: Unknown directive "includx"
+make: "directive-misspellings.mk" line 65: .include filename must be delimited by '"' or '<'
+make: "directive-misspellings.mk" line 68: Unknown directive "inf"
+make: "directive-misspellings.mk" line 70: msg
+make: "directive-misspellings.mk" line 72: Unknown directive "infx"
+make: "directive-misspellings.mk" line 74: Unknown directive "infos"
+make: "directive-misspellings.mk" line 77: Unknown directive "sinclud"
+make: "directive-misspellings.mk" line 80: Unknown directive "sincludx"
+make: "directive-misspellings.mk" line 82: .include filename must be delimited by '"' or '<'
+make: "directive-misspellings.mk" line 85: Unknown directive "unde"
+make: "directive-misspellings.mk" line 88: Unknown directive "undex"
+make: "directive-misspellings.mk" line 90: Unknown directive "undefs"
+make: "directive-misspellings.mk" line 93: Unknown directive "unexpor"
+make: "directive-misspellings.mk" line 96: Unknown directive "unexporx"
+make: "directive-misspellings.mk" line 98: Unknown directive "unexports"
+make: "directive-misspellings.mk" line 101: Unknown directive "unexport-en"
+make: "directive-misspellings.mk" line 104: The directive .unexport-env does not take arguments
+make: "directive-misspellings.mk" line 106: Unknown directive "unexport-enx"
+make: "directive-misspellings.mk" line 108: Unknown directive "unexport-envs"
+make: "directive-misspellings.mk" line 111: Unknown directive "warn"
+make: "directive-misspellings.mk" line 113: Unknown directive "warnin"
+make: "directive-misspellings.mk" line 115: warning: msg
+make: "directive-misspellings.mk" line 117: Unknown directive "warninx"
+make: "directive-misspellings.mk" line 119: Unknown directive "warnings"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/directive-misspellings.mk b/contrib/bmake/unit-tests/directive-misspellings.mk
index 5f479f03b7f1..0014076d041f 100644
--- a/contrib/bmake/unit-tests/directive-misspellings.mk
+++ b/contrib/bmake/unit-tests/directive-misspellings.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-misspellings.mk,v 1.3 2020/12/13 01:10:22 rillig Exp $
+# $NetBSD: directive-misspellings.mk,v 1.4 2023/06/01 20:56:35 rillig Exp $
#
# Tests for misspelled directives.
#
@@ -9,71 +9,111 @@
# ".information" were aliases to ".info" since the code for these diagnostic
# directives just skipped any letters following the "error", "warn" or "info".
+# expect+1: Unknown directive "dinclud"
.dinclud "file"
.dinclude "file"
+# expect+1: Unknown directive "dincludx"
.dincludx "file"
+# expect+1: .include filename must be delimited by '"' or '<'
.dincludes "file" # XXX: the 's' is not meant to be a filename
+# expect+1: Unknown directive "erro"
.erro msg
+# expect+1: Unknown directive "errox"
.errox msg
# no .error since that would exit immediately
# no .errors since that would exit immediately, even with the typo
+# expect+1: Unknown directive "expor"
.expor varname
.export varname
+# expect+1: Unknown directive "exporx"
.exporx varname
+# expect+1: Unknown directive "exports"
.exports varname # Accepted before 2020-12-13 01:07:54.
+# expect+1: Unknown directive "export-en"
.export-en # Accepted before 2020-12-13 01:07:54.
.export-env
.export-env extra argument # XXX: undetected extra argument
+# expect+1: Unknown directive "export-environment"
.export-environment # Accepted before 2020-12-13 01:07:54.
+# expect+1: Unknown directive "export-litera"
.export-litera varname # Accepted before 2020-12-13 01:07:54.
.export-literal varname
+# expect+1: Unknown directive "export-literax"
.export-literax varname # Accepted before 2020-12-13 01:07:54.
+# expect+1: Unknown directive "export-literally"
.export-literally varname # Accepted before 2020-12-13 01:07:54.
+# expect+1: Unknown directive "-includ"
.-includ "file"
.-include "file"
+# expect+1: Unknown directive "-includx"
.-includx "file"
+# expect+1: .include filename must be delimited by '"' or '<'
.-includes "file" # XXX: the 's' is not meant to be a filename
+# expect+1: Unknown directive "includ"
.includ "file"
+# expect+1: Could not find file
.include "file"
+# expect+1: Unknown directive "includx"
.includx "file"
+# expect+1: .include filename must be delimited by '"' or '<'
.includex "file" # XXX: the 's' is not meant to be a filename
+# expect+1: Unknown directive "inf"
.inf msg
+# expect+1: msg
.info msg
+# expect+1: Unknown directive "infx"
.infx msg
+# expect+1: Unknown directive "infos"
.infos msg # Accepted before 2020-12-13 01:07:54.
+# expect+1: Unknown directive "sinclud"
.sinclud "file"
.sinclude "file"
+# expect+1: Unknown directive "sincludx"
.sincludx "file"
+# expect+1: .include filename must be delimited by '"' or '<'
.sincludes "file" # XXX: the 's' is not meant to be a filename
+# expect+1: Unknown directive "unde"
.unde varname
.undef varname
+# expect+1: Unknown directive "undex"
.undex varname
+# expect+1: Unknown directive "undefs"
.undefs varname # Accepted before 2020-12-13 01:07:54.
+# expect+1: Unknown directive "unexpor"
.unexpor varname
.unexport varname
+# expect+1: Unknown directive "unexporx"
.unexporx varname
+# expect+1: Unknown directive "unexports"
.unexports varname # Accepted before 2020-12-12 18:00:18.
+# expect+1: Unknown directive "unexport-en"
.unexport-en # Accepted before 2020-12-12 18:11:42.
.unexport-env
+# expect+1: The directive .unexport-env does not take arguments
.unexport-env extra argument # Accepted before 2020-12-12 18:00:18.
+# expect+1: Unknown directive "unexport-enx"
.unexport-enx # Accepted before 2020-12-12 18:00:18.
+# expect+1: Unknown directive "unexport-envs"
.unexport-envs # Accepted before 2020-12-12 18:00:18.
+# expect+1: Unknown directive "warn"
.warn msg
+# expect+1: Unknown directive "warnin"
.warnin msg
+# expect+1: warning: msg
.warning msg
+# expect+1: Unknown directive "warninx"
.warninx msg
+# expect+1: Unknown directive "warnings"
.warnings msg # Accepted before 2020-12-13 01:07:54.
-
-all:
diff --git a/contrib/bmake/unit-tests/directive-sinclude.exp b/contrib/bmake/unit-tests/directive-sinclude.exp
index 39a9383953dd..5e8ecd710dc6 100755
--- a/contrib/bmake/unit-tests/directive-sinclude.exp
+++ b/contrib/bmake/unit-tests/directive-sinclude.exp
@@ -1 +1,4 @@
-exit status 0
+make: "directive-include-error.inc" line 1: Invalid line 'syntax error'
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/directive-sinclude.mk b/contrib/bmake/unit-tests/directive-sinclude.mk
index 1932e7b3ba13..58912c644a7e 100755
--- a/contrib/bmake/unit-tests/directive-sinclude.mk
+++ b/contrib/bmake/unit-tests/directive-sinclude.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-sinclude.mk,v 1.2 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: directive-sinclude.mk,v 1.5 2023/08/19 10:52:14 rillig Exp $
#
# Tests for the .sinclude directive, which includes another file,
# silently skipping it if it cannot be opened.
@@ -7,7 +7,17 @@
# opened. Parse errors and other errors are handled the same way as in the
# other .include directives.
-# TODO: Implementation
+# No complaint that there is no such file.
+.sinclude "${.CURDIR}/directive-include-nonexistent.inc"
-all:
- @:;
+# No complaint either, even though the operating system error is ENOTDIR, not
+# ENOENT.
+.sinclude "${MAKEFILE}/subdir"
+
+# Errors that are not related to opening the file are still reported.
+# expect: make: "directive-include-error.inc" line 1: Invalid line 'syntax error'
+_!= echo 'syntax error' > directive-include-error.inc
+.sinclude "${.CURDIR}/directive-include-error.inc"
+_!= rm directive-include-error.inc
+
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/directive-undef.exp b/contrib/bmake/unit-tests/directive-undef.exp
index 56c871429397..329dc8d6282a 100644
--- a/contrib/bmake/unit-tests/directive-undef.exp
+++ b/contrib/bmake/unit-tests/directive-undef.exp
@@ -1,6 +1,6 @@
-make: "directive-undef.mk" line 29: The .undef directive requires an argument
-make: "directive-undef.mk" line 86: Unknown modifier "Z"
-make: "directive-undef.mk" line 103: warning: UT_EXPORTED is still listed in .MAKE.EXPORTED even though spaceit is not exported anymore.
+make: "directive-undef.mk" line 30: The .undef directive requires an argument
+make: "directive-undef.mk" line 88: while evaluating variable "VARNAMES": Unknown modifier "Z"
+make: "directive-undef.mk" line 105: warning: UT_EXPORTED is still listed in .MAKE.EXPORTED even though spaceit is not exported anymore.
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/directive-undef.mk b/contrib/bmake/unit-tests/directive-undef.mk
index 41ea6b5bf8fa..ac4b20d3e858 100644
--- a/contrib/bmake/unit-tests/directive-undef.mk
+++ b/contrib/bmake/unit-tests/directive-undef.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-undef.mk,v 1.10 2021/02/16 18:02:19 rillig Exp $
+# $NetBSD: directive-undef.mk,v 1.14 2024/04/20 10:18:55 rillig Exp $
#
# Tests for the .undef directive.
#
@@ -26,6 +26,7 @@
# to delete the variable with the empty name, which never exists; see
# varname-empty.mk. Since var.c 1.737 from 2020-12-19, .undef complains
# about a missing argument.
+# expect+1: The .undef directive requires an argument
.undef
@@ -43,11 +44,11 @@
3= 3
${:U1 2 3}= one two three
VARNAMES= 1 2 3
-.undef ${VARNAMES} # undefines the variable "1 2 3"
-.if !defined(${:U1 2 3})
+.undef ${VARNAMES} # undefines the variables "1", "2" and "3"
+.if ${${:U1 2 3}} != "one two three" # still there
. error
.endif
-.if ${1:U_}${2:U_}${3:U_} != "___" # these are still defined
+.if ${1:U_}${2:U_}${3:U_} != "___" # these have been undefined
. error
.endif
@@ -83,6 +84,7 @@ ${DOLLAR}= dollar
#
# As of var.c 1.762, this doesn't happen though because the error handling
# in Var_Parse and Var_Subst is not done properly.
+# expect+1: while evaluating variable "VARNAMES": Unknown modifier "Z"
.undef ${VARNAMES:L:Z}
@@ -99,9 +101,48 @@ UT_EXPORTED= exported-value
. error
.endif
.if ${.MAKE.EXPORTED:MUT_EXPORTED}
+# expect+1: warning: UT_EXPORTED is still listed in .MAKE.EXPORTED even though spaceit is not exported anymore.
. warning UT_EXPORTED is still listed in .MAKE.EXPORTED even though $\
it is not exported anymore.
.endif
+# When an exported variable is undefined, the variable is removed both from
+# the global scope as well as from the environment.
+DIRECT= direct
+INDIRECT= in-${DIRECT}
+.export DIRECT INDIRECT
+.if ${DIRECT} != "direct"
+. error
+.endif
+.if ${INDIRECT} != "in-direct"
+. error
+.endif
+
+# Deletes the variables from the global scope and also from the environment.
+# This applies to both variables, even though 'INDIRECT' is not actually
+# exported yet since it refers to another variable.
+.undef DIRECT # Separate '.undef' directives,
+.undef INDIRECT # for backwards compatibility.
+
+.if ${DIRECT:Uundefined} != "undefined"
+. error
+.endif
+.if ${INDIRECT:Uundefined} != "undefined"
+. error
+.endif
+
+
+# Since var.c 1.570 from 2020-10-06 and before var.c 1.1014 from 2022-03-26,
+# make ran into an assertion failure when trying to undefine a variable that
+# was based on an environment variable.
+.if ${ENV_VAR} != "env-value" # see ./Makefile, ENV.directive-undef
+. error
+.endif
+ENV_VAR+= appended # moves the short-lived variable to the
+ # global scope
+.undef ENV_VAR # removes the variable from both the global
+ # scope and from the environment
+
+
all:
diff --git a/contrib/bmake/unit-tests/directive-unexport-env.exp b/contrib/bmake/unit-tests/directive-unexport-env.exp
index 6d653e65fd32..663034cee12c 100644
--- a/contrib/bmake/unit-tests/directive-unexport-env.exp
+++ b/contrib/bmake/unit-tests/directive-unexport-env.exp
@@ -1,16 +1,16 @@
-make: "directive-unexport-env.mk" line 13: Unknown directive "unexport-en"
-make: "directive-unexport-env.mk" line 15: Unknown directive "unexport-environment"
+make: "directive-unexport-env.mk" line 14: Unknown directive "unexport-en"
+make: "directive-unexport-env.mk" line 17: Unknown directive "unexport-environment"
Global: UT_EXPORTED = value
Global: UT_UNEXPORTED = value
Global: .MAKE.EXPORTED = UT_EXPORTED
-make: "directive-unexport-env.mk" line 21: The directive .unexport-env does not take arguments
+make: "directive-unexport-env.mk" line 24: The directive .unexport-env does not take arguments
Var_Parse: ${.MAKE.EXPORTED:O:u} (eval)
Evaluating modifier ${.MAKE.EXPORTED:O} on value "UT_EXPORTED"
Result of ${.MAKE.EXPORTED:O} is "UT_EXPORTED"
Evaluating modifier ${.MAKE.EXPORTED:u} on value "UT_EXPORTED"
Result of ${.MAKE.EXPORTED:u} is "UT_EXPORTED"
Unexporting "UT_EXPORTED"
-Global:delete .MAKE.EXPORTED
+Global: delete .MAKE.EXPORTED
Global: .MAKEFLAGS = -r -k -d v -d
Global: .MAKEFLAGS = -r -k -d v -d 0
make: Fatal errors encountered -- cannot continue
diff --git a/contrib/bmake/unit-tests/directive-unexport-env.mk b/contrib/bmake/unit-tests/directive-unexport-env.mk
index ef58ae732e6d..e56e47865011 100644
--- a/contrib/bmake/unit-tests/directive-unexport-env.mk
+++ b/contrib/bmake/unit-tests/directive-unexport-env.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-unexport-env.mk,v 1.7 2020/12/12 18:11:42 rillig Exp $
+# $NetBSD: directive-unexport-env.mk,v 1.9 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the .unexport-env directive.
#
@@ -10,16 +10,18 @@
# TODO: Implementation
+# expect+1: Unknown directive "unexport-en"
.unexport-en # misspelled
.unexport-env # ok
+# expect+1: Unknown directive "unexport-environment"
.unexport-environment # misspelled
.MAKEFLAGS: -dv
UT_EXPORTED= value
UT_UNEXPORTED= value
.export UT_EXPORTED
+# expect+1: The directive .unexport-env does not take arguments
.unexport-env UT_EXPORTED UT_UNEXPORTED
.MAKEFLAGS: -d0
-all:
- @:;
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/directive-unexport.exp b/contrib/bmake/unit-tests/directive-unexport.exp
index d59fb4713259..b084daf5d147 100644
--- a/contrib/bmake/unit-tests/directive-unexport.exp
+++ b/contrib/bmake/unit-tests/directive-unexport.exp
@@ -1,5 +1,5 @@
-make: "directive-unexport.mk" line 18: UT_A=a UT_B=b UT_C=c
-make: "directive-unexport.mk" line 19: UT_A UT_B UT_C
-make: "directive-unexport.mk" line 27: UT_A=a UT_B=b UT_C=c
-make: "directive-unexport.mk" line 28:
+make: "directive-unexport.mk" line 19: UT_A=a UT_B=b UT_C=c
+make: "directive-unexport.mk" line 21: UT_A UT_B UT_C
+make: "directive-unexport.mk" line 30: UT_A=a UT_B=b UT_C=c
+make: "directive-unexport.mk" line 31:
exit status 0
diff --git a/contrib/bmake/unit-tests/directive-unexport.mk b/contrib/bmake/unit-tests/directive-unexport.mk
index efc103efedf6..e759fe3e35f2 100644
--- a/contrib/bmake/unit-tests/directive-unexport.mk
+++ b/contrib/bmake/unit-tests/directive-unexport.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-unexport.mk,v 1.7 2020/12/13 01:07:54 rillig Exp $
+# $NetBSD: directive-unexport.mk,v 1.8 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the .unexport directive.
#
@@ -15,7 +15,9 @@ UT_C= c
.export UT_A UT_B UT_C
# Show the exported variables and their values.
+# expect+1: UT_A=a UT_B=b UT_C=c
.info ${:!env|sort|grep '^UT_'!}
+# expect+1: UT_A UT_B UT_C
.info ${.MAKE.EXPORTED}
# XXX: Now try to unexport all of them. The variables are still exported
@@ -24,6 +26,7 @@ UT_C= c
*= asterisk
.unexport *
+# expect+1: UT_A=a UT_B=b UT_C=c
.info ${:!env|sort|grep '^UT_'!}
.info ${.MAKE.EXPORTED}
diff --git a/contrib/bmake/unit-tests/directive-warning.exp b/contrib/bmake/unit-tests/directive-warning.exp
index b08b3207392c..6fbeed91df0f 100644
--- a/contrib/bmake/unit-tests/directive-warning.exp
+++ b/contrib/bmake/unit-tests/directive-warning.exp
@@ -1,11 +1,11 @@
-make: "directive-warning.mk" line 11: Unknown directive "warn"
+make: "directive-warning.mk" line 10: Unknown directive "warn"
make: "directive-warning.mk" line 12: Unknown directive "warn"
-make: "directive-warning.mk" line 13: Unknown directive "warnin"
make: "directive-warning.mk" line 14: Unknown directive "warnin"
-make: "directive-warning.mk" line 15: Missing argument for ".warning"
-make: "directive-warning.mk" line 16: warning: message
-make: "directive-warning.mk" line 17: Unknown directive "warnings"
-make: "directive-warning.mk" line 18: Unknown directive "warnings"
+make: "directive-warning.mk" line 16: Unknown directive "warnin"
+make: "directive-warning.mk" line 18: Missing argument for ".warning"
+make: "directive-warning.mk" line 19: warning: message
+make: "directive-warning.mk" line 21: Unknown directive "warnings"
+make: "directive-warning.mk" line 23: Unknown directive "warnings"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/directive-warning.mk b/contrib/bmake/unit-tests/directive-warning.mk
index d586c9fed170..bf0683f8911f 100644
--- a/contrib/bmake/unit-tests/directive-warning.mk
+++ b/contrib/bmake/unit-tests/directive-warning.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-warning.mk,v 1.6 2020/12/19 22:33:11 rillig Exp $
+# $NetBSD: directive-warning.mk,v 1.9 2023/12/17 09:44:00 rillig Exp $
#
# Tests for the .warning directive.
#
@@ -6,16 +6,20 @@
# produced the wrong error message "Unknown directive". Since parse.c 1.503
# from 2020-12-19, the correct "Missing argument" is produced.
-# TODO: Implementation
-
+# expect+1: Unknown directive "warn"
.warn # misspelled
+# expect+1: Unknown directive "warn"
.warn message # misspelled
+# expect+1: Unknown directive "warnin"
.warnin # misspelled
+# expect+1: Unknown directive "warnin"
.warnin message # misspelled
+# expect+1: Missing argument for ".warning"
.warning # "Missing argument"
-.warning message # ok
+.warning message # expect+0: warning: message
+# expect+1: Unknown directive "warnings"
.warnings # misspelled
+# expect+1: Unknown directive "warnings"
.warnings messages # Accepted before 2020-12-13 01:07:54.
-all:
- @:;
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/directive.exp b/contrib/bmake/unit-tests/directive.exp
index ee866b7ee2b3..cad8a9bb97e0 100644
--- a/contrib/bmake/unit-tests/directive.exp
+++ b/contrib/bmake/unit-tests/directive.exp
@@ -1,12 +1,14 @@
-make: "directive.mk" line 9: Unknown directive "indented"
make: "directive.mk" line 10: Unknown directive "indented"
-make: "directive.mk" line 11: Unknown directive "indented"
-make: "directive.mk" line 15: Unknown directive "info"
-Global: .info =
+make: "directive.mk" line 12: Unknown directive "indented"
+make: "directive.mk" line 14: Unknown directive "indented"
+make: "directive.mk" line 19: Unknown directive ""
+Global: .info = # (empty)
Global: .info = value
-make: "directive.mk" line 26: := value
+make: "directive.mk" line 31: := value
Global: .MAKEFLAGS = -r -k -d v -d
Global: .MAKEFLAGS = -r -k -d v -d 0
+make: "directive.mk" line 40: Invalid line 'target-without-colon'
+make: "directive.mk" line 43: Invalid line 'target-without-colon another-target'
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/directive.mk b/contrib/bmake/unit-tests/directive.mk
index d463ce4f009a..61938360dfc7 100644
--- a/contrib/bmake/unit-tests/directive.mk
+++ b/contrib/bmake/unit-tests/directive.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive.mk,v 1.4 2020/11/15 11:57:00 rillig Exp $
+# $NetBSD: directive.mk,v 1.9 2023/11/19 22:32:44 rillig Exp $
#
# Tests for the preprocessing directives, such as .if or .info.
@@ -6,23 +6,28 @@
# Unknown directives are correctly named in the error messages,
# even if they are indented.
+# expect+1: Unknown directive "indented"
.indented none
+# expect+1: Unknown directive "indented"
. indented 2 spaces
+# expect+1: Unknown directive "indented"
. indented tab
-# Directives must be written directly, not indirectly via variable
+# Directives must be written directly, not indirectly via
# expressions.
+# expect+1: Unknown directive ""
.${:Uinfo} directives cannot be indirect
# There is no directive called '.target', therefore this is parsed as a
# dependency declaration with 2 targets and 1 source.
.target target: source
-# This looks ambiguous. It could be either an .info message or a variable
-# assignment. It is a variable assignment.
+# The following lines demonstrate how the parser tells an .info message apart
+# from a variable assignment to ".info", which syntactically is very similar.
.MAKEFLAGS: -dv
-.info:= value
+.info:= value # This is a variable assignment.
.info?= value # This is a variable assignment as well.
+# expect+1: := value
.info := value # The space after the '.info' makes this
# a directive.
.MAKEFLAGS: -d0
@@ -31,5 +36,8 @@
# Not even the space after the '.info' can change anything about this.
.${:Uinfo} : source
-all:
- @:;
+# expect+1: Invalid line 'target-without-colon'
+target-without-colon
+
+# expect+1: Invalid line 'target-without-colon another-target'
+target-without-colon another-target
diff --git a/contrib/bmake/unit-tests/doterror.mk b/contrib/bmake/unit-tests/doterror.mk
index d46fb3581a25..0f3698ad5cf2 100644
--- a/contrib/bmake/unit-tests/doterror.mk
+++ b/contrib/bmake/unit-tests/doterror.mk
@@ -1,4 +1,4 @@
-# $NetBSD: doterror.mk,v 1.2 2020/10/24 08:34:59 rillig Exp $
+# $NetBSD: doterror.mk,v 1.3 2023/06/01 20:56:35 rillig Exp $
.BEGIN:
@@ -17,4 +17,3 @@ happy:
sad:
@echo and now: $@; exit 1
-
diff --git a/contrib/bmake/unit-tests/envfirst.mk b/contrib/bmake/unit-tests/envfirst.mk
deleted file mode 100644
index 60a331a5db64..000000000000
--- a/contrib/bmake/unit-tests/envfirst.mk
+++ /dev/null
@@ -1,44 +0,0 @@
-# $NetBSD: envfirst.mk,v 1.5 2021/02/04 21:42:47 rillig Exp $
-#
-# The -e option makes environment variables stronger than global variables.
-
-.MAKEFLAGS: -e
-
-.if ${FROM_ENV} != value-from-env
-. error ${FROM_ENV}
-.endif
-
-# Try to override the variable; this does not have any effect.
-FROM_ENV= value-from-mk
-.if ${FROM_ENV} != value-from-env
-. error ${FROM_ENV}
-.endif
-
-# Try to append to the variable; this also doesn't have any effect.
-FROM_ENV+= appended
-.if ${FROM_ENV} != value-from-env
-. error ${FROM_ENV}
-.endif
-
-# The default assignment also cannot change the variable.
-FROM_ENV?= default
-.if ${FROM_ENV} != value-from-env
-. error ${FROM_ENV}
-.endif
-
-# Neither can the assignment modifiers.
-.if ${FROM_ENV::=from-condition}
-.endif
-.if ${FROM_ENV} != value-from-env
-. error ${FROM_ENV}
-.endif
-
-# Even .undef doesn't work since it only affects the global scope,
-# which is independent from the environment variables.
-.undef FROM_ENV
-.if ${FROM_ENV} != value-from-env
-. error ${FROM_ENV}
-.endif
-
-all:
- @: nothing
diff --git a/contrib/bmake/unit-tests/error.exp b/contrib/bmake/unit-tests/error.exp
index 3adc099a4625..e782664498bf 100644
--- a/contrib/bmake/unit-tests/error.exp
+++ b/contrib/bmake/unit-tests/error.exp
@@ -1,6 +1,6 @@
-make: "error.mk" line 6: just FYI
-make: "error.mk" line 7: warning: this could be serious
-make: "error.mk" line 8: this is fatal
+make: "error.mk" line 7: just FYI
+make: "error.mk" line 9: warning: this could be serious
+make: "error.mk" line 11: this is fatal
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/error.mk b/contrib/bmake/unit-tests/error.mk
index 0029b3bc6aa9..2383e60da6fb 100644
--- a/contrib/bmake/unit-tests/error.mk
+++ b/contrib/bmake/unit-tests/error.mk
@@ -1,10 +1,13 @@
-# $NetBSD: error.mk,v 1.3 2020/11/03 17:38:45 rillig Exp $
+# $NetBSD: error.mk,v 1.4 2023/06/01 20:56:35 rillig Exp $
#
# Demonstrate that the .error directive exits immediately, without
# continuing parsing until the end of the file.
+# expect+1: just FYI
.info just FYI
+# expect+1: warning: this could be serious
.warning this could be serious
+# expect+1: this is fatal
.error this is fatal
.info this is not reached because of the .error above
diff --git a/contrib/bmake/unit-tests/escape.exp b/contrib/bmake/unit-tests/escape.exp
index 6238e27b0191..ff9c8b7cf811 100644
--- a/contrib/bmake/unit-tests/escape.exp
+++ b/contrib/bmake/unit-tests/escape.exp
@@ -1,5 +1,4 @@
var-1bs
-printf "%s=:%s:\n" VAR1BS 111\\111; printf "%s=:%s:\n" VAR1BSa 111\\aaa; printf "%s=:%s:\n" VAR1BSA 111\\aaa; printf "%s=:%s:\n" VAR1BSda 111\\\$\{a\}; printf "%s=:%s:\n" VAR1BSdA 111\\\$\{A\}; printf "%s=:%s:\n" VAR1BSc 111\#\ backslash\ escapes\ comment\ char,\ so\ this\ is\ part\ of\ the\ value; printf "%s=:%s:\n" VAR1BSsc 111\\\ ;
VAR1BS=:111\111:
VAR1BSa=:111\aaa:
VAR1BSA=:111\aaa:
@@ -8,25 +7,22 @@ VAR1BSdA=:111\${A}:
VAR1BSc=:111# backslash escapes comment char, so this is part of the value:
VAR1BSsc=:111\ :
var-2bs
-printf "%s=:%s:\n" VAR2BS 222\\\\222; printf "%s=:%s:\n" VAR2BSa 222\\\\aaa; printf "%s=:%s:\n" VAR2BSA 222\\\\aaa; printf "%s=:%s:\n" VAR2BSda 222\\\\\$\{a\}; printf "%s=:%s:\n" VAR2BSdA 222\\\\\$\{A\}; printf "%s=:%s:\n" VAR2BSc 222\\\\; printf "%s=:%s:\n" VAR2BSsc 222\\\\;
-VAR2BS=:222\\222:
-VAR2BSa=:222\\aaa:
-VAR2BSA=:222\\aaa:
-VAR2BSda=:222\\${a}:
-VAR2BSdA=:222\\${A}:
-VAR2BSc=:222\\:
-VAR2BSsc=:222\\:
-var-1bsnl
-printf "%s=:%s:\n" VAR1BSNL 111\ 111; printf "%s=:%s:\n" VAR1BSNLa 111\ aaa; printf "%s=:%s:\n" VAR1BSNLA 111\ aaa; printf "%s=:%s:\n" VAR1BSNLda 111\ \$\{a\}; printf "%s=:%s:\n" VAR1BSNLdA 111\ \$\{A\}; printf "%s=:%s:\n" VAR1BSNLc 111; printf "%s=:%s:\n" VAR1BSNLsc 111;
-VAR1BSNL=:111 111:
-VAR1BSNLa=:111 aaa:
-VAR1BSNLA=:111 aaa:
-VAR1BSNLda=:111 ${a}:
-VAR1BSNLdA=:111 ${A}:
-VAR1BSNLc=:111:
-VAR1BSNLsc=:111:
+VAR2.BS=:222\\222:
+VAR2.BS.a=:222\\aaa:
+VAR2.BS.A=:222\\aaa:
+VAR2.BS.d.a=:222\\${a}:
+VAR2.BS.d.A=:222\\${A}:
+VAR2.BS.c=:222\\:
+VAR2.BS.s.c=:222\\:
+var-1bs-nl
+VAR1.BS-NL=:111 111:
+VAR1.BS-NL.a=:111 aaa:
+VAR1.BS-NL.A=:111 aaa:
+VAR1.BS-NL.d-a=:111 ${a}:
+VAR1.BS-NL.d-A=:111 ${A}:
+VAR1.BS-NL.c=:111:
+VAR1.BS-NL.s-c=:111:
var-2bsnl
-printf "%s=:%s:\n" VAR2BSNL 222\\\\; printf "%s=:%s:\n" VAR2BSNLa 222\\\\; printf "%s=:%s:\n" VAR2BSNLA 222\\\\; printf "%s=:%s:\n" VAR2BSNLda 222\\\\; printf "%s=:%s:\n" VAR2BSNLdA 222\\\\; printf "%s=:%s:\n" VAR2BSNLc 222\\\\; printf "%s=:%s:\n" VAR2BSNLsc 222\\\\;
VAR2BSNL=:222\\:
VAR2BSNLa=:222\\:
VAR2BSNLA=:222\\:
@@ -35,7 +31,6 @@ VAR2BSNLdA=:222\\:
VAR2BSNLc=:222\\:
VAR2BSNLsc=:222\\:
var-3bsnl
-printf "%s=:%s:\n" VAR3BSNL 333\\\\\ 333=; printf "%s=:%s:\n" VAR3BSNLa 333\\\\\ aaa=; printf "%s=:%s:\n" VAR3BSNLA 333\\\\\ aaa=; printf "%s=:%s:\n" VAR3BSNLda 333\\\\\ \$\{a\}=; printf "%s=:%s:\n" VAR3BSNLdA 333\\\\\ \$\{A\}=; printf "%s=:%s:\n" VAR3BSNLc 333\\\\; printf "%s=:%s:\n" VAR3BSNLsc 333\\\\;
VAR3BSNL=:333\\ 333=:
VAR3BSNLa=:333\\ aaa=:
VAR3BSNLA=:333\\ aaa=:
@@ -44,7 +39,6 @@ VAR3BSNLdA=:333\\ ${A}=:
VAR3BSNLc=:333\\:
VAR3BSNLsc=:333\\:
var-1bsnl-space
-printf "%s=:%s:\n" VAR1BSNL00 first\ line; printf "%s=:%s:\n" VAR1BSNL0 first\ line\ no\ space\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLs first\ line\ one\ space\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLss first\ line\ two\ spaces\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLt first\ line\ one\ tab\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLtt first\ line\ two\ tabs\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLxx first\ line\ many\ spaces\ and\ tabs\ \[\ \ \ \ \]\ on\ second\ line;
VAR1BSNL00=:first line:
VAR1BSNL0=:first line no space on second line:
VAR1BSNLs=:first line one space on second line:
diff --git a/contrib/bmake/unit-tests/escape.mk b/contrib/bmake/unit-tests/escape.mk
index 8bdd3ad2ab49..a363a19f1946 100644
--- a/contrib/bmake/unit-tests/escape.mk
+++ b/contrib/bmake/unit-tests/escape.mk
@@ -1,4 +1,4 @@
-# $NetBSD: escape.mk,v 1.14 2020/11/03 17:38:45 rillig Exp $
+# $NetBSD: escape.mk,v 1.15 2023/10/19 18:24:33 rillig Exp $
#
# Test backslash escaping.
@@ -53,7 +53,7 @@ should continue the comment. \
__printvars: .USE .MADE
@echo ${.TARGET}
- ${.ALLSRC:@v@ printf "%s=:%s:\n" ${v:Q} ${${v}:Q}; @}
+ @${.ALLSRC:@v@ printf "%s=:%s:\n" ${v:Q} ${${v}:Q}; @}
# Embedded backslash in variable should be taken literally.
#
@@ -83,7 +83,8 @@ all: var-2bs
var-2bs: .PHONY __printvars VAR2BS VAR2BSa VAR2BSA VAR2BSda VAR2BSdA \
VAR2BSc VAR2BSsc
-# Backslash-newline in a variable setting is replaced by a single space.
+# In a variable assignment, when the sequence <backslash><newline> occurs at
+# the end of a physical line, it is replaced with a single space.
#
VAR1BSNL= 111\
111
diff --git a/contrib/bmake/unit-tests/export-env.mk b/contrib/bmake/unit-tests/export-env.mk
index 1605b1a71d61..80653f4bb3c9 100644
--- a/contrib/bmake/unit-tests/export-env.mk
+++ b/contrib/bmake/unit-tests/export-env.mk
@@ -1,4 +1,4 @@
-# $NetBSD: export-env.mk,v 1.4 2020/10/24 08:50:17 rillig Exp $
+# $NetBSD: export-env.mk,v 1.5 2023/06/01 20:56:35 rillig Exp $
# our normal .export, subsequent changes affect the environment
UT_TEST= this
@@ -21,7 +21,3 @@ UT_LIT= literal ${UT_TEST}
all:
@echo make:; ${UT_TEST UT_ENV UT_EXP UT_LIT:L:@v@echo $v=${$v};@}
@echo env:; ${UT_TEST UT_ENV UT_EXP UT_LIT:L:@v@echo $v=$${$v};@}
-
-
-
-
diff --git a/contrib/bmake/unit-tests/export.exp b/contrib/bmake/unit-tests/export.exp
index 648d1283fb64..5049b0e35b3b 100644
--- a/contrib/bmake/unit-tests/export.exp
+++ b/contrib/bmake/unit-tests/export.exp
@@ -1,5 +1,5 @@
MAKELEVEL=1
-TMPDIR=TMPDIR
+TMPDIR=<tmpdir>
UT_DOLLAR=This is $UT_FU
UT_FOO=foobar is fubar
UT_FU=fubar
diff --git a/contrib/bmake/unit-tests/export.mk b/contrib/bmake/unit-tests/export.mk
index 94e3a862dce1..38670eaaaf48 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.12 2022/09/09 18:36:15 sjg 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/forloop.exp b/contrib/bmake/unit-tests/forloop.exp
deleted file mode 100644
index 422711b41247..000000000000
--- a/contrib/bmake/unit-tests/forloop.exp
+++ /dev/null
@@ -1,20 +0,0 @@
-make: "forloop.mk" line 14: x=one
-make: "forloop.mk" line 14: x="two and three"
-make: "forloop.mk" line 14: x=four
-make: "forloop.mk" line 14: x="five"
-make: "forloop.mk" line 20: x=-I/this
-make: "forloop.mk" line 20: x=-I"This or that"
-make: "forloop.mk" line 20: x=-Ithat
-make: "forloop.mk" line 20: x="-DTHIS=\"this and that\""
-make: "forloop.mk" line 27: cfl=-I/this -I"This or that" -Ithat "-DTHIS=\"this and that\""
-make: "forloop.mk" line 41: newline-item=(a)
-make: "forloop.mk" line 47: a=one b="two and three"
-make: "forloop.mk" line 47: a=four b="five"
-make: "forloop.mk" line 47: a=ONE b="TWO AND THREE"
-make: "forloop.mk" line 47: a=FOUR b="FIVE"
-We expect an error next:
-make: "forloop.mk" line 46: Wrong number of words (9) in .for substitution list with 2 variables
-make: Fatal errors encountered -- cannot continue
-make: stopped in unit-tests
-OK
-exit status 0
diff --git a/contrib/bmake/unit-tests/forloop.mk b/contrib/bmake/unit-tests/forloop.mk
deleted file mode 100644
index cef05cbe4c61..000000000000
--- a/contrib/bmake/unit-tests/forloop.mk
+++ /dev/null
@@ -1,53 +0,0 @@
-# $NetBSD: forloop.mk,v 1.7 2020/11/03 17:37:57 rillig Exp $
-
-all: for-loop
-
-LIST= one "two and three" four "five"
-
-.if make(for-fail)
-for-fail:
-
-XTRA_LIST= xtra
-.else
-
-. for x in ${LIST}
-. info x=$x
-. endfor
-
-CFL= -I/this -I"This or that" -Ithat "-DTHIS=\"this and that\""
-cfl=
-. for x in ${CFL}
-. info x=$x
-. if empty(cfl)
-cfl= $x
-. else
-cfl+= $x
-. endif
-. endfor
-. info cfl=${cfl}
-
-. if ${cfl} != ${CFL}
-. error ${.newline}${cfl} != ${.newline}${CFL}
-. endif
-
-. for a b in ${EMPTY}
-. info a=$a b=$b
-. endfor
-
-# Since at least 1993, iteration stops at the first newline.
-# Back then, the .newline variable didn't exist, therefore it was unlikely
-# that a newline ever occurred.
-. for var in a${.newline}b${.newline}c
-. info newline-item=(${var})
-. endfor
-
-.endif # for-fail
-
-.for a b in ${LIST} ${LIST:tu} ${XTRA_LIST}
-. info a=$a b=$b
-.endfor
-
-for-loop:
- @echo We expect an error next:
- @(cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} for-fail) && \
- { echo "Oops that should have failed!"; exit 1; } || echo OK
diff --git a/contrib/bmake/unit-tests/forsubst.exp b/contrib/bmake/unit-tests/forsubst.exp
deleted file mode 100644
index 0a98c00aff30..000000000000
--- a/contrib/bmake/unit-tests/forsubst.exp
+++ /dev/null
@@ -1,2 +0,0 @@
-.for with :S;... OK
-exit status 0
diff --git a/contrib/bmake/unit-tests/forsubst.mk b/contrib/bmake/unit-tests/forsubst.mk
deleted file mode 100644
index 9f293ab7f94e..000000000000
--- a/contrib/bmake/unit-tests/forsubst.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-# $NetBSD: forsubst.mk,v 1.3 2020/11/03 17:59:27 rillig Exp $
-#
-# The parser used to break dependency lines at ';' without regard for
-# substitution patterns. Back then, the first ';' was interpreted as the
-# separator between the dependency and its commands. This (perhaps coupled
-# with the new handling of .for variables in ${:U<value>...) caused
-# interesting results for lines like:
-#
-# .for file in ${LIST}
-# for-subst: ${file:S;^;${here}/;g}
-# .endfor
-#
-# See the commit to unit-tests/forsubst (without the .mk) from 2009-10-07.
-
-all: for-subst
-
-here := ${.PARSEDIR}
-# this should not run foul of the parser
-.for file in ${.PARSEFILE}
-for-subst: ${file:S;^;${here}/;g}
- @echo ".for with :S;... OK"
-.endfor
diff --git a/contrib/bmake/unit-tests/hanoi-include.mk b/contrib/bmake/unit-tests/hanoi-include.mk
index 3ad0a751845a..f243af83d1df 100644
--- a/contrib/bmake/unit-tests/hanoi-include.mk
+++ b/contrib/bmake/unit-tests/hanoi-include.mk
@@ -1,41 +1,49 @@
-# $NetBSD: hanoi-include.mk,v 1.1 2020/10/03 17:30:54 rillig Exp $
+# $NetBSD: hanoi-include.mk,v 1.5 2023/10/19 18:24:33 rillig Exp $
#
-# Implements the Towers of Hanoi puzzle, thereby demonstrating a bunch of
+# Implements the Towers of Hanoi puzzle, demonstrating a bunch of more or less
# useful programming techniques:
#
-# * default assignment using the ?= assignment operator
-# * including the same file recursively
-# * extracting the current value of a variable using the .for loop
-# * using shell commands for calculations since make is a text processor
-# * using the :: dependency operator for adding commands to a target
-# * on-the-fly variable assignment expressions using the ::= modifier
+# * default assignment using the ?= assignment operator
+# * including the same file recursively (rather unusual)
+# * extracting the current value of a variable using the .for loop
+# * using the :: dependency operator for adding commands to a target
+# * on-the-fly variable assignment expressions using the ::= modifier
#
# usage:
-# env N=3 make -f hanoi-include.mk
-# endless loop:
-# make -f hanoi-include.mk N=3
+# env N=3 make -r -f hanoi-include.mk
+#
+# Specifying N in the command line instead of in the environment would produce
+# an endless loop, since variables from the command line cannot be overridden
+# by global variables:
+# make -r -f hanoi-include.mk N=3
N?= 5 # Move this number of disks ...
FROM?= A # ... from this stack ...
VIA?= B # ... via this stack ...
TO?= C # ... to this stack.
-.if $N == 1
+# Since make has no built-in arithmetic functions, convert N to a list of
+# words and use the built-in word counting instead.
+.if ${N:[#]} == 1
+N:= count ${:U:${:Urange=$N}} # 'count' + one word for every disk
+.endif
+
+.if ${N:[#]} == 2
. for from to in ${FROM} ${TO}
all::
@echo "Move the upper disk from stack ${from} to stack ${to}."
. endfor
.else
-_:= ${N::!=expr $N - 1} ${TMP::=${VIA}} ${VIA::=${TO}} ${TO::=${TMP}}
+_:= ${N::=${N:[1..-2]}} ${TMP::=${VIA}} ${VIA::=${TO}} ${TO::=${TMP}}
. include "${.PARSEDIR}/${.PARSEFILE}"
-_:= ${N::!=expr $N + 1} ${TMP::=${VIA}} ${VIA::=${TO}} ${TO::=${TMP}}
+_:= ${N::+=D} ${TMP::=${VIA}} ${VIA::=${TO}} ${TO::=${TMP}}
. for from to in ${FROM} ${TO}
all::
@echo "Move the upper disk from stack ${from} to stack ${to}."
. endfor
-_:= ${N::!=expr $N - 1} ${TMP::=${VIA}} ${VIA::=${FROM}} ${FROM::=${TMP}}
+_:= ${N::=${N:[1..-2]}} ${TMP::=${VIA}} ${VIA::=${FROM}} ${FROM::=${TMP}}
. include "${.PARSEDIR}/${.PARSEFILE}"
-_:= ${N::!=expr $N + 1} ${TMP::=${VIA}} ${VIA::=${FROM}} ${FROM::=${TMP}}
+_:= ${N::+=D} ${TMP::=${VIA}} ${VIA::=${FROM}} ${FROM::=${TMP}}
.endif
diff --git a/contrib/bmake/unit-tests/include-main.exp b/contrib/bmake/unit-tests/include-main.exp
index c8a670a1c14a..fcc38247109f 100644
--- a/contrib/bmake/unit-tests/include-main.exp
+++ b/contrib/bmake/unit-tests/include-main.exp
@@ -1,17 +1,17 @@
-make: "include-main.mk" line 14: main-before-ok
-make: "include-main.mk" line 21: main-before-for-ok
-make: "include-sub.mk" line 4: sub-before-ok
-make: "include-sub.mk" line 14: sub-before-for-ok
-ParseReadLine (5): '. info subsub-ok'
-make: "include-subsub.mk" line 5: subsub-ok
- in .for loop from include-sub.mk:31
- in .for loop from include-sub.mk:30
- in .for loop from include-sub.mk:29
- in .include from include-main.mk:27
-ParseReadLine (6): '.MAKEFLAGS: -d0'
+make: "include-main.mk" line 15: main-before-ok
+make: "include-main.mk" line 23: main-before-for-ok
+make: "include-sub.inc" line 4: sub-before-ok
+make: "include-sub.inc" line 14: sub-before-for-ok
+Parsing line 5: . info subsub-ok
+make: "include-subsub.inc" line 5: subsub-ok
+ in .for loop from include-sub.inc:31 with i = include
+ in .for loop from include-sub.inc:30 with i = nested
+ in .for loop from include-sub.inc:29 with i = deeply
+ in include-main.mk:29
+Parsing line 6: .MAKEFLAGS: -d0
ParseDependency(.MAKEFLAGS: -d0)
-make: "include-sub.mk" line 38: sub-after-ok
-make: "include-sub.mk" line 45: sub-after-for-ok
-make: "include-main.mk" line 30: main-after-ok
-make: "include-main.mk" line 37: main-after-for-ok
+make: "include-sub.inc" line 38: sub-after-ok
+make: "include-sub.inc" line 45: sub-after-for-ok
+make: "include-main.mk" line 33: main-after-ok
+make: "include-main.mk" line 41: main-after-for-ok
exit status 0
diff --git a/contrib/bmake/unit-tests/include-main.mk b/contrib/bmake/unit-tests/include-main.mk
index d3f122aef718..373bbdea1721 100644
--- a/contrib/bmake/unit-tests/include-main.mk
+++ b/contrib/bmake/unit-tests/include-main.mk
@@ -1,16 +1,17 @@
-# $NetBSD: include-main.mk,v 1.6 2021/01/22 00:44:55 rillig Exp $
+# $NetBSD: include-main.mk,v 1.9 2023/06/01 20:56:35 rillig Exp $
#
# Until 2020-09-05, the .INCLUDEDFROMFILE magic variable did not behave
# as described in the manual page.
#
# The manual page says that it is the "filename of the file this Makefile
# was included from", while before 2020-09-05 it was the "filename in which
-# the latest .include happened". See parse.c, function ParseSetIncludeFile.
+# the latest .include happened". See parse.c, function SetParseFile.
#
# Since 2020-09-05, the .INCLUDEDFROMDIR and .INCLUDEDFROMFILE variables
# properly handle nested includes and even .for loops.
.if !defined(.INCLUDEDFROMFILE)
+# expect+1: main-before-ok
. info main-before-ok
.else
. warning main-before-fail(${.INCLUDEDFROMFILE})
@@ -18,15 +19,17 @@
.for i in once
. if !defined(.INCLUDEDFROMFILE)
+# expect+1: main-before-for-ok
. info main-before-for-ok
. else
. warning main-before-for-fail(${.INCLUDEDFROMFILE})
. endif
.endfor
-.include "include-sub.mk"
+.include "include-sub.inc"
.if !defined(.INCLUDEDFROMFILE)
+# expect+1: main-after-ok
. info main-after-ok
.else
. warning main-after-fail(${.INCLUDEDFROMFILE})
@@ -34,6 +37,7 @@
.for i in once
. if !defined(.INCLUDEDFROMFILE)
+# expect+1: main-after-for-ok
. info main-after-for-ok
. else
. warning main-after-for-fail(${.INCLUDEDFROMFILE})
diff --git a/contrib/bmake/unit-tests/include-sub.mk b/contrib/bmake/unit-tests/include-sub.inc
index 0b8dc77398ab..f26f14c9d84f 100644
--- a/contrib/bmake/unit-tests/include-sub.mk
+++ b/contrib/bmake/unit-tests/include-sub.inc
@@ -1,4 +1,4 @@
-# $NetBSD: include-sub.mk,v 1.7 2020/11/02 19:07:09 rillig Exp $
+# $NetBSD: include-sub.inc,v 1.1 2023/01/19 23:26:14 rillig Exp $
.if ${.INCLUDEDFROMFILE} == "include-main.mk"
. info sub-before-ok
@@ -20,16 +20,16 @@
# To see the variable 'includes' in action:
#
# Breakpoints:
-# Parse_File at "Vector_Push(&includes)"
-# ParseMessage at entry
+# Parse_PushInput at "Vector_Push(&includes)"
+# HandleMessage at entry
# Watches:
-# ((const IFile *[10])(*includes.items))
-# *curFile
+# ((const IncludedFile *[10])(*includes.items))
+# *CurFile()
.for i in deeply
. for i in nested
. for i in include
-.include "include-subsub.mk"
+.include "include-subsub.inc"
. endfor
. endfor
.endfor
diff --git a/contrib/bmake/unit-tests/include-subsub.inc b/contrib/bmake/unit-tests/include-subsub.inc
new file mode 100644
index 000000000000..79a6a3770090
--- /dev/null
+++ b/contrib/bmake/unit-tests/include-subsub.inc
@@ -0,0 +1,9 @@
+# $NetBSD: include-subsub.inc,v 1.1 2023/01/19 23:26:14 rillig Exp $
+
+.if ${.INCLUDEDFROMFILE} == "include-sub.inc"
+.MAKEFLAGS: -dp
+. info subsub-ok
+.MAKEFLAGS: -d0
+.else
+. warning subsub-fail(${.INCLUDEDFROMFILE})
+.endif
diff --git a/contrib/bmake/unit-tests/include-subsub.mk b/contrib/bmake/unit-tests/include-subsub.mk
deleted file mode 100644
index 476d75f79556..000000000000
--- a/contrib/bmake/unit-tests/include-subsub.mk
+++ /dev/null
@@ -1,9 +0,0 @@
-# $NetBSD: include-subsub.mk,v 1.4 2021/01/26 23:44:56 rillig Exp $
-
-.if ${.INCLUDEDFROMFILE} == "include-sub.mk"
-.MAKEFLAGS: -dp
-. info subsub-ok
-.MAKEFLAGS: -d0
-.else
-. warning subsub-fail(${.INCLUDEDFROMFILE})
-.endif
diff --git a/contrib/bmake/unit-tests/job-output-null.exp b/contrib/bmake/unit-tests/job-output-null.exp
index af9b4e64dba3..628ec54a1a6b 100644
--- a/contrib/bmake/unit-tests/job-output-null.exp
+++ b/contrib/bmake/unit-tests/job-output-null.exp
@@ -1,4 +1,6 @@
-hello
-hello
-hello world without newline, hello world without newline, hello world without newline.
+1 trailing
+2a trailing
+2b trailing
+2c trailing
+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..04786dba4096 100644
--- a/contrib/bmake/unit-tests/job-output-null.mk
+++ b/contrib/bmake/unit-tests/job-output-null.mk
@@ -1,32 +1,55 @@
-# $NetBSD: job-output-null.mk,v 1.1 2021/04/15 19:02:29 rillig Exp $
+# $NetBSD: job-output-null.mk,v 1.4 2022/09/03 08:03:27 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.
#
-# As of 2021-04-15, make handles null bytes from the child process
-# inconsistently. It's an edge case though since typically the child
-# processes output text.
+# Before job.c 1.454 from 2022-09-03, make handled null bytes in the output
+# from the child process 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 1 to 3 parts, depending on the
+# exact timing, which in this test makes a crucial difference since before
+# job.c 1.454 from 2022-09-03, the outcome of the test depended on whether
+# there was a '\n' in each of the blocks from the output. Depending on the
+# exact timing, the output of that test varied, its possible values were '2a',
+# '2a 2b', '2a 2c', '2a 2b 2c'.
.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' ''
+ # The null byte from the command output is replaced with a single
+ # space by CollectOutput.
+ @printf '1\0trailing\n'
+ # expect: 1 trailing
# 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' '' '' '' '' '' ''
+ # Each null byte from the command output is replaced with a single
+ # space.
+ @printf '2a\0trailing\n''2b\0trailing\n''2c\0trailing\n'
+ # expect: 2a trailing
+ # expect: 2b trailing
+ # expect: 2c trailing
@sleep 1
- # The null bytes are replaced with spaces since they are not followed
- # by a newline.
+ # Each null byte from the command output is replaced with a single
+ # space. Because there is no trailing newline in the output, these
+ # null bytes were replaced with spaces even before job.c 1.454 from
+ # 2022-09-03, unlike in the cases above.
#
# 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.'
+ # expect: 3a without newline, 3b without newline.
diff --git a/contrib/bmake/unit-tests/jobs-error-indirect.exp b/contrib/bmake/unit-tests/jobs-error-indirect.exp
index 5c5a3801f4f6..79843a235666 100644
--- a/contrib/bmake/unit-tests/jobs-error-indirect.exp
+++ b/contrib/bmake/unit-tests/jobs-error-indirect.exp
@@ -2,7 +2,7 @@ false
*** [indirect] Error code 1
make: stopped in unit-tests
-1 error
+make: 1 error
make: stopped in unit-tests
exit status 2
diff --git a/contrib/bmake/unit-tests/jobs-error-nested-make.exp b/contrib/bmake/unit-tests/jobs-error-nested-make.exp
index 88c32ab8d1f6..2baf893c6623 100644
--- a/contrib/bmake/unit-tests/jobs-error-nested-make.exp
+++ b/contrib/bmake/unit-tests/jobs-error-nested-make.exp
@@ -3,7 +3,7 @@ false
*** [nested] Error code 1
make: stopped in unit-tests
-1 error
+make: 1 error
make: stopped in unit-tests
diff --git a/contrib/bmake/unit-tests/jobs-error-nested.exp b/contrib/bmake/unit-tests/jobs-error-nested.exp
index f96b5d016777..873613d40d48 100644
--- a/contrib/bmake/unit-tests/jobs-error-nested.exp
+++ b/contrib/bmake/unit-tests/jobs-error-nested.exp
@@ -3,13 +3,13 @@ false
*** [nested] Error code 1
make: stopped in unit-tests
-1 error
+make: 1 error
make: stopped in unit-tests
*** [all] Error code 2
make: stopped in unit-tests
-1 error
+make: 1 error
make: stopped in unit-tests
exit status 2
diff --git a/contrib/bmake/unit-tests/lint.exp b/contrib/bmake/unit-tests/lint.exp
index d7068b5e006a..78761c862298 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 target "mod-loop-varname": while evaluating variable "VAR": In the :@ modifier, 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/lint.mk b/contrib/bmake/unit-tests/lint.mk
index 5db417639d0b..431143c644ee 100755
--- a/contrib/bmake/unit-tests/lint.mk
+++ b/contrib/bmake/unit-tests/lint.mk
@@ -1,4 +1,4 @@
-# $NetBSD: lint.mk,v 1.4 2021/01/30 13:50:18 rillig Exp $
+# $NetBSD: lint.mk,v 1.5 2023/11/19 21:47:52 rillig Exp $
#
# Demonstrates stricter checks that are only enabled in lint mode, using the
# option -dL.
@@ -6,7 +6,7 @@
# Before main.c 1.421 from 2020-11-01, make exited successfully even though
# the error message had been issued as PARSE_FATAL. This was because back
# then, make checked for parse errors only after parsing each top-level
-# makefile, in Parse_File. After that, when expanding variable expressions
+# makefile, in Parse_File. After that, when expanding expressions
# in shell commands, the parse errors were not checked again.
# Ouch: as of 2020-08-03, the variable is malformed and parsing stops
diff --git a/contrib/bmake/unit-tests/make-exported.mk b/contrib/bmake/unit-tests/make-exported.mk
index 58cb15183b8d..363ea2733a47 100755
--- a/contrib/bmake/unit-tests/make-exported.mk
+++ b/contrib/bmake/unit-tests/make-exported.mk
@@ -1,4 +1,4 @@
-# $NetBSD: make-exported.mk,v 1.6 2020/10/05 19:27:48 rillig Exp $
+# $NetBSD: make-exported.mk,v 1.7 2022/09/09 18:36:15 sjg Exp $
#
# As of 2020-08-09, the code in Var_Export is shared between the .export
# directive and the .MAKE.EXPORTED variable. This leads to non-obvious
@@ -22,4 +22,4 @@ UT_VAR= ${UNEXPANDED}
.MAKE.EXPORTED= -literal UT_VAR
all:
- @env | sort | egrep '^UT_|make-exported-value' || true
+ @env | sort | ${EGREP} '^UT_|make-exported-value' || true
diff --git a/contrib/bmake/unit-tests/meta-cmd-cmp.exp b/contrib/bmake/unit-tests/meta-cmd-cmp.exp
index bfc52123e3b2..dc63da3b346b 100644
--- a/contrib/bmake/unit-tests/meta-cmd-cmp.exp
+++ b/contrib/bmake/unit-tests/meta-cmd-cmp.exp
@@ -34,4 +34,20 @@ vs
Building .meta-cmd-cmp.cmp2
This line not compared FLAGS=
Skipping meta for .END: .SPECIAL
+filter0:
+Building .meta-cmd-cmp.filter
+Skipping meta for .END: .SPECIAL
+filter1:
+.meta-cmd-cmp.filter.meta: 2: a build command has changed
+@echo ccache cc -c foo.c > .meta-cmd-cmp.filter
+vs
+@echo cc -c foo.c > .meta-cmd-cmp.filter
+Building .meta-cmd-cmp.filter
+Skipping meta for .END: .SPECIAL
+filter2:
+`.meta-cmd-cmp.filter' is up to date.
+Skipping meta for .END: .SPECIAL
+filter3:
+`.meta-cmd-cmp.filter' is up to date.
+Skipping meta for .END: .SPECIAL
exit status 0
diff --git a/contrib/bmake/unit-tests/meta-cmd-cmp.mk b/contrib/bmake/unit-tests/meta-cmd-cmp.mk
index a1c0f7c10063..a410ea0dbd07 100644
--- a/contrib/bmake/unit-tests/meta-cmd-cmp.mk
+++ b/contrib/bmake/unit-tests/meta-cmd-cmp.mk
@@ -1,15 +1,15 @@
-# $NetBSD: meta-cmd-cmp.mk,v 1.2 2020/12/05 22:51:34 sjg Exp $
+# $NetBSD: meta-cmd-cmp.mk,v 1.6 2022/03/02 19:32:15 sjg Exp $
#
# Tests META_MODE command line comparison
#
.MAIN: all
-.MAKE.MODE= meta verbose silent=yes curdirok=yes
+.MAKE.MODE= meta verbose silent=yes curdirok=yes nofilemon
tf:= .${.PARSEFILE:R}
.if ${.TARGETS:Nall} == ""
-all: prep one two change1 change2 post
+all: prep one two change1 change2 filter0 filter1 filter2 filter3 post
CLEANFILES= ${tf}*
@@ -22,6 +22,7 @@ FLAGS?=
FLAGS2?=
tests= ${tf}.cmp ${tf}.nocmp ${tf}.cmp2
+filter_tests= ${tf}.filter
${tf}.cmp:
@echo FLAGS=${FLAGS:Uempty} > $@
@@ -35,7 +36,20 @@ ${tf}.cmp2:
@echo FLAGS2=${FLAGS2:Uempty} > $@
@echo This line not compared FLAGS=${FLAGS:Uempty} ${.OODATE:MNOMETA_CMP}
-# these do the same
+COMPILER_WRAPPERS= ccache distcc icecc
+WRAPPER?= ccache
+.ifdef WITH_CMP_FILTER
+.MAKE.META.CMP_FILTER+= ${COMPILER_WRAPPERS:S,^,N,}
+.endif
+.ifdef WITH_LOCAL_CMP_FILTER
+# local variable
+${tf}.filter: .MAKE.META.CMP_FILTER= ${COMPILER_WRAPPERS:S,^,N,}
+.endif
+
+${tf}.filter:
+ @echo ${WRAPPER} cc -c foo.c > $@
+
+# these do the same
one two: .PHONY
@echo $@:
@${.MAKE} -dM -r -C ${.CURDIR} -f ${MAKEFILE} ${tests}
@@ -48,5 +62,23 @@ change2: .PHONY
@echo $@:
@${.MAKE} -dM -r -C ${.CURDIR} -f ${MAKEFILE} FLAGS2=changed ${tests}
+filter0: .PHONY
+ @echo $@:
+ @${.MAKE} -dM -r -C ${.CURDIR} -f ${MAKEFILE} ${filter_tests}
+
+filter1: .PHONY
+ @echo $@:
+ @${.MAKE} -dM -r -C ${.CURDIR} -f ${MAKEFILE} WRAPPER= ${filter_tests}
+
+filter2: .PHONY
+ @echo $@:
+ @${.MAKE} -dM -r -C ${.CURDIR} -f ${MAKEFILE} -DWITH_CMP_FILTER \
+ WRAPPER=distcc ${filter_tests}
+
+filter3: .PHONY
+ @echo $@:
+ @${.MAKE} -dM -r -C ${.CURDIR} -f ${MAKEFILE} -DWITH_LOCAL_CMP_FILTER \
+ WRAPPER=icecc ${filter_tests}
+
# don't let gcov mess up the results
.MAKE.META.IGNORE_PATTERNS+= *.gcda
diff --git a/contrib/bmake/unit-tests/meta-ignore.inc b/contrib/bmake/unit-tests/meta-ignore.inc
new file mode 100644
index 000000000000..ed74f4d79017
--- /dev/null
+++ b/contrib/bmake/unit-tests/meta-ignore.inc
@@ -0,0 +1,63 @@
+# $NetBSD: meta-ignore.inc,v 1.2 2023/02/25 19:30:32 sjg Exp $
+
+# common logic for testing .MAKE.META.IGNORE_*
+
+# we want a directory outside of .OBJDIR to drop a file
+# that our meta file refers to.
+# Note: these tests will not work if TMPDIR is /tmp or /var/tmp
+# or a subdir thereof
+IGNORE:= ${TMPDIR}/ignore
+OBJ:= ${TMPDIR}/obj
+
+# this is always ignored so make sure it isn't used above
+TMPDIR= /tmp/nothanks
+
+all: one two three
+
+setup:
+ @mkdir -p ${IGNORE} ${OBJ}
+ @echo > ${IGNORE}/check
+ @rm -f ${OBJ}/check-ignore
+
+makefile:= ${.INCLUDEDFROMDIR}/${.INCLUDEDFROMFILE}
+TEST:= ${.INCLUDEDFROMFILE:R:C,.*meta-,,:S,-,_,g:tu}
+
+DESC.one= Initialize check-ignore.meta
+DESC.two= Use .MAKE.META.${TEST} - check-ignore is up to date
+DESC.three= Skip .MAKE.META.${TEST} - check-ignore is out of date
+
+# just in case someone runs us with -jN
+.ORDER: one two three
+one two three: .MAKE setup
+ @echo "${DESC.${.TARGET}}"; \
+ ${MAKE} -C ${.CURDIR} -f ${makefile} check-ignore parent=${.TARGET}
+
+.if make(check-ignore)
+.MAKEFLAGS: -dM
+.MAKE.MODE = meta verbose silent=yes
+.OBJDIR: ${OBJ}
+.if ${parent} == "two"
+.if ${TEST} == "IGNORE_PATHS"
+# this is a prefix list - any path that matches
+# one of these prefixes will be ignored
+.MAKE.META.IGNORE_PATHS = ${IGNORE}
+.elif ${TEST} == "IGNORE_PATTERNS"
+# more flexible but more expensive
+# this example is equivalent to M*/ignore/*
+# a match means ignore
+.MAKE.META.IGNORE_PATTERNS = */ignore/*
+.elif ${TEST} == "IGNORE_FILTER"
+# this is the most flexible, but also most expensive
+# if this expands to nothing - ignore the path
+.MAKE.META.IGNORE_FILTER = N${IGNORE}/*
+.endif
+.endif
+
+# : < just reads from ${IGNORE}/check
+# so that our filemon trace will have a reference to it
+# we ensure it is always newer than the target.
+check-ignore: .META .NOPATH
+ @: < ${IGNORE}/check > ${.TARGET}
+ @sleep 1; echo ${.TARGET} > ${IGNORE}/check
+
+.endif
diff --git a/contrib/bmake/unit-tests/moderrs.exp b/contrib/bmake/unit-tests/moderrs.exp
index 9d8bd308c36c..0d1bcdc5b0f9 100644
--- a/contrib/bmake/unit-tests/moderrs.exp
+++ b/contrib/bmake/unit-tests/moderrs.exp
@@ -1,21 +1,21 @@
mod-unknown-direct:
want: Unknown modifier 'Z'
-make: Unknown modifier "Z"
+make: in target "mod-unknown-direct": while evaluating variable "VAR": Unknown modifier "Z"
VAR:Z=before--after
mod-unknown-indirect:
want: Unknown modifier 'Z'
-make: Unknown modifier "Z"
+make: in target "mod-unknown-indirect": while evaluating variable "VAR": Unknown modifier "Z"
VAR:Z=before-inner}-after
unclosed-direct:
-want: Unclosed variable expression, expecting '}' for modifier "S,V,v," of variable "VAR" with value "Thevariable"
-make: Unclosed variable expression, expecting '}' for modifier "S,V,v," of variable "VAR" with value "Thevariable"
+want: Unclosed expression, expecting '}' for modifier "S,V,v," of variable "VAR" with value "Thevariable"
+make: Unclosed expression, expecting '}' for modifier "S,V,v," of variable "VAR" with value "Thevariable"
VAR:S,V,v,=Thevariable
unclosed-indirect:
-want: Unclosed variable expression after indirect modifier, expecting '}' for variable "VAR"
-make: Unclosed variable expression after indirect modifier, expecting '}' for variable "VAR"
+want: Unclosed expression after indirect modifier, expecting '}' for variable "VAR"
+make: Unclosed expression after indirect modifier, expecting '}' for variable "VAR"
VAR:S,V,v,=Thevariable
unfinished-indirect:
@@ -33,7 +33,7 @@ make: Unfinished modifier for "UNDEF" ('@' missing)
1 2 3
loop-close:
-make: Unclosed variable expression, expecting '}' for modifier "@var@${var}}...@" of variable "UNDEF" with value "1}... 2}... 3}..."
+make: Unclosed expression, expecting '}' for modifier "@var@${var}}...@" of variable "UNDEF" with value "1}... 2}... 3}..."
1}... 2}... 3}...
1}... 2}... 3}...
@@ -67,7 +67,7 @@ make: Unfinished modifier for "VAR" (',' missing)
4:
make: Unfinished modifier for "VAR" (',' missing)
5:
-make: Unclosed variable expression, expecting '}' for modifier "S,from,to," of variable "VAR" with value "TheVariable"
+make: Unclosed expression, expecting '}' for modifier "S,from,to," of variable "VAR" with value "TheVariable"
6: TheVariable
7: TheVariable
@@ -82,7 +82,7 @@ make: Unfinished modifier for "VAR" (',' missing)
4:
make: Unfinished modifier for "VAR" (',' missing)
5:
-make: Unclosed variable expression, expecting '}' for modifier "C,from,to," of variable "VAR" with value "TheVariable"
+make: Unclosed expression, expecting '}' for modifier "C,from,to," of variable "VAR" with value "TheVariable"
6: TheVariable
7: TheVariable
@@ -119,18 +119,18 @@ then
mod-remember-parse:
1 1 2 3 5 8 13 21 34
-make: Unknown modifier "__"
+make: in target "mod-remember-parse": while evaluating variable "FIB": Unknown modifier "__"
mod-sysv-parse:
-make: Unknown modifier "3"
-make: Unclosed variable expression, expecting '}' for modifier "3" of variable "FIB" with value ""
+make: in target "mod-sysv-parse": while evaluating variable "FIB": Unknown modifier "3"
+make: Unclosed expression, expecting '}' for modifier "3" of variable "FIB" with value ""
-make: Unknown modifier "3="
-make: Unclosed variable expression, expecting '}' for modifier "3=" of variable "FIB" with value ""
+make: in target "mod-sysv-parse": while evaluating variable "FIB": Unknown modifier "3="
+make: Unclosed expression, expecting '}' for modifier "3=" of variable "FIB" with value ""
-make: Unknown modifier "3=x3"
-make: Unclosed variable expression, expecting '}' for modifier "3=x3" of variable "FIB" with value ""
+make: in target "mod-sysv-parse": while evaluating variable "FIB": Unknown modifier "3=x3"
+make: Unclosed expression, expecting '}' for modifier "3=x3" of variable "FIB" with value ""
1 1 2 x3 5 8 1x3 21 34
diff --git a/contrib/bmake/unit-tests/moderrs.mk b/contrib/bmake/unit-tests/moderrs.mk
index ffd920314c5d..bde263af4079 100644
--- a/contrib/bmake/unit-tests/moderrs.mk
+++ b/contrib/bmake/unit-tests/moderrs.mk
@@ -1,4 +1,4 @@
-# $NetBSD: moderrs.mk,v 1.30 2021/06/21 08:28:37 rillig Exp $
+# $NetBSD: moderrs.mk,v 1.31 2023/11/19 22:32:44 rillig Exp $
#
# various modifier error tests
@@ -34,11 +34,11 @@ mod-unknown-indirect: print-header print-footer
@echo 'VAR:${MOD_UNKN}=before-${VAR:${MOD_UNKN}:inner}-after'
unclosed-direct: print-header print-footer
- @echo 'want: Unclosed variable expression, expecting $'}$' for modifier "S,V,v," of variable "VAR" with value "Thevariable"'
+ @echo 'want: Unclosed expression, expecting $'}$' for modifier "S,V,v," of variable "VAR" with value "Thevariable"'
@echo VAR:S,V,v,=${VAR:S,V,v,
unclosed-indirect: print-header print-footer
- @echo 'want: Unclosed variable expression after indirect modifier, expecting $'}$' for variable "VAR"'
+ @echo 'want: Unclosed expression after indirect modifier, expecting $'}$' for variable "VAR"'
@echo VAR:${MOD_TERM},=${VAR:${MOD_S}
unfinished-indirect: print-header print-footer
diff --git a/contrib/bmake/unit-tests/modmatch.exp b/contrib/bmake/unit-tests/modmatch.exp
deleted file mode 100644
index fcaf6c02ed69..000000000000
--- a/contrib/bmake/unit-tests/modmatch.exp
+++ /dev/null
@@ -1,17 +0,0 @@
-LIB=a X_LIBS:M${LIB${LIB:tu}} is "/tmp/liba.a"
-LIB=a X_LIBS:M*/lib${LIB}.a is "/tmp/liba.a"
-LIB=a X_LIBS:M*/lib${LIB}.a:tu is "/TMP/LIBA.A"
-LIB=b X_LIBS:M${LIB${LIB:tu}} is ""
-LIB=b X_LIBS:M*/lib${LIB}.a is ""
-LIB=b X_LIBS:M*/lib${LIB}.a:tu is ""
-LIB=c X_LIBS:M${LIB${LIB:tu}} is ""
-LIB=c X_LIBS:M*/lib${LIB}.a is ""
-LIB=c X_LIBS:M*/lib${LIB}.a:tu is ""
-LIB=d X_LIBS:M${LIB${LIB:tu}} is "/tmp/libd.a"
-LIB=d X_LIBS:M*/lib${LIB}.a is "/tmp/libd.a"
-LIB=d X_LIBS:M*/lib${LIB}.a:tu is "/TMP/LIBD.A"
-LIB=e X_LIBS:M${LIB${LIB:tu}} is "/tmp/libe.a"
-LIB=e X_LIBS:M*/lib${LIB}.a is "/tmp/libe.a"
-LIB=e X_LIBS:M*/lib${LIB}.a:tu is "/TMP/LIBE.A"
-Mscanner=OK
-exit status 0
diff --git a/contrib/bmake/unit-tests/modmatch.mk b/contrib/bmake/unit-tests/modmatch.mk
deleted file mode 100644
index 7dcacf09da6d..000000000000
--- a/contrib/bmake/unit-tests/modmatch.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-# $NetBSD: modmatch.mk,v 1.9 2020/10/24 08:50:17 rillig Exp $
-#
-# Tests for the :M and :S modifiers.
-
-X= a b c d e
-
-.for x in $X
-LIB${x:tu}= /tmp/lib$x.a
-.endfor
-
-X_LIBS= ${LIBA} ${LIBD} ${LIBE}
-
-LIB?= a
-
-var= head
-res= no
-.if !empty(var:M${:Uhead\:tail:C/:.*//})
-res= OK
-.endif
-
-all: show-libs
-
-show-libs:
- @for x in $X; do ${.MAKE} -f ${MAKEFILE} show LIB=$$x; done
- @echo "Mscanner=${res}"
-
-show:
- @echo 'LIB=${LIB} X_LIBS:M$${LIB$${LIB:tu}} is "${X_LIBS:M${LIB${LIB:tu}}}"'
- @echo 'LIB=${LIB} X_LIBS:M*/lib$${LIB}.a is "${X_LIBS:M*/lib${LIB}.a}"'
- @echo 'LIB=${LIB} X_LIBS:M*/lib$${LIB}.a:tu is "${X_LIBS:M*/lib${LIB}.a:tu}"'
diff --git a/contrib/bmake/unit-tests/modmisc.exp b/contrib/bmake/unit-tests/modmisc.exp
index 10475e65ee0f..f243511ab491 100644
--- a/contrib/bmake/unit-tests/modmisc.exp
+++ b/contrib/bmake/unit-tests/modmisc.exp
@@ -6,7 +6,6 @@ path='/bin':'/tmp':'/':'/no/such/dir'
path_/usr/xbin=/opt/xbin/
paths=/bin /tmp / /no/such/dir /opt/xbin
PATHS=/BIN /TMP / /NO/SUCH/DIR /OPT/XBIN
-The answer is 42
S:
C:
@:
diff --git a/contrib/bmake/unit-tests/modmisc.mk b/contrib/bmake/unit-tests/modmisc.mk
index 9ace35c15162..4868abef92f1 100644
--- a/contrib/bmake/unit-tests/modmisc.mk
+++ b/contrib/bmake/unit-tests/modmisc.mk
@@ -1,4 +1,4 @@
-# $NetBSD: modmisc.mk,v 1.52 2020/12/20 19:29:06 rillig Exp $
+# $NetBSD: modmisc.mk,v 1.53 2023/06/16 07:20:45 rillig Exp $
#
# miscellaneous modifier tests
@@ -15,14 +15,10 @@ MOD_HOMES= S,/home/,/homes/,
MOD_OPT= @d@$${exists($$d):?$$d:$${d:S,/usr,/opt,}}@
MOD_SEP= S,:, ,g
-all: modvar modvarloop modsysv emptyvar undefvar
+all: modvar modvarloop emptyvar undefvar
all: mod-quote
all: mod-break-many-words
-# See also sysv.mk.
-modsysv:
- @echo "The answer is ${libfoo.a:L:libfoo.a=42}"
-
# Demonstrates modifiers that are given indirectly from a variable.
modvar:
@echo "path='${path}'"
@@ -60,6 +56,6 @@ undefvar:
mod-quote:
@echo $@: new${.newline:Q}${.newline:Q}line
-# Cover the bmake_realloc in Str_Words.
+# Cover the bmake_realloc in Substring_Words.
mod-break-many-words:
@echo $@: ${UNDEF:U:range=500:[#]}
diff --git a/contrib/bmake/unit-tests/modts.exp b/contrib/bmake/unit-tests/modts.exp
deleted file mode 100644
index 18837016add4..000000000000
--- a/contrib/bmake/unit-tests/modts.exp
+++ /dev/null
@@ -1,14 +0,0 @@
-make: Bad modifier ":tx" for variable "LIST"
-LIST:tx="}"
-make: Bad modifier ":ts\X" for variable "LIST"
-LIST:ts/x:tu="\X:tu}"
-FU_mod-ts="a/b/cool"
-FU_mod-ts:ts:T="cool" == cool?
-B.${AAA:ts}="Baaa" == Baaa?
-:ts :S => aaxBbxaaxbbxaaxbb
-:ts :S space => axa a axc
-:ts :S space :M => axaxaxaxc
-:ts :S => axa a axc
-:ts :S :@ => axa a axc
-:ts :S :@ :M => axaxaxaxc
-exit status 0
diff --git a/contrib/bmake/unit-tests/modts.mk b/contrib/bmake/unit-tests/modts.mk
deleted file mode 100644
index 4776c5818ea5..000000000000
--- a/contrib/bmake/unit-tests/modts.mk
+++ /dev/null
@@ -1,47 +0,0 @@
-# $NetBSD: modts.mk,v 1.8 2020/11/03 18:42:33 rillig Exp $
-
-LIST= one two three four five six
-
-FU_mod-ts= a / b / cool
-
-AAA= a a a
-B.aaa= Baaa
-
-all: mod-ts mod-ts-space
-
-# Use print or printf iff they are builtin.
-# XXX note that this causes problems, when make decides
-# there is no need to use a shell, so avoid where possible.
-.if ${(type print) 2> /dev/null || echo:L:sh:Mbuiltin} != ""
-PRINT= print -r --
-.elif ${(type printf) 2> /dev/null || echo:L:sh:Mbuiltin} != ""
-PRINT= printf '%s\n'
-.else
-PRINT= echo
-.endif
-
-mod-ts:
- @${PRINT} 'LIST:tx="${LIST:tx}"'
- @${PRINT} 'LIST:ts/x:tu="${LIST:ts\X:tu}"'
- @${PRINT} 'FU_$@="${FU_${@:ts}:ts}"'
- @${PRINT} 'FU_$@:ts:T="${FU_${@:ts}:ts:T}" == cool?'
- @${PRINT} 'B.$${AAA:ts}="${B.${AAA:ts}}" == Baaa?'
-
-mod-ts-space:
- # After the :ts modifier, the whole string is interpreted as a single
- # word since all spaces have been replaced with x.
- @${PRINT} ':ts :S => '${aa bb aa bb aa bb:L:tsx:S,b,B,:Q}
-
- # The :ts modifier also applies to word separators that are added
- # afterwards.
- @${PRINT} ':ts :S space => '${a ababa c:L:tsx:S,b, ,g:Q}
- @${PRINT} ':ts :S space :M => '${a ababa c:L:tsx:S,b, ,g:M*:Q}
-
- # Not all modifiers behave this way though. Some of them always use
- # a space as word separator instead of the :ts separator.
- # This seems like an oversight during implementation.
- @${PRINT} ':ts :S => '${a ababa c:L:tsx:S,b, ,g:Q}
- @${PRINT} ':ts :S :@ => '${a ababa c:L:tsx:S,b, ,g:@v@${v}@:Q}
-
- # A final :M* modifier applies the :ts separator again, though.
- @${PRINT} ':ts :S :@ :M => '${a ababa c:L:tsx:S,b, ,g:@v@${v}@:M*:Q}
diff --git a/contrib/bmake/unit-tests/modword.exp b/contrib/bmake/unit-tests/modword.exp
deleted file mode 100644
index 02e9974c02d6..000000000000
--- a/contrib/bmake/unit-tests/modword.exp
+++ /dev/null
@@ -1,126 +0,0 @@
-make: Bad modifier ":[]" for variable "LIST"
-LIST:[]="" is an error
-LIST:[0]="one two three four five six"
-LIST:[0x0]="one two three four five six"
-LIST:[000]="one two three four five six"
-LIST:[*]="one two three four five six"
-LIST:[@]="one two three four five six"
-LIST:[0]:C/ /,/="one,two three four five six"
-LIST:[0]:C/ /,/g="one,two,three,four,five,six"
-LIST:[0]:C/ /,/1g="one,two,three,four,five,six"
-LIST:[*]:C/ /,/="one,two three four five six"
-LIST:[*]:C/ /,/g="one,two,three,four,five,six"
-LIST:[*]:C/ /,/1g="one,two,three,four,five,six"
-LIST:[@]:C/ /,/="one two three four five six"
-LIST:[@]:C/ /,/g="one two three four five six"
-LIST:[@]:C/ /,/1g="one two three four five six"
-LIST:[@]:[0]:C/ /,/="one,two three four five six"
-LIST:[0]:[@]:C/ /,/="one two three four five six"
-LIST:[@]:[*]:C/ /,/="one,two three four five six"
-LIST:[*]:[@]:C/ /,/="one two three four five six"
-EMPTY=""
-EMPTY:[#]="1" == 1 ?
-ESCAPEDSPACE="\ "
-ESCAPEDSPACE:[#]="1" == 1 ?
-REALLYSPACE=" "
-REALLYSPACE:[#]="1" == 1 ?
-LIST:[#]="6"
-LIST:[0]:[#]="1" == 1 ?
-LIST:[*]:[#]="1" == 1 ?
-LIST:[@]:[#]="6"
-LIST:[1]:[#]="1"
-LIST:[1..3]:[#]="3"
-EMPTY:[1]=""
-ESCAPEDSPACE="\ "
-ESCAPEDSPACE:[1]="\ "
-REALLYSPACE=" "
-REALLYSPACE:[1]="" == "" ?
-REALLYSPACE:[*]:[1]=" " == " " ?
-LIST:[1]="one"
-make: Bad modifier ":[1.]" for variable "LIST"
-LIST:[1.]="" is an error
-make: Bad modifier ":[1]." for variable "LIST"
-LIST:[1].="}" is an error
-LIST:[2]="two"
-LIST:[6]="six"
-LIST:[7]=""
-LIST:[999]=""
-make: Bad modifier ":[-]" for variable "LIST"
-LIST:[-]="" is an error
-make: Bad modifier ":[--]" for variable "LIST"
-LIST:[--]="" is an error
-LIST:[-1]="six"
-LIST:[-2]="five"
-LIST:[-6]="one"
-LIST:[-7]=""
-LIST:[-999]=""
-LONGLIST:[17]="17"
-LONGLIST:[0x11]="17"
-LONGLIST:[021]="17"
-LIST:[0]:[1]="one two three four five six"
-LIST:[*]:[1]="one two three four five six"
-LIST:[@]:[1]="one"
-LIST:[0]:[2]=""
-LIST:[*]:[2]=""
-LIST:[@]:[2]="two"
-LIST:[*]:C/ /,/:[2]=""
-LIST:[*]:C/ /,/:[*]:[2]=""
-LIST:[*]:C/ /,/:[@]:[2]="three"
-LONGLIST:[012..0x12]="10 11 12 13 14 15 16 17 18"
-make: Bad modifier ":[1.]" for variable "LIST"
-LIST:[1.]="" is an error
-make: Bad modifier ":[1..]" for variable "LIST"
-LIST:[1..]="" is an error
-make: Bad modifier ":[1.. ]" for variable "LIST"
-LIST:[1.. ]="" is an error
-LIST:[1..1]="one"
-make: Bad modifier ":[1..1.]" for variable "LIST"
-LIST:[1..1.]="" is an error
-LIST:[1..2]="one two"
-LIST:[2..1]="two one"
-LIST:[3..-2]="three four five"
-LIST:[-4..4]="three four"
-make: Bad modifier ":[0..1]" for variable "LIST"
-LIST:[0..1]="" is an error
-make: Bad modifier ":[-1..0]" for variable "LIST"
-LIST:[-1..0]="" is an error
-LIST:[-1..1]="six five four three two one"
-LIST:[0..0]="one two three four five six"
-LIST:[3..99]="three four five six"
-LIST:[-3..-99]="four three two one"
-LIST:[-99..-3]="one two three four"
-HASH="#" == "#" ?
-LIST:[${HASH}]="6"
-LIST:[${ZERO}]="one two three four five six"
-LIST:[${ZERO}x${ONE}]="one"
-LIST:[${ONE}]="one"
-LIST:[${MINUSONE}]="six"
-LIST:[${STAR}]="one two three four five six"
-LIST:[${AT}]="one two three four five six"
-make: Bad modifier ":[${EMPTY" for variable "LIST"
-LIST:[${EMPTY}]="" is an error
-LIST:[${LONGLIST:[21]:S/2//}]="one"
-LIST:[${LIST:[#]}]="six"
-LIST:[${LIST:[${HASH}]}]="six"
-LIST:[ -1.. +3]="six five four three"
-LIST:S/ /,/="one two three four five six"
-LIST:S/ /,/W="one,two three four five six"
-LIST:S/ /,/gW="one,two,three,four,five,six"
-EMPTY:S/^/,/=","
-EMPTY:S/^/,/W=","
-LIST:C/ /,/="one two three four five six"
-LIST:C/ /,/W="one,two three four five six"
-LIST:C/ /,/gW="one,two,three,four,five,six"
-EMPTY:C/^/,/=","
-EMPTY:C/^/,/W=","
-LIST:tW="one two three four five six"
-LIST:tw="one two three four five six"
-LIST:tW:C/ /,/="one,two three four five six"
-LIST:tW:C/ /,/g="one,two,three,four,five,six"
-LIST:tW:C/ /,/1g="one,two,three,four,five,six"
-LIST:tw:C/ /,/="one two three four five six"
-LIST:tw:C/ /,/g="one two three four five six"
-LIST:tw:C/ /,/1g="one two three four five six"
-LIST:tw:tW:C/ /,/="one,two three four five six"
-LIST:tW:tw:C/ /,/="one two three four five six"
-exit status 0
diff --git a/contrib/bmake/unit-tests/modword.mk b/contrib/bmake/unit-tests/modword.mk
deleted file mode 100644
index 95bb1fec78c3..000000000000
--- a/contrib/bmake/unit-tests/modword.mk
+++ /dev/null
@@ -1,161 +0,0 @@
-# $NetBSD: modword.mk,v 1.6 2021/03/14 16:00:07 rillig Exp $
-#
-# Test behaviour of new :[] modifier
-# TODO: When was this modifier new?
-
-all: mod-squarebrackets mod-S-W mod-C-W mod-tW-tw
-
-LIST= one two three
-LIST+= four five six
-LONGLIST= 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
-
-EMPTY= # the space should be ignored
-ESCAPEDSPACE= \ # escaped space before the '#'
-REALLYSPACE:= ${EMPTY:C/^/ /W}
-HASH= \#
-AT= @
-STAR= *
-ZERO= 0
-ONE= 1
-MINUSONE= -1
-
-mod-squarebrackets: mod-squarebrackets-0-star-at \
- mod-squarebrackets-hash \
- mod-squarebrackets-n \
- mod-squarebrackets-start-end \
- mod-squarebrackets-nested \
- mod-squarebrackets-space
-
-mod-squarebrackets-0-star-at:
- @echo 'LIST:[]="${LIST:[]}" is an error'
- @echo 'LIST:[0]="${LIST:[0]}"'
- @echo 'LIST:[0x0]="${LIST:[0x0]}"'
- @echo 'LIST:[000]="${LIST:[000]}"'
- @echo 'LIST:[*]="${LIST:[*]}"'
- @echo 'LIST:[@]="${LIST:[@]}"'
- @echo 'LIST:[0]:C/ /,/="${LIST:[0]:C/ /,/}"'
- @echo 'LIST:[0]:C/ /,/g="${LIST:[0]:C/ /,/g}"'
- @echo 'LIST:[0]:C/ /,/1g="${LIST:[0]:C/ /,/1g}"'
- @echo 'LIST:[*]:C/ /,/="${LIST:[*]:C/ /,/}"'
- @echo 'LIST:[*]:C/ /,/g="${LIST:[*]:C/ /,/g}"'
- @echo 'LIST:[*]:C/ /,/1g="${LIST:[*]:C/ /,/1g}"'
- @echo 'LIST:[@]:C/ /,/="${LIST:[@]:C/ /,/}"'
- @echo 'LIST:[@]:C/ /,/g="${LIST:[@]:C/ /,/g}"'
- @echo 'LIST:[@]:C/ /,/1g="${LIST:[@]:C/ /,/1g}"'
- @echo 'LIST:[@]:[0]:C/ /,/="${LIST:[@]:[0]:C/ /,/}"'
- @echo 'LIST:[0]:[@]:C/ /,/="${LIST:[0]:[@]:C/ /,/}"'
- @echo 'LIST:[@]:[*]:C/ /,/="${LIST:[@]:[*]:C/ /,/}"'
- @echo 'LIST:[*]:[@]:C/ /,/="${LIST:[*]:[@]:C/ /,/}"'
-
-mod-squarebrackets-hash:
- @echo 'EMPTY="${EMPTY}"'
- @echo 'EMPTY:[#]="${EMPTY:[#]}" == 1 ?'
- @echo 'ESCAPEDSPACE="${ESCAPEDSPACE}"'
- @echo 'ESCAPEDSPACE:[#]="${ESCAPEDSPACE:[#]}" == 1 ?'
- @echo 'REALLYSPACE="${REALLYSPACE}"'
- @echo 'REALLYSPACE:[#]="${REALLYSPACE:[#]}" == 1 ?'
- @echo 'LIST:[#]="${LIST:[#]}"'
- @echo 'LIST:[0]:[#]="${LIST:[0]:[#]}" == 1 ?'
- @echo 'LIST:[*]:[#]="${LIST:[*]:[#]}" == 1 ?'
- @echo 'LIST:[@]:[#]="${LIST:[@]:[#]}"'
- @echo 'LIST:[1]:[#]="${LIST:[1]:[#]}"'
- @echo 'LIST:[1..3]:[#]="${LIST:[1..3]:[#]}"'
-
-mod-squarebrackets-n:
- @echo 'EMPTY:[1]="${EMPTY:[1]}"'
- @echo 'ESCAPEDSPACE="${ESCAPEDSPACE}"'
- @echo 'ESCAPEDSPACE:[1]="${ESCAPEDSPACE:[1]}"'
- @echo 'REALLYSPACE="${REALLYSPACE}"'
- @echo 'REALLYSPACE:[1]="${REALLYSPACE:[1]}" == "" ?'
- @echo 'REALLYSPACE:[*]:[1]="${REALLYSPACE:[*]:[1]}" == " " ?'
- @echo 'LIST:[1]="${LIST:[1]}"'
- @echo 'LIST:[1.]="${LIST:[1.]}" is an error'
- @echo 'LIST:[1].="${LIST:[1].}" is an error'
- @echo 'LIST:[2]="${LIST:[2]}"'
- @echo 'LIST:[6]="${LIST:[6]}"'
- @echo 'LIST:[7]="${LIST:[7]}"'
- @echo 'LIST:[999]="${LIST:[999]}"'
- @echo 'LIST:[-]="${LIST:[-]}" is an error'
- @echo 'LIST:[--]="${LIST:[--]}" is an error'
- @echo 'LIST:[-1]="${LIST:[-1]}"'
- @echo 'LIST:[-2]="${LIST:[-2]}"'
- @echo 'LIST:[-6]="${LIST:[-6]}"'
- @echo 'LIST:[-7]="${LIST:[-7]}"'
- @echo 'LIST:[-999]="${LIST:[-999]}"'
- @echo 'LONGLIST:[17]="${LONGLIST:[17]}"'
- @echo 'LONGLIST:[0x11]="${LONGLIST:[0x11]}"'
- @echo 'LONGLIST:[021]="${LONGLIST:[021]}"'
- @echo 'LIST:[0]:[1]="${LIST:[0]:[1]}"'
- @echo 'LIST:[*]:[1]="${LIST:[*]:[1]}"'
- @echo 'LIST:[@]:[1]="${LIST:[@]:[1]}"'
- @echo 'LIST:[0]:[2]="${LIST:[0]:[2]}"'
- @echo 'LIST:[*]:[2]="${LIST:[*]:[2]}"'
- @echo 'LIST:[@]:[2]="${LIST:[@]:[2]}"'
- @echo 'LIST:[*]:C/ /,/:[2]="${LIST:[*]:C/ /,/:[2]}"'
- @echo 'LIST:[*]:C/ /,/:[*]:[2]="${LIST:[*]:C/ /,/:[*]:[2]}"'
- @echo 'LIST:[*]:C/ /,/:[@]:[2]="${LIST:[*]:C/ /,/:[@]:[2]}"'
- @echo 'LONGLIST:[012..0x12]="${LONGLIST:[012..0x12]}"'
-
-mod-squarebrackets-start-end:
- @echo 'LIST:[1.]="${LIST:[1.]}" is an error'
- @echo 'LIST:[1..]="${LIST:[1..]}" is an error'
- @echo 'LIST:[1.. ]="${LIST:[1.. ]}" is an error'
- @echo 'LIST:[1..1]="${LIST:[1..1]}"'
- @echo 'LIST:[1..1.]="${LIST:[1..1.]}" is an error'
- @echo 'LIST:[1..2]="${LIST:[1..2]}"'
- @echo 'LIST:[2..1]="${LIST:[2..1]}"'
- @echo 'LIST:[3..-2]="${LIST:[3..-2]}"'
- @echo 'LIST:[-4..4]="${LIST:[-4..4]}"'
- @echo 'LIST:[0..1]="${LIST:[0..1]}" is an error'
- @echo 'LIST:[-1..0]="${LIST:[-1..0]}" is an error'
- @echo 'LIST:[-1..1]="${LIST:[-1..1]}"'
- @echo 'LIST:[0..0]="${LIST:[0..0]}"'
- @echo 'LIST:[3..99]="${LIST:[3..99]}"'
- @echo 'LIST:[-3..-99]="${LIST:[-3..-99]}"'
- @echo 'LIST:[-99..-3]="${LIST:[-99..-3]}"'
-
-mod-squarebrackets-nested:
- @echo 'HASH="${HASH}" == "#" ?'
- @echo 'LIST:[$${HASH}]="${LIST:[${HASH}]}"'
- @echo 'LIST:[$${ZERO}]="${LIST:[${ZERO}]}"'
- @echo 'LIST:[$${ZERO}x$${ONE}]="${LIST:[${ZERO}x${ONE}]}"'
- @echo 'LIST:[$${ONE}]="${LIST:[${ONE}]}"'
- @echo 'LIST:[$${MINUSONE}]="${LIST:[${MINUSONE}]}"'
- @echo 'LIST:[$${STAR}]="${LIST:[${STAR}]}"'
- @echo 'LIST:[$${AT}]="${LIST:[${AT}]}"'
- @echo 'LIST:[$${EMPTY}]="${LIST:[${EMPTY}]}" is an error'
- @echo 'LIST:[$${LONGLIST:[21]:S/2//}]="${LIST:[${LONGLIST:[21]:S/2//}]}"'
- @echo 'LIST:[$${LIST:[#]}]="${LIST:[${LIST:[#]}]}"'
- @echo 'LIST:[$${LIST:[$${HASH}]}]="${LIST:[${LIST:[${HASH}]}]}"'
-
-mod-squarebrackets-space:
- # As of 2020-11-01, it is possible to have spaces before the numbers
- # but not after them. This is an unintended side-effect of using
- # strtol for parsing the numbers.
- @echo 'LIST:[ -1.. +3]="${LIST:[ -1.. +3]}"'
-
-mod-C-W:
- @echo 'LIST:C/ /,/="${LIST:C/ /,/}"'
- @echo 'LIST:C/ /,/W="${LIST:C/ /,/W}"'
- @echo 'LIST:C/ /,/gW="${LIST:C/ /,/gW}"'
- @echo 'EMPTY:C/^/,/="${EMPTY:C/^/,/}"'
- @echo 'EMPTY:C/^/,/W="${EMPTY:C/^/,/W}"'
-
-mod-S-W:
- @echo 'LIST:S/ /,/="${LIST:S/ /,/}"'
- @echo 'LIST:S/ /,/W="${LIST:S/ /,/W}"'
- @echo 'LIST:S/ /,/gW="${LIST:S/ /,/gW}"'
- @echo 'EMPTY:S/^/,/="${EMPTY:S/^/,/}"'
- @echo 'EMPTY:S/^/,/W="${EMPTY:S/^/,/W}"'
-
-mod-tW-tw:
- @echo 'LIST:tW="${LIST:tW}"'
- @echo 'LIST:tw="${LIST:tw}"'
- @echo 'LIST:tW:C/ /,/="${LIST:tW:C/ /,/}"'
- @echo 'LIST:tW:C/ /,/g="${LIST:tW:C/ /,/g}"'
- @echo 'LIST:tW:C/ /,/1g="${LIST:tW:C/ /,/1g}"'
- @echo 'LIST:tw:C/ /,/="${LIST:tw:C/ /,/}"'
- @echo 'LIST:tw:C/ /,/g="${LIST:tw:C/ /,/g}"'
- @echo 'LIST:tw:C/ /,/1g="${LIST:tw:C/ /,/1g}"'
- @echo 'LIST:tw:tW:C/ /,/="${LIST:tw:tW:C/ /,/}"'
- @echo 'LIST:tW:tw:C/ /,/="${LIST:tW:tw:C/ /,/}"'
diff --git a/contrib/bmake/unit-tests/objdir-writable.exp b/contrib/bmake/unit-tests/objdir-writable.exp
index 9c507f647f8c..dc5cd706349e 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/roobj
-TMPDIR/roobj
+make: warning: <tmpdir>/roobj: Permission denied.
+<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..03a42c485dbe 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.7 2022/02/09 21:24:29 rillig 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
@@ -13,19 +14,19 @@ do-objdir:
all: no-objdir ro-objdir explicit-objdir
# make it now
-x!= echo; mkdir -p ${RO_OBJDIR}; chmod 555 ${RO_OBJDIR}
+_!= mkdir -p ${RO_OBJDIR}
+_!= chmod 555 ${RO_OBJDIR}
.END: rm-objdir
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-chdir.exp b/contrib/bmake/unit-tests/opt-chdir.exp
index d9759cf9ed8b..3d89360f9a62 100644
--- a/contrib/bmake/unit-tests/opt-chdir.exp
+++ b/contrib/bmake/unit-tests/opt-chdir.exp
@@ -1,5 +1,3 @@
-make: chdir /./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././: File name too long
-*** Error code 2 (ignored)
cwd: /
make: chdir /nonexistent: No such file or directory
*** Error code 2 (ignored)
diff --git a/contrib/bmake/unit-tests/opt-chdir.mk b/contrib/bmake/unit-tests/opt-chdir.mk
index a8806149f31c..e94b8799af2e 100644
--- a/contrib/bmake/unit-tests/opt-chdir.mk
+++ b/contrib/bmake/unit-tests/opt-chdir.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-chdir.mk,v 1.6 2021/05/18 17:05:45 sjg Exp $
+# $NetBSD: opt-chdir.mk,v 1.7 2024/04/02 11:11:00 rillig Exp $
#
# Tests for the -C command line option, which changes the directory at the
# beginning.
@@ -7,15 +7,9 @@
.MAKEFLAGS: -d0 # switch stdout to line-buffered
-all: chdir-filename-too-long
all: chdir-root
all: chdir-nonexistent
-# Try to overflow the internal buffer for .CURDIR, which is curdir.
-chdir-filename-too-long: .PHONY .IGNORE
- # 5000 slashes, separated by dots: /./././.../././
- @${MAKE} -C ${:U:range=5000:@@/@:ts.}
-
# Changing to another directory is possible via the command line.
# In this test, it is the root directory since almost any other directory
# is not guaranteed to exist on every platform.
diff --git a/contrib/bmake/unit-tests/opt-debug-cond.exp b/contrib/bmake/unit-tests/opt-debug-cond.exp
index 39a9383953dd..01f73a4cfad5 100644
--- a/contrib/bmake/unit-tests/opt-debug-cond.exp
+++ b/contrib/bmake/unit-tests/opt-debug-cond.exp
@@ -1 +1,6 @@
+CondParser_Eval: ${:U12345} > ${:U55555}
+Comparing 12345.000000 > 55555.000000
+CondParser_Eval: "string" != "string"
+Comparing "string" != "string"
+CondParser_Eval: "nonempty"
exit status 0
diff --git a/contrib/bmake/unit-tests/opt-debug-cond.mk b/contrib/bmake/unit-tests/opt-debug-cond.mk
index 2b9d1029c7d9..28ad59587789 100644
--- a/contrib/bmake/unit-tests/opt-debug-cond.mk
+++ b/contrib/bmake/unit-tests/opt-debug-cond.mk
@@ -1,10 +1,24 @@
-# $NetBSD: opt-debug-cond.mk,v 1.1 2020/09/05 06:20:51 rillig Exp $
+# $NetBSD: opt-debug-cond.mk,v 1.3 2022/04/15 09:33:20 rillig Exp $
#
# Tests for the -dc command line option, which adds debug logging for the
# evaluation of conditional expressions, such as in .if directives and
# ${cond:?then:else} expressions.
-# TODO: Implementation
+.MAKEFLAGS: -dc
-all:
- @:;
+# expect: CondParser_Eval: ${:U12345} > ${:U55555}
+# expect: Comparing 12345.000000 > 55555.000000
+.if ${:U12345} > ${:U55555}
+
+# expect: CondParser_Eval: "string" != "string"
+# expect: Comparing "string" != "string"
+.elif "string" != "string"
+
+# expect: CondParser_Eval: "nonempty"
+.elif "nonempty"
+
+.endif
+
+.MAKEFLAGS: -d0
+
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/opt-debug-curdir.mk b/contrib/bmake/unit-tests/opt-debug-curdir.mk
index 3c37d2988675..ac5750e7e18c 100644
--- a/contrib/bmake/unit-tests/opt-debug-curdir.mk
+++ b/contrib/bmake/unit-tests/opt-debug-curdir.mk
@@ -1,8 +1,8 @@
-# $NetBSD: opt-debug-curdir.mk,v 1.1 2020/09/05 06:20:51 rillig Exp $
+# $NetBSD: opt-debug-curdir.mk,v 1.2 2022/01/23 16:09:38 rillig Exp $
#
-# Tests for the -dC command line option, which does nothing, as of 2020-09-05.
+# Tests for the -dC command line option, which does nothing, as of 2020-09-05,
+# as the string "DEBUG(CWD" does not occur in the source code.
-# TODO: Implementation
+.MAKEFLAGS: -dC
-all:
- @:;
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/opt-debug-errors-jobs.exp b/contrib/bmake/unit-tests/opt-debug-errors-jobs.exp
index 25eb2b470b72..614bb33b9208 100644
--- a/contrib/bmake/unit-tests/opt-debug-errors-jobs.exp
+++ b/contrib/bmake/unit-tests/opt-debug-errors-jobs.exp
@@ -2,6 +2,7 @@ echo '3 spaces'; false
3 spaces
*** Failed target: fail-spaces
+*** In directory: <curdir>
*** Failed commands:
echo '3 spaces'; false
*** [fail-spaces] Error code 1
@@ -11,6 +12,7 @@ echo \ indented; false
indented
*** Failed target: fail-escaped-space
+*** In directory: <curdir>
*** Failed commands:
echo \ indented; false
*** [fail-escaped-space] Error code 1
@@ -22,8 +24,11 @@ line1
line2
*** Failed target: fail-newline
+*** In directory: <curdir>
*** Failed commands:
echo 'line1${.newline}line2'; false
+ => echo 'line1
+line2'; false
*** [fail-newline] Error code 1
make: stopped in unit-tests
@@ -31,6 +36,7 @@ echo 'line1 line2'; false
line1 line2
*** Failed target: fail-multiline
+*** In directory: <curdir>
*** Failed commands:
echo 'line1 line2'; false
*** [fail-multiline] Error code 1
@@ -40,9 +46,19 @@ echo 'word1' 'word2'; false
word1 word2
*** Failed target: fail-multiline-intention
+*** In directory: <curdir>
*** Failed commands:
echo 'word1' 'word2'; false
*** [fail-multiline-intention] Error code 1
make: stopped in unit-tests
+
+*** Failed target: fail-vars
+*** In directory: <curdir>
+*** 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-file.exp b/contrib/bmake/unit-tests/opt-debug-file.exp
index 39a9383953dd..8bdaca612310 100644
--- a/contrib/bmake/unit-tests/opt-debug-file.exp
+++ b/contrib/bmake/unit-tests/opt-debug-file.exp
@@ -1 +1,12 @@
-exit status 0
+make: "opt-debug-file.mk" line 44: This goes to stderr only, once.
+make: "opt-debug-file.mk" line 47: This goes to stderr only, once.
+make: "opt-debug-file.mk" line 50: This goes to stderr, and in addition to the debug log.
+CondParser_Eval: ${:!cat opt-debug-file.debuglog!:Maddition:[#]} != 1
+Comparing 1.000000 != 1.000000
+make: Missing delimiter for modifier ':S'
+make: Missing delimiter for modifier ':S'
+make: Missing delimiter for modifier ':S'
+CondParser_Eval: ${:!cat opt-debug-file.debuglog!:Mdelimiter:[#]} != 1
+Comparing 1.000000 != 1.000000
+Cannot open debug file "/nonexistent-6f21c672-a22d-4ef7/opt-debug-file.debuglog"
+exit status 2
diff --git a/contrib/bmake/unit-tests/opt-debug-file.mk b/contrib/bmake/unit-tests/opt-debug-file.mk
index 1ed477ef3c40..e6c23c4faa1a 100644
--- a/contrib/bmake/unit-tests/opt-debug-file.mk
+++ b/contrib/bmake/unit-tests/opt-debug-file.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-debug-file.mk,v 1.4 2020/10/05 19:27:48 rillig Exp $
+# $NetBSD: opt-debug-file.mk,v 1.10 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the -dF command line option, which redirects the debug log
# to a file instead of writing it to stderr.
@@ -11,14 +11,16 @@
VAR= value ${:Uexpanded}
# Hide the logging output for the remaining actions.
-# As of 2020-10-03, it is not possible to disable debug logging again.
+# Before main.c 1.362 from 2020-10-03, it was not possible to disable debug
+# logging again. Since then, an easier way is the undocumented option '-d0'.
.MAKEFLAGS: -dF/dev/null
# Make sure that the debug logging file contains some logging.
DEBUG_OUTPUT:= ${:!cat opt-debug-file.debuglog!}
# Grmbl. Because of the := operator in the above line, the variable
-# value contains ${:Uexpanded}. This variable expression is expanded
-# upon further processing. Therefore, don't read from untrusted input.
+# value contains ${:Uexpanded}. This expression is expanded
+# when it is used in the condition below. Therefore, be careful when storing
+# untrusted input in variables.
#.MAKEFLAGS: -dc -dFstderr
.if !${DEBUG_OUTPUT:tW:M*VAR = value expanded*}
. error ${DEBUG_OUTPUT}
@@ -26,12 +28,47 @@ DEBUG_OUTPUT:= ${:!cat opt-debug-file.debuglog!}
# To get the unexpanded text that was actually written to the debug log
# file, the content of that log file must not be stored in a variable.
-# XXX: In the :M modifier, a dollar is escaped as '$$', not '\$'.
+#
+# XXX: In the :M modifier, a dollar is escaped using '$$', not '\$'. This
+# escaping scheme unnecessarily differs from all other modifiers.
.if !${:!cat opt-debug-file.debuglog!:tW:M*VAR = value $${:Uexpanded}*}
. error
.endif
-_!= rm opt-debug-file.debuglog
+.MAKEFLAGS: -d0
+
+
+# See Parse_Error.
+.MAKEFLAGS: -dFstdout
+# expect+1: This goes to stderr only, once.
+. info This goes to stderr only, once.
+.MAKEFLAGS: -dFstderr
+# expect+1: This goes to stderr only, once.
+. info This goes to stderr only, once.
+.MAKEFLAGS: -dFopt-debug-file.debuglog
+# expect+1: This goes to stderr, and in addition to the debug log.
+. info This goes to stderr, and in addition to the debug log.
+.MAKEFLAGS: -dFstderr -d0c
+.if ${:!cat opt-debug-file.debuglog!:Maddition:[#]} != 1
+. error
+.endif
+
+
+# See ApplyModifier_Subst, which calls Error.
+.MAKEFLAGS: -dFstdout
+: This goes to stderr only, once. ${:U:S
+.MAKEFLAGS: -dFstderr
+: This goes to stderr only, once. ${:U:S
+.MAKEFLAGS: -dFopt-debug-file.debuglog
+: This goes to stderr, and in addition to the debug log. ${:U:S
+.MAKEFLAGS: -dFstderr -d0c
+.if ${:!cat opt-debug-file.debuglog!:Mdelimiter:[#]} != 1
+. error
+.endif
+
-all:
- @:;
+# If the debug log file cannot be opened, make prints an error message and
+# exits immediately since the debug log file is usually selected from the
+# command line.
+_:= ${:!rm opt-debug-file.debuglog!}
+.MAKEFLAGS: -dF/nonexistent-6f21c672-a22d-4ef7/opt-debug-file.debuglog
diff --git a/contrib/bmake/unit-tests/opt-debug-for.exp b/contrib/bmake/unit-tests/opt-debug-for.exp
index ea811b9bfcf5..a8f63b85fec3 100644
--- a/contrib/bmake/unit-tests/opt-debug-for.exp
+++ b/contrib/bmake/unit-tests/opt-debug-for.exp
@@ -1,22 +1,22 @@
For: new loop 2
For: end for 2
For: end for 1
-For: loop body:
+For: loop body with outer = a:
. for inner in 1 2
VAR.${:Ua}${inner}= value
. endfor
For: end for 1
-For: loop body:
+For: loop body with inner = 1:
VAR.${:Ua}${:U1}= value
-For: loop body:
+For: loop body with inner = 2:
VAR.${:Ua}${:U2}= value
-For: loop body:
+For: loop body with outer = b:
. for inner in 1 2
VAR.${:Ub}${inner}= value
. endfor
For: end for 1
-For: loop body:
+For: loop body with inner = 1:
VAR.${:Ub}${:U1}= value
-For: loop body:
+For: loop body with inner = 2:
VAR.${:Ub}${:U2}= value
exit status 0
diff --git a/contrib/bmake/unit-tests/opt-debug-graph1.exp b/contrib/bmake/unit-tests/opt-debug-graph1.exp
index 4d4aa0c3faea..d01a98a31fdb 100644
--- a/contrib/bmake/unit-tests/opt-debug-graph1.exp
+++ b/contrib/bmake/unit-tests/opt-debug-graph1.exp
@@ -16,12 +16,14 @@
#*** Global Variables:
.ALLTARGETS = all made-target made-target-no-sources made-source unmade-target unmade-sources unmade-silent-source unmade-target-no-sources
.CURDIR = <curdir>
-.INCLUDES =
-.LIBS =
+.INCLUDES = # (empty)
+.LIBS = # (empty)
.MAKE = <details omitted>
.MAKE.DEPENDFILE = <details omitted>
.MAKE.GID = <details omitted>
+.MAKE.JOBS.C = <details omitted>
.MAKE.LEVEL = <details omitted>
+.MAKE.LEVEL.ENV = MAKELEVEL
.MAKE.MAKEFILES = <details omitted>
.MAKE.MAKEFILE_PREFERENCE = <details omitted>
.MAKE.OS = <details omitted>
@@ -29,24 +31,22 @@
.MAKE.PPID = <details omitted>
.MAKE.UID = <details omitted>
.MAKEFLAGS = -r -k -d g1
-.MAKEOVERRIDES =
+.MAKEOVERRIDES = # (empty)
.OBJDIR = <curdir>
.PATH = . <curdir>
-.TARGETS =
+.TARGETS = # (empty)
.newline =
-
+# (ends with space)
MACHINE = <details omitted>
MACHINE_ARCH = <details omitted>
MAKE = <details omitted>
MFLAGS = -r -k -d g1
#*** Command-line Variables:
-.MAKE.LEVEL.ENV = MAKELEVEL
#*** Directory Cache:
# 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..d4182650baed 100644
--- a/contrib/bmake/unit-tests/opt-debug-graph2.exp
+++ b/contrib/bmake/unit-tests/opt-debug-graph2.exp
@@ -50,12 +50,14 @@ all : made-target error-target aborted-target
#*** Global Variables:
.ALLTARGETS = made-target error-target aborted-target aborted-target-dependency all .END
.CURDIR = <curdir>
-.INCLUDES =
-.LIBS =
+.INCLUDES = # (empty)
+.LIBS = # (empty)
.MAKE = <details omitted>
.MAKE.DEPENDFILE = <details omitted>
.MAKE.GID = <details omitted>
+.MAKE.JOBS.C = <details omitted>
.MAKE.LEVEL = <details omitted>
+.MAKE.LEVEL.ENV = MAKELEVEL
.MAKE.MAKEFILES = <details omitted>
.MAKE.MAKEFILE_PREFERENCE = <details omitted>
.MAKE.OS = <details omitted>
@@ -63,25 +65,23 @@ all : made-target error-target aborted-target
.MAKE.PPID = <details omitted>
.MAKE.UID = <details omitted>
.MAKEFLAGS = -r -k -d g2
-.MAKEOVERRIDES =
+.MAKEOVERRIDES = # (empty)
.OBJDIR = <curdir>
.PATH = . <curdir>
.TARGETS = all
.newline =
-
+# (ends with space)
MACHINE = <details omitted>
MACHINE_ARCH = <details omitted>
MAKE = <details omitted>
MFLAGS = -r -k -d g2
#*** Command-line Variables:
-.MAKE.LEVEL.ENV = MAKELEVEL
.SHELL = <details omitted>
#*** Directory Cache:
# 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..fea3c658cb72 100644
--- a/contrib/bmake/unit-tests/opt-debug-graph3.exp
+++ b/contrib/bmake/unit-tests/opt-debug-graph3.exp
@@ -50,12 +50,14 @@ all : made-target error-target aborted-target
#*** Global Variables:
.ALLTARGETS = made-target error-target aborted-target aborted-target-dependency all .END
.CURDIR = <curdir>
-.INCLUDES =
-.LIBS =
+.INCLUDES = # (empty)
+.LIBS = # (empty)
.MAKE = <details omitted>
.MAKE.DEPENDFILE = <details omitted>
.MAKE.GID = <details omitted>
+.MAKE.JOBS.C = <details omitted>
.MAKE.LEVEL = <details omitted>
+.MAKE.LEVEL.ENV = MAKELEVEL
.MAKE.MAKEFILES = <details omitted>
.MAKE.MAKEFILE_PREFERENCE = <details omitted>
.MAKE.OS = <details omitted>
@@ -63,25 +65,23 @@ all : made-target error-target aborted-target
.MAKE.PPID = <details omitted>
.MAKE.UID = <details omitted>
.MAKEFLAGS = -r -k -d g3
-.MAKEOVERRIDES =
+.MAKEOVERRIDES = # (empty)
.OBJDIR = <curdir>
.PATH = . <curdir>
.TARGETS = all
.newline =
-
+# (ends with space)
MACHINE = <details omitted>
MACHINE_ARCH = <details omitted>
MAKE = <details omitted>
MFLAGS = -r -k -d g3
#*** Command-line Variables:
-.MAKE.LEVEL.ENV = MAKELEVEL
.SHELL = <details omitted>
#*** Directory Cache:
# 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-hash.exp b/contrib/bmake/unit-tests/opt-debug-hash.exp
index 39a9383953dd..194b08daa5f2 100644
--- a/contrib/bmake/unit-tests/opt-debug-hash.exp
+++ b/contrib/bmake/unit-tests/opt-debug-hash.exp
@@ -1 +1,6 @@
-exit status 0
+make: "opt-debug-hash.mk" line 12: Missing argument for ".error"
+make: Fatal errors encountered -- cannot continue
+HashTable targets: size=16 numEntries=0 maxchain=0
+HashTable Global variables: size=16 numEntries=<entries> maxchain=3
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/opt-debug-hash.mk b/contrib/bmake/unit-tests/opt-debug-hash.mk
index c8cb99acd261..a1b21e145bf0 100644
--- a/contrib/bmake/unit-tests/opt-debug-hash.mk
+++ b/contrib/bmake/unit-tests/opt-debug-hash.mk
@@ -1,10 +1,12 @@
-# $NetBSD: opt-debug-hash.mk,v 1.1 2020/09/05 06:20:51 rillig Exp $
+# $NetBSD: opt-debug-hash.mk,v 1.4 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the -dh command line option, which adds debug logging for
# hash tables. Even more detailed logging is available by compiling
# make with -DDEBUG_HASH_LOOKUP.
-# TODO: Implementation
+.MAKEFLAGS: -dh
-all:
- @:;
+# Force a parse error, to demonstrate the newline character in the diagnostic
+# that had been missing before parse.c 1.655 from 2022-01-22.
+# expect+1: Missing argument for ".error"
+.error
diff --git a/contrib/bmake/unit-tests/opt-debug-jobs.mk b/contrib/bmake/unit-tests/opt-debug-jobs.mk
index f3732df7e25d..ac63bb9c5e86 100644
--- a/contrib/bmake/unit-tests/opt-debug-jobs.mk
+++ b/contrib/bmake/unit-tests/opt-debug-jobs.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-debug-jobs.mk,v 1.5 2020/11/12 21:54:52 rillig Exp $
+# $NetBSD: opt-debug-jobs.mk,v 1.6 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the -dj command line option, which adds debug logging about
# running jobs in multiple shells.
@@ -11,7 +11,7 @@
all:
# Only the actual command is logged.
- # To see the evaluation of the variable expressions, use -dv.
+ # To see the evaluation of the expressions, use -dv.
: ${:Uexpanded} expression
# Undefined variables expand to empty strings.
diff --git a/contrib/bmake/unit-tests/opt-debug-lint.exp b/contrib/bmake/unit-tests/opt-debug-lint.exp
index 05b341b30dae..c6cd748acd5d 100644
--- a/contrib/bmake/unit-tests/opt-debug-lint.exp
+++ b/contrib/bmake/unit-tests/opt-debug-lint.exp
@@ -1,8 +1,10 @@
-make: "opt-debug-lint.mk" line 19: Variable "X" is undefined
-make: "opt-debug-lint.mk" line 41: Variable "UNDEF" is undefined
-make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "L"
-make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "P"
-make: "opt-debug-lint.mk" line 69: Unknown modifier "${"
+make: "opt-debug-lint.mk" line 21: Variable "X" is undefined
+make: "opt-debug-lint.mk" line 21: Malformed conditional ($X)
+make: "opt-debug-lint.mk" line 45: Variable "UNDEF" is undefined
+make: "opt-debug-lint.mk" line 45: Malformed conditional (${UNDEF})
+make: "opt-debug-lint.mk" line 67: while evaluating variable "value": Missing delimiter ':' after modifier "L"
+make: "opt-debug-lint.mk" line 67: while evaluating variable "value": Missing delimiter ':' after modifier "P"
+make: "opt-debug-lint.mk" line 76: while evaluating variable "value": Unknown modifier "${"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/opt-debug-lint.mk b/contrib/bmake/unit-tests/opt-debug-lint.mk
index 155e1a3de3be..3e946ac6ad61 100644
--- a/contrib/bmake/unit-tests/opt-debug-lint.mk
+++ b/contrib/bmake/unit-tests/opt-debug-lint.mk
@@ -1,7 +1,7 @@
-# $NetBSD: opt-debug-lint.mk,v 1.14 2021/03/14 10:57:12 rillig Exp $
+# $NetBSD: opt-debug-lint.mk,v 1.17 2024/04/20 10:18:55 rillig Exp $
#
# Tests for the -dL command line option, which runs additional checks
-# to catch common mistakes, such as unclosed variable expressions.
+# to catch common mistakes, such as unclosed expressions.
.MAKEFLAGS: -dL
@@ -16,6 +16,8 @@
#
# See also:
# cond-undef-lint.mk
+# expect+2: Malformed conditional ($X)
+# expect+1: Variable "X" is undefined
.if $X
. error
.endif
@@ -38,6 +40,8 @@
# hoping for the caller to print an error message. This resulted in the
# well-known "Malformed conditional" error message, even though the
# conditional was well-formed and the only error was an undefined variable.
+# expect+2: Malformed conditional (${UNDEF})
+# expect+1: Variable "UNDEF" is undefined
.if ${UNDEF}
. error
.endif
@@ -58,6 +62,8 @@ ${UNDEF}: ${UNDEF}
# Since 2020-10-03, in lint mode the variable modifier must be separated
# by colons. See varparse-mod.mk.
+# expect+2: while evaluating variable "value": Missing delimiter ':' after modifier "L"
+# expect+1: while evaluating variable "value": Missing delimiter ':' after modifier "P"
.if ${value:LPL} != "value"
. error
.endif
@@ -66,6 +72,7 @@ ${UNDEF}: ${UNDEF}
# variable modifier had to be separated by colons. This was wrong though
# since make always fell back trying to parse the indirect modifier as a
# SysV modifier.
+# expect+1: while evaluating variable "value": Unknown modifier "${"
.if ${value:${:UL}PL} != "LPL}" # FIXME: "LPL}" is unexpected here.
. error ${value:${:UL}PL}
.endif
@@ -84,7 +91,7 @@ ${UNDEF}: ${UNDEF}
#
# Before var.c 1.856 from 2021-03-14, this regular expression was then
# compiled even though that was not necessary for checking the syntax at the
-# level of variable expressions. The unexpanded '$' then resulted in a wrong
+# level of expressions. The unexpanded '$' then resulted in a wrong
# error message.
#
# This only happened in lint mode since in default mode the early check for
diff --git a/contrib/bmake/unit-tests/opt-debug-loud.mk b/contrib/bmake/unit-tests/opt-debug-loud.mk
index 38a3c7d7a8e1..5ea1f90ff7be 100644
--- a/contrib/bmake/unit-tests/opt-debug-loud.mk
+++ b/contrib/bmake/unit-tests/opt-debug-loud.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-debug-loud.mk,v 1.4 2020/10/05 19:27:48 rillig Exp $
+# $NetBSD: opt-debug-loud.mk,v 1.5 2023/12/19 19:33:40 rillig Exp $
#
# Tests for the -dl command line option, which prints the commands before
# running them, ignoring the command line option for silent mode (-s) as
@@ -8,8 +8,8 @@
.MAKEFLAGS: -dl -s
.SILENT:
-# The -dl command line option does not affect commands that are run during
-# variable expansion, such as :!cmd! or :sh.
+# The -dl command line option does not affect commands that are run when
+# evaluating expressions and their modifiers, such as :!cmd! or :sh.
.if ${:!echo word!} != "word"
. error
.endif
diff --git a/contrib/bmake/unit-tests/opt-debug-parse.exp b/contrib/bmake/unit-tests/opt-debug-parse.exp
index 39a9383953dd..a19a58bcc965 100644
--- a/contrib/bmake/unit-tests/opt-debug-parse.exp
+++ b/contrib/bmake/unit-tests/opt-debug-parse.exp
@@ -1 +1,28 @@
+Parsing line 16: .for var in value
+Parse_PushInput: .for loop in opt-debug-parse.mk, line 16
+SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `opt-debug-parse.mk'
+Parsing line 21: .info trace with multi-line .for loop head
+make: "opt-debug-parse.mk" line 21: trace with multi-line .for loop head
+ in .for loop from opt-debug-parse.mk:16 with var = value
+ParseEOF: returning to file opt-debug-parse.mk, line 23
+SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `opt-debug-parse.mk'
+Parsing line 26: .include "/dev/null"
+Parse_PushInput: file /dev/null, line 1
+SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `null'
+SetFilenameVars: ${.INCLUDEDFROMDIR} = <some-dir> ${.INCLUDEDFROMFILE} = `opt-debug-parse.mk'
+ParseEOF: returning to file opt-debug-parse.mk, line 27
+SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `opt-debug-parse.mk'
+Parsing line 31: .for a b c in 1 2 3 ${:U4 5 6}
+Parse_PushInput: .for loop in opt-debug-parse.mk, line 31
+SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `opt-debug-parse.mk'
+Parsing line 34: .info trace
+make: "opt-debug-parse.mk" line 34: trace
+ in .for loop from opt-debug-parse.mk:31 with a = 1, b = 2, c = 3
+Parsing line 34: .info trace
+make: "opt-debug-parse.mk" line 34: trace
+ in .for loop from opt-debug-parse.mk:31 with a = 4, b = 5, c = 6
+ParseEOF: returning to file opt-debug-parse.mk, line 36
+SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `opt-debug-parse.mk'
+Parsing line 38: .MAKEFLAGS: -d0
+ParseDependency(.MAKEFLAGS: -d0)
exit status 0
diff --git a/contrib/bmake/unit-tests/opt-debug-parse.mk b/contrib/bmake/unit-tests/opt-debug-parse.mk
index 3427b68beb96..347537015b52 100644
--- a/contrib/bmake/unit-tests/opt-debug-parse.mk
+++ b/contrib/bmake/unit-tests/opt-debug-parse.mk
@@ -1,9 +1,40 @@
-# $NetBSD: opt-debug-parse.mk,v 1.1 2020/09/05 06:20:51 rillig Exp $
+# $NetBSD: opt-debug-parse.mk,v 1.8 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the -dp command line option, which adds debug logging about
# makefile parsing.
+.MAKEFLAGS: -dp
+
# TODO: Implementation
-all:
- @:;
+# Before parse.c 1.639 from 2022-01-08, PrintStackTrace and other diagnostics
+# printed a wrong line number, using the last line before the loop body, while
+# it should rather be the line number where the .for loop starts.
+#
+# Before parse.c 1.643 from 2022-01-08, PrintStackTrace tried to be too clever
+# by merging stack trace entries, printing confusing line numbers as a result.
+.for \
+ var \
+ in \
+ value
+# expect+1: trace with multi-line .for loop head
+.info trace with multi-line .for loop head
+.endfor
+
+# Before parse.c 1.641 from 2022-01-08, the debug log said it returned to
+# the line of the '.include' instead of the line following it.
+.include "/dev/null"
+
+
+# In .for loops with multiple variables, the variable details are included in
+# the stack trace, just as with a single variable.
+.for a b c in 1 2 3 ${:U4 5 6}
+# expect+2: trace
+# expect+1: trace
+.info trace
+.endfor
+
+
+.MAKEFLAGS: -d0
+
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/opt-debug-var.exp b/contrib/bmake/unit-tests/opt-debug-var.exp
index 39a9383953dd..5e9d10c671f1 100644
--- a/contrib/bmake/unit-tests/opt-debug-var.exp
+++ b/contrib/bmake/unit-tests/opt-debug-var.exp
@@ -1 +1,7 @@
+Global: ASSIGNED = value
+Global: SUBST = # (empty)
+Global: SUBST = value
+Var_Parse: y(ASSIGNED) (eval)
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
exit status 0
diff --git a/contrib/bmake/unit-tests/opt-debug-var.mk b/contrib/bmake/unit-tests/opt-debug-var.mk
index 4d0ef9447324..9017f18e81d3 100644
--- a/contrib/bmake/unit-tests/opt-debug-var.mk
+++ b/contrib/bmake/unit-tests/opt-debug-var.mk
@@ -1,9 +1,31 @@
-# $NetBSD: opt-debug-var.mk,v 1.1 2020/09/05 06:20:51 rillig Exp $
+# $NetBSD: opt-debug-var.mk,v 1.3 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the -dv command line option, which adds debug logging about
# variable assignment and evaluation.
-# TODO: Implementation
+.MAKEFLAGS: -dv
-all:
- @:;
+# expect: Global: ASSIGNED = value
+ASSIGNED= value
+
+# TODO: Explain why the empty assignment "Global: SUBST = " is needed.
+# expect: Global: SUBST = value
+SUBST:= value
+
+.if defined(ASSIGNED)
+.endif
+
+# The usual form of expressions is ${VAR}. The form $(VAR) is used
+# less often as it can be visually confused with the shell construct for
+# capturing the output of a subshell, which looks the same.
+#
+# In conditions, a call to the function 'empty' is syntactically similar to
+# the form $(VAR), only that the initial '$' is the 'y' of 'empty'.
+#
+# expect: Var_Parse: y(ASSIGNED) (eval)
+.if !empty(ASSIGNED)
+.endif
+
+.MAKEFLAGS: -d0
+
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/opt-debug-x-trace.exp b/contrib/bmake/unit-tests/opt-debug-x-trace.exp
index 39a9383953dd..abd4a61e2d82 100644
--- a/contrib/bmake/unit-tests/opt-debug-x-trace.exp
+++ b/contrib/bmake/unit-tests/opt-debug-x-trace.exp
@@ -1 +1,3 @@
++ echo 'Counting 1 2 3 4 5 6 7'
+Counting 1 2 3 4 5 6 7
exit status 0
diff --git a/contrib/bmake/unit-tests/opt-debug-x-trace.mk b/contrib/bmake/unit-tests/opt-debug-x-trace.mk
index 0936ba506966..54c04a9a4cf8 100644
--- a/contrib/bmake/unit-tests/opt-debug-x-trace.mk
+++ b/contrib/bmake/unit-tests/opt-debug-x-trace.mk
@@ -1,10 +1,12 @@
-# $NetBSD: opt-debug-x-trace.mk,v 1.1 2020/09/05 06:20:51 rillig Exp $
+# $NetBSD: opt-debug-x-trace.mk,v 1.2 2022/01/23 16:09:38 rillig Exp $
#
# Tests for the -dx command line option, which runs shell commands with
# the -x option, thereby printing the actual commands as they are
# executed.
-# TODO: Implementation
+.MAKEFLAGS: -dx
-all:
- @:;
+# expect: + echo 'Counting 1 2 3 4 5 6 7'
+# expect: Counting 1 2 3 4 5 6 7
+all: .PHONY
+ @echo 'Counting ${:U:range=7}'
diff --git a/contrib/bmake/unit-tests/opt-define.mk b/contrib/bmake/unit-tests/opt-define.mk
index ce0516ba44bc..f508a9b1592f 100644
--- a/contrib/bmake/unit-tests/opt-define.mk
+++ b/contrib/bmake/unit-tests/opt-define.mk
@@ -1,8 +1,40 @@
-# $NetBSD: opt-define.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: opt-define.mk,v 1.4 2022/06/12 14:27:06 rillig Exp $
#
-# Tests for the -D command line option.
+# Tests for the -D command line option, which defines global variables to the
+# value 1, like in the C preprocessor.
-# TODO: Implementation
+.MAKEFLAGS: -DVAR
-all:
- @:;
+# The variable has the exact value "1", not "1.0".
+.if ${VAR} != "1"
+. error
+.endif
+
+# The variable can be overwritten by assigning another value to it. This
+# would not be possible if the variable had been specified on the command line
+# as 'VAR=1' instead of '-DVAR'.
+VAR= overwritten
+.if ${VAR} != "overwritten"
+. error
+.endif
+
+# The variable can be undefined. If the variable had been defined in the
+# "Internal" or in the "Command" scope instead, undefining it would have no
+# effect.
+.undef VAR
+.if defined(VAR)
+. error
+.endif
+
+# The C preprocessor allows to define a macro with a specific value. Make
+# behaves differently, it defines a variable with the name 'VAR=value' and the
+# value 1.
+.MAKEFLAGS: -DVAR=value
+.if defined(VAR)
+. error
+.endif
+.if ${VAR=value} != "1"
+. error
+.endif
+
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/opt-env.mk b/contrib/bmake/unit-tests/opt-env.mk
index 32e95ef41f5a..125fc6ff9518 100644
--- a/contrib/bmake/unit-tests/opt-env.mk
+++ b/contrib/bmake/unit-tests/opt-env.mk
@@ -1,8 +1,49 @@
-# $NetBSD: opt-env.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: opt-env.mk,v 1.4 2022/03/26 13:32:31 rillig Exp $
#
-# Tests for the -e command line option.
+# Tests for the -e command line option, which looks up environment variables
+# before those from the global scope. It has no influence on variables from
+# the command line though.
+#
+# This option is required by POSIX.
+
+# The variable FROM_ENV is defined in ./Makefile.
+
+.MAKEFLAGS: -e
+
+.if ${FROM_ENV} != value-from-env
+. error ${FROM_ENV}
+.endif
+
+# Try to override the variable; this does not have any effect.
+FROM_ENV= value-from-mk
+.if ${FROM_ENV} != value-from-env
+. error ${FROM_ENV}
+.endif
+
+# Try to append to the variable; this also doesn't have any effect.
+FROM_ENV+= appended
+.if ${FROM_ENV} != value-from-env
+. error ${FROM_ENV}
+.endif
+
+# The default assignment also cannot change the variable.
+FROM_ENV?= default
+.if ${FROM_ENV} != value-from-env
+. error ${FROM_ENV}
+.endif
+
+# Neither can the assignment modifiers.
+.if ${FROM_ENV::=from-condition}
+.endif
+.if ${FROM_ENV} != value-from-env
+. error ${FROM_ENV}
+.endif
-# TODO: Implementation
+# Even .undef doesn't work since it only affects the global scope,
+# which is independent from the environment variables.
+.undef FROM_ENV
+.if ${FROM_ENV} != value-from-env
+. error ${FROM_ENV}
+.endif
-all:
- @:;
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/opt-file.exp b/contrib/bmake/unit-tests/opt-file.exp
index 76a832949aca..9550958fea37 100644
--- a/contrib/bmake/unit-tests/opt-file.exp
+++ b/contrib/bmake/unit-tests/opt-file.exp
@@ -2,9 +2,7 @@ value
value
line-with-trailing-whitespace
make: "(stdin)" line 1: Zero byte read from file
-make: Fatal errors encountered -- cannot continue
-make: stopped in unit-tests
-*** Error code 1 (continuing)
+*** Error code 2 (continuing)
`all' not remade because of errors.
Stop.
diff --git a/contrib/bmake/unit-tests/opt-file.mk b/contrib/bmake/unit-tests/opt-file.mk
index b7a1c09e6d16..0f07ede560f5 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.16 2024/04/01 12:26:02 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,16 +11,19 @@ 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.
+# that backslash is trimmed. See ReadLowLevelLine.
#
# make-2014.01.01.00.00.00 invoked undefined behavior, reading text from
# 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
@@ -48,7 +52,7 @@ file-ending-in-backslash-mmap: .PHONY
# Since parse.c 1.511 from 2020-12-22, an assertion in ParseGetLine failed
# for lines that contained trailing whitespace. Worked around in parse.c
-# 1.513, properly fixed in parse.c 1.514.
+# 1.513, properly fixed in parse.c 1.514 from 2020-12-22.
line-with-trailing-whitespace: .PHONY
@printf '%s' 'VAR=$@ ' > opt-file-trailing-whitespace
@${MAKE} -r -f opt-file-trailing-whitespace -V VAR
@@ -75,7 +79,7 @@ line-with-trailing-whitespace: .PHONY
# exit status 0
#
# 2008 to 2010:
-# make: "zero-byte.in" line 1: Zero byte read from file
+# make: "(stdin)" line 1: Zero byte read from file
# make: Fatal errors encountered -- cannot continue
#
# make: stopped in .
@@ -88,14 +92,18 @@ line-with-trailing-whitespace: .PHONY
# exit status 2
#
# 2014 to 2020-12-06:
-# make: "zero-byte.in" line 1: warning: Zero byte read from file, skipping rest of line.
+# make: "(stdin)" line 1: warning: Zero byte read from file, skipping rest of line.
# exit status 0
#
# Since 2020-12-07:
-# make: "zero-byte.in" line 1: Zero byte read from file
+# make: "(stdin)" line 1: Zero byte read from file
# make: Fatal errors encountered -- cannot continue
# make: stopped in .
# exit status 1
+#
+# Since 2024-04-01:
+# make: "(stdin)" line 1: Zero byte read from file
+# *** Error code 2 (continuing)
file-containing-null-byte: .PHONY
@printf '%s\n' 'VAR=value' 'VAR2=VALUE2' \
| tr 'l' '\0' \
diff --git a/contrib/bmake/unit-tests/opt-jobs-internal.exp b/contrib/bmake/unit-tests/opt-jobs-internal.exp
index 39a9383953dd..470bdbddd0f8 100644
--- a/contrib/bmake/unit-tests/opt-jobs-internal.exp
+++ b/contrib/bmake/unit-tests/opt-jobs-internal.exp
@@ -1 +1,6 @@
-exit status 0
+make: internal error -- J option malformed (garbage)
+usage: make [-BeikNnqrSstWwX]
+ [-C directory] [-D variable] [-d flags] [-f makefile]
+ [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file]
+ [-V variable] [-v variable] [variable=value] [target ...]
+exit status 2
diff --git a/contrib/bmake/unit-tests/opt-jobs-internal.mk b/contrib/bmake/unit-tests/opt-jobs-internal.mk
index 5426807ca98b..44755a797751 100644
--- a/contrib/bmake/unit-tests/opt-jobs-internal.mk
+++ b/contrib/bmake/unit-tests/opt-jobs-internal.mk
@@ -1,8 +1,9 @@
-# $NetBSD: opt-jobs-internal.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: opt-jobs-internal.mk,v 1.3 2022/01/23 16:09:38 rillig Exp $
#
# Tests for the (intentionally undocumented) -J command line option.
+#
+# Only test the error handling here, the happy path is covered in other tests
+# as a side effect.
-# TODO: Implementation
-
-all:
- @:;
+# expect: make: internal error -- J option malformed (garbage)
+.MAKEFLAGS: -Jgarbage
diff --git a/contrib/bmake/unit-tests/opt-jobs-no-action.mk b/contrib/bmake/unit-tests/opt-jobs-no-action.mk
index 19d82c5bf4b8..fe720c9e0e61 100644
--- a/contrib/bmake/unit-tests/opt-jobs-no-action.mk
+++ b/contrib/bmake/unit-tests/opt-jobs-no-action.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-jobs-no-action.mk,v 1.9 2021/04/04 09:58:51 rillig Exp $
+# $NetBSD: opt-jobs-no-action.mk,v 1.10 2022/05/08 06:51:27 rillig Exp $
#
# Tests for the combination of the options -j and -n, which prints the
# commands instead of actually running them.
@@ -21,7 +21,7 @@
# The shell attributes are handled by Job_ParseShell.
# The shell attributes 'quiet' and 'echo' don't need a trailing newline,
# this is handled by the [0] != '\0' checks in Job_ParseShell.
-# The '\#' is handled by ParseGetLine.
+# The '\#' is handled by ParseRawLine.
# The '\n' is handled by Str_Words in Job_ParseShell.
# The '$$' is handled by Var_Subst in ParseDependencyLine.
.SHELL: \
diff --git a/contrib/bmake/unit-tests/opt-jobs.mk b/contrib/bmake/unit-tests/opt-jobs.mk
index 7d54d08a8421..ce84e47e91e1 100644
--- a/contrib/bmake/unit-tests/opt-jobs.mk
+++ b/contrib/bmake/unit-tests/opt-jobs.mk
@@ -1,8 +1,54 @@
-# $NetBSD: opt-jobs.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: opt-jobs.mk,v 1.5 2023/09/10 16:25:32 sjg Exp $
#
-# Tests for the -j command line option.
+# Tests for the -j command line option, which creates the targets in parallel.
-# TODO: Implementation
-all:
- @:;
+# The option '-j <integer>' specifies the number of targets that can be made
+# in parallel.
+ARGS= 0 1 2 8 08 017 0x10 -5 1000
+EXPECT.0= argument '0' to option '-j' must be a positive number (exit 2)
+EXPECT.1= 1
+EXPECT.2= 2
+EXPECT.8= 8
+EXPECT.08= argument '08' to option '-j' must be a positive number (exit 2)
+EXPECT.017= 15
+EXPECT.0x10= 16
+EXPECT.-5= argument '-5' to option '-j' must be a positive number (exit 2)
+EXPECT.1000= 1000
+
+.for arg in ${ARGS}
+OUTPUT!= ${MAKE} -r -f /dev/null -j ${arg} -v .MAKE.JOBS 2>&1 || echo "(exit $$?)"
+. if ${OUTPUT:[2..-1]} != ${EXPECT.${arg}}
+. warning ${arg}:${.newline} have: ${OUTPUT:[2..-1]}${.newline} want: ${EXPECT.${arg}}
+. endif
+.endfor
+
+
+# The options '-j <float>' and '-j <integer>C' multiply the given number with
+# the number of available CPUs.
+ARGS= 0.0 0C 0.0C .00001 .00001C 1C 1CPUs 1.2 .5e1C 07.5C 08.5C
+EXPECT.0.0= argument '0.0' to option '-j' must be a positive number (exit 2)
+EXPECT.0C= <integer> # rounded up to 1C
+EXPECT.0.0C= argument '0.0C' to option '-j' must be a positive number (exit 2)
+EXPECT..00001= argument '.00001' to option '-j' must be a positive number (exit 2)
+EXPECT..00001C= argument '.00001C' to option '-j' must be a positive number (exit 2)
+EXPECT.1C= <integer>
+EXPECT.1CPUs= <integer>
+EXPECT.1.2= <integer>
+EXPECT..5e1C= <integer> # unlikely to occur in practice
+EXPECT.07.5C= <integer>
+EXPECT.08.5C= argument '08.5C' to option '-j' must be a positive number (exit 2)
+
+.if ${.MAKE.JOBS.C} == "yes"
+. for arg in ${ARGS}
+OUTPUT!= ${MAKE} -r -f /dev/null -j ${arg} -v .MAKE.JOBS 2>&1 || echo "(exit $$?)"
+. if ${OUTPUT:C,^[0-9]+$,numeric,W} == numeric
+OUTPUT= <integer>
+. endif
+. if ${OUTPUT:[2..-1]} != ${EXPECT.${arg}}
+. warning ${arg}:${.newline} have: ${OUTPUT:[2..-1]}${.newline} want: ${EXPECT.${arg}}
+. endif
+. endfor
+.endif
+
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/opt-keep-going-indirect.exp b/contrib/bmake/unit-tests/opt-keep-going-indirect.exp
new file mode 100644
index 000000000000..0c00c75395fa
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-keep-going-indirect.exp
@@ -0,0 +1,32 @@
+direct compat
+false
+*** Error code 1 (continuing)
+
+Stop.
+make: stopped in unit-tests
+exited 1
+
+direct jobs
+false
+*** [direct] Error code 1
+
+make: stopped in unit-tests
+exited 1
+
+indirect compat
+false
+*** Error code 1 (continuing)
+`indirect' not remade because of errors.
+
+Stop.
+make: stopped in unit-tests
+exited 1
+
+indirect jobs
+false
+*** [direct] Error code 1
+
+make: stopped in unit-tests
+exited 1
+
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-keep-going-indirect.mk b/contrib/bmake/unit-tests/opt-keep-going-indirect.mk
new file mode 100644
index 000000000000..5d18553fa512
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-keep-going-indirect.mk
@@ -0,0 +1,90 @@
+# $NetBSD: opt-keep-going-indirect.mk,v 1.3 2024/04/02 15:05:15 rillig Exp $
+#
+# Tests for the -k command line option, which stops building a target as soon
+# as an error is detected, but continues building the other, independent
+# targets, as far as possible.
+#
+# History:
+# In 1993, the exit status for the option '-k' was always 0, even if a
+# direct or an indirect target failed.
+#
+# Since 2000.12.30.02.05.21, the word '(continuing)' is missing in jobs
+# mode, both for direct as well as indirect targets.
+#
+# Since 2001.10.16.18.50.12, the exit status for a direct failure in
+# compat mode is the correct 1, while jobs mode and indirect failures
+# still return the wrong exit status 0. The number of empty lines
+# between the various error messages differs between the modes, for no
+# reason.
+#
+# At 2006.11.17.22.07.39, the exit status for direct failures in both
+# modes and for indirect failures in jobs mode was fixed to the correct
+# 1. The exit status for indirect failures in compat mode is still the
+# wrong 0. On the downside, a failed indirect target in jobs mode is no
+# longer listed as "not remade because of errors".
+#
+# At 2016.08.26.23.28.39, the additional empty line for a direct failure
+# in compat mode was removed, making it consistent with a direct failure
+# in jobs mode. This left only one inconsistency, in that indirect
+# failures in jobs mode (by far the most common when building large
+# projects) did not produce any empty line.
+#
+# Since 2020.12.07.00.53.30, the exit status is consistently 1 for
+# failures in all 4 modes.
+#
+# Bugs:
+# The output in case of a failure needlessly differs between compat and
+# jobs mode. As of 2022-02-12, compat mode outputs '(continuing)' while
+# jobs mode doesn't. In compat mode, the output does not mention which
+# target failed.
+#
+# See also:
+# https://gnats.netbsd.org/49720
+
+.PHONY: all direct indirect
+
+# The 'set +e' was necessary in 2003, when the shell was run with '-e' by
+# default.
+# The 'env -i' prevents that the environment variable MAKEFLAGS is passed down
+# to the child processes.
+all:
+ @echo 'direct compat'
+ @set +e; env -i "PATH=$$PATH" ${MAKE} -r -f ${MAKEFILE} -k direct; echo "exited $$?"
+ @echo
+
+ @echo 'direct jobs'
+ @set +e; env -i "PATH=$$PATH" ${MAKE} -r -f ${MAKEFILE} -k direct -j1; echo "exited $$?"
+ @echo
+
+ @echo 'indirect compat'
+ @set +e; env -i "PATH=$$PATH" ${MAKE} -r -f ${MAKEFILE} -k indirect; echo "exited $$?"
+ @echo
+
+ @echo 'indirect jobs'
+ @set +e; env -i "PATH=$$PATH" ${MAKE} -r -f ${MAKEFILE} -k indirect -j1; echo "exited $$?"
+ @echo
+
+indirect: direct
+direct:
+ false
+
+# TODO: Mention the target that failed, maybe even the chain of targets.
+# expect: direct compat
+# expect: *** Error code 1 (continuing)
+# expect: exited 1
+
+# TODO: Add '(continuing)'.
+# expect: direct jobs
+# expect: *** [direct] Error code 1
+# expect: exited 1
+
+# TODO: Mention the target that failed, maybe even the chain of targets.
+# expect: indirect compat
+# expect: *** Error code 1 (continuing)
+# expect: exited 1
+
+# TODO: Add '(continuing)'.
+# TODO: Add 'not remade because of errors'.
+# expect: indirect jobs
+# expect: *** [direct] Error code 1
+# expect: exited 1
diff --git a/contrib/bmake/unit-tests/opt-m-include-dir.mk b/contrib/bmake/unit-tests/opt-m-include-dir.mk
index 6e0801390395..b677f172e85b 100644
--- a/contrib/bmake/unit-tests/opt-m-include-dir.mk
+++ b/contrib/bmake/unit-tests/opt-m-include-dir.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-m-include-dir.mk,v 1.4 2020/09/01 20:14:34 rillig Exp $
+# $NetBSD: opt-m-include-dir.mk,v 1.5 2024/04/30 16:13:34 sjg Exp $
#
# Tests for the -m command line option, which adds a directory to the
# search path for the .include <...> directive.
@@ -22,11 +22,14 @@
TEST_DIR:= ${.PARSEFILE:R}.tmp/sub/sub/sub/workdir
CANARY_FILE:= ${.PARSEFILE:R}.tmp/sub/opt-m-canary.mk
ACTUAL_FILE:= ${.PARSEFILE:R}.tmp/sub/opt-m-step3.mk
+WANTED_FILE:= ${.PARSEFILE:R}.tmp/sub/opt-m-check.mk
_!= mkdir -p ${TEST_DIR}
_!= > ${CANARY_FILE}
_!= cp ${MAKEFILE} ${TEST_DIR}/step2.mk
_!= cp ${MAKEFILE} ${ACTUAL_FILE}
+_!= echo CHECK=ok > ${WANTED_FILE}
+_!= echo CHECK=${WANTED_FILE:T} found in .CURDIR > ${TEST_DIR}/${WANTED_FILE:T}
step1:
@${.MAKE} -C ${TEST_DIR} -f step2.mk step2
@@ -52,9 +55,10 @@ step1:
.elif ${.PARSEFILE:T} == "opt-m-step3.mk"
# This file is included by step2.mk.
+.include <opt-m-check.mk>
step2:
- @echo ok
+ @echo ${CHECK}
.else
. error
diff --git a/contrib/bmake/unit-tests/opt-query.exp b/contrib/bmake/unit-tests/opt-query.exp
index 38025dcf4d3a..0ba62780d844 100644
--- a/contrib/bmake/unit-tests/opt-query.exp
+++ b/contrib/bmake/unit-tests/opt-query.exp
@@ -1,2 +1,24 @@
+Making commands:
command during parsing
-exit status 1
+commands: query status 1
+
+Making opt-query-file.out-of-date in compat mode:
+opt-query-file.out-of-date in compat mode: query status 1
+
+Making opt-query-file.up-to-date in compat mode:
+`opt-query-file.up-to-date' is up to date.
+opt-query-file.up-to-date in compat mode: query status 0
+
+Making phony in compat mode:
+phony in compat mode: query status 1
+
+Making opt-query-file.out-of-date in jobs mode:
+opt-query-file.out-of-date in jobs mode: query status 1
+
+Making opt-query-file.up-to-date in jobs mode:
+opt-query-file.up-to-date in jobs mode: query status 0
+
+Making phony in jobs mode:
+phony in jobs mode: query status 1
+
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-query.mk b/contrib/bmake/unit-tests/opt-query.mk
index 0a7d5219a8fe..3554d69afad6 100644
--- a/contrib/bmake/unit-tests/opt-query.mk
+++ b/contrib/bmake/unit-tests/opt-query.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-query.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $
+# $NetBSD: opt-query.mk,v 1.7 2022/08/18 05:37:05 rillig Exp $
#
# Tests for the -q command line option.
#
@@ -6,7 +6,57 @@
# None of the commands in the targets are run, not even those that are
# prefixed with '+'.
-.MAKEFLAGS: -q
+# This test consists of several parts:
+#
+# main Delegates to the actual tests.
+#
+# commands Ensures that none of the targets is made.
+#
+# variants Ensures that the up-to-date status is correctly
+# reported in both compat and jobs mode, and for several
+# kinds of make targets.
+PART?= main
+
+.if ${PART} == "main"
+
+all: .PHONY variants cleanup
+
+_!= touch -f opt-query-file.up-to-date
+
+variants: .PHONY
+
+. for target in commands
+ @echo 'Making ${target}':
+ @${MAKE} -r -f ${MAKEFILE} -q ${mode:Mjobs:%=-j1} ${target} PART=commands \
+ && echo "${target}: query status $$?" \
+ || echo "${target}: query status $$?"
+ @echo
+. endfor
+
+. for mode in compat jobs
+. for target in opt-query-file.out-of-date opt-query-file.up-to-date phony
+ @echo 'Making ${target} in ${mode} mode':
+ @${MAKE} -r -f ${MAKEFILE} -q ${mode:Mjobs:%=-j1} ${target} PART=variants \
+ && echo "${target} in ${mode} mode: query status $$?" \
+ || echo "${target} in ${mode} mode: query status $$?"
+ @echo
+. endfor
+. endfor
+
+# Between 1994 and before 2022-08-17, the exit status for '-q' was always 1,
+# the cause for that exit code varied over time though.
+#
+# expect: opt-query-file.out-of-date in compat mode: query status 1
+# expect: opt-query-file.up-to-date in compat mode: query status 0
+# expect: phony in compat mode: query status 1
+# expect: opt-query-file.out-of-date in jobs mode: query status 1
+# expect: opt-query-file.up-to-date in jobs mode: query status 0
+# expect: phony in jobs mode: query status 1
+
+cleanup: .PHONY
+ @rm -f opt-query-file.up-to-date
+
+.elif ${PART} == "commands"
# This command cannot be prevented from being run since it is used at parse
# time, and any later variable assignments may depend on its result.
@@ -18,9 +68,18 @@
@+echo '$@: run always'
# None of these commands are run.
-all:
+commands:
@echo '$@: hidden command'
@+echo '$@: run always'
-
-# The exit status 1 is because the "all" target has to be made, that is,
+# The exit status 1 is because the "commands" target has to be made, that is,
# it is not up-to-date.
+
+.elif ${PART} == "variants"
+
+opt-query-file.out-of-date: ${MAKEFILE}
+opt-query-file.up-to-date: ${MAKEFILE}
+phony: .PHONY
+
+.else
+. error Invalid part '${PART}'
+.endif
diff --git a/contrib/bmake/unit-tests/opt-raw.mk b/contrib/bmake/unit-tests/opt-raw.mk
index d3591bb99dab..91caffcd72ae 100644
--- a/contrib/bmake/unit-tests/opt-raw.mk
+++ b/contrib/bmake/unit-tests/opt-raw.mk
@@ -1,8 +1,14 @@
-# $NetBSD: opt-raw.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: opt-raw.mk,v 1.3 2022/01/23 16:09:38 rillig Exp $
#
-# Tests for the -r command line option.
+# Tests for the -r command line option, which skips the system-defined default
+# rules from <sys.mk>.
-# TODO: Implementation
+# To provide a clean testing environment without unintended side effects,
+# these unit tests run make with the option '-r' by default. This means there
+# are no predefined suffixes and no predefined tools.
-all:
- @:;
+.if defined(CC)
+. error
+.endif
+
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/opt-silent.exp b/contrib/bmake/unit-tests/opt-silent.exp
index 39a9383953dd..8863b8a2965d 100644
--- a/contrib/bmake/unit-tests/opt-silent.exp
+++ b/contrib/bmake/unit-tests/opt-silent.exp
@@ -1 +1,3 @@
+message
+silent message
exit status 0
diff --git a/contrib/bmake/unit-tests/opt-silent.mk b/contrib/bmake/unit-tests/opt-silent.mk
index 7822d46ac48a..01e5b18e2b12 100644
--- a/contrib/bmake/unit-tests/opt-silent.mk
+++ b/contrib/bmake/unit-tests/opt-silent.mk
@@ -1,8 +1,10 @@
-# $NetBSD: opt-silent.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: opt-silent.mk,v 1.3 2022/01/23 16:09:38 rillig Exp $
#
# Tests for the -s command line option.
-# TODO: Implementation
+.MAKEFLAGS: -s
+# No matter whether a command is prefixed by '@' or not, it is not echoed.
all:
- @:;
+ echo 'message'
+ @echo 'silent message'
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/opt-version.exp b/contrib/bmake/unit-tests/opt-version.exp
new file mode 100644
index 000000000000..1636aafc11e5
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-version.exp
@@ -0,0 +1,2 @@
+
+exit status 0
diff --git a/contrib/bmake/unit-tests/opt-version.mk b/contrib/bmake/unit-tests/opt-version.mk
new file mode 100644
index 000000000000..cdba9180ec01
--- /dev/null
+++ b/contrib/bmake/unit-tests/opt-version.mk
@@ -0,0 +1,12 @@
+# $NetBSD: opt-version.mk,v 1.2 2022/05/08 07:27:50 rillig Exp $
+#
+# Tests for the command line option '--version', which may be expected to
+# output the version number of make. NetBSD's make does not have a version
+# number, but the bmake distribution created from it has.
+
+# As of 2021-12-23, the output is a single empty line since the '--' does not
+# end the command line options. Command line parsing then continues as if
+# nothing had happened, and the '-version' is split into '-v ersion', which is
+# interpreted as "print the expanded variable named 'ersion'".
+
+.MAKEFLAGS: --version
diff --git a/contrib/bmake/unit-tests/opt-warnings-as-errors.exp b/contrib/bmake/unit-tests/opt-warnings-as-errors.exp
index 1db56b753bed..87d1db249a20 100644
--- a/contrib/bmake/unit-tests/opt-warnings-as-errors.exp
+++ b/contrib/bmake/unit-tests/opt-warnings-as-errors.exp
@@ -1,6 +1,6 @@
-make: "opt-warnings-as-errors.mk" line 12: warning: message 1
+make: "opt-warnings-as-errors.mk" line 13: warning: message 1
make: parsing warnings being treated as errors
-make: "opt-warnings-as-errors.mk" line 13: warning: message 2
+make: "opt-warnings-as-errors.mk" line 15: warning: message 2
parsing continues
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
diff --git a/contrib/bmake/unit-tests/opt-warnings-as-errors.mk b/contrib/bmake/unit-tests/opt-warnings-as-errors.mk
index c29343f960a7..3896dad10f1a 100644
--- a/contrib/bmake/unit-tests/opt-warnings-as-errors.mk
+++ b/contrib/bmake/unit-tests/opt-warnings-as-errors.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-warnings-as-errors.mk,v 1.5 2021/01/27 00:02:38 rillig Exp $
+# $NetBSD: opt-warnings-as-errors.mk,v 1.6 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the -W command line option, which turns warnings into errors.
#
@@ -9,7 +9,9 @@
.MAKEFLAGS: -W
+# expect+1: warning: message 1
.warning message 1
+# expect+1: warning: message 2
.warning message 2
_!= echo 'parsing continues' 1>&2
diff --git a/contrib/bmake/unit-tests/opt-where-am-i.exp b/contrib/bmake/unit-tests/opt-where-am-i.exp
index 39a9383953dd..e64df44b83b7 100644
--- a/contrib/bmake/unit-tests/opt-where-am-i.exp
+++ b/contrib/bmake/unit-tests/opt-where-am-i.exp
@@ -1 +1,4 @@
+make: Entering directory `/'
+make: Leaving directory `/'
+make: Leaving directory `<curdir>'
exit status 0
diff --git a/contrib/bmake/unit-tests/opt-where-am-i.mk b/contrib/bmake/unit-tests/opt-where-am-i.mk
index 9158a598174c..f1eeca920a32 100644
--- a/contrib/bmake/unit-tests/opt-where-am-i.mk
+++ b/contrib/bmake/unit-tests/opt-where-am-i.mk
@@ -1,8 +1,14 @@
-# $NetBSD: opt-where-am-i.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: opt-where-am-i.mk,v 1.4 2022/01/27 02:24:46 sjg Exp $
#
-# Tests for the -w command line option.
+# Tests for the -w command line option, which outputs the current directory
+# at the beginning and end of running make. This is useful when building
+# large source trees that involve several nested make calls.
-# TODO: Implementation
+# The first "Entering directory" is missing since the below .MAKEFLAGS comes
+# too late for it.
+.MAKEFLAGS: -w
all:
- @:;
+.if ${.CURDIR} != "/"
+ @MAKE_OBJDIR_CHECK_WRITABLE=no ${MAKE} -r -f ${MAKEFILE:tA} -C /
+.endif
diff --git a/contrib/bmake/unit-tests/opt-x-reduce-exported.exp b/contrib/bmake/unit-tests/opt-x-reduce-exported.exp
index 39a9383953dd..99570f2c30cb 100644
--- a/contrib/bmake/unit-tests/opt-x-reduce-exported.exp
+++ b/contrib/bmake/unit-tests/opt-x-reduce-exported.exp
@@ -1 +1,5 @@
+ordinary:
+BEFORE=before
+submake:
+BEFORE=before
exit status 0
diff --git a/contrib/bmake/unit-tests/opt-x-reduce-exported.mk b/contrib/bmake/unit-tests/opt-x-reduce-exported.mk
index 7ee8e7c7eff0..a42a85d21a53 100644
--- a/contrib/bmake/unit-tests/opt-x-reduce-exported.mk
+++ b/contrib/bmake/unit-tests/opt-x-reduce-exported.mk
@@ -1,8 +1,20 @@
-# $NetBSD: opt-x-reduce-exported.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: opt-x-reduce-exported.mk,v 1.3 2022/05/08 07:27:50 rillig Exp $
#
-# Tests for the -x command line option.
+# Tests for the -X command line option, which prevents variables passed on the
+# command line from being exported to the environment of child commands.
-# TODO: Implementation
+# The variable 'BEFORE' is exported, the variable 'AFTER' isn't.
+.MAKEFLAGS: BEFORE=before -X AFTER=after
-all:
- @:;
+all: .PHONY ordinary submake
+
+ordinary: .PHONY
+ @echo 'ordinary:'
+ @env | sort | grep -e '^BEFORE' -e '^AFTER'
+
+submake: .PHONY
+ @echo 'submake:'
+ @${MAKE} -r -f ${MAKEFILE} show-env
+
+show-env: .PHONY
+ @env | sort | grep -e '^BEFORE' -e '^AFTER'
diff --git a/contrib/bmake/unit-tests/opt.mk b/contrib/bmake/unit-tests/opt.mk
index 0931a66d3d15..939d5ec35aeb 100644
--- a/contrib/bmake/unit-tests/opt.mk
+++ b/contrib/bmake/unit-tests/opt.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt.mk,v 1.6 2020/11/18 01:06:59 sjg Exp $
+# $NetBSD: opt.mk,v 1.7 2023/02/25 00:07:08 rillig Exp $
#
# Tests for the command line options.
@@ -7,7 +7,7 @@
all: .IGNORE
# The options from the top-level make are passed to the sub-makes via
# the environment variable MAKEFLAGS. This is where the " -r -k -d 0"
- # comes from. See MainParseArg.
+ # comes from. See MainParseOption.
${MAKE} -r -f /dev/null -V MAKEFLAGS
@echo
diff --git a/contrib/bmake/unit-tests/parse-var.mk b/contrib/bmake/unit-tests/parse-var.mk
index bd6c59f0e5cb..b35726e76efc 100644
--- a/contrib/bmake/unit-tests/parse-var.mk
+++ b/contrib/bmake/unit-tests/parse-var.mk
@@ -1,13 +1,129 @@
-# $NetBSD: parse-var.mk,v 1.1 2020/10/04 06:53:15 rillig Exp $
+# $NetBSD: parse-var.mk,v 1.9 2023/11/19 21:47:52 rillig Exp $
+#
+# Tests for parsing expressions.
+#
+# TODO: Add systematic tests for all of the below combinations.
+#
+# Written form:
+# short form
+# long form with braces endc == '}'
+# long form with parentheses endc == ')'
+# indirect modifiers endc == '\0'
+#
+# Based on:
+# undefined variable
+# global variable
+# command-line variable
+# environment variable
+# target-local variable
+# legacy variable '@F'
+#
+# VarEvalMode:
+# parse
+# eval
+# eval-undeferr
+# eval-keep-dollar
+# eval-keep-undef
+# eval-keep-dollar-undef
+#
+# Global mode:
+# without -dL
+# with -dL
+#
+# Modifiers:
+# no
+# yes, stay undefined
+# convert to defined
+# indirect modifiers, involving changes to VarEvalMode
+#
+# Error conditions:
+# for the short form, EOF after the '$'
+# for the short form, each character
+# for the long forms, EOF right after '${'
+# for the long forms, EOF after the variable name
+# for the long forms, EOF after the ':'
+# for the long forms, EOF after parsing a modifier
+# for the long forms, ':}'
+# for each modifier: syntactic error
+# for each modifier: evaluation error
+#
+# Context:
+# in a condition, only operand, unquoted
+# in a condition, only operand, quoted
+# in a condition, left-hand side, unquoted
+# in a condition, left-hand side, quoted
+# in a condition, right-hand side, unquoted
+# in a condition, right-hand side, quoted
+# left-hand side of a variable assignment
+# right-hand side of a ':=' variable assignment
+# right-hand side of a '!=' variable assignment
+# shell command in a target
+# .info directive
+# dependency line
+# items in a .for loop
+# everywhere else Var_Parse is called
+#
+# Further influences:
+# multi-level evaluations like 'other=${OTHER}' with OTHER='$$ ${THIRD}'
+#
+# Effects:
+# How much does the parsing position advance (pp)?
+# What's the value of the expression (return value)?
+# What error messages are printed (Parse_Error)?
+# What no-effect error messages are printed (Error)?
+# What error messages should be printed but aren't?
+# What other side effects are there?
.MAKEFLAGS: -dL
-# In variable assignments, there may be spaces on the left-hand side of the
-# assignment, but only if they occur inside variable expressions.
+# In variable assignments, there may be spaces in the middle of the left-hand
+# side of the assignment, but only if they occur inside expressions.
+# Leading spaces (but not tabs) are possible but unusual.
+# Trailing spaces are common in some coding styles, others omit them.
VAR.${:U param }= value
.if ${VAR.${:U param }} != "value"
. error
.endif
-all:
- @:;
+# Since var.c 1.323 from 2020-07-26 18:11 and until var.c 1.1047 from
+# 2023-02-18, the exact way of parsing an expression with subexpressions
+# depended on whether the expression was actually evaluated or merely parsed.
+#
+# If it was evaluated, nested expressions were parsed correctly, parsing each
+# modifier according to its exact definition (see varmod.mk).
+#
+# If the expression was merely parsed but not evaluated (for example, because
+# its value would not influence the outcome of the condition, or during the
+# first pass of the ':@var@body@' modifier), and the expression contained a
+# modifier, and that modifier contained a nested expression, the nested
+# expression was not parsed correctly. Instead, make only counted the opening
+# and closing delimiters, which failed for nested modifiers with unbalanced
+# braces.
+
+#.MAKEFLAGS: -dcpv
+# Keep these braces outside the conditions below, to keep them simple to
+# understand. If the expression ${BRACE_PAIR:...} had been replaced with the
+# literal ${:U{}}, the '}' would have to be escaped, but not the '{'. This
+# asymmetry would have made the example even more complicated to understand.
+BRACE_PAIR= {}
+# In this test word, the below conditions will replace the '{{}' in the middle
+# with the string '<lbraces>'.
+BRACE_GROUP= {{{{}}}}
+
+# The inner ':S' modifier turns the word '{}' into '{{}'.
+# The outer ':S' modifier then replaces '{{}' with '<lbraces>'.
+# Due to the always-true condition '1', the outer expression is relevant and
+# is parsed correctly.
+.if 1 && ${BRACE_GROUP:S,${BRACE_PAIR:S,{,{{,},<lbraces>,}
+.endif
+# Due to the always-false condition '0', the outer expression is irrelevant.
+# In this case, in the parts of the outer ':S' modifier, the expression parser
+# only counted the braces, and since the inner expression '${BRACE_PAIR:...}'
+# contains more '{' than '}', parsing failed with the error message 'Unfinished
+# modifier for "BRACE_GROUP"'. Fixed in var.c 1.1047 from 2023-02-18.
+.if 0 && ${BRACE_GROUP:S,${BRACE_PAIR:S,{,{{,},<lbraces>,}
+.endif
+#.MAKEFLAGS: -d0
+
+
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/parse.exp b/contrib/bmake/unit-tests/parse.exp
new file mode 100644
index 000000000000..cc8c450d51ac
--- /dev/null
+++ b/contrib/bmake/unit-tests/parse.exp
@@ -0,0 +1,6 @@
+make: "parse.mk" line 7: Invalid line '<<<<<< old'
+make: "parse.mk" line 14: Invalid line '>>>>>> new'
+make: "parse.mk" line 25: Invalid line 'one-target ${:U }', expanded to 'one-target '
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/parse.mk b/contrib/bmake/unit-tests/parse.mk
new file mode 100644
index 000000000000..80a51f2de11e
--- /dev/null
+++ b/contrib/bmake/unit-tests/parse.mk
@@ -0,0 +1,55 @@
+# $NetBSD: parse.mk,v 1.7 2023/08/19 11:09:02 rillig Exp $
+#
+# Test those parts of the parsing that do not belong in any of the other
+# categories.
+
+# expect+1: Invalid line '<<<<<< old'
+<<<<<< old
+
+# No diagnostic since the following line is parsed as a variable assignment,
+# even though the variable name is empty. See also varname-empty.mk.
+====== middle
+
+# expect+1: Invalid line '>>>>>> new'
+>>>>>> new
+
+
+# Since parse.c 1.578 from 2021-12-14 and before parse.c 1.681 from
+# 2022-07-24, if a line of a makefile could only be a dependency specification
+# but didn't contain any of the dependency operators ':', '!', '::' and its
+# expansion ended with a space, make read a single byte from the memory beyond
+# the expanded line's terminating '\0'.
+#
+# https://bugs.freebsd.org/265119
+# expect+1: Invalid line 'one-target ${:U }', expanded to 'one-target '
+one-target ${:U }
+
+
+# Since parse.c 1.656 from 2022-01-27 and before parse.c 1.662 from
+# 2022-02-05, there was an out-of-bounds read in Parse_IsVar when looking for
+# a variable assignment in a dependency line with trailing whitespace. Lines
+# without trailing whitespace were not affected. Global variable assignments
+# were guaranteed to have no trailing whitespace and were thus not affected.
+#
+# Try to reproduce some variants that may lead to a crash, depending on the
+# memory allocator. To get a crash, the terminating '\0' of the line must be
+# the last byte of a memory page. The expression '${:U}' forces this trailing
+# whitespace.
+
+# On FreeBSD x86_64, a crash could in some cases be forced using the following
+# line, which has length 47, and if the memory for the expanded line starts at
+# 0xXXXX_XXd0, the terminating '\0' may end up at 0xXXXX_Xfff:
+Try_to_crash_FreeBSD.xxxxxxxxxxxxxxxxxx: 12345 ${:U}
+
+# The following line has length 4095 after being expanded, so line[4095] ==
+# '\0'. If the line is
+# allocated on a page boundary and the following page is not mapped, this line
+# leads to a segmentation fault.
+${:U:range=511:@_@1234567@:ts.}: 12345 ${:U}
+
+# The following line has length 8191, so line[8191] == '\0'. If the line is
+# allocated on a page boundary and the following page is not mapped, this line
+# leads to a segmentation fault.
+${:U:range=1023:@_@1234567@:ts.}: 12345 ${:U}
+
+12345:
diff --git a/contrib/bmake/unit-tests/posix.mk b/contrib/bmake/unit-tests/posix.mk
index fc4cbead3263..43219258306e 100644
--- a/contrib/bmake/unit-tests/posix.mk
+++ b/contrib/bmake/unit-tests/posix.mk
@@ -1,4 +1,4 @@
-# $NetBSD: posix.mk,v 1.2 2020/10/24 08:34:59 rillig Exp $
+# $NetBSD: posix.mk,v 1.3 2022/01/23 18:15:29 rillig Exp $
all: x plus subs err
@@ -14,11 +14,10 @@ plus:
subs:
@echo make -n
- @${.MAKE} -f ${MAKEFILE} -n plus
+ @${.MAKE} -r -f ${MAKEFILE} -n plus
@echo make -n -j1
- @${.MAKE} -f ${MAKEFILE} -n -j1 plus
+ @${.MAKE} -r -f ${MAKEFILE} -n -j1 plus
err:
@(echo Now we expect an error...; exit 1)
@echo "Oops! you shouldn't see this!"
-
diff --git a/contrib/bmake/unit-tests/recursive.exp b/contrib/bmake/unit-tests/recursive.exp
index 36cd1c989532..f63b7a54049e 100644
--- a/contrib/bmake/unit-tests/recursive.exp
+++ b/contrib/bmake/unit-tests/recursive.exp
@@ -1,5 +1,5 @@
-make: "recursive.mk" line 36: Unclosed variable "MISSING_PAREN"
-make: "recursive.mk" line 37: Unclosed variable "MISSING_BRACE"
+make: "recursive.mk" line 38: Unclosed variable "MISSING_PAREN"
+make: "recursive.mk" line 40: Unclosed variable "MISSING_BRACE"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/recursive.mk b/contrib/bmake/unit-tests/recursive.mk
index 5265cec59a2d..b97c4b37eabb 100644
--- a/contrib/bmake/unit-tests/recursive.mk
+++ b/contrib/bmake/unit-tests/recursive.mk
@@ -1,4 +1,4 @@
-# $NetBSD: recursive.mk,v 1.5 2021/03/15 12:15:03 rillig Exp $
+# $NetBSD: recursive.mk,v 1.7 2023/10/19 18:24:33 rillig Exp $
#
# In -dL mode, a variable may get expanded before it makes sense.
# This would stop make from doing anything since the "recursive" error
@@ -8,22 +8,23 @@
# whether there are unclosed variables. The variable value is therefore
# parsed with VARE_PARSE_ONLY for that purpose.
#
-# Seen in pkgsrc/x11/libXfixes, and probably many more package that use
-# GNU Automake.
.MAKEFLAGS: -dL
+
AM_V_lt= ${am__v_lt_${V}}
am__v_lt_= ${am__v_lt_${AM_DEFAULT_VERBOSITY}}
am__v_lt_0= --silent
am__v_lt_1=
-# On 2020-08-06, make reported: "Variable am__v_lt_ is recursive."
+# Since parse.c 1.243 from 2020-07-31 and before parse.c 1.249 from
+# 2020-08-06, when make ran in -dL mode, it reported: "Variable am__v_lt_ is
+# recursive."
+#
+# Seen in pkgsrc/x11/libXfixes, and probably many more package that use
+# GNU Automake.
libXfixes_la_LINK= ... ${AM_V_lt} ...
-# somewhere later ...
-AM_DEFAULT_VERBOSITY= 1
-
# The purpose of the -dL flag is to detect unclosed variables. This
# can be achieved by just parsing the variable and not evaluating it.
@@ -33,6 +34,8 @@ AM_DEFAULT_VERBOSITY= 1
# therefore that's acceptable. In most practical cases, the missing
# brace would be detected directly in the line where it is produced.
MISSING_BRACE_INDIRECT:= ${:U\${MISSING_BRACE}
+# expect+1: Unclosed variable "MISSING_PAREN"
UNCLOSED= $(MISSING_PAREN
+# expect+1: Unclosed variable "MISSING_BRACE"
UNCLOSED= ${MISSING_BRACE
UNCLOSED= ${MISSING_BRACE_INDIRECT}
diff --git a/contrib/bmake/unit-tests/sh-dots.mk b/contrib/bmake/unit-tests/sh-dots.mk
index f85af9025e55..5294a4175b63 100755
--- a/contrib/bmake/unit-tests/sh-dots.mk
+++ b/contrib/bmake/unit-tests/sh-dots.mk
@@ -1,4 +1,4 @@
-# $NetBSD: sh-dots.mk,v 1.3 2020/10/25 22:04:24 rillig Exp $
+# $NetBSD: sh-dots.mk,v 1.4 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the special shell command line "...", which does not run the
# commands below it but appends them to the list of commands that are run
@@ -29,8 +29,8 @@ commented: .IGNORE
... # Run the below commands later
@echo commented delayed ${.TARGET}
-# The dots don't have to be written literally, they can also come from a
-# variable expression.
+# The dots don't have to be written literally, they can also come from an
+# expression.
indirect:
@echo indirect regular
${:U...}
diff --git a/contrib/bmake/unit-tests/sh-flags.exp b/contrib/bmake/unit-tests/sh-flags.exp
index 2fec7de2dd99..265826217985 100644
--- a/contrib/bmake/unit-tests/sh-flags.exp
+++ b/contrib/bmake/unit-tests/sh-flags.exp
@@ -1102,7 +1102,6 @@ opt-_j____-tgt-__s-cmd-__s
running
opt-_j____-tgt-__s-cmd-_i_
-echo running; false
running
*** [opt-_j____-tgt-__s-cmd-_i_] Error code 1 (ignored)
@@ -1117,7 +1116,6 @@ opt-_j____-tgt-__s-cmd-a_s
running
opt-_j____-tgt-__s-cmd-ai_
-echo running; false
running
*** [opt-_j____-tgt-__s-cmd-ai_] Error code 1 (ignored)
@@ -1170,7 +1168,6 @@ running
*** [opt-_j____-tgt-_is-cmd-__s] Error code 1 (ignored)
opt-_j____-tgt-_is-cmd-_i_
-echo running; false
running
*** [opt-_j____-tgt-_is-cmd-_i_] Error code 1 (ignored)
@@ -1187,7 +1184,6 @@ running
*** [opt-_j____-tgt-_is-cmd-a_s] Error code 1 (ignored)
opt-_j____-tgt-_is-cmd-ai_
-echo running; false
running
*** [opt-_j____-tgt-_is-cmd-ai_] Error code 1 (ignored)
@@ -1234,7 +1230,6 @@ opt-_j____-tgt-a_s-cmd-__s
running
opt-_j____-tgt-a_s-cmd-_i_
-echo running; false
running
*** [opt-_j____-tgt-a_s-cmd-_i_] Error code 1 (ignored)
@@ -1249,7 +1244,6 @@ opt-_j____-tgt-a_s-cmd-a_s
running
opt-_j____-tgt-a_s-cmd-ai_
-echo running; false
running
*** [opt-_j____-tgt-a_s-cmd-ai_] Error code 1 (ignored)
@@ -1302,7 +1296,6 @@ running
*** [opt-_j____-tgt-ais-cmd-__s] Error code 1 (ignored)
opt-_j____-tgt-ais-cmd-_i_
-echo running; false
running
*** [opt-_j____-tgt-ais-cmd-_i_] Error code 1 (ignored)
@@ -1319,7 +1312,6 @@ running
*** [opt-_j____-tgt-ais-cmd-a_s] Error code 1 (ignored)
opt-_j____-tgt-ais-cmd-ai_
-echo running; false
running
*** [opt-_j____-tgt-ais-cmd-ai_] Error code 1 (ignored)
@@ -1496,7 +1488,6 @@ opt-_j_n__-tgt-a_s-cmd-__s
running
opt-_j_n__-tgt-a_s-cmd-_i_
-echo running; false
running
opt-_j_n__-tgt-a_s-cmd-_is
@@ -1509,7 +1500,6 @@ opt-_j_n__-tgt-a_s-cmd-a_s
running
opt-_j_n__-tgt-a_s-cmd-ai_
-echo running; false
running
opt-_j_n__-tgt-a_s-cmd-ais
@@ -1550,7 +1540,6 @@ opt-_j_n__-tgt-ais-cmd-__s
running
opt-_j_n__-tgt-ais-cmd-_i_
-echo running; false
running
opt-_j_n__-tgt-ais-cmd-_is
@@ -1563,7 +1552,6 @@ opt-_j_n__-tgt-ais-cmd-a_s
running
opt-_j_n__-tgt-ais-cmd-ai_
-echo running; false
running
opt-_j_n__-tgt-ais-cmd-ais
@@ -1612,12 +1600,10 @@ opt-_jl___-tgt-__s-cmd-__s
running
opt-_jl___-tgt-__s-cmd-_i_
-echo running; false
running
*** [opt-_jl___-tgt-__s-cmd-_i_] Error code 1 (ignored)
opt-_jl___-tgt-__s-cmd-_is
-echo running; false
running
*** [opt-_jl___-tgt-__s-cmd-_is] Error code 1 (ignored)
@@ -1628,12 +1614,10 @@ opt-_jl___-tgt-__s-cmd-a_s
running
opt-_jl___-tgt-__s-cmd-ai_
-echo running; false
running
*** [opt-_jl___-tgt-__s-cmd-ai_] Error code 1 (ignored)
opt-_jl___-tgt-__s-cmd-ais
-echo running; false
running
*** [opt-_jl___-tgt-__s-cmd-ais] Error code 1 (ignored)
@@ -1686,12 +1670,10 @@ running
*** [opt-_jl___-tgt-_is-cmd-__s] Error code 1 (ignored)
opt-_jl___-tgt-_is-cmd-_i_
-echo running; false
running
*** [opt-_jl___-tgt-_is-cmd-_i_] Error code 1 (ignored)
opt-_jl___-tgt-_is-cmd-_is
-echo running; false
running
*** [opt-_jl___-tgt-_is-cmd-_is] Error code 1 (ignored)
@@ -1704,12 +1686,10 @@ running
*** [opt-_jl___-tgt-_is-cmd-a_s] Error code 1 (ignored)
opt-_jl___-tgt-_is-cmd-ai_
-echo running; false
running
*** [opt-_jl___-tgt-_is-cmd-ai_] Error code 1 (ignored)
opt-_jl___-tgt-_is-cmd-ais
-echo running; false
running
*** [opt-_jl___-tgt-_is-cmd-ais] Error code 1 (ignored)
@@ -1756,12 +1736,10 @@ opt-_jl___-tgt-a_s-cmd-__s
running
opt-_jl___-tgt-a_s-cmd-_i_
-echo running; false
running
*** [opt-_jl___-tgt-a_s-cmd-_i_] Error code 1 (ignored)
opt-_jl___-tgt-a_s-cmd-_is
-echo running; false
running
*** [opt-_jl___-tgt-a_s-cmd-_is] Error code 1 (ignored)
@@ -1772,12 +1750,10 @@ opt-_jl___-tgt-a_s-cmd-a_s
running
opt-_jl___-tgt-a_s-cmd-ai_
-echo running; false
running
*** [opt-_jl___-tgt-a_s-cmd-ai_] Error code 1 (ignored)
opt-_jl___-tgt-a_s-cmd-ais
-echo running; false
running
*** [opt-_jl___-tgt-a_s-cmd-ais] Error code 1 (ignored)
@@ -1830,12 +1806,10 @@ running
*** [opt-_jl___-tgt-ais-cmd-__s] Error code 1 (ignored)
opt-_jl___-tgt-ais-cmd-_i_
-echo running; false
running
*** [opt-_jl___-tgt-ais-cmd-_i_] Error code 1 (ignored)
opt-_jl___-tgt-ais-cmd-_is
-echo running; false
running
*** [opt-_jl___-tgt-ais-cmd-_is] Error code 1 (ignored)
@@ -1848,12 +1822,10 @@ running
*** [opt-_jl___-tgt-ais-cmd-a_s] Error code 1 (ignored)
opt-_jl___-tgt-ais-cmd-ai_
-echo running; false
running
*** [opt-_jl___-tgt-ais-cmd-ai_] Error code 1 (ignored)
opt-_jl___-tgt-ais-cmd-ais
-echo running; false
running
*** [opt-_jl___-tgt-ais-cmd-ais] Error code 1 (ignored)
@@ -2032,11 +2004,9 @@ opt-_jln__-tgt-a_s-cmd-__s
running
opt-_jln__-tgt-a_s-cmd-_i_
-echo running; false
running
opt-_jln__-tgt-a_s-cmd-_is
-echo running; false
running
opt-_jln__-tgt-a_s-cmd-a__
@@ -2046,11 +2016,9 @@ opt-_jln__-tgt-a_s-cmd-a_s
running
opt-_jln__-tgt-a_s-cmd-ai_
-echo running; false
running
opt-_jln__-tgt-a_s-cmd-ais
-echo running; false
running
opt-_jln__-tgt-ai_-cmd-___
@@ -2092,11 +2060,9 @@ opt-_jln__-tgt-ais-cmd-__s
running
opt-_jln__-tgt-ais-cmd-_i_
-echo running; false
running
opt-_jln__-tgt-ais-cmd-_is
-echo running; false
running
opt-_jln__-tgt-ais-cmd-a__
@@ -2106,11 +2072,9 @@ opt-_jln__-tgt-ais-cmd-a_s
running
opt-_jln__-tgt-ais-cmd-ai_
-echo running; false
running
opt-_jln__-tgt-ais-cmd-ais
-echo running; false
running
opt-i_____-tgt-___-cmd-___
@@ -3278,7 +3242,6 @@ running
*** [opt-ij____-tgt-__s-cmd-__s] Error code 1 (ignored)
opt-ij____-tgt-__s-cmd-_i_
-echo running; false
running
*** [opt-ij____-tgt-__s-cmd-_i_] Error code 1 (ignored)
@@ -3295,7 +3258,6 @@ running
*** [opt-ij____-tgt-__s-cmd-a_s] Error code 1 (ignored)
opt-ij____-tgt-__s-cmd-ai_
-echo running; false
running
*** [opt-ij____-tgt-__s-cmd-ai_] Error code 1 (ignored)
@@ -3348,7 +3310,6 @@ running
*** [opt-ij____-tgt-_is-cmd-__s] Error code 1 (ignored)
opt-ij____-tgt-_is-cmd-_i_
-echo running; false
running
*** [opt-ij____-tgt-_is-cmd-_i_] Error code 1 (ignored)
@@ -3365,7 +3326,6 @@ running
*** [opt-ij____-tgt-_is-cmd-a_s] Error code 1 (ignored)
opt-ij____-tgt-_is-cmd-ai_
-echo running; false
running
*** [opt-ij____-tgt-_is-cmd-ai_] Error code 1 (ignored)
@@ -3418,7 +3378,6 @@ running
*** [opt-ij____-tgt-a_s-cmd-__s] Error code 1 (ignored)
opt-ij____-tgt-a_s-cmd-_i_
-echo running; false
running
*** [opt-ij____-tgt-a_s-cmd-_i_] Error code 1 (ignored)
@@ -3435,7 +3394,6 @@ running
*** [opt-ij____-tgt-a_s-cmd-a_s] Error code 1 (ignored)
opt-ij____-tgt-a_s-cmd-ai_
-echo running; false
running
*** [opt-ij____-tgt-a_s-cmd-ai_] Error code 1 (ignored)
@@ -3488,7 +3446,6 @@ running
*** [opt-ij____-tgt-ais-cmd-__s] Error code 1 (ignored)
opt-ij____-tgt-ais-cmd-_i_
-echo running; false
running
*** [opt-ij____-tgt-ais-cmd-_i_] Error code 1 (ignored)
@@ -3505,7 +3462,6 @@ running
*** [opt-ij____-tgt-ais-cmd-a_s] Error code 1 (ignored)
opt-ij____-tgt-ais-cmd-ai_
-echo running; false
running
*** [opt-ij____-tgt-ais-cmd-ai_] Error code 1 (ignored)
@@ -3686,7 +3642,6 @@ opt-ij_n__-tgt-a_s-cmd-__s
running
opt-ij_n__-tgt-a_s-cmd-_i_
-echo running; false
running
opt-ij_n__-tgt-a_s-cmd-_is
@@ -3699,7 +3654,6 @@ opt-ij_n__-tgt-a_s-cmd-a_s
running
opt-ij_n__-tgt-a_s-cmd-ai_
-echo running; false
running
opt-ij_n__-tgt-a_s-cmd-ais
@@ -3740,7 +3694,6 @@ opt-ij_n__-tgt-ais-cmd-__s
running
opt-ij_n__-tgt-ais-cmd-_i_
-echo running; false
running
opt-ij_n__-tgt-ais-cmd-_is
@@ -3753,7 +3706,6 @@ opt-ij_n__-tgt-ais-cmd-a_s
running
opt-ij_n__-tgt-ais-cmd-ai_
-echo running; false
running
opt-ij_n__-tgt-ais-cmd-ais
@@ -3808,12 +3760,10 @@ running
*** [opt-ijl___-tgt-__s-cmd-__s] Error code 1 (ignored)
opt-ijl___-tgt-__s-cmd-_i_
-echo running; false
running
*** [opt-ijl___-tgt-__s-cmd-_i_] Error code 1 (ignored)
opt-ijl___-tgt-__s-cmd-_is
-echo running; false
running
*** [opt-ijl___-tgt-__s-cmd-_is] Error code 1 (ignored)
@@ -3826,12 +3776,10 @@ running
*** [opt-ijl___-tgt-__s-cmd-a_s] Error code 1 (ignored)
opt-ijl___-tgt-__s-cmd-ai_
-echo running; false
running
*** [opt-ijl___-tgt-__s-cmd-ai_] Error code 1 (ignored)
opt-ijl___-tgt-__s-cmd-ais
-echo running; false
running
*** [opt-ijl___-tgt-__s-cmd-ais] Error code 1 (ignored)
@@ -3884,12 +3832,10 @@ running
*** [opt-ijl___-tgt-_is-cmd-__s] Error code 1 (ignored)
opt-ijl___-tgt-_is-cmd-_i_
-echo running; false
running
*** [opt-ijl___-tgt-_is-cmd-_i_] Error code 1 (ignored)
opt-ijl___-tgt-_is-cmd-_is
-echo running; false
running
*** [opt-ijl___-tgt-_is-cmd-_is] Error code 1 (ignored)
@@ -3902,12 +3848,10 @@ running
*** [opt-ijl___-tgt-_is-cmd-a_s] Error code 1 (ignored)
opt-ijl___-tgt-_is-cmd-ai_
-echo running; false
running
*** [opt-ijl___-tgt-_is-cmd-ai_] Error code 1 (ignored)
opt-ijl___-tgt-_is-cmd-ais
-echo running; false
running
*** [opt-ijl___-tgt-_is-cmd-ais] Error code 1 (ignored)
@@ -3960,12 +3904,10 @@ running
*** [opt-ijl___-tgt-a_s-cmd-__s] Error code 1 (ignored)
opt-ijl___-tgt-a_s-cmd-_i_
-echo running; false
running
*** [opt-ijl___-tgt-a_s-cmd-_i_] Error code 1 (ignored)
opt-ijl___-tgt-a_s-cmd-_is
-echo running; false
running
*** [opt-ijl___-tgt-a_s-cmd-_is] Error code 1 (ignored)
@@ -3978,12 +3920,10 @@ running
*** [opt-ijl___-tgt-a_s-cmd-a_s] Error code 1 (ignored)
opt-ijl___-tgt-a_s-cmd-ai_
-echo running; false
running
*** [opt-ijl___-tgt-a_s-cmd-ai_] Error code 1 (ignored)
opt-ijl___-tgt-a_s-cmd-ais
-echo running; false
running
*** [opt-ijl___-tgt-a_s-cmd-ais] Error code 1 (ignored)
@@ -4036,12 +3976,10 @@ running
*** [opt-ijl___-tgt-ais-cmd-__s] Error code 1 (ignored)
opt-ijl___-tgt-ais-cmd-_i_
-echo running; false
running
*** [opt-ijl___-tgt-ais-cmd-_i_] Error code 1 (ignored)
opt-ijl___-tgt-ais-cmd-_is
-echo running; false
running
*** [opt-ijl___-tgt-ais-cmd-_is] Error code 1 (ignored)
@@ -4054,12 +3992,10 @@ running
*** [opt-ijl___-tgt-ais-cmd-a_s] Error code 1 (ignored)
opt-ijl___-tgt-ais-cmd-ai_
-echo running; false
running
*** [opt-ijl___-tgt-ais-cmd-ai_] Error code 1 (ignored)
opt-ijl___-tgt-ais-cmd-ais
-echo running; false
running
*** [opt-ijl___-tgt-ais-cmd-ais] Error code 1 (ignored)
@@ -4242,11 +4178,9 @@ opt-ijln__-tgt-a_s-cmd-__s
running
opt-ijln__-tgt-a_s-cmd-_i_
-echo running; false
running
opt-ijln__-tgt-a_s-cmd-_is
-echo running; false
running
opt-ijln__-tgt-a_s-cmd-a__
@@ -4256,11 +4190,9 @@ opt-ijln__-tgt-a_s-cmd-a_s
running
opt-ijln__-tgt-a_s-cmd-ai_
-echo running; false
running
opt-ijln__-tgt-a_s-cmd-ais
-echo running; false
running
opt-ijln__-tgt-ai_-cmd-___
@@ -4302,11 +4234,9 @@ opt-ijln__-tgt-ais-cmd-__s
running
opt-ijln__-tgt-ais-cmd-_i_
-echo running; false
running
opt-ijln__-tgt-ais-cmd-_is
-echo running; false
running
opt-ijln__-tgt-ais-cmd-a__
@@ -4316,10 +4246,8 @@ opt-ijln__-tgt-ais-cmd-a_s
running
opt-ijln__-tgt-ais-cmd-ai_
-echo running; false
running
opt-ijln__-tgt-ais-cmd-ais
-echo running; false
running
exit status 0
diff --git a/contrib/bmake/unit-tests/sh-leading-at.exp b/contrib/bmake/unit-tests/sh-leading-at.exp
index 8347fda085f7..8197a0d2b71a 100644
--- a/contrib/bmake/unit-tests/sh-leading-at.exp
+++ b/contrib/bmake/unit-tests/sh-leading-at.exp
@@ -3,4 +3,5 @@ space after @
echo 'echoed'
echoed
3
+whitespace in leading part
exit status 0
diff --git a/contrib/bmake/unit-tests/sh-leading-at.mk b/contrib/bmake/unit-tests/sh-leading-at.mk
index 9f98005ec088..cff3d4da1263 100644
--- a/contrib/bmake/unit-tests/sh-leading-at.mk
+++ b/contrib/bmake/unit-tests/sh-leading-at.mk
@@ -1,4 +1,4 @@
-# $NetBSD: sh-leading-at.mk,v 1.5 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: sh-leading-at.mk,v 1.6 2023/01/19 19:55:27 rillig Exp $
#
# Tests for shell commands preceded by an '@', to suppress printing
# the command to stdout.
@@ -16,3 +16,7 @@ all:
# The leading '@' can be repeated.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@echo '3'
+
+ # Since 2023-01-17, the leading '@', '+' and '-' may contain
+ # whitespace, for compatibility with GNU make.
+ @ @ @ echo 'whitespace in leading part'
diff --git a/contrib/bmake/unit-tests/sh-leading-hyphen.exp b/contrib/bmake/unit-tests/sh-leading-hyphen.exp
index 39a9383953dd..50bcbbf9bb71 100644
--- a/contrib/bmake/unit-tests/sh-leading-hyphen.exp
+++ b/contrib/bmake/unit-tests/sh-leading-hyphen.exp
@@ -1 +1,11 @@
+true
+false
+*** Error code 1 (ignored)
+unknown-command 'needed for needshell in compat.c'
+unknown-command: not found
+*** Error code 127 (ignored)
+unknown-long-option 'needed for needshell in compat.c'
+unknown-long-option: not found
+*** Error code 127 (ignored)
+whitespace in leading part
exit status 0
diff --git a/contrib/bmake/unit-tests/sh-leading-hyphen.mk b/contrib/bmake/unit-tests/sh-leading-hyphen.mk
index d760abb9afdd..08b50a2ddc42 100644
--- a/contrib/bmake/unit-tests/sh-leading-hyphen.mk
+++ b/contrib/bmake/unit-tests/sh-leading-hyphen.mk
@@ -1,4 +1,4 @@
-# $NetBSD: sh-leading-hyphen.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: sh-leading-hyphen.mk,v 1.4 2023/01/19 19:55:27 rillig Exp $
#
# Tests for shell commands preceded by a '-', to ignore the exit status of
# the command line.
@@ -11,4 +11,19 @@
# TODO: Implementation
all:
- @:;
+ -true
+ -false
+
+ # An undefined variable expands to an empty string, without warning.
+ # This is used in practice for prefixing tool names or for DESTDIR.
+ # The '-' before 'unknown' is interpreted by make as '.IGNORE' flag.
+ ${UNDEF}-unknown-command 'needed for needshell in compat.c'
+
+ # Expanding undefined variables may lead to strange error messages
+ # when the shell interprets single-character options as commands
+ # instead.
+ ${UNDEF} --unknown-long-option 'needed for needshell in compat.c'
+
+ # Since 2023-01-17, the leading '@', '+' and '-' may contain
+ # whitespace, for compatibility with GNU make.
+ - - - @echo 'whitespace in leading part'
diff --git a/contrib/bmake/unit-tests/sh-leading-plus.exp b/contrib/bmake/unit-tests/sh-leading-plus.exp
index eb586d29f1c2..8cc26deaacb5 100644
--- a/contrib/bmake/unit-tests/sh-leading-plus.exp
+++ b/contrib/bmake/unit-tests/sh-leading-plus.exp
@@ -1,4 +1,6 @@
echo 'this command is not run'
echo 'this command is run'
this command is run
+echo 'whitespace in leading part'
+whitespace in leading part
exit status 0
diff --git a/contrib/bmake/unit-tests/sh-leading-plus.mk b/contrib/bmake/unit-tests/sh-leading-plus.mk
index ff57b4a38a7d..83e7e7a15e24 100644
--- a/contrib/bmake/unit-tests/sh-leading-plus.mk
+++ b/contrib/bmake/unit-tests/sh-leading-plus.mk
@@ -1,4 +1,4 @@
-# $NetBSD: sh-leading-plus.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $
+# $NetBSD: sh-leading-plus.mk,v 1.5 2023/01/19 19:55:27 rillig Exp $
#
# Tests for shell commands preceded by a '+', to run them even if
# the command line option -n is given.
@@ -8,3 +8,7 @@
all:
@echo 'this command is not run'
@+echo 'this command is run'
+
+ # Since 2023-01-17, the leading '@', '+' and '-' may contain
+ # whitespace, for compatibility with GNU make.
+ + + + @echo 'whitespace in leading part'
diff --git a/contrib/bmake/unit-tests/sh.mk b/contrib/bmake/unit-tests/sh.mk
index f79a4099e990..3bbedf4d678a 100644
--- a/contrib/bmake/unit-tests/sh.mk
+++ b/contrib/bmake/unit-tests/sh.mk
@@ -1,7 +1,10 @@
-# $NetBSD: sh.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: sh.mk,v 1.3 2022/01/23 16:09:38 rillig Exp $
#
# Tests for running shell commands from the targets, or from the != variable
# assignment operator or the :sh variable modifier.
+#
+# See also:
+# var-op-shell.mk for the assignment operator '!='
# TODO: Implementation
diff --git a/contrib/bmake/unit-tests/shell-sh.mk b/contrib/bmake/unit-tests/shell-sh.mk
index b3d4f18bbac9..5f7b04716ee1 100644
--- a/contrib/bmake/unit-tests/shell-sh.mk
+++ b/contrib/bmake/unit-tests/shell-sh.mk
@@ -1,9 +1,9 @@
-# $NetBSD: shell-sh.mk,v 1.1 2020/10/03 14:39:36 rillig Exp $
+# $NetBSD: shell-sh.mk,v 1.2 2023/12/24 16:48:30 sjg Exp $
#
# Tests for using a bourne shell for running the commands.
# This is the default shell, so there's nothing surprising.
-.SHELL: name="sh" path="sh"
+.SHELL: name="sh"
all:
: normal
diff --git a/contrib/bmake/unit-tests/suff-incomplete.exp b/contrib/bmake/unit-tests/suff-incomplete.exp
index 2331436d378e..acb5f0542dbe 100644
--- a/contrib/bmake/unit-tests/suff-incomplete.exp
+++ b/contrib/bmake/unit-tests/suff-incomplete.exp
@@ -1,17 +1,17 @@
-ParseReadLine (9): '.SUFFIXES:'
+Parsing line 9: .SUFFIXES:
ParseDependency(.SUFFIXES:)
Clearing all suffixes
-ParseReadLine (11): '.SUFFIXES: .a .b .c'
+Parsing line 11: .SUFFIXES: .a .b .c
ParseDependency(.SUFFIXES: .a .b .c)
Adding suffix ".a"
Adding suffix ".b"
Adding suffix ".c"
-ParseReadLine (17): '.a.b:'
+Parsing line 17: .a.b:
ParseDependency(.a.b:)
defining transformation from `.a' to `.b'
inserting ".a" (1) at end of list
inserting ".b" (2) at end of list
-ParseReadLine (21): '.a.c: ${.PREFIX}.dependency'
+Parsing line 21: .a.c: ${.PREFIX}.dependency
deleting incomplete transformation from `.a' to `.b'
ParseDependency(.a.c: ${.PREFIX}.dependency)
defining transformation from `.a' to `.c'
@@ -20,10 +20,10 @@ inserting ".c" (3) at end of list
# LinkSource: added child .a.c - ${.PREFIX}.dependency
# .a.c, unmade, type OP_DEPENDS|OP_TRANSFORM, flags none
# ${.PREFIX}.dependency, unmade, type none, flags none
-ParseReadLine (23): '.DEFAULT:'
+Parsing line 23: .DEFAULT:
transformation .a.c complete
ParseDependency(.DEFAULT:)
-ParseReadLine (24): ' : Making ${.TARGET} from ${.IMPSRC} all ${.ALLSRC} by default.'
+Parsing line 24: : Making ${.TARGET} from ${.IMPSRC} all ${.ALLSRC} by default.
transformation .DEFAULT complete
Wildcard expanding "all"...
SuffFindDeps "all"
diff --git a/contrib/bmake/unit-tests/suff-main-several.exp b/contrib/bmake/unit-tests/suff-main-several.exp
index 09fa6d63bffa..d19a392f5962 100644
--- a/contrib/bmake/unit-tests/suff-main-several.exp
+++ b/contrib/bmake/unit-tests/suff-main-several.exp
@@ -1,11 +1,11 @@
-ParseReadLine (8): '.1.2 .1.3 .1.4:'
+Parsing line 8: .1.2 .1.3 .1.4:
ParseDependency(.1.2 .1.3 .1.4:)
Setting main node to ".1.2"
-ParseReadLine (9): ' : Making ${.TARGET} from ${.IMPSRC}.'
-ParseReadLine (14): 'next-main:'
+Parsing line 9: : Making ${.TARGET} from ${.IMPSRC}.
+Parsing line 14: next-main:
ParseDependency(next-main:)
-ParseReadLine (15): ' : Making ${.TARGET}'
-ParseReadLine (19): '.SUFFIXES: .1 .2 .3 .4'
+Parsing line 15: : Making ${.TARGET}
+Parsing line 19: .SUFFIXES: .1 .2 .3 .4
ParseDependency(.SUFFIXES: .1 .2 .3 .4)
Adding suffix ".1"
Adding suffix ".2"
@@ -26,42 +26,42 @@ defining transformation from `.1' to `.4'
inserting ".1" (1) at end of list
inserting ".4" (4) at end of list
Setting main node to "next-main"
-ParseReadLine (24): '.SUFFIXES:'
+Parsing line 24: .SUFFIXES:
ParseDependency(.SUFFIXES:)
Clearing all suffixes
-ParseReadLine (32): '.SUFFIXES: .4 .3 .2 .1'
+Parsing line 32: .SUFFIXES: .4 .3 .2 .1
ParseDependency(.SUFFIXES: .4 .3 .2 .1)
Adding suffix ".4"
Adding suffix ".3"
Adding suffix ".2"
Adding suffix ".1"
-ParseReadLine (33): '.SUFFIXES:'
+Parsing line 33: .SUFFIXES:
ParseDependency(.SUFFIXES:)
Clearing all suffixes
-ParseReadLine (34): '.SUFFIXES: .1 .2 .3 .4'
+Parsing line 34: .SUFFIXES: .1 .2 .3 .4
ParseDependency(.SUFFIXES: .1 .2 .3 .4)
Adding suffix ".1"
Adding suffix ".2"
Adding suffix ".3"
Adding suffix ".4"
-ParseReadLine (35): '.SUFFIXES:'
+Parsing line 35: .SUFFIXES:
ParseDependency(.SUFFIXES:)
Clearing all suffixes
-ParseReadLine (36): '.SUFFIXES: .4 .3 .2 .1'
+Parsing line 36: .SUFFIXES: .4 .3 .2 .1
ParseDependency(.SUFFIXES: .4 .3 .2 .1)
Adding suffix ".4"
Adding suffix ".3"
Adding suffix ".2"
Adding suffix ".1"
-ParseReadLine (38): 'suff-main-several.1:'
+Parsing line 38: suff-main-several.1:
ParseDependency(suff-main-several.1:)
-ParseReadLine (39): ' : Making ${.TARGET} out of nothing.'
-ParseReadLine (40): 'next-main: suff-main-several.{2,3,4}'
+Parsing line 39: : Making ${.TARGET} out of nothing.
+Parsing line 40: next-main: suff-main-several.{2,3,4}
ParseDependency(next-main: suff-main-several.{2,3,4})
# LinkSource: added child next-main - suff-main-several.{2,3,4}
# next-main, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
# suff-main-several.{2,3,4}, unmade, type none, flags none
-ParseReadLine (42): '.MAKEFLAGS: -d0 -dg1'
+Parsing line 42: .MAKEFLAGS: -d0 -dg1
ParseDependency(.MAKEFLAGS: -d0 -dg1)
#*** Input graph:
# .1.2, unmade, type OP_TRANSFORM, flags none
@@ -81,12 +81,14 @@ ParseDependency(.MAKEFLAGS: -d0 -dg1)
#*** Global Variables:
.ALLTARGETS = .1.2 .1.3 .1.4 next-main suff-main-several.1 suff-main-several.{2,3,4}
.CURDIR = <curdir>
-.INCLUDES =
-.LIBS =
+.INCLUDES = # (empty)
+.LIBS = # (empty)
.MAKE = <details omitted>
.MAKE.DEPENDFILE = <details omitted>
.MAKE.GID = <details omitted>
+.MAKE.JOBS.C = <details omitted>
.MAKE.LEVEL = <details omitted>
+.MAKE.LEVEL.ENV = MAKELEVEL
.MAKE.MAKEFILES = <details omitted>
.MAKE.MAKEFILE_PREFERENCE = <details omitted>
.MAKE.OS = <details omitted>
@@ -94,24 +96,22 @@ ParseDependency(.MAKEFLAGS: -d0 -dg1)
.MAKE.PPID = <details omitted>
.MAKE.UID = <details omitted>
.MAKEFLAGS = -r -k -d mps -d 0 -d g1
-.MAKEOVERRIDES =
+.MAKEOVERRIDES = # (empty)
.OBJDIR = <curdir>
.PATH = . <curdir>
-.TARGETS =
+.TARGETS = # (empty)
.newline =
-
+# (ends with space)
MACHINE = <details omitted>
MACHINE_ARCH = <details omitted>
MAKE = <details omitted>
MFLAGS = -r -k -d mps -d 0 -d g1
#*** Command-line Variables:
-.MAKE.LEVEL.ENV = MAKELEVEL
#*** Directory Cache:
# 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-rebuild.exp b/contrib/bmake/unit-tests/suff-rebuild.exp
index 7ef53ae2e151..8c0979537524 100644
--- a/contrib/bmake/unit-tests/suff-rebuild.exp
+++ b/contrib/bmake/unit-tests/suff-rebuild.exp
@@ -1,36 +1,36 @@
-ParseReadLine (10): '.SUFFIXES:'
+Parsing line 10: .SUFFIXES:
ParseDependency(.SUFFIXES:)
Clearing all suffixes
-ParseReadLine (12): '.SUFFIXES: .a .b .c'
+Parsing line 12: .SUFFIXES: .a .b .c
ParseDependency(.SUFFIXES: .a .b .c)
Adding suffix ".a"
Adding suffix ".b"
Adding suffix ".c"
-ParseReadLine (14): 'suff-rebuild-example.a:'
+Parsing line 14: suff-rebuild-example.a:
ParseDependency(suff-rebuild-example.a:)
Adding "suff-rebuild-example.a" to all targets.
-ParseReadLine (15): ' : Making ${.TARGET} out of nothing.'
-ParseReadLine (17): '.a.b:'
+Parsing line 15: : Making ${.TARGET} out of nothing.
+Parsing line 17: .a.b:
ParseDependency(.a.b:)
defining transformation from `.a' to `.b'
inserting ".a" (1) at end of list
inserting ".b" (2) at end of list
-ParseReadLine (18): ' : Making ${.TARGET} from ${.IMPSRC}.'
-ParseReadLine (19): '.b.c:'
+Parsing line 18: : Making ${.TARGET} from ${.IMPSRC}.
+Parsing line 19: .b.c:
transformation .a.b complete
ParseDependency(.b.c:)
defining transformation from `.b' to `.c'
inserting ".b" (2) at end of list
inserting ".c" (3) at end of list
-ParseReadLine (20): ' : Making ${.TARGET} from ${.IMPSRC}.'
-ParseReadLine (21): '.c:'
+Parsing line 20: : Making ${.TARGET} from ${.IMPSRC}.
+Parsing line 21: .c:
transformation .b.c complete
ParseDependency(.c:)
defining transformation from `.c' to `'
inserting ".c" (3) at end of list
inserting "" (0) at end of list
-ParseReadLine (22): ' : Making ${.TARGET} from ${.IMPSRC}.'
-ParseReadLine (44): '.SUFFIXES: .c .b .a'
+Parsing line 22: : Making ${.TARGET} from ${.IMPSRC}.
+Parsing line 44: .SUFFIXES: .c .b .a
transformation .c complete
ParseDependency(.SUFFIXES: .c .b .a)
Adding ".END" to all targets.
diff --git a/contrib/bmake/unit-tests/suff-transform-debug.exp b/contrib/bmake/unit-tests/suff-transform-debug.exp
index 0634ff616d0d..2e88db58bc8c 100644
--- a/contrib/bmake/unit-tests/suff-transform-debug.exp
+++ b/contrib/bmake/unit-tests/suff-transform-debug.exp
@@ -7,12 +7,14 @@
#*** Global Variables:
.ALLTARGETS = all
.CURDIR = <curdir>
-.INCLUDES =
-.LIBS =
+.INCLUDES = # (empty)
+.LIBS = # (empty)
.MAKE = <details omitted>
.MAKE.DEPENDFILE = <details omitted>
.MAKE.GID = <details omitted>
+.MAKE.JOBS.C = <details omitted>
.MAKE.LEVEL = <details omitted>
+.MAKE.LEVEL.ENV = MAKELEVEL
.MAKE.MAKEFILES = <details omitted>
.MAKE.MAKEFILE_PREFERENCE = <details omitted>
.MAKE.OS = <details omitted>
@@ -20,24 +22,22 @@
.MAKE.PPID = <details omitted>
.MAKE.UID = <details omitted>
.MAKEFLAGS = -r -k -d g1
-.MAKEOVERRIDES =
+.MAKEOVERRIDES = # (empty)
.OBJDIR = <curdir>
.PATH = . <curdir>
-.TARGETS =
+.TARGETS = # (empty)
.newline =
-
+# (ends with space)
MACHINE = <details omitted>
MACHINE_ARCH = <details omitted>
MAKE = <details omitted>
MFLAGS = -r -k -d g1
#*** Command-line Variables:
-.MAKE.LEVEL.ENV = MAKELEVEL
#*** Directory Cache:
# 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/suff-use.exp b/contrib/bmake/unit-tests/suff-use.exp
new file mode 100644
index 000000000000..4a9374d8e156
--- /dev/null
+++ b/contrib/bmake/unit-tests/suff-use.exp
@@ -0,0 +1,7 @@
+: 'Making demo.c out of nothing'
+make: don't know how to make demo.o (continuing)
+`all' not remade because of errors.
+
+Stop.
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/suff-use.mk b/contrib/bmake/unit-tests/suff-use.mk
new file mode 100644
index 000000000000..b648545e1f5c
--- /dev/null
+++ b/contrib/bmake/unit-tests/suff-use.mk
@@ -0,0 +1,50 @@
+# $NetBSD: suff-use.mk,v 1.2 2022/02/09 21:09:24 rillig Exp $
+#
+# This test combines a .USE node with suffix rules, trying to add an
+# additional command before and after successful compilation of a .c file.
+#
+# History:
+# make-2001.11.12.21.58.18
+# | : 'Making demo.c out of nothing'
+# | make: don't know how to make demo.o. Stop
+# |
+# | make: stopped in <curdir>
+# | exit status 2
+# make-2007.10.11.21.19.28
+#
+# make-2014.08.23.15.05.40
+# | : 'Making demo.c out of nothing'
+# | : 'Compiling demo.c to demo.o'
+# | exit status 0
+# make-2014.09.05.06.57.20
+#
+# make-2014.09.07.20.55.34
+# | : 'Making demo.c out of nothing'
+# | make: don't know how to make demo.o. Stop
+# |
+# | make: stopped in <curdir>
+# | exit status 2
+# ...
+#
+# See also:
+# https://gnats.netbsd.org/20993
+
+
+.SUFFIXES: .c .o
+
+all: demo.o
+
+.c.o:
+ : 'Compiling ${.IMPSRC} to ${.TARGET}'
+
+demo.c:
+ : 'Making ${.TARGET} out of nothing'
+
+using-before: .USEBEFORE
+ : 'Before making ${.TARGET} from ${.ALLSRCS}'
+
+using-after: .USE
+ : 'After making ${.TARGET} from ${.ALLSRCS}'
+
+# expect: make: don't know how to make demo.o (continuing)
+.c.o: using-before using-after
diff --git a/contrib/bmake/unit-tests/unexport.mk b/contrib/bmake/unit-tests/unexport.mk
index 4363aaac3eee..4bcc5b21ca02 100644
--- a/contrib/bmake/unit-tests/unexport.mk
+++ b/contrib/bmake/unit-tests/unexport.mk
@@ -1,4 +1,4 @@
-# $NetBSD: unexport.mk,v 1.5 2020/10/24 08:50:17 rillig Exp $
+# $NetBSD: unexport.mk,v 1.6 2023/10/19 18:24:33 rillig Exp $
# pick up a bunch of exported vars
FILTER_CMD= grep ^UT_
@@ -10,7 +10,7 @@ UT_TEST= unexport
# Until 2020-08-08, Var_UnExport had special handling for '\n', that code
# was not reachable though. At that point, backslash-newline has already
-# been replaced with a simple space, and variables are not yet expanded.
+# been replaced with a simple space, and expressions are not yet expanded.
UT_BEFORE_NL= before
UT_AFTER_NL= after
.export UT_BEFORE_NL UT_AFTER_NL
diff --git a/contrib/bmake/unit-tests/var-class-cmdline.exp b/contrib/bmake/unit-tests/var-class-cmdline.exp
deleted file mode 100644
index 6df2155ca7eb..000000000000
--- a/contrib/bmake/unit-tests/var-class-cmdline.exp
+++ /dev/null
@@ -1,4 +0,0 @@
-make: "var-class-cmdline.mk" line 67: global
-make: "var-class-cmdline.mk" line 76: makeflags
-makeflags
-exit status 0
diff --git a/contrib/bmake/unit-tests/var-class-global.mk b/contrib/bmake/unit-tests/var-class-global.mk
deleted file mode 100644
index 81345ffda463..000000000000
--- a/contrib/bmake/unit-tests/var-class-global.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-# $NetBSD: var-class-global.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
-#
-# Tests for global variables, which are the most common variables.
-
-# TODO: Implementation
-
-all:
- @:;
diff --git a/contrib/bmake/unit-tests/var-class-local-legacy.mk b/contrib/bmake/unit-tests/var-class-local-legacy.mk
deleted file mode 100644
index bfd9733fd42b..000000000000
--- a/contrib/bmake/unit-tests/var-class-local-legacy.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-# $NetBSD: var-class-local-legacy.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
-#
-# Tests for legacy target-local variables, such as ${<F} or ${@D}.
-
-# TODO: Implementation
-
-all:
- @:;
diff --git a/contrib/bmake/unit-tests/var-class-local.exp b/contrib/bmake/unit-tests/var-class-local.exp
deleted file mode 100644
index db85b47cae06..000000000000
--- a/contrib/bmake/unit-tests/var-class-local.exp
+++ /dev/null
@@ -1,5 +0,0 @@
-: Making var-class-local.c out of nothing.
-: Making var-class-local.o from var-class-local.c.
-: Making basename "var-class-local.o" in "." from "var-class-local.c" in ".".
-: all overwritten
-exit status 0
diff --git a/contrib/bmake/unit-tests/var-class-local.mk b/contrib/bmake/unit-tests/var-class-local.mk
deleted file mode 100644
index f9d56e539ff0..000000000000
--- a/contrib/bmake/unit-tests/var-class-local.mk
+++ /dev/null
@@ -1,45 +0,0 @@
-# $NetBSD: var-class-local.mk,v 1.5 2020/11/05 18:08:39 rillig Exp $
-#
-# Tests for target-local variables, such as ${.TARGET} or $@.
-
-# TODO: Implementation
-
-# Ensure that the name of the variable is exactly the given one.
-# The variable "@" is an alias for ".TARGET", so the implementation might
-# canonicalize these aliases at some point, and that might be surprising.
-# This aliasing happens for single-character variable names like $@ or $<
-# (see VarFind, CanonicalVarname), but not for braced or parenthesized
-# expressions like ${@}, ${.TARGET} ${VAR:Mpattern} (see Var_Parse,
-# ParseVarname).
-.if ${@:L} != "@"
-. error
-.endif
-.if ${.TARGET:L} != ".TARGET"
-. error
-.endif
-.if ${@F:L} != "@F"
-. error
-.endif
-.if ${@D:L} != "@D"
-. error
-.endif
-
-all:
-
-.SUFFIXES: .c .o
-
-var-class-local.c:
- : Making ${.TARGET} out of nothing.
-
-.c.o:
- : Making ${.TARGET} from ${.IMPSRC}.
-
- # The local variables @F, @D, <F, <D are legacy forms.
- # See the manual page for details.
- : Making basename "${@F}" in "${@D}" from "${<F}" in "${<D}".
-
-all: var-class-local.o
- # The ::= modifier overwrites the .TARGET variable in the node
- # 'all', not in the global scope. This can be seen with the -dv
- # option, looking for "all:@ = overwritten".
- : ${.TARGET} ${.TARGET::=overwritten}${.TARGET}
diff --git a/contrib/bmake/unit-tests/var-class.mk b/contrib/bmake/unit-tests/var-class.mk
deleted file mode 100644
index b20fca565e16..000000000000
--- a/contrib/bmake/unit-tests/var-class.mk
+++ /dev/null
@@ -1,9 +0,0 @@
-# $NetBSD: var-class.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
-#
-# Tests for the different variable classes (local, command-line, global,
-# environment), and which of them takes precedence over the others.
-
-# TODO: Implementation
-
-all:
- @:;
diff --git a/contrib/bmake/unit-tests/var-eval-short.exp b/contrib/bmake/unit-tests/var-eval-short.exp
index ae0aff7d7c2c..6b42c27e22bc 100644
--- a/contrib/bmake/unit-tests/var-eval-short.exp
+++ b/contrib/bmake/unit-tests/var-eval-short.exp
@@ -1,26 +1,28 @@
-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 46: while evaluating "${:Uword:@${FAIL}@expr@}": In the :@ modifier, the variable name "${FAIL}" must not contain a dollar
+make: "var-eval-short.mk" line 46: Malformed conditional (0 && ${:Uword:@${FAIL}@expr@})
+Parsing line 159: .if 0 && ${0:?${FAIL}then:${FAIL}else}
CondParser_Eval: 0 && ${0:?${FAIL}then:${FAIL}else}
Var_Parse: ${0:?${FAIL}then:${FAIL}else} (parse-only)
Parsing modifier ${0:?...}
+Var_Parse: ${FAIL}then:${FAIL}else} (parse-only)
Modifier part: "${FAIL}then"
+Var_Parse: ${FAIL}else} (parse-only)
Modifier part: "${FAIL}else"
Result of ${0:?${FAIL}then:${FAIL}else} is "" (parse-only, defined)
-ParseReadLine (158): 'DEFINED= defined'
+Parsing line 167: DEFINED= defined
Global: DEFINED = defined
+Parsing line 168: .if 0 && ${DEFINED:L:?${FAIL}then:${FAIL}else}
CondParser_Eval: 0 && ${DEFINED:L:?${FAIL}then:${FAIL}else}
Var_Parse: ${DEFINED:L:?${FAIL}then:${FAIL}else} (parse-only)
Parsing modifier ${DEFINED:L}
Result of ${DEFINED:L} is "defined" (parse-only, regular)
Parsing modifier ${DEFINED:?...}
+Var_Parse: ${FAIL}then:${FAIL}else} (parse-only)
Modifier part: "${FAIL}then"
+Var_Parse: ${FAIL}else} (parse-only)
Modifier part: "${FAIL}else"
Result of ${DEFINED:?${FAIL}then:${FAIL}else} is "defined" (parse-only, regular)
-ParseReadLine (161): '.MAKEFLAGS: -d0'
+Parsing line 170: .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..5a42335a4474 100644
--- a/contrib/bmake/unit-tests/var-eval-short.mk
+++ b/contrib/bmake/unit-tests/var-eval-short.mk
@@ -1,12 +1,13 @@
-# $NetBSD: var-eval-short.mk,v 1.5 2021/04/04 13:35:26 rillig Exp $
+# $NetBSD: var-eval-short.mk,v 1.12 2024/04/20 10:18:55 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
-# should only parse the modifier but not actually evaluate it.
+# necessary computations. If the result of the expression is irrelevant,
+# the modifier should only be parsed. The modifier should not be evaluated,
+# but if it is evaluated for simplicity of the code (such as ':ts'), it must
+# not have any observable side effects.
#
# See also:
# var.c, the comment starting with 'The ApplyModifier functions'
-# ApplyModifier, for the order of the modifiers
# ParseModifierPart, for evaluating nested expressions
# cond-short.mk
@@ -17,6 +18,8 @@ FAIL= ${:!echo unexpected 1>&2!}
# is ignored as well. To do that, it is necessary to step through the code of
# each modifier.
+# TODO: Test the modifiers in the same order as they occur in ApplyModifier.
+
.if 0 && ${FAIL}
.endif
@@ -38,19 +41,21 @@ FAIL= ${:!echo unexpected 1>&2!}
# after the loop, when undefining the temporary global loop variable.
# Since var.c 1.907 from 2021-04-04, a '$' is no longer allowed in the
# variable name.
+# expect+2: while evaluating "${:Uword:@${FAIL}@expr@}": In the :@ modifier, the variable name "${FAIL}" must not contain a dollar
+# expect+1: Malformed conditional (0 && ${:Uword:@${FAIL}@expr@})
.if 0 && ${:Uword:@${FAIL}@expr@}
.endif
.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 +63,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
@@ -74,8 +81,9 @@ DEFINED= # defined
.if 0 && ${:Uword:E}
.endif
-# As of 2021-03-14, the error 'Invalid time value: ${FAIL}}' is ok since
-# ':gmtime' does not expand its argument.
+# Before var.c 1.1050 from 2023-05-09, the ':gmtime' modifier produced the
+# error message 'Invalid time value: ${FAIL}}' since it did not expand its
+# argument.
.if 0 && ${:Uword:gmtime=${FAIL}}
.endif
@@ -88,8 +96,9 @@ DEFINED= # defined
.if 0 && ${value:L}
.endif
-# As of 2021-03-14, the error 'Invalid time value: ${FAIL}}' is ok since
-# ':localtime' does not expand its argument.
+# Before var.c 1.1050 from 2023-05-09, the ':localtime' modifier produced the
+# error message 'Invalid time value: ${FAIL}}' since it did not expand its
+# argument.
.if 0 && ${:Uword:localtime=${FAIL}}
.endif
diff --git a/contrib/bmake/unit-tests/var-op-append.mk b/contrib/bmake/unit-tests/var-op-append.mk
index 420ee376b75d..e16b89139cc1 100644
--- a/contrib/bmake/unit-tests/var-op-append.mk
+++ b/contrib/bmake/unit-tests/var-op-append.mk
@@ -1,7 +1,20 @@
-# $NetBSD: var-op-append.mk,v 1.9 2021/04/04 10:13:09 rillig Exp $
+# $NetBSD: var-op-append.mk,v 1.12 2023/11/02 05:46:26 rillig Exp $
#
-# Tests for the += variable assignment operator, which appends to a variable,
-# creating it if necessary.
+# Tests for the '+=' variable assignment operator, which appends to a
+# variable, creating it if necessary.
+#
+# See also
+# var-op.mk
+#
+# Standards
+# The '+=' variable assignment operator is planned to be added in
+# POSIX.1-202x.
+#
+# This implementation does not support the immediate-expansion macros
+# specified in POSIX.1-202x. All variables are delayed-expansion.
+#
+# History
+# The '+=' variable assignment operator was added before 1993-03-21.
# Appending to an undefined variable is possible.
# The variable is created, and no extra space is added before the value.
@@ -26,7 +39,7 @@ VAR+= # empty
# '+=' assignment operator. As far as possible, the '+' is interpreted as
# part of the assignment operator.
#
-# See Parse_Var
+# See Parse_Var, AdjustVarassignOp.
C++= value
.if ${C+} != "value" || defined(C++)
. error
@@ -43,4 +56,33 @@ VAR.${:U\$\$\$\$\$\$\$\$}+= dollars
. error
.endif
+
+# Appending to an environment variable in the global scope creates a global
+# variable of the same name, taking its initial value from the environment
+# variable. After the assignment, the environment variable is left as-is,
+# the value of the global variable is not synced back to the environment
+# variable.
+export ENV_PLUS_GLOBAL=from-env-value
+ENV_PLUS_GLOBAL+= appended-value
+.if ${ENV_PLUS_GLOBAL} != "from-env-value appended-value"
+. error
+.endif
+EXPORTED!= echo "$$ENV_PLUS_GLOBAL"
+.if ${EXPORTED} != "from-env-value"
+. error
+.endif
+
+# Appending to an environment variable in the command line scope ignores the
+# environment variable.
+export ENV_PLUS_COMMAND=from-env-value
+.MAKEFLAGS: ENV_PLUS_COMMAND+=appended-command
+.if ${ENV_PLUS_COMMAND} != "appended-command"
+. error ${ENV_PLUS_COMMAND}
+.endif
+EXPORTED!= echo "$$ENV_PLUS_GLOBAL"
+.if ${EXPORTED} != "from-env-value"
+. error
+.endif
+
+
all:
diff --git a/contrib/bmake/unit-tests/var-op-assign.exp b/contrib/bmake/unit-tests/var-op-assign.exp
index 73e580403d78..5fad47ca67c4 100644
--- a/contrib/bmake/unit-tests/var-op-assign.exp
+++ b/contrib/bmake/unit-tests/var-op-assign.exp
@@ -1,6 +1,6 @@
this will be evaluated later
-make: "var-op-assign.mk" line 59: Invalid line type
-make: "var-op-assign.mk" line 93: Parsing still continues until here.
+make: "var-op-assign.mk" line 60: Invalid line 'VARIABLE NAME= variable value'
+make: "var-op-assign.mk" line 95: Parsing still continues until here.
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/var-op-assign.mk b/contrib/bmake/unit-tests/var-op-assign.mk
index 18ecf8d0d5ed..a900c28a918d 100644
--- a/contrib/bmake/unit-tests/var-op-assign.mk
+++ b/contrib/bmake/unit-tests/var-op-assign.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-op-assign.mk,v 1.8 2021/03/15 19:15:04 rillig Exp $
+# $NetBSD: var-op-assign.mk,v 1.11 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the = variable assignment operator, which overwrites an existing
# variable or creates it.
@@ -56,6 +56,7 @@ VAR= ${:! echo 'this will be evaluated later' 1>&2 !}
# In a variable assignment, the variable name must consist of a single word.
# The following line therefore generates a parse error.
+# expect+1: Invalid line 'VARIABLE NAME= variable value'
VARIABLE NAME= variable value
# But if the whitespace appears inside parentheses or braces, everything is
@@ -65,7 +66,7 @@ VARIABLE NAME= variable value
# neither contain parentheses nor braces. This is only a side-effect from
# the implementation of the parser, which cheats when parsing a variable
# name. It only counts parentheses and braces instead of properly parsing
-# nested variable expressions such as VAR.${param}.
+# nested expressions such as VAR.${param}.
#
VAR(spaces in parentheses)= ()
VAR{spaces in braces}= {}
@@ -90,6 +91,7 @@ VARNAME_BRACES= VAR{spaces in braces}
# unexpected variable values.
#
# Therefore, just output an info message.
+# expect+1: Parsing still continues until here.
.info Parsing still continues until here.
all:
diff --git a/contrib/bmake/unit-tests/var-op-default.mk b/contrib/bmake/unit-tests/var-op-default.mk
index ca4fbcc27c88..9d07ddf39e41 100644
--- a/contrib/bmake/unit-tests/var-op-default.mk
+++ b/contrib/bmake/unit-tests/var-op-default.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-op-default.mk,v 1.3 2020/12/07 21:35:43 rillig Exp $
+# $NetBSD: var-op-default.mk,v 1.5 2023/11/19 22:32:44 rillig Exp $
#
# Tests for the ?= variable assignment operator, which only assigns
# if the variable is still undefined.
@@ -45,7 +45,7 @@ i?= default
# and 'VAR.${param}' expand to 'VAR.param', and the second '?=' assignment
# has no effect.
#
-# Since 2000.05.11.07.43.42 it has been possible to use nested variable
+# Since 2000.05.11.07.43.42 it has been possible to use nested
# expressions in variable names, which made make much more versatile.
# On 2008.03.31.00.12.21, this particular case of the '?=' operator has been
# fixed. Before, the '?=' operator had not expanded the variable name
@@ -61,8 +61,8 @@ VAR.${:Uparam}?= not used
# Now demonstrate that the variable name is indeed expanded exactly once.
# This is tricky to measure correctly since there are many inconsistencies
-# in and around the code that expands variable expressions in the various
-# places where variable expressions can occur. If in doubt, enable the
+# in and around the code that expands expressions in the various
+# places where expressions can occur. If in doubt, enable the
# following debug flags to see what happens:
#.MAKEFLAGS: -dcpv
EXPAND_NAME= EXPAND.$$$$ # The full variable name is EXPAND.$$
diff --git a/contrib/bmake/unit-tests/var-op-expand.exp b/contrib/bmake/unit-tests/var-op-expand.exp
index 39a9383953dd..0eafc9ae4c39 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 274: while evaluating variable "indirect": while evaluating variable "later": Unknown modifier "s,value,replaced,"
+make: "var-op-expand.mk" line 278: warning: XXX Neither branch should be taken.
+make: "var-op-expand.mk" line 283: while evaluating variable "indirect": while evaluating variable "later": Unknown modifier "s,value,replaced,"
+make: "var-op-expand.mk" line 285: 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..fb1e6d2c390f 100644
--- a/contrib/bmake/unit-tests/var-op-expand.mk
+++ b/contrib/bmake/unit-tests/var-op-expand.mk
@@ -1,9 +1,15 @@
-# $NetBSD: var-op-expand.mk,v 1.11 2021/01/01 23:07:48 sjg Exp $
+# $NetBSD: var-op-expand.mk,v 1.20 2024/04/20 10:18:55 rillig Exp $
#
# Tests for the := variable assignment operator, which expands its
# right-hand side.
+#
+# See also:
+# varname-dot-make-save_dollars.mk
-.MAKE.SAVE_DOLLARS:= yes
+# 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
# operator has the same effect as the '=' assignment operator.
@@ -14,9 +20,9 @@ VAR:= value
# When a ':=' assignment is performed, its right-hand side is evaluated and
# expanded as far as possible. Contrary to other situations, '$$' and
-# variable expressions based on undefined variables are preserved though.
+# expressions based on undefined variables are preserved though.
#
-# Whether a variable expression is undefined or not is determined at the end
+# Whether an expression is undefined or not is determined at the end
# of evaluating the expression. The consequence is that ${:Ufallback} expands
# to "fallback"; initially this expression is undefined since it is based on
# the variable named "", which is guaranteed to be never defined, but at the
@@ -31,7 +37,7 @@ VAR:= $$ $$$$ $$$$$$$$
.endif
-# reference to a variable containing a literal dollar sign
+# reference to a variable containing literal dollar signs
REF= $$ $$$$ $$$$$$$$
VAR:= ${REF}
REF= too late
@@ -43,6 +49,9 @@ REF= too late
# reference to an undefined variable
.undef UNDEF
VAR:= <${UNDEF}>
+.if ${VAR} != "<>"
+. error
+.endif
UNDEF= after
.if ${VAR} != "<after>"
. error
@@ -62,6 +71,9 @@ REF= too late
# expression with an indirect modifier referring to an undefined variable
.undef UNDEF
VAR:= ${:${UNDEF}}
+.if ${VAR} != ""
+. error
+.endif
UNDEF= Uwas undefined
.if ${VAR} != "was undefined"
. error
@@ -93,6 +105,9 @@ UNDEF= Uwas undefined
REF2= <${REF3}>
REF= ${REF2}
VAR:= ${REF}
+.if ${VAR} != "<>"
+. error
+.endif
REF3= too late
.if ${VAR} != "<too late>"
. error
@@ -174,5 +189,104 @@ 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: while evaluating variable "indirect": while evaluating variable "later": Unknown modifier "s,value,replaced,"
+.if ${indirect} != " ok "
+. error
+.else
+# expect+1: warning: XXX Neither branch should be taken.
+. warning XXX Neither branch should be taken.
+.endif
+LATER= uppercase-value
+later= lowercase-value
+# expect+1: while evaluating variable "indirect": while evaluating variable "later": Unknown modifier "s,value,replaced,"
+.if ${indirect} != "uppercase-replaced ok uppercase-sysv"
+# expect+1: warning: XXX Neither branch should be taken.
+. warning XXX Neither branch should be taken.
+.else
+. error
+.endif
+
+
all:
@:;
diff --git a/contrib/bmake/unit-tests/var-op-shell.exp b/contrib/bmake/unit-tests/var-op-shell.exp
index 890bfa43c38e..0837cd4f832e 100644
--- a/contrib/bmake/unit-tests/var-op-shell.exp
+++ b/contrib/bmake/unit-tests/var-op-shell.exp
@@ -1,7 +1,11 @@
-make: "var-op-shell.mk" line 28: warning: "echo "failed"; false" returned non-zero status
-make: "var-op-shell.mk" line 34: warning: "false" returned non-zero status
-make: "var-op-shell.mk" line 56: warning: "kill $$" exited on a signal
+make: "var-op-shell.mk" line 32: warning: "echo "failed"; false" returned non-zero status
+make: "var-op-shell.mk" line 39: warning: "false" returned non-zero status
+make: "var-op-shell.mk" line 62: warning: "kill $$" exited on a signal
/bin/no/such/command: not found
-make: "var-op-shell.mk" line 62: warning: "/bin/no/such/command" returned non-zero status
+make: "var-op-shell.mk" line 69: warning: "/bin/no/such/command" returned non-zero status
stderr
+Capturing the output of command "echo '$$$$'"
+Global: OUTPUT = $$$$
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
exit status 0
diff --git a/contrib/bmake/unit-tests/var-op-shell.mk b/contrib/bmake/unit-tests/var-op-shell.mk
index 0fdc54fc6041..4441efaf4a90 100644
--- a/contrib/bmake/unit-tests/var-op-shell.mk
+++ b/contrib/bmake/unit-tests/var-op-shell.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-op-shell.mk,v 1.4 2021/02/06 04:55:08 sjg Exp $
+# $NetBSD: var-op-shell.mk,v 1.8 2024/01/05 23:36:45 rillig Exp $
#
# Tests for the != variable assignment operator, which runs its right-hand
# side through the shell.
@@ -15,7 +15,7 @@ OUTPUT!= echo "success"'ful'
# an empty output produced the error message "Couldn't read shell's output
# for \"%s\"".
#
-# The error message is still there but reserved for technical errors.
+# The error message is still in Cmd_Exec but reserved for technical errors.
# It may be possible to trigger the error message by killing the shell after
# reading part of its output.
OUTPUT!= true
@@ -24,13 +24,18 @@ OUTPUT!= true
.endif
# The output of a shell command that failed is processed nevertheless.
-# TODO: Make this an error in lint mode.
+# Unlike the other places that run external commands (expression modifier
+# '::!=', expression modifier ':!...!'), a failed command generates only a
+# warning, not an "error". These "errors" are ignored in default mode, for
+# compatibility, but not in lint mode (-dL).
+# expect+1: warning: "echo "failed"; false" returned non-zero status
OUTPUT!= echo "failed"; false
.if ${OUTPUT} != "failed"
. error
.endif
# A command with empty output may fail as well.
+# expect+1: warning: "false" returned non-zero status
OUTPUT!= false
.if ${OUTPUT} != ""
. error
@@ -53,12 +58,14 @@ OUTPUT!= echo "before"; false; echo "after"
# This should result in a warning about "exited on a signal".
# This used to be kill -14 (SIGALRM), but that stopped working on
# Darwin18 after recent update.
+# expect+1: warning: "kill $$" exited on a signal
OUTPUT!= kill $$$$
.if ${OUTPUT} != ""
. error
.endif
# A nonexistent command produces a non-zero exit status.
+# expect+1: warning: "/bin/no/such/command" returned non-zero status
OUTPUT!= /bin/no/such/command
.if ${OUTPUT} != ""
. error
@@ -78,4 +85,28 @@ OUTPUT!= echo '$$$$$$$$'
. error
.endif
+
+# As a debugging aid, log the exact command that is run via the shell.
+.MAKEFLAGS: -dv
+OUTPUT!= echo '$$$$$$$$'
+.MAKEFLAGS: -d0
+
+
+# Since main.c 1.607 from 2024-01-05, long shell commands are not run directly
+# via '$shell -c $command', they are first written to a temporary file that is
+# then fed to the shell via '$shell $tmpfile'.
+OUTPUT_SHORT!= echo "$$0"
+OUTPUT_LONG!= echo "$$0" || : ${:U:range=1000}
+# When running '$shell -c $command', '$0' in the shell evaluates to the name
+# of the shell.
+.if ${OUTPUT_SHORT} != ${.SHELL:T}
+. error
+.endif
+# When running '$shell $tmpfile', '$0' in the shell evaluates to the name of
+# the temporary file.
+.if !${OUTPUT_LONG:M*/make*}
+. error
+.endif
+
+
all:
diff --git a/contrib/bmake/unit-tests/var-op-sunsh.mk b/contrib/bmake/unit-tests/var-op-sunsh.mk
index 0d15b8c88b92..520cedc93515 100644
--- a/contrib/bmake/unit-tests/var-op-sunsh.mk
+++ b/contrib/bmake/unit-tests/var-op-sunsh.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-op-sunsh.mk,v 1.8 2021/04/04 10:13:09 rillig Exp $
+# $NetBSD: var-op-sunsh.mk,v 1.10 2022/02/09 21:09:24 rillig Exp $
#
# Tests for the :sh= variable assignment operator, which runs its right-hand
# side through the shell. It is a seldom-used alternative to the !=
@@ -24,9 +24,12 @@ VAR :sh = echo colon-sh-spaced
# This was neither documented by NetBSD make nor by Solaris make and was
# an implementation error.
#
-# Since 2020-10-04, this is a normal variable assignment using the '='
-# assignment operator.
+# Since 2020-10-04, this is a normal variable assignment to the variable named
+# 'VAR:shell', using the '=' assignment operator.
VAR:shell= echo colon-shell
+# The variable name needs to be generated using a ${:U...} expression because
+# it is not possible to express the ':' as part of a literal variable name,
+# see ParseVarname.
.if ${${:UVAR\:shell}} != "echo colon-shell"
. error
.endif
@@ -95,30 +98,52 @@ VAR :sh :sh :sh :sh= echo multiple
# expanding nested expressions, the token ' :sh' can be used to add arbitrary
# text between the variable name and the assignment operator, it just has to
# be enclosed in braces or parentheses.
+#
+# Since the text to the left of the assignment operator '=' does not end with
+# ':sh', the effective assignment operator becomes '=', not '!='.
VAR :sh(Put a comment here)= comment in parentheses
.if ${VAR} != "comment in parentheses"
. error
.endif
# The unintended comment can include multiple levels of nested braces and
-# parentheses, they don't even need to be balanced since they are only
-# counted by Parse_IsVar and ignored by Parse_Var.
+# parentheses. Braces and parentheses are interchangeable, that is, a '(' can
+# be closed by either ')' or '}'. These braces and parentheses are only
+# counted by Parse_IsVar, in particular Parse_Var doesn't see them.
VAR :sh{Put}((((a}{comment}}}}{here}= comment in braces
.if ${VAR} != "comment in braces"
. error
.endif
-# Syntactically, the ':sh' modifier can be combined with the '+=' assignment
-# operator. In such a case the ':sh' modifier is silently ignored.
+# The assignment modifier ':sh' can be combined with the assignment operator
+# '+='. In such a case the ':sh' is silently ignored, and the effective
+# assignment operator is '+='.
#
-# XXX: This combination should not be allowed at all.
+# XXX: This combination should not be allowed at all, as it is confusing.
VAR= one
VAR :sh += echo two
.if ${VAR} != "one echo two"
. error ${VAR}
.endif
-# TODO: test VAR:sh!=command
+# The assignment modifier ':sh' can be combined with the assignment operator
+# '!='. In such a case the ':sh' is silently ignored, and the effective
+# assignment operator is '!=', just like with '+=' or the other compound
+# assignment operators.
+#
+# XXX: This combination should not be allowed at all, as it is confusing.
+VAR :sh != echo echo echo echo spaces-around
+.if ${VAR} != "echo echo echo spaces-around"
+. error ${VAR}
+.endif
+
+# If there is no space between the variable name and the assignment modifier
+# ':sh', the ':sh' becomes part of the variable name, as the parser only
+# expects a single assignment modifier to the left of the '=', which in this
+# case is the '!'.
+VAR:sh != echo echo echo echo space-after
+.if ${${:UVAR\:sh}} != "echo echo echo space-after"
+. error ${${:UVAR\:sh}}
+.endif
-all:
- @:;
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/var-readonly.exp b/contrib/bmake/unit-tests/var-readonly.exp
new file mode 100644
index 000000000000..ae266753ee71
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-readonly.exp
@@ -0,0 +1,4 @@
+Global: ignoring delete 'N' as it is read-only
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
+exit status 0
diff --git a/contrib/bmake/unit-tests/var-readonly.mk b/contrib/bmake/unit-tests/var-readonly.mk
new file mode 100644
index 000000000000..e9ff6f38819f
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-readonly.mk
@@ -0,0 +1,27 @@
+# $NetBSD: var-readonly.mk,v 1.4 2023/12/20 08:42:10 rillig Exp $
+
+# the answer
+N = 42
+.READONLY: N
+# this should be ignored
+N = 666
+.if ${N} != 42
+.error N ($N) should be 42
+.endif
+
+# undef should fail
+.MAKEFLAGS: -dv
+.undef N
+.ifndef N
+.error N should not be undef'd
+.endif
+.MAKEFLAGS: -d0
+
+.NOREADONLY: N
+# now we can change it
+N = 69
+.if ${N} == 42
+.error N should not be 42
+.endif
+
+all:
diff --git a/contrib/bmake/unit-tests/var-recursive.exp b/contrib/bmake/unit-tests/var-recursive.exp
index 9739d8bcca13..5415dc93a53d 100644
--- a/contrib/bmake/unit-tests/var-recursive.exp
+++ b/contrib/bmake/unit-tests/var-recursive.exp
@@ -1,12 +1,19 @@
-make: "var-recursive.mk" line 20: still there
-Variable DIRECT is recursive.
+make: "var-recursive.mk" line 21: still there
+make: Variable DIRECT is recursive.
+ in var-recursive.mk:22
make: stopped in unit-tests
-Variable INDIRECT1 is recursive.
+make: Variable INDIRECT1 is recursive.
+ in var-recursive.mk:29
make: stopped in unit-tests
-make: "var-recursive.mk" line 35: ok
-Variable V is recursive.
+make: "var-recursive.mk" line 37: ok
+make: Variable V is recursive.
+ in var-recursive.mk:45
+
+make: stopped in unit-tests
+: OK
+In a command near "var-recursive.mk" line 57: make[1]: Variable VAR is recursive.
make: stopped in unit-tests
exit status 0
diff --git a/contrib/bmake/unit-tests/var-recursive.mk b/contrib/bmake/unit-tests/var-recursive.mk
index da1fb696d655..72231656673c 100644
--- a/contrib/bmake/unit-tests/var-recursive.mk
+++ b/contrib/bmake/unit-tests/var-recursive.mk
@@ -1,9 +1,9 @@
-# $NetBSD: var-recursive.mk,v 1.2 2020/10/31 13:45:00 rillig Exp $
+# $NetBSD: var-recursive.mk,v 1.6 2023/11/19 21:47:52 rillig Exp $
#
-# Tests for variable expressions that refer to themselves and thus
+# Tests for expressions that refer to themselves and thus
# cannot be evaluated.
-TESTS= direct indirect conditional short
+TESTS= direct indirect conditional short target
# Since make exits immediately when it detects a recursive expression,
# the actual tests are run in sub-makes.
@@ -17,6 +17,7 @@ all:
.elif ${TEST} == direct
DIRECT= ${DIRECT} # Defining a recursive variable is not yet an error.
+# expect+1: still there
. info still there # Therefore this line is printed.
. info ${DIRECT} # But expanding the variable is an error.
@@ -32,6 +33,7 @@ INDIRECT2= ${INDIRECT1}
# The variable refers to itself, but only in the branch of a condition that
# is never satisfied and is thus not evaluated.
CONDITIONAL= ${1:?ok:${CONDITIONAL}}
+# expect+1: ok
. info ${CONDITIONAL}
.elif ${TEST} == short
@@ -42,6 +44,18 @@ CONDITIONAL= ${1:?ok:${CONDITIONAL}}
V= $V
. info $V
+.elif ${TEST} == target
+
+# If a recursive variable is accessed in a command of a target, the makefiles
+# are not parsed anymore, so there is no location information from the
+# .includes and .for directives. In such a case, use the location of the last
+# command of the target to provide at least a hint to the location.
+VAR= ${VAR}
+target:
+ : OK
+ : ${VAR}
+ : OK
+
.else
. error Unknown test "${TEST}"
.endif
diff --git a/contrib/bmake/unit-tests/var-scope-cmdline.exp b/contrib/bmake/unit-tests/var-scope-cmdline.exp
new file mode 100644
index 000000000000..c663a9069a9f
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-scope-cmdline.exp
@@ -0,0 +1,4 @@
+make: "var-scope-cmdline.mk" line 72: global
+make: "var-scope-cmdline.mk" line 82: makeflags
+makeflags
+exit status 0
diff --git a/contrib/bmake/unit-tests/var-class-cmdline.mk b/contrib/bmake/unit-tests/var-scope-cmdline.mk
index 679e051bb242..5c0f246a0a22 100644
--- a/contrib/bmake/unit-tests/var-class-cmdline.mk
+++ b/contrib/bmake/unit-tests/var-scope-cmdline.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-class-cmdline.mk,v 1.5 2021/02/23 21:59:31 rillig Exp $
+# $NetBSD: var-scope-cmdline.mk,v 1.4 2023/11/19 21:47:52 rillig Exp $
#
# Tests for variables specified on the command line.
#
@@ -55,15 +55,20 @@
# temporary loop variable after finishing the loop. It was probably not
# intended back then that a side effect of this seemingly simple change was
# that both global and cmdline variables could now be undefined at will as a
-# side effect of evaluating a variable expression. As of 2021-02-23, this is
+# side effect of evaluating an expression. As of 2021-02-23, this is
# still possible.
#
# Most cmdline variables are set at the very beginning, when parsing the
# command line arguments. Using the special target '.MAKEFLAGS', it is
# possible to set cmdline variables at any later time.
+#
+# See also:
+# varcmd.mk
+# varname-makeflags.mk
# A normal global variable, without any cmdline variable nearby.
VAR= global
+# expect+1: global
.info ${VAR}
# The global variable is "overridden" by simply deleting it and then
@@ -73,6 +78,7 @@ VAR= global
#
# See varmod-loop.mk for a non-obvious way to undefine a cmdline variable.
.MAKEFLAGS: VAR=makeflags
+# expect+1: makeflags
.info ${VAR}
# If Var_SetWithFlags should ever forget to delete the global variable,
diff --git a/contrib/bmake/unit-tests/var-class-env.exp b/contrib/bmake/unit-tests/var-scope-env.exp
index 39a9383953dd..39a9383953dd 100644
--- a/contrib/bmake/unit-tests/var-class-env.exp
+++ b/contrib/bmake/unit-tests/var-scope-env.exp
diff --git a/contrib/bmake/unit-tests/var-class-env.mk b/contrib/bmake/unit-tests/var-scope-env.mk
index 6e6b4891d3fd..d58ad7c6f2e2 100644
--- a/contrib/bmake/unit-tests/var-class-env.mk
+++ b/contrib/bmake/unit-tests/var-scope-env.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-class-env.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: var-scope-env.mk,v 1.1 2022/01/23 16:25:54 rillig Exp $
#
# Tests for variables specified in the process environment.
diff --git a/contrib/bmake/unit-tests/var-class-global.exp b/contrib/bmake/unit-tests/var-scope-global.exp
index 39a9383953dd..39a9383953dd 100644
--- a/contrib/bmake/unit-tests/var-class-global.exp
+++ b/contrib/bmake/unit-tests/var-scope-global.exp
diff --git a/contrib/bmake/unit-tests/var-scope-global.mk b/contrib/bmake/unit-tests/var-scope-global.mk
new file mode 100644
index 000000000000..02ed8fe701c0
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-scope-global.mk
@@ -0,0 +1,18 @@
+# $NetBSD: var-scope-global.mk,v 1.1 2022/01/23 16:25:54 rillig Exp $
+#
+# Tests for global variables, which are the most common variables.
+
+# Global variables can be assigned and appended to.
+GLOBAL= value
+GLOBAL+= addition
+.if ${GLOBAL} != "value addition"
+. error
+.endif
+
+# Global variables can be removed from their scope.
+.undef GLOBAL
+.if defined(GLOBAL)
+. error
+.endif
+
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/var-scope-local-legacy.exp b/contrib/bmake/unit-tests/var-scope-local-legacy.exp
new file mode 100644
index 000000000000..33ce145fb8fd
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-scope-local-legacy.exp
@@ -0,0 +1,6 @@
+: LEN4=undef_
+: XY=undef_
+: AF=undef_
+: %D=undef_ %F=undef_
+: @D=global-value_ @F=all_
+exit status 0
diff --git a/contrib/bmake/unit-tests/var-scope-local-legacy.mk b/contrib/bmake/unit-tests/var-scope-local-legacy.mk
new file mode 100644
index 000000000000..70bc20fd9848
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-scope-local-legacy.mk
@@ -0,0 +1,35 @@
+# $NetBSD: var-scope-local-legacy.mk,v 1.3 2023/12/17 14:07:22 rillig Exp $
+#
+# Tests for legacy target-local variables, such as ${<F} or ${@D}.
+
+
+# In the global or command line scopes, the legacy forms are not recognized,
+# as the target-specific variables are not available either. The expressions
+# are retained so that they can be resolved later, in the target scope.
+.if "${@D}" != "\${@D}"
+. error
+.endif
+
+# It's possible to define variables of the legacy name in the global or
+# command line scope, and they override the target-local variables, leading to
+# unnecessary confusion.
+@D= global-value
+.if "${@D}" != "global-value"
+. error
+.endif
+
+
+all: .PHONY
+ # Only variables of length 2 can be legacy, this one cannot.
+ : LEN4=${LEN4:Uundef}_
+ # The second character of the name must be 'D' or 'F'.
+ : XY=${XY:Uundef}_
+ # The first character must name one of the 7 predefined local
+ # variables, 'A' is not such a character.
+ : AF=${AF:Uundef}_
+ # The variable '.MEMBER' is undefined, therefore '%D' and '%F' are
+ # undefined as well.
+ : %D=${%D:Uundef}_ %F=${%F:Uundef}_
+ # The directory name of the target is shadowed by the global variable,
+ # it would be '.' otherwise. The basename is 'all'.
+ : @D=${@D:Uundef}_ @F=${@F:Uundef}_
diff --git a/contrib/bmake/unit-tests/var-scope-local.exp b/contrib/bmake/unit-tests/var-scope-local.exp
new file mode 100644
index 000000000000..eddf5985a0ed
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-scope-local.exp
@@ -0,0 +1,71 @@
+Global: .ALLTARGETS = all target-rule.ext dir/subdir/target-rule.ext target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from inference-rule.ir-to dir/subdir/inference-rule.ir-to inference-rule.ir-from dir/subdir/inference-rule.ir-from inference-rule-chain.ir-to dir/subdir/inference-rule-chain.ir-to inference-rule-chain.ir-gen-from dir/subdir/inference-rule-chain.ir-gen-from one
+Global: .ALLTARGETS = all target-rule.ext dir/subdir/target-rule.ext target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from inference-rule.ir-to dir/subdir/inference-rule.ir-to inference-rule.ir-from dir/subdir/inference-rule.ir-from inference-rule-chain.ir-to dir/subdir/inference-rule-chain.ir-to inference-rule-chain.ir-gen-from dir/subdir/inference-rule-chain.ir-gen-from one two
+Var_Parse: ${.MAKE.TARGET_LOCAL_VARIABLES} (eval)
+one: ignoring ' = three' as the variable name '' expands to empty
+two: ignoring ' = three' as the variable name '' expands to empty
+Global: one two = # (empty)
+Global: one two = three
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
+target-rule.ext: @ = <target-rule.ext>
+target-rule.ext: % = <undefined>
+target-rule.ext: ? = <>
+target-rule.ext: < = <undefined>
+target-rule.ext: * = <target-rule.ext>
+dir/subdir/target-rule.ext: @ = <dir/subdir/target-rule.ext>
+dir/subdir/target-rule.ext: % = <undefined>
+dir/subdir/target-rule.ext: ? = <>
+dir/subdir/target-rule.ext: < = <undefined>
+dir/subdir/target-rule.ext: * = <dir/subdir/target-rule.ext>
+target-rule.ir-gen-from: @ = <target-rule.ir-gen-from>
+target-rule.ir-gen-from: % = <undefined>
+target-rule.ir-gen-from: ? = <>
+target-rule.ir-gen-from: < = <undefined>
+target-rule.ir-gen-from: * = <target-rule>
+dir/subdir/target-rule-dir.ir-gen-from: @ = <dir/subdir/target-rule-dir.ir-gen-from>
+dir/subdir/target-rule-dir.ir-gen-from: % = <undefined>
+dir/subdir/target-rule-dir.ir-gen-from: ? = <>
+dir/subdir/target-rule-dir.ir-gen-from: < = <undefined>
+dir/subdir/target-rule-dir.ir-gen-from: * = <dir/subdir/target-rule-dir>
+inference-rule.ir-to: @ = <inference-rule.ir-to>
+inference-rule.ir-to: % = <undefined>
+inference-rule.ir-to: ? = <inference-rule.ir-from>
+inference-rule.ir-to: < = <inference-rule.ir-from>
+inference-rule.ir-to: * = <inference-rule>
+dir/subdir/inference-rule.ir-to: @ = <dir/subdir/inference-rule.ir-to>
+dir/subdir/inference-rule.ir-to: % = <undefined>
+dir/subdir/inference-rule.ir-to: ? = <dir/subdir/inference-rule.ir-from>
+dir/subdir/inference-rule.ir-to: < = <dir/subdir/inference-rule.ir-from>
+dir/subdir/inference-rule.ir-to: * = <dir/subdir/inference-rule>
+inference-rule-chain.ir-from: @ = <inference-rule-chain.ir-from>
+inference-rule-chain.ir-from: % = <undefined>
+inference-rule-chain.ir-from: ? = <inference-rule-chain.ir-gen-from>
+inference-rule-chain.ir-from: < = <inference-rule-chain.ir-gen-from>
+inference-rule-chain.ir-from: * = <inference-rule-chain>
+inference-rule-chain.ir-to: @ = <inference-rule-chain.ir-to>
+inference-rule-chain.ir-to: % = <undefined>
+inference-rule-chain.ir-to: ? = <inference-rule-chain.ir-from>
+inference-rule-chain.ir-to: < = <inference-rule-chain.ir-from>
+inference-rule-chain.ir-to: * = <inference-rule-chain>
+dir/subdir/inference-rule-chain.ir-from: @ = <dir/subdir/inference-rule-chain.ir-from>
+dir/subdir/inference-rule-chain.ir-from: % = <undefined>
+dir/subdir/inference-rule-chain.ir-from: ? = <dir/subdir/inference-rule-chain.ir-gen-from>
+dir/subdir/inference-rule-chain.ir-from: < = <dir/subdir/inference-rule-chain.ir-gen-from>
+dir/subdir/inference-rule-chain.ir-from: * = <dir/subdir/inference-rule-chain>
+dir/subdir/inference-rule-chain.ir-to: @ = <dir/subdir/inference-rule-chain.ir-to>
+dir/subdir/inference-rule-chain.ir-to: % = <undefined>
+dir/subdir/inference-rule-chain.ir-to: ? = <dir/subdir/inference-rule-chain.ir-from>
+dir/subdir/inference-rule-chain.ir-to: < = <dir/subdir/inference-rule-chain.ir-from>
+dir/subdir/inference-rule-chain.ir-to: * = <dir/subdir/inference-rule-chain>
+: Making var-scope-local.c out of nothing.
+: Making var-scope-local.o from var-scope-local.c.
+: Making basename "var-scope-local.o" in "." from "var-scope-local.c" in ".".
+Making var-scope-local-assign.o with make 'local' and env 'local'.
+Making var-scope-local-append.o with make 'local to var-scope-local-append.o' and env 'local to var-scope-local-append.o'.
+Making var-scope-local-append-global.o with make 'global+local' and env 'global+local'.
+Making var-scope-local-default.o with make 'global' and env 'global'.
+Making var-scope-local-subst.o with make 'global+local' and env 'global+local'.
+Making var-scope-local-shell.o with make 'output' and env 'output'.
+Making .USE var-scope-local-use.o with make 'global' and env 'global'.
+: all overwritten
+exit status 0
diff --git a/contrib/bmake/unit-tests/var-scope-local.mk b/contrib/bmake/unit-tests/var-scope-local.mk
new file mode 100644
index 000000000000..7a031373e7da
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-scope-local.mk
@@ -0,0 +1,270 @@
+# $NetBSD: var-scope-local.mk,v 1.11 2024/03/05 23:07:58 rillig Exp $
+#
+# Tests for target-local variables, such as ${.TARGET} or $@. These variables
+# are relatively short-lived as they are created just before making the
+# target. In contrast, global variables are typically created when the
+# makefiles are read in.
+#
+# The 7 built-in target-local variables are listed in the manual page. They
+# are defined just before the target is actually made. Additional
+# target-local variables can be defined in dependency lines like
+# 'target: VAR=value', one at a time.
+
+.MAIN: all
+
+# Target-local variables in a target rule
+#
+# In target rules, '$*' only strips the extension off the pathname if the
+# extension is listed in '.SUFFIXES'.
+#
+# expect: target-rule.ext: * = <target-rule.ext>
+all: target-rule.ext dir/subdir/target-rule.ext
+target-rule.ext dir/subdir/target-rule.ext: .PHONY
+ @echo '$@: @ = <${@:Uundefined}>'
+ @echo '$@: % = <${%:Uundefined}>'
+ @echo '$@: ? = <${?:Uundefined}>'
+ @echo '$@: < = <${<:Uundefined}>'
+ @echo '$@: * = <${*:Uundefined}>'
+
+.SUFFIXES: .ir-gen-from .ir-from .ir-to
+
+# In target rules, '$*' strips the extension off the pathname of the target
+# if the extension is listed in '.SUFFIXES'.
+#
+# expect: target-rule.ir-gen-from: * = <target-rule>
+all: target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from
+target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from:
+ @echo '$@: @ = <${@:Uundefined}>'
+ @echo '$@: % = <${%:Uundefined}>'
+ @echo '$@: ? = <${?:Uundefined}>'
+ @echo '$@: < = <${<:Uundefined}>'
+ @echo '$@: * = <${*:Uundefined}>'
+
+.ir-from.ir-to:
+ @echo '$@: @ = <${@:Uundefined}>'
+ @echo '$@: % = <${%:Uundefined}>'
+ @echo '$@: ? = <${?:Uundefined}>'
+ @echo '$@: < = <${<:Uundefined}>'
+ @echo '$@: * = <${*:Uundefined}>'
+.ir-gen-from.ir-from:
+ @echo '$@: @ = <${@:Uundefined}>'
+ @echo '$@: % = <${%:Uundefined}>'
+ @echo '$@: ? = <${?:Uundefined}>'
+ @echo '$@: < = <${<:Uundefined}>'
+ @echo '$@: * = <${*:Uundefined}>'
+
+# Target-local variables in an inference rule
+all: inference-rule.ir-to dir/subdir/inference-rule.ir-to
+inference-rule.ir-from: .PHONY
+dir/subdir/inference-rule.ir-from: .PHONY
+
+# Target-local variables in a chain of inference rules
+all: inference-rule-chain.ir-to dir/subdir/inference-rule-chain.ir-to
+inference-rule-chain.ir-gen-from: .PHONY
+dir/subdir/inference-rule-chain.ir-gen-from: .PHONY
+
+# The run-time 'check' directives from above happen after the parse-time
+# 'check' directives from below.
+#
+# expect-reset
+
+# Deferred evaluation during parsing
+#
+# The target-local variables can be used in expressions, just like other
+# variables. When these expressions are evaluated outside of a target, these
+# expressions are not yet expanded, instead their text is preserved, to allow
+# these expressions to expand right in time when the target-local variables
+# are actually set.
+#
+# Conditions from .if directives are evaluated in the scope of the command
+# line, which means that variables from the command line, from the global
+# scope and from the environment are resolved, in this precedence order (but
+# see the command line option '-e'). In that phase, expressions involving
+# target-local variables need to be preserved, including the exact names of
+# the variables.
+#
+# Each of the built-in target-local variables has two equivalent names, for
+# example '@' is equivalent to '.TARGET'. The implementation might
+# canonicalize these aliases at some point, and that might be surprising.
+# This aliasing happens for single-character variable names like $@ or $<
+# (see VarFind, CanonicalVarname), but not for braced or parenthesized
+# expressions like ${@}, ${.TARGET} ${VAR:Mpattern} (see Var_Parse,
+# ParseVarname).
+#
+# In the following condition, make expands '$@' to the long-format alias
+# '$(.TARGET)'; note that the alias is not written with braces, as would be
+# common in BSD makefiles, but with parentheses. This alternative spelling
+# behaves the same though.
+.if $@ != "\$\(.TARGET)"
+. error
+.endif
+# In the long form of writing a target-local variable, the text of the
+# expression is preserved exactly as written, no matter whether it is written
+# with '{' or '('.
+.if ${@} != "\$\{@}"
+. error
+.endif
+.if $(@) != "\$\(@)"
+. error
+.endif
+# If the expression contains modifiers, the behavior depends on the
+# actual modifiers. The modifier ':M' keeps the expression in the state
+# 'undefined'. Since the expression is still undefined after evaluating all
+# the modifiers, the value of the expression is discarded and the expression
+# text is used instead. This preserves the expressions based on target-local
+# variables as long as possible.
+.if ${@:M*} != "\$\{@:M*}"
+. error
+.endif
+# In the following examples, the expressions are based on target-local
+# variables but use the modifier ':L', which turns an undefined expression
+# into a defined one. At the end of evaluating the expression, the state of
+# the expression is not 'undefined' anymore. The value of the expression
+# is the name of the variable, since that's what the modifier ':L' does.
+.if ${@:L} != "@"
+. error
+.endif
+.if ${.TARGET:L} != ".TARGET"
+. error
+.endif
+.if ${@F:L} != "@F"
+. error
+.endif
+.if ${@D:L} != "@D"
+. error
+.endif
+
+
+# Custom local variables
+#
+# Additional target-local variables may be defined in dependency lines.
+.MAKEFLAGS: -dv
+# In the following line, the ':=' may either be interpreted as an assignment
+# operator or as the dependency operator ':', followed by an empty variable
+# name and the assignment operator '='. It is the latter since in an
+# assignment, the left-hand side must be a single word or empty.
+#
+# The empty variable name is expanded twice, once for 'one' and once for
+# 'two'.
+# expect: one: ignoring ' = three' as the variable name '' expands to empty
+# expect: two: ignoring ' = three' as the variable name '' expands to empty
+one two:=three
+# If the two targets to the left are generated by an expression, the
+# line is parsed as a variable assignment since its left-hand side is a single
+# word.
+# expect: Global: one two = three
+${:Uone two}:=three
+.MAKEFLAGS: -d0
+
+
+.SUFFIXES: .c .o
+
+# One of the dynamic target-local variables is '.TARGET'. Since this is not
+# a suffix transformation rule, the variable '.IMPSRC' is not defined.
+# expect: : Making var-scope-local.c out of nothing.
+var-scope-local.c:
+ : Making ${.TARGET} ${.IMPSRC:Dfrom ${.IMPSRC}:Uout of nothing}.
+
+# This is a suffix transformation rule, so both '.TARGET' and '.IMPSRC' are
+# defined.
+# expect: : Making var-scope-local.o from var-scope-local.c.
+# expect: : Making basename "var-scope-local.o" in "." from "var-scope-local.c" in ".".
+.c.o:
+ : Making ${.TARGET} from ${.IMPSRC}.
+
+ # The local variables @F, @D, <F, <D are legacy forms.
+ # See the manual page for details.
+ : Making basename "${@F}" in "${@D}" from "${<F}" in "${<D}".
+
+# expect: : all overwritten
+all: var-scope-local.o
+ # The ::= modifier overwrites the .TARGET variable in the node
+ # 'all', not in the global scope. This can be seen with the -dv
+ # option, looking for "all: @ = overwritten".
+ : ${.TARGET} ${.TARGET::=overwritten}${.TARGET}
+
+
+# Begin tests for custom target-local variables, for all 5 variable assignment
+# operators.
+all: var-scope-local-assign.o
+all: var-scope-local-append.o
+all: var-scope-local-append-global.o
+all: var-scope-local-default.o
+all: var-scope-local-subst.o
+all: var-scope-local-shell.o
+
+var-scope-local-assign.o \
+var-scope-local-append.o \
+var-scope-local-append-global.o \
+var-scope-local-default.o \
+var-scope-local-subst.o \
+var-scope-local-shell.o:
+ @echo "Making ${.TARGET} with make '"${VAR:Q}"' and env '$$VAR'."
+
+# Target-local variables are enabled by default. Force them to be enabled
+# just in case a test above has disabled them.
+.MAKE.TARGET_LOCAL_VARIABLES= yes
+
+VAR= global
+.export VAR
+
+# If the sources of a dependency line look like a variable assignment, make
+# treats them as such. There is only a single variable assignment per
+# dependency line, which makes whitespace around the assignment operator
+# irrelevant.
+#
+# expect-reset
+# expect: Making var-scope-local-assign.o with make 'local' and env 'local'.
+var-scope-local-assign.o: VAR= local
+
+# Assignments using '+=' do *not* look up the global value, instead they only
+# look up the variable in the target's own scope.
+var-scope-local-append.o: VAR+= local
+# Once a variable is defined in the target-local scope, appending using '+='
+# behaves as expected. Note that the expression '${.TARGET}' is not resolved
+# when parsing the dependency line, its evaluation is deferred until the
+# target is actually made.
+# expect: Making var-scope-local-append.o with make 'local to var-scope-local-append.o' and env 'local to var-scope-local-append.o'.
+var-scope-local-append.o: VAR += to ${.TARGET}
+# To access the value of a global variable, use an expression. This
+# expression is expanded before parsing the whole dependency line. Since the
+# expansion happens to the right of the dependency operator ':', the expanded
+# text does not influence parsing of the dependency line. Since the expansion
+# happens to the right of the assignment operator '=', the expanded text does
+# not influence the parsing of the variable assignment. The effective
+# variable assignment, after expanding the whole line first, is thus
+# 'VAR= global+local'.
+# expect: Making var-scope-local-append-global.o with make 'global+local' and env 'global+local'.
+var-scope-local-append-global.o: VAR= ${VAR}+local
+
+var-scope-local-default.o: VAR ?= first
+var-scope-local-default.o: VAR ?= second
+# XXX: '?=' does look at the global variable. That's a long-standing
+# inconsistency between the assignment operators '+=' and '?='. See
+# Var_AppendExpand and VarAssign_Eval.
+# expect: Making var-scope-local-default.o with make 'global' and env 'global'.
+
+# Using the variable assignment operator ':=' provides another way of
+# accessing a global variable and extending it with local modifications. The
+# '$' has to be written as '$$' though to survive the expansion of the
+# dependency line as a whole. After that, the parser sees the variable
+# assignment as 'VAR := ${VAR}+local' and searches for the variable 'VAR' in
+# the usual scopes, picking up the variable from the global scope.
+# expect: Making var-scope-local-subst.o with make 'global+local' and env 'global+local'.
+var-scope-local-subst.o: VAR := $${VAR}+local
+
+# The variable assignment operator '!=' assigns the output of the shell
+# command, as everywhere else. The shell command is run when the dependency
+# line is parsed.
+var-scope-local-shell.o: VAR != echo output
+
+
+# While VAR=use will be set for a .USE node, it will never be seen since only
+# the ultimate target's context is searched; the variable assignments from the
+# .USE target are not copied to the ultimate target's.
+# expect: Making .USE var-scope-local-use.o with make 'global' and env 'global'.
+a_use: .USE VAR=use
+ @echo "Making .USE ${.TARGET} with make '"${VAR:Q}"' and env '$$VAR'."
+
+all: var-scope-local-use.o
+var-scope-local-use.o: a_use
diff --git a/contrib/bmake/unit-tests/var-class-local-legacy.exp b/contrib/bmake/unit-tests/var-scope.exp
index 39a9383953dd..39a9383953dd 100644
--- a/contrib/bmake/unit-tests/var-class-local-legacy.exp
+++ b/contrib/bmake/unit-tests/var-scope.exp
diff --git a/contrib/bmake/unit-tests/var-scope.mk b/contrib/bmake/unit-tests/var-scope.mk
new file mode 100644
index 000000000000..cbbcea526d54
--- /dev/null
+++ b/contrib/bmake/unit-tests/var-scope.mk
@@ -0,0 +1,9 @@
+# $NetBSD: var-scope.mk,v 1.1 2022/01/23 16:25:54 rillig Exp $
+#
+# Tests for the different variable scopes (local, command-line, global,
+# environment), and which of them takes precedence over the others.
+
+# TODO: Implementation
+
+all:
+ @:;
diff --git a/contrib/bmake/unit-tests/varcmd.mk b/contrib/bmake/unit-tests/varcmd.mk
index 12739df30926..ec0cf96ed75c 100644
--- a/contrib/bmake/unit-tests/varcmd.mk
+++ b/contrib/bmake/unit-tests/varcmd.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varcmd.mk,v 1.6 2021/02/16 19:43:09 rillig Exp $
+# $NetBSD: varcmd.mk,v 1.7 2023/04/07 05:54:16 rillig Exp $
#
# Test behaviour of recursive make and vars set on command line.
#
@@ -12,6 +12,10 @@
# be rewritten to make it clear why there is a difference and why this is
# actually intended. Removing that large block of code makes only this test
# and vardebug.mk fail, which is not enough.
+#
+# See also:
+# var-scope-cmdline.mk
+# varname-makeflags.mk
FU= fu
FOO?= foo
diff --git a/contrib/bmake/unit-tests/vardebug.exp b/contrib/bmake/unit-tests/vardebug.exp
index 6d00acc977af..86f2ac0b420d 100644
--- a/contrib/bmake/unit-tests/vardebug.exp
+++ b/contrib/bmake/unit-tests/vardebug.exp
@@ -1,13 +1,13 @@
-Global:delete FROM_CMDLINE (not found)
-Command: FROM_CMDLINE =
+Global: ignoring delete 'FROM_CMDLINE' as it is not found
+Command: FROM_CMDLINE = # (empty)
Global: .MAKEOVERRIDES = FROM_CMDLINE
Global: VAR = added
Global: VAR = overwritten
-Global:delete VAR
-Global:delete VAR (not found)
-Var_SetExpand: variable name "${:U}" expands to empty string, with value "empty name" - ignored
-Var_AppendExpand: variable name "${:U}" expands to empty string, with value "empty name" - ignored
-Global: FROM_CMDLINE = overwritten ignored!
+Global: delete VAR
+Global: ignoring delete 'VAR' as it is not found
+Global: ignoring ' = empty name' as the variable name '${:U}' expands to empty
+Global: ignoring ' += empty name' as the variable name '${:U}' expands to empty
+Global: ignoring 'FROM_CMDLINE = overwritten' due to a command line variable of the same name
Global: VAR = 1
Global: VAR = 1 2
Global: VAR = 1 2 3
@@ -43,25 +43,25 @@ 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
+Global: delete VAR
Var_Parse: ${:Uvariable:unknown} (eval-defined)
Evaluating modifier ${:U...} on value "" (eval-defined, undefined)
Result of ${:Uvariable} is "variable" (eval-defined, defined)
Evaluating modifier ${:u...} on value "variable" (eval-defined, defined)
-make: "vardebug.mk" line 44: Unknown modifier "unknown"
+make: "vardebug.mk" line 63: while evaluating "${:Uvariable:unknown}": Unknown modifier "unknown"
Result of ${:unknown} is error (eval-defined, defined)
-make: "vardebug.mk" line 44: Malformed conditional (${:Uvariable:unknown})
+make: "vardebug.mk" line 63: Malformed conditional (${:Uvariable:unknown})
Var_Parse: ${UNDEFINED} (eval-defined)
-make: "vardebug.mk" line 53: Malformed conditional (${UNDEFINED})
-Global:delete .SHELL (not found)
+make: "vardebug.mk" line 73: Malformed conditional (${UNDEFINED})
+Global: ignoring delete '.SHELL' as it is not found
Command: .SHELL = </path/to/shell>
-Command: .SHELL = overwritten ignored (read-only)
+Command: ignoring '.SHELL = overwritten' as it is read-only
Global: .MAKEFLAGS = -r -k -d v -d
Global: .MAKEFLAGS = -r -k -d v -d 0
make: Fatal errors encountered -- cannot continue
diff --git a/contrib/bmake/unit-tests/vardebug.mk b/contrib/bmake/unit-tests/vardebug.mk
index 4a16a7f2797f..6c5703cb526f 100644
--- a/contrib/bmake/unit-tests/vardebug.mk
+++ b/contrib/bmake/unit-tests/vardebug.mk
@@ -1,32 +1,46 @@
-# $NetBSD: vardebug.mk,v 1.7 2021/02/04 21:42:47 rillig Exp $
+# $NetBSD: vardebug.mk,v 1.10 2024/04/20 10:18:55 rillig Exp $
#
# Demonstrates the debugging output for var.c.
.MAKEFLAGS: -dv FROM_CMDLINE=
+# expect: Global: VAR = added
VAR= added # VarAdd
+# expect: Global: VAR = overwritten
VAR= overwritten # Var_Set
-.undef VAR # Var_Delete (found)
-.undef VAR # Var_Delete (not found)
+# expect: Global: delete VAR
+.undef VAR
+# expect: Global: ignoring delete 'VAR' as it is not found
+.undef VAR
# The variable with the empty name cannot be set at all.
+# expect: Global: ignoring ' = empty name' as the variable name '${:U}' expands to empty
${:U}= empty name # Var_Set
+# expect: Global: ignoring ' += empty name' as the variable name '${:U}' expands to empty
${:U}+= empty name # Var_Append
FROM_CMDLINE= overwritten # Var_Set (ignored)
+# expect: Global: VAR = 1
VAR= 1
+# expect: Global: VAR = 1 2
VAR+= 2
+# expect: Global: VAR = 1 2 3
VAR+= 3
+# expect: Pattern for ':M' is "[2]"
+# expect: Result of ${VAR:M[2]} is "2"
.if ${VAR:M[2]} # ModifyWord_Match
.endif
-.if ${VAR:N[2]} # ModifyWord_NoMatch (no debug output)
+# expect: Pattern for ':N' is "[2]"
+# expect: Result of ${VAR:N[2]} is "1 3"
+.if ${VAR:N[2]} # ModifyWord_NoMatch
.endif
.if ${VAR:S,2,two,} # ParseModifierPart
.endif
+# expect: Result of ${VAR:Q} is "1\ 2\ 3"
.if ${VAR:Q} # VarQuote
.endif
@@ -34,13 +48,18 @@ VAR+= 3
.endif
# ApplyModifiers, "Got ..."
+# expect: Result of ${:Mvalu[e]} is "value" (eval-defined, defined)
.if ${:Uvalue:${:UM*e}:Mvalu[e]}
.endif
+# expect: Global: delete VAR
.undef ${:UVAR} # Var_Delete
# When ApplyModifiers results in an error, this appears in the debug log
# as "is error", without surrounding quotes.
+# expect: Result of ${:unknown} is error (eval-defined, defined)
+# expect+2: Malformed conditional (${:Uvariable:unknown})
+# expect+1: while evaluating "${:Uvariable:unknown}": Unknown modifier "unknown"
.if ${:Uvariable:unknown}
.endif
@@ -50,15 +69,14 @@ VAR+= 3
# There is a specialized error message for "Undefined variable", but as of
# 2020-08-08, that is not covered by any unit tests. It might even be
# unreachable.
+# expect+1: Malformed conditional (${UNDEFINED})
.if ${UNDEFINED}
.endif
# By default, .SHELL is not defined and thus can be set. As soon as it is
# accessed, it is initialized in the command line scope (during VarFind),
# where it is set to read-only. Assigning to it is ignored.
+# expect: Command: ignoring '.SHELL = overwritten' as it is read-only
.MAKEFLAGS: .SHELL=overwritten
.MAKEFLAGS: -d0
-
-all:
- @:
diff --git a/contrib/bmake/unit-tests/varmisc.exp b/contrib/bmake/unit-tests/varmisc.exp
index f56f72d0ab9c..dd24a419fe75 100644
--- a/contrib/bmake/unit-tests/varmisc.exp
+++ b/contrib/bmake/unit-tests/varmisc.exp
@@ -17,12 +17,9 @@ false
FALSE
do not evaluate or expand :? if discarding
is set
-year=2016 month=04 day=01
-date=20160401
Version=123.456.789 == 123456789
Literal=3.4.5 == 3004005
We have target specific vars
-MAN= make.1
save-dollars: 0 = $
save-dollars: 1 = $$
save-dollars: 2 = $$
@@ -47,26 +44,26 @@ parse-dynamic: parse-dynamic parse-dynamic before
parse-dynamic: parse-dynamic parse-dynamic after
parse-dynamic: parse-dynamic parse-dynamic after
varerror-unclosed:begin
-make: Unclosed variable ""
+make: in target "varerror-unclosed": Unclosed variable ""
-make: Unclosed variable "UNCLOSED"
+make: in target "varerror-unclosed": Unclosed variable "UNCLOSED"
-make: Unclosed variable "UNCLOSED"
+make: in target "varerror-unclosed": Unclosed variable "UNCLOSED"
-make: Unclosed variable "PATTERN"
-make: Unclosed variable expression, expecting '}' for modifier "M${PATTERN" of variable "UNCLOSED" with value ""
+make: in target "varerror-unclosed": while evaluating variable "UNCLOSED": Unclosed variable "PATTERN"
+make: Unclosed expression, expecting '}' for modifier "M${PATTERN" of variable "UNCLOSED" with value ""
-make: Unclosed variable "param"
-make: Unclosed variable "UNCLOSED."
+make: in target "varerror-unclosed": Unclosed variable "param"
+make: in target "varerror-unclosed": Unclosed variable "UNCLOSED."
-make: Unclosed variable "UNCLOSED.1"
+make: in target "varerror-unclosed": Unclosed variable "UNCLOSED.1"
-make: Unclosed variable "UNCLOSED.2"
+make: in target "varerror-unclosed": Unclosed variable "UNCLOSED.2"
-make: Unclosed variable "UNCLOSED.3"
+make: in target "varerror-unclosed": Unclosed variable "UNCLOSED.3"
-make: Unclosed variable "UNCLOSED_ORIG"
+make: in target "varerror-unclosed": while evaluating variable "UNCLOSED_INDIR_2": while evaluating variable "UNCLOSED_INDIR_1": Unclosed variable "UNCLOSED_ORIG"
varerror-unclosed:end
target1-flags: we have: one two
diff --git a/contrib/bmake/unit-tests/varmisc.mk b/contrib/bmake/unit-tests/varmisc.mk
index e5ab375d8f39..f6a0e4da2d88 100644
--- a/contrib/bmake/unit-tests/varmisc.mk
+++ b/contrib/bmake/unit-tests/varmisc.mk
@@ -1,10 +1,10 @@
-# $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.26 2023/11/25 01:39:31 sjg Exp $
+# $NetBSD: varmisc.mk,v 1.33 2023/10/19 18:24:33 rillig Exp $
#
# Miscellaneous variable tests.
all: unmatched_var_paren D_true U_true D_false U_false Q_lhs Q_rhs NQ_none \
- strftime cmpv manok
+ cmpv
all: save-dollars
all: export-appended
all: parse-dynamic
@@ -47,13 +47,6 @@ NQ_none:
@echo do not evaluate or expand :? if discarding
@echo ${VSET:U${1:L:?${True}:${False}}}
-April1= 1459494000
-
-# slightly contorted syntax to use utc via variable
-strftime:
- @echo ${year=%Y month=%m day=%d:L:gmtime=1459494000}
- @echo date=${%Y%m%d:L:${gmtime=${April1}:L}}
-
# big jumps to handle 3 digits per step
M_cmpv.units= 1 1000 1000000
M_cmpv= S,., ,g:_:range:@i@+ $${_:[-$$i]} \* $${M_cmpv.units:[$$i]}@:S,^,expr 0 ,1:sh
@@ -66,24 +59,16 @@ 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
-MAN=
-MAN1= make.1
-.for s in 1 2
-. if defined(MAN$s) && !empty(MAN$s)
-MAN+= ${MAN$s}
-. endif
-.endfor
-
-manok:
- @echo MAN=${MAN}
+# Test parsing of boolean values.
# begin .MAKE.SAVE_DOLLARS; see Var_SetWithFlags and ParseBoolean.
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 +77,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
@@ -127,10 +113,10 @@ VAR.${PARAM}+= 2
.if ${VAR.+} != "1 2"
. error "${VAR.+}"
.endif
-.for param in + ! ?
+.for param in : + ! ?
VAR.${param}= ${param}
.endfor
-.if ${VAR.+} != "+" || ${VAR.!} != "!" || ${VAR.?} != "?"
+.if ${VAR.${:U\:}} != ":" || ${VAR.+} != "+" || ${VAR.!} != "!" || ${VAR.?} != "?"
. error "${VAR.+}" "${VAR.!}" "${VAR.?}"
.endif
diff --git a/contrib/bmake/unit-tests/varmod-assign-shell.exp b/contrib/bmake/unit-tests/varmod-assign-shell.exp
new file mode 100644
index 000000000000..b7c6cd223f24
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-assign-shell.exp
@@ -0,0 +1,14 @@
+make: "varmod-assign-shell.mk" line 28: warning: "echo output; false" returned non-zero status
+Global: _ = # (empty)
+Var_Parse: ${ASSIGNED::!=echo output; ${:Ufalse}} (eval-keep-dollar-and-undefined)
+Evaluating modifier ${ASSIGNED::...} on value "previous" (eval-keep-dollar-and-undefined, regular)
+Modifier part: "echo output; false"
+Capturing the output of command "echo output; false"
+make: "echo output; false" returned non-zero status
+Result of ${ASSIGNED::!=echo output; ${:Ufalse}} is "" (eval-keep-dollar-and-undefined, regular)
+Global: _ = # (empty)
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
+DIRECT=output
+ASSIGNED=previous
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-assign-shell.mk b/contrib/bmake/unit-tests/varmod-assign-shell.mk
new file mode 100644
index 000000000000..6158bac14eaa
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-assign-shell.mk
@@ -0,0 +1,37 @@
+# $NetBSD: varmod-assign-shell.mk,v 1.5 2023/06/01 20:56:35 rillig Exp $
+#
+# Tests for the variable modifier '::!=', which assigns the output of a shell
+# command to the variable, but only if the command exited successfully. This
+# is different from the other places that capture the output of an external
+# command (variable assignment operator '!=', expression modifier ':sh',
+# expression modifier ':!...!'), which also use the output when the shell
+# command fails or crashes.
+#
+# The variable modifier '::!=' and its close relatives have been around since
+# var.c 1.45 from 2000-06-01.
+#
+# Before 2020.08.25.21.16.53, the variable modifier '::!=' had a bug for
+# unsuccessful commands, it put the previous value of the variable into the
+# error message instead of the command that was executed. That's where the
+# counterintuitive error message 'make: "previous" returned non-zero status'
+# comes from.
+#
+# BUGS
+# Even though the variable modifier '::!=' produces an error message,
+# the exit status of make is still 0.
+#
+# Having an error message instead of a warning like for the variable
+# assignment operator '!=' is another unnecessary inconsistency.
+
+DIRECT= previous
+# expect+1: warning: "echo output; false" returned non-zero status
+DIRECT!= echo output; false
+
+ASSIGNED= previous
+.MAKEFLAGS: -dv # to see the actual command
+_:= ${ASSIGNED::!=echo output; ${:Ufalse}}
+.MAKEFLAGS: -d0
+
+all:
+ @echo DIRECT=${DIRECT:Q}
+ @echo ASSIGNED=${ASSIGNED:Q}
diff --git a/contrib/bmake/unit-tests/varmod-assign.exp b/contrib/bmake/unit-tests/varmod-assign.exp
index 1e43714d500b..db1fa64c8479 100644
--- a/contrib/bmake/unit-tests/varmod-assign.exp
+++ b/contrib/bmake/unit-tests/varmod-assign.exp
@@ -12,24 +12,37 @@ 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
+Var_Parse: ${CMD_CMD_VAR::=new-value} || ${CMD_GLOBAL_VAR::=new-value} || ${CMD_ENV_VAR::=new-value} || "${CMD_NEW_VAR::=new-value}" (eval-defined)
+Evaluating modifier ${CMD_CMD_VAR::...} on value "cmd-value"
+Modifier part: "new-value"
+Command: CMD_CMD_VAR = new-value
+Global: .MAKEOVERRIDES = FIRST LAST LAST LAST APPENDED RAN RAN RAN IT1 THEN1 IE2 ELSE2 CMD_CMD_VAR CMD_CMD_VAR
+Result of ${CMD_CMD_VAR::=new-value} is ""
+Var_Parse: ${CMD_GLOBAL_VAR::=new-value} || ${CMD_ENV_VAR::=new-value} || "${CMD_NEW_VAR::=new-value}" (eval-defined)
+Evaluating modifier ${CMD_GLOBAL_VAR::...} on value "global-value"
+Modifier part: "new-value"
+Global: CMD_GLOBAL_VAR = new-value
+Result of ${CMD_GLOBAL_VAR::=new-value} is ""
+Var_Parse: ${CMD_ENV_VAR::=new-value} || "${CMD_NEW_VAR::=new-value}" (eval-defined)
+Evaluating modifier ${CMD_ENV_VAR::...} on value "env-value"
+Modifier part: "new-value"
+Global: CMD_ENV_VAR = new-value
+Result of ${CMD_ENV_VAR::=new-value} is ""
+Var_Parse: ${CMD_NEW_VAR::=new-value}" (eval)
+Evaluating modifier ${CMD_NEW_VAR::...} on value "" (eval, undefined)
+Modifier part: "new-value"
+Global: ignoring delete 'CMD_NEW_VAR' as it is not found
+Command: CMD_NEW_VAR = new-value
+Global: .MAKEOVERRIDES = FIRST LAST LAST LAST APPENDED RAN RAN RAN IT1 THEN1 IE2 ELSE2 CMD_CMD_VAR CMD_CMD_VAR CMD_NEW_VAR
+Result of ${CMD_NEW_VAR::=new-value} is "" (eval, undefined)
+Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d 0
make: Bad modifier ":" for variable ""
mod-assign-empty: value}
make: Bad modifier ":" for variable ""
mod-assign-empty: overwritten}
mod-assign-empty: VAR=overwritten
-make: Unknown modifier ":x"
+make: in target "mod-assign-parse": while evaluating variable "ASSIGN": Unknown modifier ":x"
sysv:y
make: Unfinished modifier for "ASSIGN" ('}' missing)
@@ -37,4 +50,11 @@ make: Unfinished modifier for "ASSIGN" ('}' missing)
ok=word
make: " echo word; false " returned non-zero status
err=previous
+Command: TARGET_CMD_VAR = cmd-value
+Global: TARGET_GLOBAL_VAR = global-value
+target: TARGET_TARGET_VAR = target-value
+target: TARGET_TARGET_VAR = new-value
+Global: TARGET_GLOBAL_VAR = new-value
+Global: TARGET_ENV_VAR = new-value
+target: TARGET_NEW_VAR = new-value
exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-assign.mk b/contrib/bmake/unit-tests/varmod-assign.mk
index f50c654f5bcf..17d9df764be1 100644
--- a/contrib/bmake/unit-tests/varmod-assign.mk
+++ b/contrib/bmake/unit-tests/varmod-assign.mk
@@ -1,70 +1,85 @@
-# $NetBSD: varmod-assign.mk,v 1.12 2021/03/15 18:56:38 rillig Exp $
+# $NetBSD: varmod-assign.mk,v 1.20 2024/04/20 10:18:55 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
+.if !make(target)
+
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}
+# In the following loop expression,
+# the '::?=' modifier applies the assignment operator '?=' 3 times. The
+# operator '?=' only has an effect for the first time, therefore the variable
+# FIRST ends up with the value 1.
+.if "${1 2 3:L:@i@${FIRST::?=$i}@} first=${FIRST}" != " first=1"
+. error
+.endif
+
+# In the following loop expression,
+# the modifier '::=' applies the assignment operator '=' 3 times. The
+# operator '=' overwrites the previous value, therefore the variable LAST ends
+# up with the value 3.
+.if "${1 2 3:L:@i@${LAST::=$i}@} last=${LAST}" != " last=3"
+. error
+.endif
+
+# In the following loop expression,
+# the modifier '::+=' applies the assignment operator '+=' 3 times. The
+# operator '+=' appends 3 times to the variable, therefore the variable
+# APPENDED ends up with the value "1 2 3".
+.if "${1 2 3:L:@i@${APPENDED::+=$i}@} appended=${APPENDED}" != " appended=1 2 3"
+. error
+.endif
+
+# In the following loop expression,
+# the modifier '::!=' applies the assignment operator '!=' 3 times. Just as
+# with the modifier '::=', the last value is stored in the RAN variable.
+.if "${1 2 3:L:@i@${RAN::!=${i:%=echo '<%>';}}@} ran=${RAN}" != " ran=<3>"
+. error
+.endif
+
+# When a '::=' modifier is evaluated as part of an .if condition, it happens
+# in the command line scope.
+.if "${FIRST}, ${LAST}, ${APPENDED}, ${RAN}" != "1, 3, 1 2 3, <3>"
+. error
+.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
@@ -75,7 +90,8 @@ mod-assign-empty:
mod-assign-parse:
# The modifier for assignment operators starts with a ':'.
# An 'x' after that is an invalid modifier.
- @echo ${ASSIGN::x} # 'x' is an unknown assignment operator
+ # expect: make: in target "mod-assign-parse": while evaluating variable "ASSIGN": Unknown modifier ":x"
+ @echo ${ASSIGN::x}
# When parsing an assignment operator fails because the operator is
# incomplete, make falls back to the SysV modifier.
@@ -106,7 +122,7 @@ APPEND.dollar= $${APPEND.indirect}
.endif
-# The assignment modifier can be used in a variable expression that is
+# The assignment modifier can be used in an expression that is
# enclosed in parentheses. In such a case, parsing stops at the first ')',
# not at the first '}'.
VAR= previous
@@ -139,3 +155,54 @@ ${VARNAME}= initial-value # Sets 'VAR.${param}' to 'expanded'.
. error
.endif
.MAKEFLAGS: -d0
+
+
+# Conditional directives are evaluated in command line scope. An assignment
+# modifier that creates a new variable creates it in the command line scope.
+# Existing variables are updated in their previous scope, and environment
+# variables are created in the global scope, as in other situations.
+.MAKEFLAGS: CMD_CMD_VAR=cmd-value
+CMD_GLOBAL_VAR=global-value
+export CMD_ENV_VAR=env-value
+.MAKEFLAGS: -dv
+# expect-reset
+# expect: Command: CMD_CMD_VAR = new-value
+# expect: Global: CMD_GLOBAL_VAR = new-value
+# expect: Global: CMD_ENV_VAR = new-value
+# expect: Global: ignoring delete 'CMD_NEW_VAR' as it is not found
+# expect: Command: CMD_NEW_VAR = new-value
+.if ${CMD_CMD_VAR::=new-value} \
+ || ${CMD_GLOBAL_VAR::=new-value} \
+ || ${CMD_ENV_VAR::=new-value} \
+ || "${CMD_NEW_VAR::=new-value}"
+. error
+.endif
+.MAKEFLAGS: -d0
+
+# Run the 'target' test in a separate sub-make, with reduced debug logging.
+all: run-target
+run-target: .PHONY
+ @${MAKE} -r -f ${MAKEFILE} -dv target 2>&1 | grep ': TARGET_'
+
+.else # make(target)
+
+# The commands of a target are evaluated in target scope. An assignment
+# modifier that creates a new variable creates it in the target scope.
+# Existing variables are updated in their previous scope, and environment
+# variables are created in the global scope, as in other situations.
+#
+# expect: target: TARGET_TARGET_VAR = new-value
+# expect: Global: TARGET_GLOBAL_VAR = new-value
+# expect: Global: TARGET_ENV_VAR = new-value
+# expect: target: TARGET_NEW_VAR = new-value
+.MAKEFLAGS: TARGET_CMD_VAR=cmd-value
+TARGET_GLOBAL_VAR=global-value
+export TARGET_ENV_VAR=env-value
+target: .PHONY TARGET_TARGET_VAR=target-value
+ : ${TARGET_TARGET_VAR::=new-value}
+ : ${TARGET_CMD_VAR::=new-value}
+ : ${TARGET_GLOBAL_VAR::=new-value}
+ : ${TARGET_ENV_VAR::=new-value}
+ : ${TARGET_NEW_VAR::=new-value}
+
+.endif
diff --git a/contrib/bmake/unit-tests/varmod-defined.exp b/contrib/bmake/unit-tests/varmod-defined.exp
index 2f7d4dbf4baa..d82a292292a4 100644
--- a/contrib/bmake/unit-tests/varmod-defined.exp
+++ b/contrib/bmake/unit-tests/varmod-defined.exp
@@ -1,5 +1,5 @@
Global: 8_DOLLARS = $$$$$$$$
-Global: VAR =
+Global: VAR = # (empty)
Var_Parse: ${8_DOLLARS} (eval-keep-dollar-and-undefined)
Global: VAR = $$$$$$$$
Var_Parse: ${VAR:D${8_DOLLARS}} (eval-keep-dollar-and-undefined)
@@ -11,11 +11,11 @@ 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 "$$$$"
-Global:delete var
+ModifyWord_Loop: expand "${8_DOLLARS}" to "$$$$"
+Global: delete var
Result of ${VAR:@var@${8_DOLLARS}@} is "$$$$" (eval-keep-dollar-and-undefined, regular)
Global: VAR = $$$$
Global: .MAKEFLAGS = -r -k -d v -d
diff --git a/contrib/bmake/unit-tests/varmod-defined.mk b/contrib/bmake/unit-tests/varmod-defined.mk
index a44b9f993146..2ee9def9e164 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.16 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the :D variable modifier, which returns the given string
# if the variable is defined. It is closely related to the :U modifier.
+# 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
@@ -43,10 +46,10 @@ DEF= defined
. error
.endif
-# Like in several other places in variable expressions, when
+# Like in several other places in expressions, when
# ApplyModifier_Defined calls Var_Parse, double dollars lead to a parse
# error that is silently ignored. This makes all dollar signs disappear,
-# except for the last, which is a well-formed variable expression.
+# except for the last, which is a well-formed expression.
#
.if ${DEF:D$$$$$${DEF}} != "defined"
. error
@@ -55,7 +58,7 @@ DEF= defined
# Any other text is written without any further escaping. In contrast
# to the :M modifier, parentheses and braces do not need to be nested.
# Instead, the :D modifier is implemented sanely by parsing nested
-# expressions as such, without trying any shortcuts. See ApplyModifier_Match
+# expressions as such, without trying any shortcuts. See ParseModifier_Match
# for an inferior variant.
#
.if ${DEF:D!&((((} != "!&(((("
@@ -101,5 +104,13 @@ VAR:= ${VAR:D${8_DOLLARS}}
VAR:= ${VAR:@var@${8_DOLLARS}@}
.MAKEFLAGS: -d0
-all:
- @:;
+
+# Before var.c 1.1030 from 2022-08-24, the following expression caused an
+# out-of-bounds read when parsing the indirect ':U' modifier.
+M_U_backslash:= ${:UU\\}
+.if ${:${M_U_backslash}} != "\\"
+. error
+.endif
+
+
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/varmod-edge.exp b/contrib/bmake/unit-tests/varmod-edge.exp
index d9db72b2e2ef..fad5cb2c3c94 100644
--- a/contrib/bmake/unit-tests/varmod-edge.exp
+++ b/contrib/bmake/unit-tests/varmod-edge.exp
@@ -1,27 +1,27 @@
-make: "varmod-edge.mk" line 166: ok M-paren
-make: "varmod-edge.mk" line 166: ok M-mixed
-make: "varmod-edge.mk" line 166: ok M-unescape
-make: Unclosed variable expression, expecting '}' for modifier "U*)" of variable "" with value "*)"
-make: "varmod-edge.mk" line 166: ok M-nest-mix
-make: "varmod-edge.mk" line 166: ok M-nest-brk
-make: "varmod-edge.mk" line 166: ok M-pat-err
-make: "varmod-edge.mk" line 166: ok M-bsbs
-make: "varmod-edge.mk" line 166: ok M-bs1-par
-make: "varmod-edge.mk" line 166: ok M-bs2-par
-make: "varmod-edge.mk" line 166: ok M-128
-make: "varmod-edge.mk" line 166: ok eq-ext
-make: "varmod-edge.mk" line 166: ok eq-q
-make: "varmod-edge.mk" line 166: ok eq-bs
+make: "varmod-edge.mk" line 184: ok M-paren
+make: "varmod-edge.mk" line 184: ok M-mixed
+make: "varmod-edge.mk" line 184: ok M-unescape
+make: Unclosed expression, expecting '}' for modifier "U*)" of variable "" with value "*)"
+make: "varmod-edge.mk" line 184: ok M-nest-mix
+make: "varmod-edge.mk" line 184: ok M-nest-brk
+make: "varmod-edge.mk" line 184: ok M-pat-err
+make: "varmod-edge.mk" line 184: ok M-bsbs
+make: "varmod-edge.mk" line 184: ok M-bs1-par
+make: "varmod-edge.mk" line 184: ok M-bs2-par
+make: "varmod-edge.mk" line 184: ok M-128
+make: "varmod-edge.mk" line 184: ok eq-ext
+make: "varmod-edge.mk" line 184: ok eq-q
+make: "varmod-edge.mk" line 184: ok eq-bs
make: Unfinished modifier for "INP.eq-esc" ('=' missing)
-make: "varmod-edge.mk" line 166: ok eq-esc
-make: "varmod-edge.mk" line 166: ok colon
-make: "varmod-edge.mk" line 165: Unknown modifier ":"
-make: "varmod-edge.mk" line 165: Unknown modifier ":"
-make: "varmod-edge.mk" line 166: ok colons
-make: "varmod-edge.mk" line 175: Unknown modifier "Z"
-make: "varmod-edge.mk" line 175: Malformed conditional (${:Z})
+make: "varmod-edge.mk" line 184: ok eq-esc
+make: "varmod-edge.mk" line 184: ok colon
+make: "varmod-edge.mk" line 167: while evaluating variable "MOD.colons": while evaluating variable "INP.colons": Unknown modifier ":"
+make: "varmod-edge.mk" line 167: while evaluating variable "MOD.colons": while evaluating variable "INP.colons": Unknown modifier ":"
+make: "varmod-edge.mk" line 184: ok colons
+make: "varmod-edge.mk" line 195: while evaluating "${:Z}": Unknown modifier "Z"
+make: "varmod-edge.mk" line 195: Malformed conditional (${:Z})
make: Unfinished modifier for "" (',' missing)
-make: "varmod-edge.mk" line 188: Malformed conditional (${:S,})
+make: "varmod-edge.mk" line 209: Malformed conditional (${:S,})
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-edge.mk b/contrib/bmake/unit-tests/varmod-edge.mk
index 762053d281a3..2f8f8c793de1 100644
--- a/contrib/bmake/unit-tests/varmod-edge.mk
+++ b/contrib/bmake/unit-tests/varmod-edge.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-edge.mk,v 1.16 2021/02/23 15:56:30 rillig Exp $
+# $NetBSD: varmod-edge.mk,v 1.20 2024/04/20 10:18:55 rillig Exp $
#
# Tests for edge cases in variable modifiers.
#
@@ -16,7 +16,7 @@ MOD.M-paren= ${INP.M-paren:M(*)}
EXP.M-paren= (parentheses) ()
# The first closing brace matches the opening parenthesis.
-# The second closing brace actually ends the variable expression.
+# The second closing brace actually ends the expression.
#
# XXX: This is unexpected but rarely occurs in practice.
TESTS+= M-mixed
@@ -40,7 +40,7 @@ EXP.M-unescape= \(\{}\):
# as open_parens + open_braces == closing_parens + closing_braces. This
# means that ( and } form a matching pair.
#
-# Nested variable expressions are not parsed as such. Instead, only the
+# Nested expressions are not parsed as such. Instead, only the
# parentheses and braces are counted. This leads to a parse error since
# the nested expression is not "${:U*)}" but only "${:U*)", which is
# missing the closing brace. The expression is evaluated anyway.
@@ -51,7 +51,7 @@ TESTS+= M-nest-mix
INP.M-nest-mix= (parentheses)
MOD.M-nest-mix= ${INP.M-nest-mix:M${:U*)}}
EXP.M-nest-mix= (parentheses)}
-# make: Unclosed variable expression, expecting '}' for modifier "U*)" of variable "" with value "*)"
+# make: Unclosed expression, expecting '}' for modifier "U*)" of variable "" with value "*)"
# In contrast to parentheses and braces, the brackets are not counted
# when the :M modifier is parsed since Makefile variables only take the
@@ -162,7 +162,25 @@ MOD.colons= ${INP.colons::::}
EXP.colons= # empty
.for test in ${TESTS}
+# expect+2: while evaluating variable "MOD.colons": while evaluating variable "INP.colons": Unknown modifier ":"
+# expect+1: while evaluating variable "MOD.colons": while evaluating variable "INP.colons": Unknown modifier ":"
. if ${MOD.${test}} == ${EXP.${test}}
+# expect+16: ok M-paren
+# expect+15: ok M-mixed
+# expect+14: ok M-unescape
+# expect+13: ok M-nest-mix
+# expect+12: ok M-nest-brk
+# expect+11: ok M-pat-err
+# expect+10: ok M-bsbs
+# expect+09: ok M-bs1-par
+# expect+08: ok M-bs2-par
+# expect+07: ok M-128
+# expect+06: ok eq-ext
+# expect+05: ok eq-q
+# expect+04: ok eq-bs
+# expect+03: ok eq-esc
+# expect+02: ok colon
+# expect+01: ok colons
. info ok ${test}
. else
. warning error in ${test}: expected "${EXP.${test}}", got "${MOD.${test}}"
@@ -172,6 +190,8 @@ EXP.colons= # empty
# Even in expressions based on an unnamed variable, there may be errors.
# XXX: The error message should mention the variable name of the expression,
# even though that name is empty in this case.
+# expect+2: Malformed conditional (${:Z})
+# expect+1: while evaluating "${:Z}": Unknown modifier "Z"
.if ${:Z}
. error
.else
@@ -185,6 +205,7 @@ EXP.colons= # empty
# modifier for (',' missing)", having two spaces in a row.
#
# XXX: The error message should report the filename:lineno.
+# expect+1: Malformed conditional (${:S,})
.if ${:S,}
. error
.else
diff --git a/contrib/bmake/unit-tests/varmod-gmtime.exp b/contrib/bmake/unit-tests/varmod-gmtime.exp
index 5d5806b92d26..1b12ead96d85 100644
--- a/contrib/bmake/unit-tests/varmod-gmtime.exp
+++ b/contrib/bmake/unit-tests/varmod-gmtime.exp
@@ -1,13 +1,13 @@
-make: "varmod-gmtime.mk" line 57: Invalid time value: ${:U1593536400}} != "mtime=11593536400}"
-make: "varmod-gmtime.mk" line 57: Malformed conditional (${%Y:L:gmtime=${:U1593536400}} != "mtime=11593536400}")
-make: "varmod-gmtime.mk" line 67: Invalid time value: -1} != ""
-make: "varmod-gmtime.mk" line 67: Malformed conditional (${:L:gmtime=-1} != "")
-make: "varmod-gmtime.mk" line 76: Invalid time value: 1} != ""
-make: "varmod-gmtime.mk" line 76: Malformed conditional (${:L:gmtime= 1} != "")
-make: "varmod-gmtime.mk" line 119: Invalid time value: 10000000000000000000000000000000} != ""
-make: "varmod-gmtime.mk" line 119: Malformed conditional (${:L:gmtime=10000000000000000000000000000000} != "")
-make: "varmod-gmtime.mk" line 130: Invalid time value: error} != ""
-make: "varmod-gmtime.mk" line 130: Malformed conditional (${:L:gmtime=error} != "")
+make: "varmod-gmtime.mk" line 61: while evaluating "${:L:gmtime=-1} != """: Invalid time value "-1"
+make: "varmod-gmtime.mk" line 61: Malformed conditional (${:L:gmtime=-1} != "")
+make: "varmod-gmtime.mk" line 72: while evaluating "${:L:gmtime= 1} != """: Invalid time value " 1"
+make: "varmod-gmtime.mk" line 72: Malformed conditional (${:L:gmtime= 1} != "")
+make: "varmod-gmtime.mk" line 120: while evaluating "${:L:gmtime=10000000000000000000000000000000} != """: Invalid time value "10000000000000000000000000000000"
+make: "varmod-gmtime.mk" line 120: Malformed conditional (${:L:gmtime=10000000000000000000000000000000} != "")
+make: "varmod-gmtime.mk" line 133: while evaluating "${:L:gmtime=error} != """: Invalid time value "error"
+make: "varmod-gmtime.mk" line 133: Malformed conditional (${:L:gmtime=error} != "")
+make: "varmod-gmtime.mk" line 144: while evaluating variable "%Y": Invalid time value "100000S,1970,bad,"
+make: "varmod-gmtime.mk" line 144: Malformed conditional (${%Y:L:gmtime=100000S,1970,bad,} != "bad")
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-gmtime.mk b/contrib/bmake/unit-tests/varmod-gmtime.mk
index cb3d4e7eb241..db24b1680c46 100644
--- a/contrib/bmake/unit-tests/varmod-gmtime.mk
+++ b/contrib/bmake/unit-tests/varmod-gmtime.mk
@@ -1,7 +1,10 @@
-# $NetBSD: varmod-gmtime.mk,v 1.10 2021/01/19 05:26:34 rillig Exp $
+# $NetBSD: varmod-gmtime.mk,v 1.22 2024/04/20 10:18:55 rillig Exp $
#
# Tests for the :gmtime variable modifier, which formats a timestamp
# using strftime(3) in UTC.
+#
+# See also:
+# varmod-localtime.mk
.if ${TZ:Uundefined} != "undefined" # see unit-tests/Makefile
. error
@@ -41,20 +44,9 @@
.endif
-# As of 2020-08-16, it is not possible to pass the seconds via a
-# variable expression. This is because parsing of the :gmtime
-# modifier stops at the '$' and returns to ApplyModifiers.
-#
-# There, a colon would be skipped but not a dollar.
-# Parsing therefore continues at the '$' of the ${:U159...}, looking
-# for an ordinary variable modifier.
-#
-# At this point, the ${:U} is expanded and interpreted as a variable
-# modifier, which results in the error message "Unknown modifier '1'".
-#
-# If ApplyModifier_Gmtime were to pass its argument through
-# ParseModifierPart, this would work.
-.if ${%Y:L:gmtime=${:U1593536400}} != "mtime=11593536400}"
+# Before var.c 1.1050 from 2023-05-09, it was not possible to pass the
+# seconds via an expression.
+.if ${%Y:L:gmtime=${:U1593536400}} != "2020"
. error
.endif
@@ -64,6 +56,8 @@
# 1970. Going back 50 years in the past is not a practical use case for
# make. Therefore, since var.c 1.631, negative time stamps produce a
# parse error.
+# expect+2: while evaluating "${:L:gmtime=-1} != """: Invalid time value "-1"
+# expect+1: Malformed conditional (${:L:gmtime=-1} != "")
.if ${:L:gmtime=-1} != ""
. error
.else
@@ -73,8 +67,12 @@
# Spaces were allowed before var.c 1.631 from 2020-10-31 21:40:20, not
# because it would make sense but just as a side-effect from using strtoul.
+# expect+2: while evaluating "${:L:gmtime= 1} != """: Invalid time value " 1"
+# expect+1: Malformed conditional (${:L:gmtime= 1} != "")
.if ${:L:gmtime= 1} != ""
. error
+.else
+. error
.endif
@@ -115,7 +113,10 @@
# ULONG_MAX, which got converted to -1. This resulted in a time stamp of
# the second before 1970.
#
-# Since var.c 1.631, the overflow is detected and produces a parse error.
+# Since var.c 1.631 from 2020-10-31, the overflow is detected and produces a
+# parse error.
+# expect+2: while evaluating "${:L:gmtime=10000000000000000000000000000000} != """: Invalid time value "10000000000000000000000000000000"
+# expect+1: Malformed conditional (${:L:gmtime=10000000000000000000000000000000} != "")
.if ${:L:gmtime=10000000000000000000000000000000} != ""
. error
.else
@@ -127,11 +128,61 @@
# stopped after the '=', and the remaining string was parsed for more variable
# modifiers. Because of the unknown modifier 'e' from the 'error', the whole
# variable value was discarded and thus not printed.
+# expect+2: while evaluating "${:L:gmtime=error} != """: Invalid time value "error"
+# expect+1: Malformed conditional (${:L:gmtime=error} != "")
.if ${:L:gmtime=error} != ""
. error
.else
. error
.endif
+# Before var.c 1.1050 from 2023-05-09, the timestamp could be directly
+# followed by the next modifier, without a ':' separator. This was the same
+# bug as for the ':L' and ':P' modifiers.
+# expect+2: while evaluating variable "%Y": Invalid time value "100000S,1970,bad,"
+# expect+1: Malformed conditional (${%Y:L:gmtime=100000S,1970,bad,} != "bad")
+.if ${%Y:L:gmtime=100000S,1970,bad,} != "bad"
+. error
+.endif
+
+
+# Before var.c 1.1062 from 2023-08-19, ':gmtime' but not ':localtime' reported
+# wrong values for '%s', depending on the operating system and the timezone.
+export TZ=UTC
+.for t in ${%s:L:gmtime} ${%s:L:localtime}
+TIMESTAMPS+= $t
+.endfor
+export TZ=Europe/Berlin
+.for t in ${%s:L:gmtime} ${%s:L:localtime}
+TIMESTAMPS+= $t
+.endfor
+export TZ=UTC
+.for t in ${%s:L:gmtime} ${%s:L:localtime}
+TIMESTAMPS+= $t
+.endfor
+export TZ=America/Los_Angeles
+.for t in ${%s:L:gmtime} ${%s:L:localtime}
+TIMESTAMPS+= $t
+.endfor
+export TZ=UTC
+.for t in ${%s:L:gmtime} ${%s:L:localtime}
+TIMESTAMPS+= $t
+.endfor
+.for a b in ${TIMESTAMPS:[1]} ${TIMESTAMPS:@t@$t $t@} ${TIMESTAMPS:[-1]}
+. if $a > $b
+. warning timestamp $a > $b
+. endif
+.endfor
+
+
+.if ${year=%Y month=%m day=%d:L:gmtime=1459494000} != "year=2016 month=04 day=01"
+. error
+.endif
+# Slightly contorted syntax to convert a UTC timestamp from an expression to a
+# formatted timestamp.
+.if ${%Y%m%d:L:${gmtime=${:U1459494000}:L}} != "20160401"
+. error
+.endif
+
all:
diff --git a/contrib/bmake/unit-tests/varmod-hash.exp b/contrib/bmake/unit-tests/varmod-hash.exp
index 1286b456c6c2..e385c3b3ae11 100644
--- a/contrib/bmake/unit-tests/varmod-hash.exp
+++ b/contrib/bmake/unit-tests/varmod-hash.exp
@@ -1,9 +1,9 @@
-make: Unknown modifier "has"
+make: in target "all": while evaluating variable "12345": Unknown modifier "has"
26bb0f5f
12345
-make: Unknown modifier "hasX"
+make: in target "all": while evaluating variable "12345": Unknown modifier "hasX"
-make: Unknown modifier "hashed"
+make: in target "all": while evaluating variable "12345": Unknown modifier "hashed"
exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-head.exp b/contrib/bmake/unit-tests/varmod-head.exp
index 651844439f5f..39a9383953dd 100644
--- a/contrib/bmake/unit-tests/varmod-head.exp
+++ b/contrib/bmake/unit-tests/varmod-head.exp
@@ -1,11 +1 @@
-head (dirname) of 'a/b/c' is 'a/b'
-head (dirname) of 'def' is '.'
-head (dirname) of 'a.b.c' is '.'
-head (dirname) of 'a.b/c' is 'a.b'
-head (dirname) of 'a' is '.'
-head (dirname) of 'a.a' is '.'
-head (dirname) of '.gitignore' is '.'
-head (dirname) of 'a' is '.'
-head (dirname) of 'a.a' is '.'
-head (dirname) of 'trailing/' is 'trailing'
exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-head.mk b/contrib/bmake/unit-tests/varmod-head.mk
index 66347b4bce61..f1a135cb328d 100644
--- a/contrib/bmake/unit-tests/varmod-head.mk
+++ b/contrib/bmake/unit-tests/varmod-head.mk
@@ -1,9 +1,64 @@
-# $NetBSD: varmod-head.mk,v 1.4 2020/12/20 22:57:40 rillig Exp $
+# $NetBSD: varmod-head.mk,v 1.5 2022/07/10 21:11:49 rillig Exp $
#
# Tests for the :H variable modifier, which returns the dirname of
# each of the words in the variable value.
-all:
-.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a trailing/
- @echo "head (dirname) of '"${path:Q}"' is '"${path:H:Q}"'"
-.endfor
+.if ${:U a/b/c :H} != "a/b"
+. error
+.endif
+
+.if ${:U def :H} != "."
+. error
+.endif
+
+.if ${:U a.b.c :H} != "."
+. error
+.endif
+
+.if ${:U a.b/c :H} != "a.b"
+. error
+.endif
+
+.if ${:U a :H} != "."
+. error
+.endif
+
+.if ${:U a.a :H} != "."
+. error
+.endif
+
+.if ${:U .gitignore :H} != "."
+. error
+.endif
+
+.if ${:U trailing/ :H} != "trailing"
+. error
+.endif
+
+.if ${:U /abs/dir/file :H} != "/abs/dir"
+. error
+.endif
+
+.if ${:U rel/dir/file :H} != "rel/dir"
+. error
+.endif
+
+# The head of "/" was an empty string before 2020.07.20.14.50.41, leading to
+# the output "before after", with two spaces. Since 2020.07.20.14.50.41, the
+# output is "before after", discarding the empty word.
+.if ${:U before/ / after/ :H} == "before after"
+# OK
+.elif ${:U before/ / after/ :H} == "before after"
+# No '.info' to keep the file compatible with old make versions.
+_!= echo "The modifier ':H' generates an empty word." 1>&2; echo
+.else
+. error
+.endif
+
+# An empty list is split into a single empty word.
+# The dirname of this empty word is ".".
+.if ${:U :H} != "."
+. error
+.endif
+
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/varmod-ifelse.exp b/contrib/bmake/unit-tests/varmod-ifelse.exp
index e42e39525f1c..8b9d41bd2427 100644
--- a/contrib/bmake/unit-tests/varmod-ifelse.exp
+++ b/contrib/bmake/unit-tests/varmod-ifelse.exp
@@ -1,32 +1,51 @@
-make: Bad conditional expression 'variable expression == "literal"' in 'variable expression == "literal"?bad:bad'
-make: "varmod-ifelse.mk" line 27: Malformed conditional (${${:Uvariable expression} == "literal":?bad:bad})
-make: Bad conditional expression ' == ""' in ' == ""?bad-assign:bad-assign'
-make: Bad conditional expression ' == ""' in ' == ""?bad-cond:bad-cond'
-make: "varmod-ifelse.mk" line 44: Malformed conditional (${${UNDEF} == "":?bad-cond:bad-cond})
-make: Bad conditional expression '1 == == 2' in '1 == == 2?yes:no'
-make: "varmod-ifelse.mk" line 66: Malformed conditional (${1 == == 2:?yes:no} != "")
+make: Bad conditional expression 'bare words == "literal"' before '?bad:bad'
+make: "varmod-ifelse.mk" line 28: Malformed conditional (${${:Ubare words} == "literal":?bad:bad})
+make: Bad conditional expression ' == ""' before '?bad-assign:bad-assign'
+make: Bad conditional expression ' == ""' before '?bad-cond:bad-cond'
+make: "varmod-ifelse.mk" line 46: Malformed conditional (${${UNDEF} == "":?bad-cond:bad-cond})
+make: Bad conditional expression '1 == == 2' before '?yes:no'
+make: "varmod-ifelse.mk" line 69: Malformed conditional (${1 == == 2:?yes:no} != "")
CondParser_Eval: "${1 == == 2:?yes:no}" != ""
CondParser_Eval: 1 == == 2
-lhs = 1.000000, rhs = 0.000000, op = ==
-make: Bad conditional expression '1 == == 2' in '1 == == 2?yes:no'
-lhs = "", rhs = "", op = !=
-make: "varmod-ifelse.mk" line 92: warning: Oops, the parse error should have been propagated.
-CondParser_Eval: ${ ${:U\$}{VAR} == value :?ok:bad} != "ok"
-CondParser_Eval: ${VAR} == value
-lhs = "value", rhs = "value", op = ==
-lhs = "ok", rhs = "ok", op = !=
-make: "varmod-ifelse.mk" line 153: no.
-make: "varmod-ifelse.mk" line 154: String comparison operator must be either == or !=
-make: Bad conditional expression 'string == "literal" || no >= 10' in 'string == "literal" || no >= 10?yes:no'
-make: "varmod-ifelse.mk" line 154: .
-make: Bad conditional expression 'string == "literal" && >= 10' in 'string == "literal" && >= 10?yes:no'
-make: "varmod-ifelse.mk" line 159: .
-make: Bad conditional expression 'string == "literal" || >= 10' in 'string == "literal" || >= 10?yes:no'
-make: "varmod-ifelse.mk" line 160: .
-make: "varmod-ifelse.mk" line 167: true
-make: "varmod-ifelse.mk" line 169: false
-make: Bad conditional expression ' ' in ' ?true:false'
-make: "varmod-ifelse.mk" line 171:
+Comparing 1.000000 == 0.000000
+make: Bad conditional expression '1 == == 2' before '?yes:no'
+Comparing "" != ""
+make: "varmod-ifelse.mk" line 96: warning: Oops, the parse error should have been propagated.
+CondParser_Eval: ${ ${:U\$}{VAR} == value:?ok:bad} != "ok"
+CondParser_Eval: ${VAR} == value
+Comparing "value" == "value"
+Comparing "ok" != "ok"
+make: "varmod-ifelse.mk" line 158: no.
+make: "varmod-ifelse.mk" line 162: while evaluating variable "string == "literal" || no >= 10": Comparison with '>=' requires both operands 'no' and '10' to be numeric
+make: Bad conditional expression 'string == "literal" || no >= 10' before '?yes:no'
+make: "varmod-ifelse.mk" line 162: .
+make: Bad conditional expression 'string == "literal" && >= 10' before '?yes:no'
+make: "varmod-ifelse.mk" line 169: .
+make: Bad conditional expression 'string == "literal" || >= 10' before '?yes:no'
+make: "varmod-ifelse.mk" line 172: .
+make: "varmod-ifelse.mk" line 180: <true>
+make: "varmod-ifelse.mk" line 183: <false>
+make: Bad conditional expression ' ' before '?true:false'
+make: "varmod-ifelse.mk" line 186: <>
+CondParser_Eval: 0 && ${1:?${:Uthen0:S,}},,}:${:Uelse0:S,}},,}} != "not evaluated"
+CondParser_Eval: 1 && ${0:?${:Uthen1:S,}},,}:${:Uelse1:S,}},,}} != "else1"
+CondParser_Eval: 0
+Comparing "else1" != "else1"
+CondParser_Eval: 2 && ${1:?${:Uthen2:S,}},,}:${:Uelse2:S,}},,}} != "then2"
+CondParser_Eval: 1
+Comparing "then2" != "then2"
+CondParser_Eval: ${DELAYED} == "one"
+Comparing "two" == "one"
+make: "varmod-ifelse.mk" line 282: no
+CondParser_Eval: ${DELAYED} == "two"
+Comparing "two" == "two"
+make: "varmod-ifelse.mk" line 284: yes
+CondParser_Eval: ${DELAYED} == "one"
+Comparing "two" == "one"
+make: "varmod-ifelse.mk" line 287: no
+CondParser_Eval: ${DELAYED} == "two"
+Comparing "two" == "two"
+make: "varmod-ifelse.mk" line 290: yes
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-ifelse.mk b/contrib/bmake/unit-tests/varmod-ifelse.mk
index 0e16032a6543..3bf433027950 100644
--- a/contrib/bmake/unit-tests/varmod-ifelse.mk
+++ b/contrib/bmake/unit-tests/varmod-ifelse.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-ifelse.mk,v 1.17 2021/06/11 13:01:28 rillig Exp $
+# $NetBSD: varmod-ifelse.mk,v 1.28 2024/04/23 22:51:28 rillig Exp $
#
# Tests for the ${cond:?then:else} variable modifier, which evaluates either
# the then-expression or the else-expression, depending on the condition.
@@ -11,20 +11,21 @@
# TODO: Implementation
# The variable name of the expression is expanded and then taken as the
-# condition. In this case it becomes:
+# condition. In the below example it becomes:
#
-# variable expression == "variable expression"
+# bare words == "literal"
#
# This confuses the parser, which expects an operator instead of the bare
# word "expression". If the name were expanded lazily, everything would be
# fine since the condition would be:
#
-# ${:Uvariable expression} == "literal"
+# ${:Ubare words} == "literal"
#
# Evaluating the variable name lazily would require additional code in
# Var_Parse and ParseVarname, it would be more useful and predictable
# though.
-.if ${${:Uvariable expression} == "literal":?bad:bad}
+# expect+1: Malformed conditional (${${:Ubare words} == "literal":?bad:bad})
+.if ${${:Ubare words} == "literal":?bad:bad}
. error
.else
. error
@@ -41,6 +42,7 @@ COND:= ${${UNDEF} == "":?bad-assign:bad-assign}
# "Undefined variable" error message is generated.
# The difference to the ':=' variable assignment is the additional
# "Malformed conditional" error message.
+# expect+1: Malformed conditional (${${UNDEF} == "":?bad-cond:bad-cond})
.if ${${UNDEF} == "":?bad-cond:bad-cond}
. error
.else
@@ -59,10 +61,11 @@ COND:= ${${UNDEF} == "":?bad-assign:bad-assign}
# This line generates 2 error messages. The first comes from evaluating the
# malformed conditional "1 == == 2", which is reported as "Bad conditional
-# expression" by ApplyModifier_IfElse. The variable expression containing that
+# expression" by ApplyModifier_IfElse. The expression containing that
# conditional therefore returns a parse error from Var_Parse, and this parse
# error propagates to CondEvalExpression, where the "Malformed conditional"
# comes from.
+# expect+1: Malformed conditional (${1 == == 2:?yes:no} != "")
.if ${1 == == 2:?yes:no} != ""
. error
.else
@@ -76,7 +79,7 @@ COND:= ${${UNDEF} == "":?bad-assign:bad-assign}
# XXX: The left-hand side is enclosed in quotes. This results in Var_Parse
# being called without VARE_UNDEFERR. When ApplyModifier_IfElse
# returns AMR_CLEANUP as result, Var_Parse returns varUndefined since the
-# value of the variable expression is still undefined. CondParser_String is
+# value of the expression is still undefined. CondParser_String is
# then supposed to do proper error handling, but since varUndefined is local
# to var.c, it cannot distinguish this return value from an ordinary empty
# string. The left-hand side of the comparison is therefore just an empty
@@ -89,15 +92,16 @@ COND:= ${${UNDEF} == "":?bad-assign:bad-assign}
.if "${1 == == 2:?yes:no}" != ""
. error
.else
+# expect+1: warning: Oops, the parse error should have been propagated.
. warning Oops, the parse error should have been propagated.
.endif
.MAKEFLAGS: -d0
-# As of 2020-12-10, the variable "name" is first expanded, and the result of
-# this expansion is then taken as the condition. To force the variable
+# As of 2020-12-10, the variable "VAR" is first expanded, and the result of
+# this expansion is then taken as the condition. To force the
# expression in the condition to be evaluated at exactly the right point,
# the '$' of the intended '${VAR}' escapes from the parser in form of the
-# expression ${:U\$}. Because of this escaping, the variable "name" and thus
+# expression ${:U\$}. Because of this escaping, the variable "VAR" and thus
# the condition ends up as "${VAR} == value", just as intended.
#
# This hack does not work for variables from .for loops since these are
@@ -106,7 +110,7 @@ COND:= ${${UNDEF} == "":?bad-assign:bad-assign}
# from the parser of the .for loop body. See ForLoop_SubstVarLong.
.MAKEFLAGS: -dc
VAR= value
-.if ${ ${:U\$}{VAR} == value :?ok:bad} != "ok"
+.if ${ ${:U\$}{VAR} == value:?ok:bad} != "ok"
. error
.endif
.MAKEFLAGS: -d0
@@ -133,7 +137,7 @@ VAR= value
# When parsing such an expression, the parser used to be strict. It first
# evaluated the left-hand side of the operator '&&' and then started parsing
# the right-hand side 'no >= 10'. The word 'no' is obviously a string
-# literal, not enclosed in quotes, which is ok, even on the left-hand side of
+# literal, not enclosed in quotes, which is OK, even on the left-hand side of
# the comparison operator, but only because this is a condition in the
# modifier ':?'. In an ordinary directive '.if', this would be a parse error.
# For strings, only the comparison operators '==' and '!=' are defined,
@@ -150,13 +154,21 @@ VAR= value
# instead of just saying that the whole condition is bad.
STRING= string
NUMBER= no # not really a number
+# expect+1: no.
.info ${${STRING} == "literal" && ${NUMBER} >= 10:?yes:no}.
+# expect+3: while evaluating variable "string == "literal" || no >= 10": Comparison with '>=' requires both operands 'no' and '10' to be numeric
+# expect: make: Bad conditional expression 'string == "literal" || no >= 10' before '?yes:no'
+# expect+1: .
.info ${${STRING} == "literal" || ${NUMBER} >= 10:?yes:no}.
# The following situation occasionally occurs with MKINET6 or similar
# variables.
NUMBER= # empty, not really a number either
+# expect: make: Bad conditional expression 'string == "literal" && >= 10' before '?yes:no'
+# expect+1: .
.info ${${STRING} == "literal" && ${NUMBER} >= 10:?yes:no}.
+# expect: make: Bad conditional expression 'string == "literal" || >= 10' before '?yes:no'
+# expect+1: .
.info ${${STRING} == "literal" || ${NUMBER} >= 10:?yes:no}.
# CondParser_LeafToken handles [0-9-+] specially, treating them as a number.
@@ -164,8 +176,132 @@ PLUS= +
ASTERISK= *
EMPTY= # empty
# "true" since "+" is not the empty string.
-.info ${${PLUS} :?true:false}
+# expect+1: <true>
+.info <${${PLUS} :?true:false}>
# "false" since the variable named "*" is not defined.
-.info ${${ASTERISK} :?true:false}
+# expect+1: <false>
+.info <${${ASTERISK} :?true:false}>
# syntax error since the condition is completely blank.
-.info ${${EMPTY} :?true:false}
+# expect+1: <>
+.info <${${EMPTY} :?true:false}>
+
+
+# Since the condition of the '?:' modifier is expanded before being parsed and
+# evaluated, it is common practice to enclose expressions in quotes, to avoid
+# producing syntactically invalid conditions such as ' == value'. This only
+# works if the expanded values neither contain quotes nor backslashes. For
+# strings containing quotes or backslashes, the '?:' modifier should not be
+# used.
+PRIMES= 2 3 5 7 11
+.if ${1 2 3 4 5:L:@n@$n:${ ("${PRIMES:M$n}" != "") :?prime:not_prime}@} != \
+ "1:not_prime 2:prime 3:prime 4:not_prime 5:prime"
+. error
+.endif
+
+# When parsing the modifier ':?', there are 3 possible cases:
+#
+# 1. The whole expression is only parsed.
+# 2. The expression is parsed and the 'then' branch is evaluated.
+# 3. The expression is parsed and the 'else' branch is evaluated.
+#
+# In all of these cases, the expression must be parsed in the same way,
+# especially when one of the branches contains unbalanced '{}' braces.
+#
+# At 2020-01-01, the expressions from the 'then' and 'else' branches were
+# parsed differently, depending on whether the branch was taken or not. When
+# the branch was taken, the parser recognized that in the modifier ':S,}},,',
+# the '}}' were ordinary characters. When the branch was not taken, the
+# parser only counted balanced '{' and '}', ignoring any escaping or other
+# changes in the interpretation.
+#
+# In var.c 1.285 from 2020-07-20, the parsing of the expressions changed so
+# that in both cases the expression is parsed in the same way, taking the
+# unbalanced braces in the ':S' modifiers into account. This change was not
+# on purpose, the commit message mentioned 'has the same effect', which was a
+# wrong assumption.
+#
+# In var.c 1.323 from 2020-07-26, the unintended fix from var.c 1.285 was
+# reverted, still not knowing about the difference between regular parsing and
+# balanced-mode parsing.
+#
+# In var.c 1.1028 from 2022-08-08, there was another attempt at fixing this
+# inconsistency in parsing, but since that broke parsing of the modifier ':@',
+# it was reverted in var.c 1.1029 from 2022-08-23.
+#
+# In var.c 1.1047 from 2023-02-18, the inconsistency in parsing was finally
+# fixed. The modifier ':@' now parses the body in balanced mode, while
+# everywhere else the modifier parts have their subexpressions parsed in the
+# same way, no matter whether they are evaluated or not.
+#
+# The modifiers ':@' and ':?' are similar in that they conceptually contain
+# text to be evaluated later or conditionally, still they parse that text
+# differently. The crucial difference is that the body of the modifier ':@'
+# is always parsed using balanced mode. The modifier ':?', on the other hand,
+# must parse both of its branches in the same way, no matter whether they are
+# evaluated or not. Since balanced mode and standard mode are incompatible,
+# it's impossible to use balanced mode in the modifier ':?'.
+.MAKEFLAGS: -dc
+.if 0 && ${1:?${:Uthen0:S,}},,}:${:Uelse0:S,}},,}} != "not evaluated"
+# At 2020-01-07, the expression evaluated to 'then0,,}}', even though it was
+# irrelevant as the '0' had already been evaluated to 'false'.
+. error
+.endif
+.if 1 && ${0:?${:Uthen1:S,}},,}:${:Uelse1:S,}},,}} != "else1"
+. error
+.endif
+.if 2 && ${1:?${:Uthen2:S,}},,}:${:Uelse2:S,}},,}} != "then2"
+# At 2020-01-07, the whole expression evaluated to 'then2,,}}' instead of the
+# expected 'then2'. The 'then' branch of the ':?' modifier was parsed
+# normally, parsing and evaluating the ':S' modifier, thereby treating the
+# '}}' as ordinary characters and resulting in 'then2'. The 'else' branch was
+# parsed in balanced mode, ignoring that the inner '}}' were ordinary
+# characters. The '}}' were thus interpreted as the end of the 'else' branch
+# and the whole expression. This left the trailing ',,}}', which together
+# with the 'then2' formed the result 'then2,,}}'.
+. error
+.endif
+
+
+# Since the condition is taken from the variable name of the expression, not
+# from its value, it is evaluated early. It is possible though to construct
+# conditions that are evaluated lazily, at exactly the right point. There is
+# no way to escape a '$' directly in the variable name, but there are
+# alternative ways to bring a '$' into the condition.
+#
+# In an indirect condition using the ':U' modifier, each '$', ':' and
+# '}' must be escaped as '\$', '\:' and '\}', respectively, but '{' must
+# not be escaped.
+#
+# In an indirect condition using a separate variable, each '$' must be
+# escaped as '$$'.
+#
+# These two forms allow the variables to contain arbitrary characters, as the
+# condition parser does not see them.
+DELAYED= two
+# expect+1: no
+.info ${ ${:U \${DELAYED\} == "one"}:?yes:no}
+# expect+1: yes
+.info ${ ${:U \${DELAYED\} == "two"}:?yes:no}
+INDIRECT_COND1= $${DELAYED} == "one"
+# expect+1: no
+.info ${ ${INDIRECT_COND1}:?yes:no}
+INDIRECT_COND2= $${DELAYED} == "two"
+# expect+1: yes
+.info ${ ${INDIRECT_COND2}:?yes:no}
+
+
+.MAKEFLAGS: -d0
+
+
+# In the modifier parts for the 'then' and 'else' branches, subexpressions are
+# parsed by inspecting the actual modifiers. In 2008, 2015, 2020, 2022 and
+# 2023, the exact parsing algorithm switched a few times, counting balanced
+# braces instead of proper subexpressions, which meant that unbalanced braces
+# were parsed differently, depending on whether the branch was active or not.
+BRACES= }}}
+NO= ${0:?${BRACES:S,}}},yes,}:${BRACES:S,}}},no,}}
+YES= ${1:?${BRACES:S,}}},yes,}:${BRACES:S,}}},no,}}
+BOTH= <${YES}> <${NO}>
+.if ${BOTH} != "<yes> <no>"
+. error
+.endif
diff --git a/contrib/bmake/unit-tests/varmod-indirect.exp b/contrib/bmake/unit-tests/varmod-indirect.exp
index 63ed988d0c0e..376beb8edef9 100644
--- a/contrib/bmake/unit-tests/varmod-indirect.exp
+++ b/contrib/bmake/unit-tests/varmod-indirect.exp
@@ -1,40 +1,40 @@
-make: "varmod-indirect.mk" line 19: Unknown modifier "${"
-make: "varmod-indirect.mk" line 52: Unknown modifier "${"
-make: "varmod-indirect.mk" line 55: warning: FIXME: this expression should have resulted in a parse error rather than returning the unparsed portion of the expression.
-make: "varmod-indirect.mk" line 140: before
-make: "varmod-indirect.mk" line 140: after
-make: "varmod-indirect.mk" line 146: before
-make: "varmod-indirect.mk" line 146: after
-make: "varmod-indirect.mk" line 152: before
-make: "varmod-indirect.mk" line 152: after
-make: "varmod-indirect.mk" line 156: Unknown modifier "Z"
-make: "varmod-indirect.mk" line 157: before
-make: "varmod-indirect.mk" line 157: after
-ParseReadLine (166): '_:= before ${UNDEF} after'
-Global: _ =
+make: "varmod-indirect.mk" line 19: while evaluating variable "value": Unknown modifier "${"
+make: "varmod-indirect.mk" line 52: while evaluating variable "value": Unknown modifier "${"
+make: "varmod-indirect.mk" line 54: warning: FIXME: this expression should have resulted in a parse error rather than returning the unparsed portion of the expression.
+make: "varmod-indirect.mk" line 143: before
+make: "varmod-indirect.mk" line 143: after
+make: "varmod-indirect.mk" line 151: before
+make: "varmod-indirect.mk" line 151: after
+make: "varmod-indirect.mk" line 159: before
+make: "varmod-indirect.mk" line 159: after
+make: "varmod-indirect.mk" line 164: while evaluating variable "UNDEF": Unknown modifier "Z"
+make: "varmod-indirect.mk" line 167: before
+make: "varmod-indirect.mk" line 167: after
+Parsing line 176: _:= before ${UNDEF} after
+Global: _ = # (empty)
Var_Parse: ${UNDEF} after (eval-keep-dollar-and-undefined)
Global: _ = before ${UNDEF} after
-ParseReadLine (169): '_:= before ${UNDEF:${:US,a,a,}} after'
+Parsing line 179: _:= before ${UNDEF:${:US,a,a,}} after
Var_Parse: ${UNDEF:${:US,a,a,}} after (eval-keep-dollar-and-undefined)
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'
+Parsing line 189: _:= before ${UNDEF:${:U}} after
Var_Parse: ${UNDEF:${:U}} after (eval-keep-dollar-and-undefined)
Indirect modifier "" from "${:U}"
Global: _ = before ${UNDEF:} after
-ParseReadLine (184): '_:= before ${UNDEF:${:UZ}} after'
+Parsing line 195: _:= before ${UNDEF:${:UZ}} after
Var_Parse: ${UNDEF:${:UZ}} after (eval-keep-dollar-and-undefined)
Indirect modifier "Z" from "${:UZ}"
Evaluating modifier ${UNDEF:Z} on value "" (eval-keep-dollar-and-undefined, undefined)
-make: "varmod-indirect.mk" line 184: Unknown modifier "Z"
+make: "varmod-indirect.mk" line 195: while evaluating variable "UNDEF": Unknown modifier "Z"
Result of ${UNDEF:Z} is error (eval-keep-dollar-and-undefined, undefined)
Global: _ = before ${UNDEF:Z} after
-ParseReadLine (186): '.MAKEFLAGS: -d0'
+Parsing line 197: .MAKEFLAGS: -d0
ParseDependency(.MAKEFLAGS: -d0)
Global: .MAKEFLAGS = -r -k -d 0 -d pv -d
Global: .MAKEFLAGS = -r -k -d 0 -d pv -d 0
diff --git a/contrib/bmake/unit-tests/varmod-indirect.mk b/contrib/bmake/unit-tests/varmod-indirect.mk
index fa58997cc849..869231d47ebc 100644
--- a/contrib/bmake/unit-tests/varmod-indirect.mk
+++ b/contrib/bmake/unit-tests/varmod-indirect.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-indirect.mk,v 1.9 2021/03/15 20:00:50 rillig Exp $
+# $NetBSD: varmod-indirect.mk,v 1.19 2024/04/20 10:18:55 rillig Exp $
#
# Tests for indirect variable modifiers, such as in ${VAR:${M_modifiers}}.
# These can be used for very basic purposes like converting a string to either
@@ -11,11 +11,11 @@
# To apply a modifier indirectly via another variable, the whole
-# modifier must be put into a single variable expression.
+# modifier must be put into a single expression.
# The following expression generates a parse error since its indirect
-# modifier contains more than a sole variable expression.
+# modifier contains more than a sole expression.
#
-# expect+1: Unknown modifier '$'
+# expect+1: while evaluating variable "value": Unknown modifier "${"
.if ${value:L:${:US}${:U,value,replacement,}} != "S,value,replacement,}"
. warning unexpected
.endif
@@ -47,9 +47,10 @@
# error. Because of this parse error, this feature cannot be used reasonably
# in practice.
#
-# expect+1: Unknown modifier '$'
+# expect+2: while evaluating variable "value": Unknown modifier "${"
#.MAKEFLAGS: -dvc
.if ${value:L:${:UM*}S,value,replaced,} == "M*S,value,replaced,}"
+# expect+1: warning: FIXME: this expression should have resulted in a parse error rather than returning the unparsed portion of the expression.
. warning FIXME: this expression should have resulted in a parse $\
error rather than returning the unparsed portion of the $\
expression.
@@ -70,20 +71,20 @@
.endif
-# The nested variable expression expands to "tu", and this is interpreted as
+# The nested expression expands to "tu", and this is interpreted as
# a variable modifier for the value "Upper", resulting in "UPPER".
.if ${Upper:L:${:Utu}} != "UPPER"
. error
.endif
-# The nested variable expression expands to "tl", and this is interpreted as
+# The nested expression expands to "tl", and this is interpreted as
# a variable modifier for the value "Lower", resulting in "lower".
.if ${Lower:L:${:Utl}} != "lower"
. error
.endif
-# The nested variable expression is ${1 != 1:?Z:tl}, consisting of the
+# The nested expression is ${1 != 1:?Z:tl}, consisting of the
# condition "1 != 1", the then-branch "Z" and the else-branch "tl". Since
# the condition evaluates to false, the then-branch is ignored (it would
# have been an unknown modifier anyway) and the ":tl" modifier is applied.
@@ -132,28 +133,37 @@ M_NoPrimes= ${PRIMES:${M_ListToSkip}}
.MAKEFLAGS: -d0
-# In contrast to the .if conditions, the .for loop allows undefined variable
+# In contrast to the .if conditions, the .for loop allows undefined
# expressions. These expressions expand to empty strings.
# An undefined expression without any modifiers expands to an empty string.
.for var in before ${UNDEF} after
+# expect+2: before
+# expect+1: after
. info ${var}
.endfor
# An undefined expression with only modifiers that keep the expression
# undefined expands to an empty string.
.for var in before ${UNDEF:${:US,a,a,}} after
+# expect+2: before
+# expect+1: after
. info ${var}
.endfor
# Even in an indirect modifier based on an undefined variable, the value of
# the expression in Var_Parse is a simple empty string.
.for var in before ${UNDEF:${:U}} after
+# expect+2: before
+# expect+1: after
. info ${var}
.endfor
# An error in an indirect modifier.
+# expect+1: while evaluating variable "UNDEF": Unknown modifier "Z"
.for var in before ${UNDEF:${:UZ}} after
+# expect+2: before
+# expect+1: after
. info ${var}
.endfor
@@ -162,10 +172,10 @@ M_NoPrimes= ${PRIMES:${M_ListToSkip}}
# a variable assignment using ':='.
.MAKEFLAGS: -dpv
-# The undefined variable expression is kept as-is.
+# The undefined expression is kept as-is.
_:= before ${UNDEF} after
-# The undefined variable expression is kept as-is.
+# The undefined expression is kept as-is.
_:= before ${UNDEF:${:US,a,a,}} after
# XXX: The subexpression ${:U} is fully defined, therefore it is expanded.
@@ -179,8 +189,9 @@ _:= before ${UNDEF:${:US,a,a,}} after
_:= before ${UNDEF:${:U}} after
# XXX: This expands to ${UNDEF:Z}, which will behave differently if the
-# variable '_' is used in a context where the variable expression ${_} is
+# variable '_' is used in a context where the expression ${_} is
# parsed but not evaluated.
+# expect+1: while evaluating variable "UNDEF": Unknown modifier "Z"
_:= before ${UNDEF:${:UZ}} after
.MAKEFLAGS: -d0
@@ -190,7 +201,7 @@ _:= before ${UNDEF:${:UZ}} after
# When evaluating indirect modifiers, these modifiers may expand to ':tW',
# which modifies the interpretation of the expression value. This modified
# interpretation only lasts until the end of the indirect modifier, it does
-# not influence the outer variable expression.
+# not influence the outer expression.
.if ${1 2 3:L:tW:[#]} != 1 # direct :tW applies to the :[#]
. error
.endif
@@ -202,7 +213,7 @@ _:= before ${UNDEF:${:UZ}} after
# When evaluating indirect modifiers, these modifiers may expand to ':ts*',
# which modifies the interpretation of the expression value. This modified
# interpretation only lasts until the end of the indirect modifier, it does
-# not influence the outer variable expression.
+# not influence the outer expression.
#
# In this first expression, the direct ':ts*' has no effect since ':U' does not
# treat the expression value as a list of words but as a single word. It has
@@ -244,4 +255,28 @@ _:= before ${UNDEF:${:UZ}} after
. error
.endif
-all:
+
+# In parse-only mode, the indirect modifiers must not be evaluated.
+#
+# Before var.c 1.1098 from 2024-02-04, the expression for an indirect modifier
+# was partially evaluated (only the variable value, without applying any
+# modifiers) and then interpreted as modifiers to the main expression.
+#
+# The expression ${:UZ} starts with the value "", and in parse-only mode, the
+# modifier ':UZ' does not modify the expression value. This results in an
+# empty string for the indirect modifiers, generating no warning.
+.if 0 && ${VAR:${:UZ}}
+.endif
+# The expression ${M_invalid} starts with the value "Z", which is an unknown
+# modifier. Trying to apply this unknown modifier generated a warning.
+M_invalid= Z
+.if 0 && ${VAR:${M_invalid}}
+.endif
+# The ':S' modifier does not change the expression value in parse-only mode,
+# keeping the "Z", which is then skipped in parse-only mode.
+.if 0 && ${VAR:${M_invalid:S,^,N*,:ts:}}
+.endif
+# The ':@' modifier does not change the expression value in parse-only mode,
+# keeping the "Z", which is then skipped in parse-only mode.
+.if 0 && ${VAR:${M_invalid:@m@N*$m@:ts:}}
+.endif
diff --git a/contrib/bmake/unit-tests/varmod-l-name-to-value.mk b/contrib/bmake/unit-tests/varmod-l-name-to-value.mk
index 354622cf098b..e87e68967544 100644
--- a/contrib/bmake/unit-tests/varmod-l-name-to-value.mk
+++ b/contrib/bmake/unit-tests/varmod-l-name-to-value.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-l-name-to-value.mk,v 1.7 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: varmod-l-name-to-value.mk,v 1.8 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the :L modifier, which returns the variable name as the new value.
@@ -28,7 +28,7 @@
.endif
# Between 2020-09-22 (var.c 1.527) and 2020-09-30 (var.c 1.553), there was
-# a bug in the evaluation of variable expressions. Indirect modifiers like
+# a bug in the evaluation of expressions. Indirect modifiers like
# the below :L did not update the definedness of the enclosing expression.
# This resulted in a wrong "Malformed conditional".
.if ${value:${:UL}} == ""
diff --git a/contrib/bmake/unit-tests/varmod-localtime.exp b/contrib/bmake/unit-tests/varmod-localtime.exp
index ed4d4f053c61..1bb547289edd 100644
--- a/contrib/bmake/unit-tests/varmod-localtime.exp
+++ b/contrib/bmake/unit-tests/varmod-localtime.exp
@@ -1,13 +1,13 @@
-make: "varmod-localtime.mk" line 57: Invalid time value: ${:U1593536400}} != "mtime=11593536400}"
-make: "varmod-localtime.mk" line 57: Malformed conditional (${%Y:L:localtime=${:U1593536400}} != "mtime=11593536400}")
-make: "varmod-localtime.mk" line 67: Invalid time value: -1} != ""
-make: "varmod-localtime.mk" line 67: Malformed conditional (${:L:localtime=-1} != "")
-make: "varmod-localtime.mk" line 76: Invalid time value: 1} != ""
-make: "varmod-localtime.mk" line 76: Malformed conditional (${:L:localtime= 1} != "")
-make: "varmod-localtime.mk" line 119: Invalid time value: 10000000000000000000000000000000} != ""
-make: "varmod-localtime.mk" line 119: Malformed conditional (${:L:localtime=10000000000000000000000000000000} != "")
-make: "varmod-localtime.mk" line 130: Invalid time value: error} != ""
-make: "varmod-localtime.mk" line 130: Malformed conditional (${:L:localtime=error} != "")
+make: "varmod-localtime.mk" line 61: while evaluating "${:L:localtime=-1} != """: Invalid time value "-1"
+make: "varmod-localtime.mk" line 61: Malformed conditional (${:L:localtime=-1} != "")
+make: "varmod-localtime.mk" line 72: while evaluating "${:L:localtime= 1} != """: Invalid time value " 1"
+make: "varmod-localtime.mk" line 72: Malformed conditional (${:L:localtime= 1} != "")
+make: "varmod-localtime.mk" line 120: while evaluating "${:L:localtime=10000000000000000000000000000000} != """: Invalid time value "10000000000000000000000000000000"
+make: "varmod-localtime.mk" line 120: Malformed conditional (${:L:localtime=10000000000000000000000000000000} != "")
+make: "varmod-localtime.mk" line 133: while evaluating "${:L:localtime=error} != """: Invalid time value "error"
+make: "varmod-localtime.mk" line 133: Malformed conditional (${:L:localtime=error} != "")
+make: "varmod-localtime.mk" line 144: while evaluating variable "%Y": Invalid time value "100000S,1970,bad,"
+make: "varmod-localtime.mk" line 144: Malformed conditional (${%Y:L:localtime=100000S,1970,bad,} != "bad")
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-localtime.mk b/contrib/bmake/unit-tests/varmod-localtime.mk
index f2867b61f8e9..233e556e0c77 100644
--- a/contrib/bmake/unit-tests/varmod-localtime.mk
+++ b/contrib/bmake/unit-tests/varmod-localtime.mk
@@ -1,9 +1,12 @@
-# $NetBSD: varmod-localtime.mk,v 1.8 2021/01/19 05:26:34 rillig Exp $
+# $NetBSD: varmod-localtime.mk,v 1.15 2024/04/20 10:18:55 rillig Exp $
#
# Tests for the :localtime variable modifier, which formats a timestamp
# using strftime(3) in local time.
+#
+# See also:
+# varmod-gmtime.mk
-.if ${TZ} != "Europe/Berlin" # see unit-tests/Makefile
+.if ${TZ:Uno:NEurope/Berlin:NUTC-1} != "" # see unit-tests/Makefile
. error
.endif
@@ -41,20 +44,9 @@
.endif
-# As of 2020-08-16, it is not possible to pass the seconds via a
-# variable expression. This is because parsing of the :localtime
-# modifier stops at the '$' and returns to ApplyModifiers.
-#
-# There, a colon would be skipped but not a dollar.
-# Parsing therefore continues at the '$' of the ${:U159...}, looking
-# for an ordinary variable modifier.
-#
-# At this point, the ${:U} is expanded and interpreted as a variable
-# modifier, which results in the error message "Unknown modifier '1'".
-#
-# If ApplyModifier_Localtime were to pass its argument through
-# ParseModifierPart, this would work.
-.if ${%Y:L:localtime=${:U1593536400}} != "mtime=11593536400}"
+# Before var.c 1.1050 from 2023-05-09, it was not possible to pass the
+# seconds via an expression.
+.if ${%Y:L:localtime=${:U1593536400}} != "2020"
. error
.endif
@@ -64,6 +56,8 @@
# 1970. Going back 50 years in the past is not a practical use case for
# make. Therefore, since var.c 1.631, negative time stamps produce a
# parse error.
+# expect+2: while evaluating "${:L:localtime=-1} != """: Invalid time value "-1"
+# expect+1: Malformed conditional (${:L:localtime=-1} != "")
.if ${:L:localtime=-1} != ""
. error
.else
@@ -73,8 +67,12 @@
# Spaces were allowed before var.c 1.631 from 2020-10-31 21:40:20, not
# because it would make sense but just as a side-effect from using strtoul.
+# expect+2: while evaluating "${:L:localtime= 1} != """: Invalid time value " 1"
+# expect+1: Malformed conditional (${:L:localtime= 1} != "")
.if ${:L:localtime= 1} != ""
. error
+.else
+. error
.endif
@@ -115,7 +113,10 @@
# ULONG_MAX, which got converted to -1. This resulted in a time stamp of
# the second before 1970.
#
-# Since var.c 1.631, the overflow is detected and produces a parse error.
+# Since var.c 1.631 from 2020-10-31, the overflow is detected and produces a
+# parse error.
+# expect+2: while evaluating "${:L:localtime=10000000000000000000000000000000} != """: Invalid time value "10000000000000000000000000000000"
+# expect+1: Malformed conditional (${:L:localtime=10000000000000000000000000000000} != "")
.if ${:L:localtime=10000000000000000000000000000000} != ""
. error
.else
@@ -127,11 +128,21 @@
# stopped after the '=', and the remaining string was parsed for more variable
# modifiers. Because of the unknown modifier 'e' from the 'error', the whole
# variable value was discarded and thus not printed.
+# expect+2: while evaluating "${:L:localtime=error} != """: Invalid time value "error"
+# expect+1: Malformed conditional (${:L:localtime=error} != "")
.if ${:L:localtime=error} != ""
. error
.else
. error
.endif
+# Before var.c 1.1050 from 2023-05-09, the timestamp could be directly
+# followed by the next modifier, without a ':' separator. This was the same
+# bug as for the ':L' and ':P' modifiers.
+# expect+2: while evaluating variable "%Y": Invalid time value "100000S,1970,bad,"
+# expect+1: Malformed conditional (${%Y:L:localtime=100000S,1970,bad,} != "bad")
+.if ${%Y:L:localtime=100000S,1970,bad,} != "bad"
+. error
+.endif
all:
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..5c508a7a6420
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-loop-delete.exp
@@ -0,0 +1,4 @@
+make: "varmod-loop-delete.mk" line 20: while evaluating variable "VAR": while evaluating "${:U:@VAR@@} rest of the value": 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..cf0611991194
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-loop-delete.mk
@@ -0,0 +1,34 @@
+# $NetBSD: varmod-loop-delete.mk,v 1.4 2024/04/20 10:18:55 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.
+# expect+1: while evaluating variable "VAR": while evaluating "${:U:@VAR@@} rest of the value": Cannot delete variable "VAR" while it is used
+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..cefd82093e29 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 18: while evaluating "${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@} != "+one+ +two+ +three+"": In the :@ modifier, the variable name "${:Ubar:S,b,v,}" must not contain a dollar
+make: "varmod-loop-varname.mk" line 18: Malformed conditional (${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@} != "+one+ +two+ +three+")
+make: "varmod-loop-varname.mk" line 89: while evaluating variable "1 2 3": In the :@ modifier, the variable name "v$" must not contain a dollar
+make: "varmod-loop-varname.mk" line 89: Malformed conditional (${1 2 3:L:@v$@($v)@} != "(1) (2) (3)")
+make: "varmod-loop-varname.mk" line 96: while evaluating variable "1 2 3": In the :@ modifier, the variable name "v$$" must not contain a dollar
+make: "varmod-loop-varname.mk" line 96: Malformed conditional (${1 2 3:L:@v$$@($v)@} != "() () ()")
+make: "varmod-loop-varname.mk" line 103: while evaluating variable "1 2 3": In the :@ modifier, the variable name "v$$$" must not contain a dollar
+make: "varmod-loop-varname.mk" line 103: 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..7abf8610911a 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.7 2024/04/20 10:18:55 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
@@ -10,8 +13,12 @@
# dynamically. There was no practical use-case for this.
# Since var.c 1.907 from 2021-04-04, a '$' is no longer allowed in the
# variable name.
+# expect+2: while evaluating "${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@} != "+one+ +two+ +three+"": In the :@ modifier, the variable name "${:Ubar:S,b,v,}" must not contain a dollar
+# expect+1: Malformed conditional (${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@} != "+one+ +two+ +three+")
.if ${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@} != "+one+ +two+ +three+"
. error
+.else
+. error
.endif
@@ -77,16 +84,22 @@ RES3= 3
# There's no point in allowing a dollar sign in that position.
# Since var.c 1.907 from 2021-04-04, a '$' is no longer allowed in the
# variable name.
+# expect+2: while evaluating variable "1 2 3": In the :@ modifier, the variable name "v$" must not contain a dollar
+# expect+1: Malformed conditional (${1 2 3:L:@v$@($v)@} != "(1) (2) (3)")
.if ${1 2 3:L:@v$@($v)@} != "(1) (2) (3)"
. error
.else
. error
.endif
+# expect+2: while evaluating variable "1 2 3": In the :@ modifier, the variable name "v$$" must not contain a dollar
+# expect+1: Malformed conditional (${1 2 3:L:@v$$@($v)@} != "() () ()")
.if ${1 2 3:L:@v$$@($v)@} != "() () ()"
. error
.else
. error
.endif
+# expect+2: while evaluating variable "1 2 3": In the :@ modifier, the variable name "v$$$" must not contain a dollar
+# expect+1: Malformed conditional (${1 2 3:L:@v$$$@($v)@} != "() () ()")
.if ${1 2 3:L:@v$$$@($v)@} != "() () ()"
. error
.else
@@ -99,7 +112,7 @@ RES3= 3
#
# As of 2020-10-18, the :@ modifier is implemented by actually setting a
# variable in the scope of the expression and deleting it again after the
-# loop. This is different from the .for loops, which substitute the variable
+# loop. This is different from the .for loops, which substitute the
# expression with ${:Uvalue}, leading to different unwanted side effects.
#
# To make the behavior more predictable, the :@ modifier should restore the
diff --git a/contrib/bmake/unit-tests/varmod-loop.exp b/contrib/bmake/unit-tests/varmod-loop.exp
index a4704973f6e2..6aef3fe86857 100644
--- a/contrib/bmake/unit-tests/varmod-loop.exp
+++ b/contrib/bmake/unit-tests/varmod-loop.exp
@@ -1,10 +1,12 @@
-ParseReadLine (75): 'USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$'
+Parsing line 91: USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$
+Parsing line 92: .if ${USE_8_DOLLARS} != "\$\$\$\$ \$\$\$\$ \$\$\$\$"
CondParser_Eval: ${USE_8_DOLLARS} != "\$\$\$\$ \$\$\$\$ \$\$\$\$"
-lhs = "$$$$ $$$$ $$$$", rhs = "$$$$ $$$$ $$$$", op = !=
-ParseReadLine (80): 'SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}'
+Comparing "$$$$ $$$$ $$$$" != "$$$$ $$$$ $$$$"
+Parsing line 96: SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}
+Parsing line 118: .if ${SUBST_CONTAINING_LOOP} != "\$\$ \$\$\$\$ \$\$\$\$"
CondParser_Eval: ${SUBST_CONTAINING_LOOP} != "\$\$ \$\$\$\$ \$\$\$\$"
-lhs = "$$ $$$$ $$$$", rhs = "$$ $$$$ $$$$", op = !=
-ParseReadLine (105): '.MAKEFLAGS: -d0'
+Comparing "$$ $$$$ $$$$" != "$$ $$$$ $$$$"
+Parsing line 121: .MAKEFLAGS: -d0
ParseDependency(.MAKEFLAGS: -d0)
:varname-overwriting-target: :x1y x2y x3y: ::
mod-loop-dollar:1:
@@ -13,4 +15,10 @@ mod-loop-dollar:$3$:
mod-loop-dollar:$${word}$$:
mod-loop-dollar:$$5$$:
mod-loop-dollar:$$${word}$$$:
+: t=$(( ${t:-0} + 1 ))
+: dollar=end
+: backslash=\ end
+: dollar=$ at=@ backslash=\ end
+: dollar=$$ at=@@ backslash=\\ end
+: dollar=$$ at=@@ backslash=\\ end
exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-loop.mk b/contrib/bmake/unit-tests/varmod-loop.mk
index 4fdaa3ff4e61..64cc6ca85043 100644
--- a/contrib/bmake/unit-tests/varmod-loop.mk
+++ b/contrib/bmake/unit-tests/varmod-loop.mk
@@ -1,7 +1,24 @@
-# $NetBSD: varmod-loop.mk,v 1.15 2021/04/11 13:35:56 rillig Exp $
+# $NetBSD: varmod-loop.mk,v 1.24 2023/11/19 21:47:52 rillig Exp $
#
-# Tests for the :@var@...${var}...@ variable modifier.
+# Tests for the expression modifier ':@var@body@', which replaces each word of
+# the expression with the expanded body, which may contain references to the
+# variable 'var'. For example, '${1 2 3:L:@word@<${word}>@}' encloses each
+# word in angle quotes, resulting in '<1> <2> <3>'.
+#
+# The variable name can be chosen freely, except that it must not contain a
+# '$'. For simplicity and readability, variable names should only use the
+# characters 'A-Za-z0-9'.
+#
+# The body may contain subexpressions in the form '${...}' or '$(...)'. These
+# subexpressions differ from everywhere else in makefiles in that the parser
+# only scans '${...}' for balanced '{' and '}', likewise for '$(...)'. Any
+# other '$' is left as-is during parsing. Later, when the body is expanded
+# for each word, each '$$' is interpreted as a single '$', and the remaining
+# '$' are interpreted as expressions, like when evaluating a regular variable.
+# 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
@@ -16,7 +33,6 @@ varname-overwriting-target:
@echo :$@: :${:U1 2 3:@\@@x${@}y@}: :$@:
-
# Demonstrate that it is possible to generate dollar signs using the
# :@ modifier.
#
@@ -36,7 +52,7 @@ mod-loop-dollar:
#
# As of 2020-10-18, the :@ modifier is implemented by actually setting a
# variable in the scope of the expression and deleting it again after the
-# loop. This is different from the .for loops, which substitute the variable
+# loop. This is different from the .for loops, which substitute the
# expression with ${:Uvalue}, leading to different unwanted side effects.
#
# To make the behavior more predictable, the :@ modifier should restore the
@@ -95,7 +111,7 @@ SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}
# The variable SUBST_CONTAINING_LOOP therefore gets assigned the raw value
# "$$$$ $$$$$$$$ $$$$$$$$".
#
-# The variable expression in the condition then expands this raw stored value
+# The expression in the condition then expands this raw stored value
# once, resulting in "$$ $$$$ $$$$". The effects from VARE_KEEP_DOLLAR no
# longer take place since they had only been active during the evaluation of
# the variable assignment.
@@ -184,6 +200,47 @@ CMDLINE= global # needed for deleting the environment
.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'.
+# In the loop body text of the ':@' modifier, a literal '$' is written as '$$',
+# not '\$'. In the following example, each '$$' turns into a single '$',
+# except for '$i', which is replaced with the then-current value '1' of the
+# iteration variable.
+#
+# See parse-var.mk, keyword 'BRACE_GROUP'.
+all: varmod-loop-literal-dollar
+varmod-loop-literal-dollar: .PHONY
+ : ${:U1:@i@ t=$$(( $${t:-0} + $i ))@}
+
+
+# When parsing the loop body, each '\$', '\@' and '\\' is unescaped to '$',
+# '@' and '\', respectively; all other backslashes are retained.
+#
+# In practice, the '$' is not escaped as '\$', as there is a second round of
+# unescaping '$$' to '$' later when the loop body is expanded after setting the
+# iteration variable.
+#
+# After the iteration variable has been set, the loop body is expanded with
+# this unescaping, regardless of whether .MAKE.SAVE_DOLLARS is set or not:
+# $$ a literal '$'
+# $x, ${var}, $(var) a nested expression
+# any other character itself
+all: escape-modifier
+escape-modifier: .PHONY
+ # In the first round, '\$ ' is unescaped to '$ ', and since the
+ # variable named ' ' is not defined, the expression '$ ' expands to an
+ # empty string.
+ # expect: : dollar=end
+ : ${:U1:@i@ dollar=\$ end@}
+
+ # Like in other modifiers, '\ ' is preserved, since ' ' is not one of
+ # the characters that _must_ be escaped.
+ # expect: : backslash=\ end
+ : ${:U1:@i@ backslash=\ end@}
+
+ # expect: : dollar=$ at=@ backslash=\ end
+ : ${:U1:@i@ dollar=\$\$ at=\@ backslash=\\ end@}
+ # expect: : dollar=$$ at=@@ backslash=\\ end
+ : ${:U1:@i@ dollar=\$\$\$\$ at=\@\@ backslash=\\\\ end@}
+ # expect: : dollar=$$ at=@@ backslash=\\ end
+ : ${:U1:@i@ dollar=$$$$ at=\@\@ backslash=\\\\ end@}
+
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/varmod-match-escape.exp b/contrib/bmake/unit-tests/varmod-match-escape.exp
index 42cdd7a87ac9..d6bd804a7e89 100755
--- a/contrib/bmake/unit-tests/varmod-match-escape.exp
+++ b/contrib/bmake/unit-tests/varmod-match-escape.exp
@@ -10,7 +10,7 @@ Evaluating modifier ${SPECIALS:M...} on value "\: : \\ * \*"
Pattern for ':M' is ":"
ModifyWords: split "\: : \\ * \*" into 5 words
Result of ${SPECIALS:M\:${:U}} is ":"
-lhs = ":", rhs = ":", op = !=
+Comparing ":" != ":"
Global: VALUES = : :: :\:
CondParser_Eval: ${VALUES:M\:${:U\:}} != ${VALUES:M${:U\:}\:}
Var_Parse: ${VALUES:M\:${:U\:}} != ${VALUES:M${:U\:}\:} (eval-defined)
@@ -29,11 +29,13 @@ Result of ${:U\:} is ":" (eval-defined, defined)
Pattern for ':M' is ":\:"
ModifyWords: split ": :: :\:" into 3 words
Result of ${VALUES:M${:U\:}\:} is "::"
-lhs = ":", rhs = "::", op = !=
-make: "varmod-match-escape.mk" line 42: warning: XXX: Oops
+Comparing ":" != "::"
+make: "varmod-match-escape.mk" line 43: warning: XXX: Oops
Global: .MAKEFLAGS = -r -k -d cv -d
Global: .MAKEFLAGS = -r -k -d cv -d 0
-make: "varmod-match-escape.mk" line 67: Dollar followed by nothing
+make: "varmod-match-escape.mk" line 69: while evaluating "${:U\$:M\$} != """: Dollar followed by nothing
+make: "varmod-match-escape.mk" line 110: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[A-]' of modifier ':M'
+make: "varmod-match-escape.mk" line 110: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[^A-]' of modifier ':M'
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-match-escape.mk b/contrib/bmake/unit-tests/varmod-match-escape.mk
index 5ac69f964a68..d39ece1cba2c 100755
--- a/contrib/bmake/unit-tests/varmod-match-escape.mk
+++ b/contrib/bmake/unit-tests/varmod-match-escape.mk
@@ -1,8 +1,8 @@
-# $NetBSD: varmod-match-escape.mk,v 1.7 2021/04/03 11:08:40 rillig Exp $
+# $NetBSD: varmod-match-escape.mk,v 1.13 2024/04/20 10:18:55 rillig Exp $
#
# As of 2020-08-01, the :M and :N modifiers interpret backslashes differently,
-# depending on whether there was a variable expression somewhere before the
-# first backslash or not. See ApplyModifier_Match, "copy = true".
+# depending on whether there was an expression somewhere before the
+# first backslash or not. See ParseModifier_Match, "copy = true".
#
# Apart from the different and possibly confusing debug output, there is no
# difference in behavior. When parsing the modifier text, only \{, \} and \:
@@ -18,27 +18,28 @@ SPECIALS= \: : \\ * \*
.endif
# And now both cases combined: A single modifier with both an escaped ':'
-# as well as a variable expression that expands to a ':'.
+# as well as an expression that expands to a ':'.
#
-# XXX: As of 2020-11-01, when an escaped ':' occurs before the variable
+# XXX: As of 2020-11-01, when an escaped ':' occurs before the
# expression, the whole modifier text is subject to unescaping '\:' to ':',
-# before the variable expression is expanded. This means that the '\:' in
-# the variable expression is expanded as well, turning ${:U\:} into a simple
+# before the expression is expanded. This means that the '\:' in
+# the expression is expanded as well, turning ${:U\:} into a simple
# ${:U:}, which silently expands to an empty string, instead of generating
# an error message.
#
# XXX: As of 2020-11-01, the modifier on the right-hand side of the
-# comparison is parsed differently though. First, the variable expression
+# comparison is parsed differently though. First, the expression
# is parsed, resulting in ':' and needSubst=true. After that, the escaped
# ':' is seen, and this time, copy=true is not executed but stays copy=false.
# Therefore the escaped ':' is kept as-is, and the final pattern becomes
# ':\:'.
#
-# If ApplyModifier_Match had used the same parsing algorithm as Var_Subst,
+# If ParseModifier_Match had used the same parsing algorithm as Var_Subst,
# both patterns would end up as '::'.
#
VALUES= : :: :\:
.if ${VALUES:M\:${:U\:}} != ${VALUES:M${:U\:}\:}
+# expect+1: warning: XXX: Oops
. warning XXX: Oops
.endif
@@ -52,7 +53,7 @@ VALUES= : :: :\:
.endif
# XXX: As of 2020-11-01, unlike all other variable modifiers, '\$' is not
-# parsed as an escaped '$'. Instead, ApplyModifier_Match first scans for
+# parsed as an escaped '$'. Instead, ParseModifier_Match first scans for
# the ':' at the end of the modifier, which results in the pattern '\$'.
# No unescaping takes place since the pattern neither contained '\:' nor
# '\{' nor '\}'. But the text is expanded, and a lonely '$' at the end
@@ -64,6 +65,7 @@ VALUES= : :: :\:
# In lint mode, the case of a lonely '$' is covered with an error message.
.MAKEFLAGS: -dL
+# expect+1: while evaluating "${:U\$:M\$} != """: Dollar followed by nothing
.if ${:U\$:M\$} != ""
. error
.endif
@@ -75,12 +77,41 @@ VALUES= : :: :\:
#
# TODO: Str_Match("a-z]", "[a-z]")
# TODO: Str_Match("012", "[0-]]")
-# TODO: Str_Match("0]", "[0-]]")
-# TODO: Str_Match("1]", "[0-]]")
# TODO: Str_Match("[", "[[]")
# TODO: Str_Match("]", "[]")
# TODO: Str_Match("]", "[[-]]")
+# Demonstrate an inconsistency between positive and negative character lists
+# when the range ends with the character ']'.
+#
+# 'A' begins the range, 'B' is in the middle of the range, ']' ends the range,
+# 'a' is outside the range.
+WORDS= A A] A]] B B] B]] ] ]] ]]] a a] a]]
+# The ']' is part of the character range and at the same time ends the
+# character list.
+EXP.[A-]= A B ]
+# The first ']' is part of the character range and at the same time ends the
+# character list.
+EXP.[A-]]= A] B] ]]
+# The first ']' is part of the character range and at the same time ends the
+# character list.
+EXP.[A-]]]= A]] B]] ]]]
+# For negative character lists, the ']' ends the character range but does not
+# end the character list.
+# XXX: This is unnecessarily inconsistent but irrelevant in practice as there
+# is no practical need for a character range that ends at ']'.
+EXP.[^A-]= a
+EXP.[^A-]]= a
+EXP.[^A-]]]= a]
+
+.for pattern in [A-] [A-]] [A-]]] [^A-] [^A-]] [^A-]]]
+# expect+2: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[A-]' of modifier ':M'
+# expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[^A-]' of modifier ':M'
+. if ${WORDS:M${pattern}} != ${EXP.${pattern}}
+. warning ${pattern}: ${WORDS:M${pattern}} != ${EXP.${pattern}}
+. endif
+.endfor
+
# In brackets, the backslash is just an ordinary character.
# Outside brackets, it is an escape character for a few special characters.
# TODO: Str_Match("\\", "[\\-]]")
diff --git a/contrib/bmake/unit-tests/varmod-match.exp b/contrib/bmake/unit-tests/varmod-match.exp
index 080e9e0f74de..3425eae2d421 100644
--- a/contrib/bmake/unit-tests/varmod-match.exp
+++ b/contrib/bmake/unit-tests/varmod-match.exp
@@ -1,12 +1,14 @@
-CondParser_Eval: ${NUMBERS:M[A-Z]*} != "One Two Three Four"
-lhs = "One Two Three Four", rhs = "One Two Three Four", op = !=
-CondParser_Eval: ${NUMBERS:M[^A-Z]*} != "five six seven"
-lhs = "five six seven", rhs = "five six seven", op = !=
-CondParser_Eval: ${NUMBERS:M[^s]*[ex]} != "One Three five"
-lhs = "One Three five", rhs = "One Three five", op = !=
-CondParser_Eval: ${:U****************:M****************b}
-CondParser_Eval: ${:Ua \$ sign:M*$$*} != "\$"
-lhs = "$", rhs = "$", op = !=
-CondParser_Eval: ${:Ua \$ sign any-asterisk:M*\$*} != "any-asterisk"
-lhs = "any-asterisk", rhs = "any-asterisk", op = !=
-exit status 0
+make: "varmod-match.mk" line 290: while evaluating variable "WORDS": warning: Unfinished character list in pattern 'a[' of modifier ':M'
+make: "varmod-match.mk" line 298: while evaluating variable "WORDS": warning: Unfinished character list in pattern 'a[^' of modifier ':M'
+make: "varmod-match.mk" line 306: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[-x1-3' of modifier ':M'
+make: "varmod-match.mk" line 314: while evaluating variable "WORDS": warning: Unfinished character list in pattern '*[-x1-3' of modifier ':M'
+make: "varmod-match.mk" line 323: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[^-x1-3' of modifier ':M'
+make: "varmod-match.mk" line 337: while evaluating variable "WORDS": warning: Unfinished character list in pattern '?[\' of modifier ':M'
+make: "varmod-match.mk" line 345: while evaluating variable "WORDS": warning: Unfinished character range in pattern '[x-' of modifier ':M'
+make: "varmod-match.mk" line 357: while evaluating variable "WORDS": warning: Unfinished character range in pattern '[^x-' of modifier ':M'
+make: "varmod-match.mk" line 365: while evaluating variable " : :: ": warning: Unfinished character list in pattern '[' of modifier ':M'
+make: "varmod-match.mk" line 365: while evaluating variable " : :: ": Unknown modifier "]"
+make: "varmod-match.mk" line 365: Malformed conditional (${ ${:U\:} ${:U\:\:} :L:M[:]} != ":")
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-match.mk b/contrib/bmake/unit-tests/varmod-match.mk
index 9b56fb451eda..e91ddc7ce04b 100644
--- a/contrib/bmake/unit-tests/varmod-match.mk
+++ b/contrib/bmake/unit-tests/varmod-match.mk
@@ -1,42 +1,234 @@
-# $NetBSD: varmod-match.mk,v 1.6 2020/11/15 18:33:41 rillig Exp $
+# $NetBSD: varmod-match.mk,v 1.22 2024/04/23 22:51:28 rillig Exp $
#
-# Tests for the :M variable modifier, which filters words that match the
+# Tests for the ':M' modifier, which keeps only those words that match the
# given pattern.
#
-# See ApplyModifier_Match and ModifyWord_Match for the implementation.
+# Table of contents
+#
+# 1. Pattern characters '*', '?' and '\'
+# 2. Character lists and character ranges
+# 3. Parsing and escaping
+# 4. Interaction with other modifiers
+# 5. Performance
+# 6. Error handling
+# 7. Historical bugs
+#
+# See ApplyModifier_Match, ParseModifier_Match, ModifyWord_Match and
+# Str_Match.
+
+
+# 1. Pattern characters '*', '?' and '\'
+#
+# * matches 0 or more characters
+# ? matches 1 character
+# \x matches the character 'x'
+
+# The pattern is anchored both at the beginning and at the end of the word.
+# Since the pattern 'e' does not contain any pattern matching characters, it
+# matches exactly the word 'e', twice.
+.if ${a c e aa cc ee e f g:L:Me} != "e e"
+. error
+.endif
+
+# The pattern character '?' matches exactly 1 character, the pattern character
+# '*' matches 0 or more characters. The whole pattern matches all words that
+# start with 's' and have 3 or more characters.
+.if ${One Two Three Four five six seven so s:L:Ms??*} != "six seven"
+. error
+.endif
-.MAKEFLAGS: -dc
+# A pattern without placeholders only matches itself.
+.if ${a aa aaa b ba baa bab:L:Ma} != "a"
+. error
+.endif
-NUMBERS= One Two Three Four five six seven
+# A pattern that ends with '*' is anchored at the
+# beginning.
+.if ${a aa aaa b ba baa bab:L:Ma*} != "a aa aaa"
+. error
+.endif
+
+# A pattern that starts with '*' is anchored at the end.
+.if ${a aa aaa b ba baa bab:L:M*a} != "a aa aaa ba baa"
+. error
+.endif
+
+# Test the fast code path for '*' followed by a regular character.
+.if ${:U file.c file.*c file.h file\.c :M*.c} != "file.c file\\.c"
+. error
+.endif
+# Ensure that the fast code path correctly handles the backslash.
+.if ${:U file.c file.*c file.h file\.c :M*\.c} != "file.c file\\.c"
+. error
+.endif
+# Ensure that the fast code path correctly handles '\*'.
+.if ${:U file.c file.*c file.h file\.c :M*\*c} != "file.*c"
+. error
+.endif
+# Ensure that the partial match '.c' doesn't confuse the fast code path.
+.if ${:U file.c.cc file.cc.cc file.cc.c :M*.cc} != "file.c.cc file.cc.cc"
+. error
+.endif
+# Ensure that the substring '.cc' doesn't confuse the fast code path for '.c'.
+.if ${:U file.c.cc file.cc.cc file.cc.c :M*.c} != "file.cc.c"
+. error
+.endif
+
+
+# 2. Character lists and character ranges
+#
+# [...] matches 1 character from the listed characters
+# [^...] matches 1 character from the unlisted characters
+# [a-z] matches 1 character from the range 'a' to 'z'
+# [z-a] matches 1 character from the range 'a' to 'z'
# Only keep words that start with an uppercase letter.
-.if ${NUMBERS:M[A-Z]*} != "One Two Three Four"
+.if ${One Two Three Four five six seven:L:M[A-Z]*} != "One Two Three Four"
. error
.endif
# Only keep words that start with a character other than an uppercase letter.
-.if ${NUMBERS:M[^A-Z]*} != "five six seven"
+.if ${One Two Three Four five six seven:L:M[^A-Z]*} != "five six seven"
+. error
+.endif
+
+# [] matches never
+.if ${ ab a[]b a[b a b :L:M[]} != ""
+. error
+.endif
+
+# a[]b matches never
+.if ${ ab a[]b a[b a b [ ] :L:Ma[]b} != ""
+. error
+.endif
+
+# [^] matches exactly 1 arbitrary character
+.if ${ ab a[]b a[b a b [ ] :L:M[^]} != "a b [ ]"
+. error
+.endif
+
+# a[^]b matches 'a', then exactly 1 arbitrary character, then 'b'
+.if ${ ab a[]b a[b a b :L:Ma[^]b} != "a[b"
+. error
+.endif
+
+# [Nn0] matches exactly 1 character from the set 'N', 'n', '0'
+.if ${ a b N n 0 Nn0 [ ] :L:M[Nn0]} != "N n 0"
+. error
+.endif
+
+# [a-c] matches exactly 1 character from the range 'a' to 'c'
+.if ${ A B C a b c d [a-c] [a] :L:M[a-c]} != "a b c"
+. error
+.endif
+
+# [c-a] matches the same as [a-c]
+.if ${ A B C a b c d [a-c] [a] :L:M[c-a]} != "a b c"
+. error
+.endif
+
+# [^a-c67]
+# matches a single character, except for 'a', 'b', 'c', '6' or
+# '7'
+.if ${ A B C a b c d 5 6 7 8 [a-c] [a] :L:M[^a-c67]} != "A B C d 5 8"
+. error
+.endif
+
+# [\] matches a single backslash; no escaping takes place in
+# character ranges
+# Without the 'b' in the below words, the backslash would end a word and thus
+# influence how the string is split into words.
+WORDS= a\b a[\]b ab a\\b
+.if ${WORDS:Ma[\]b} != "a\\b"
+. error
+.endif
+
+# [[-]] May look like it would match a single '[', '\' or ']', but
+# the inner ']' has two roles: it is the upper bound of the
+# character range as well as the closing character of the
+# character list. The outer ']' is just a regular character.
+WORDS= [ ] [] \] ]]
+.if ${WORDS:M[[-]]} != "[] \\] ]]"
+. error
+.endif
+
+# [b[-]a]
+# Same as for '[[-]]': the character list stops at the first
+# ']', and the 'a]' is treated as a literal string.
+WORDS= [a \a ]a []a \]a ]]a [a] \a] ]a] ba]
+.if ${WORDS:M[b[-]a]} != "[a] \\a] ]a] ba]"
+. error
+.endif
+
+# [-] Matches a single '-' since the '-' only becomes part of a
+# character range if it is preceded and followed by another
+# character.
+WORDS= - -]
+.if ${WORDS:M[-]} != "-"
. error
.endif
# Only keep words that don't start with s and at the same time end with
# either of [ex].
#
-# This test case ensures that the negation from the first character class
-# does not propagate to the second character class.
-.if ${NUMBERS:M[^s]*[ex]} != "One Three five"
+# This test case ensures that the negation from the first character list
+# '[^s]' does not propagate to the second character list '[ex]'.
+.if ${One Two Three Four five six seven:L:M[^s]*[ex]} != "One Three five"
. error
.endif
-# Before 2020-06-13, this expression took quite a long time in Str_Match,
-# calling itself 601080390 times for 16 asterisks.
-.if ${:U****************:M****************b}
+
+# 3. Parsing and escaping
+#
+# * matches 0 or more characters
+# ? matches 1 character
+# \ outside a character list, escapes the following character
+# [ starts a character list for matching 1 character
+# ] ends a character list for matching 1 character
+# - in a character list, forms a character range
+# ^ at the beginning of a character list, negates the list
+# ( while parsing the pattern, starts a nesting level
+# ) while parsing the pattern, ends a nesting level
+# { while parsing the pattern, starts a nesting level
+# } while parsing the pattern, ends a nesting level
+# : while parsing the pattern, terminates the pattern
+# $ while parsing the pattern, starts a nested expression
+# # in a line except a shell command, starts a comment
+
+# The pattern can come from an expression. For single-letter
+# variables, either the short form or the long form can be used, just as
+# everywhere else.
+PRIMES= 2 3 5 7 11
+n= 2
+.if ${PRIMES:M$n} != "2"
+. error
+.endif
+.if ${PRIMES:M${n}} != "2"
+. error
+.endif
+.if ${PRIMES:M${:U2}} != "2"
+. error
+.endif
+
+# : terminates the pattern
+.if ${ A * :L:M:} != ""
+. error
+.endif
+
+# \: matches a colon
+.if ${ ${:U\: \:\:} :L:M\:} != ":"
+. error
+.endif
+
+# ${:U\:} matches a colon
+.if ${ ${:U\:} ${:U\:\:} :L:M${:U\:}} != ":"
+. error
.endif
# To match a dollar sign in a word, double it.
#
-# This is different from the :S and :C variable modifiers, where a '$'
-# has to be escaped as '\$'.
+# This is different from the :S and :C modifiers, where a '$' has to be
+# escaped as '\$'.
.if ${:Ua \$ sign:M*$$*} != "\$"
. error
.endif
@@ -45,7 +237,7 @@ NUMBERS= One Two Three Four five six seven
# interpreted as a backslash followed by whatever expression the
# '$' starts.
#
-# This differs from the :S, :C and several other variable modifiers.
+# This differs from the :S, :C and several other modifiers.
${:U*}= asterisk
.if ${:Ua \$ sign any-asterisk:M*\$*} != "any-asterisk"
. error
@@ -56,5 +248,141 @@ ${:U*}= asterisk
# TODO: ${VAR:M${UNBALANCED}}
# TODO: ${VAR:M${:U(((\}\}\}}}
-all:
- @:;
+
+# 4. Interaction with other modifiers
+
+# The modifier ':tW' prevents splitting at whitespace. Even leading and
+# trailing whitespace is preserved.
+.if ${ plain string :L:tW:M*} != " plain string "
+. error
+.endif
+
+# Without the modifier ':tW', the string is split into words. Whitespace
+# around the words is discarded, and whitespace between the words is
+# normalized to a single space.
+.if ${ plain string :L:M*} != "plain string"
+. error
+.endif
+
+
+# 5. Performance
+
+# Before 2020-06-13, this expression called Str_Match 601,080,390 times.
+# Since 2020-06-13, this expression calls Str_Match 1 time.
+.if ${:U****************:M****************b}
+.endif
+
+# Before 2023-06-22, this expression called Str_Match 2,621,112 times.
+# Adding another '*?' to the pattern called Str_Match 20,630,572 times.
+# Adding another '*?' to the pattern called Str_Match 136,405,672 times.
+# Adding another '*?' to the pattern called Str_Match 773,168,722 times.
+# Adding another '*?' to the pattern called Str_Match 3,815,481,072 times.
+# Since 2023-06-22, Str_Match no longer backtracks.
+.if ${:U..................................................b:M*?*?*?*?*?a}
+.endif
+
+
+# 6. Error handling
+
+# [ Incomplete empty character list, never matches.
+WORDS= a a[
+# expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern 'a[' of modifier ':M'
+.if ${WORDS:Ma[} != ""
+. error
+.endif
+
+# [^ Incomplete negated empty character list, matches any single
+# character.
+WORDS= a a[ aX
+# expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern 'a[^' of modifier ':M'
+.if ${WORDS:Ma[^} != "a[ aX"
+. error
+.endif
+
+# [-x1-3 Incomplete character list, matches those elements that can be
+# parsed without lookahead.
+WORDS= - + x xx 0 1 2 3 4 [x1-3
+# expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[-x1-3' of modifier ':M'
+.if ${WORDS:M[-x1-3} != "- x 1 2 3"
+. error
+.endif
+
+# *[-x1-3 Incomplete character list after a wildcard, matches those
+# words that end with one of the characters from the list.
+WORDS= - + x xx 0 1 2 3 4 00 01 10 11 000 001 010 011 100 101 110 111 [x1-3
+# expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern '*[-x1-3' of modifier ':M'
+.if ${WORDS:M*[-x1-3} != "- x xx 1 2 3 01 11 001 011 101 111 [x1-3"
+. warning ${WORDS:M*[-x1-3}
+.endif
+
+# [^-x1-3
+# Incomplete negated character list, matches any character
+# except those elements that can be parsed without lookahead.
+WORDS= - + x xx 0 1 2 3 4 [x1-3
+# expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern '[^-x1-3' of modifier ':M'
+.if ${WORDS:M[^-x1-3} != "+ 0 4"
+. error
+.endif
+
+# [\ Incomplete character list containing a single '\'.
+#
+# A word can only end with a backslash if the preceding
+# character is a backslash as well; in all other cases the final
+# backslash would escape the following space, making the space
+# part of the word. Only the very last word of a string can be
+# '\', as there is no following space that could be escaped.
+WORDS= \\ \a ${:Ux\\}
+PATTERN= ${:U?[\\}
+# expect+1: while evaluating variable "WORDS": warning: Unfinished character list in pattern '?[\' of modifier ':M'
+.if ${WORDS:M${PATTERN}} != "\\\\ x\\"
+. error
+.endif
+
+# [x- Incomplete character list containing an incomplete character
+# range, matches only the 'x'.
+WORDS= [x- x x- y
+# expect+1: while evaluating variable "WORDS": warning: Unfinished character range in pattern '[x-' of modifier ':M'
+.if ${WORDS:M[x-} != "x"
+. error
+.endif
+
+# [^x- Incomplete negated character list containing an incomplete
+# character range; matches each word that does not have an 'x'
+# at the position of the character list.
+#
+# XXX: Even matches strings that are longer than a single
+# character.
+WORDS= [x- x x- y yyyyy
+# expect+1: while evaluating variable "WORDS": warning: Unfinished character range in pattern '[^x-' of modifier ':M'
+.if ${WORDS:M[^x-} != "[x- y yyyyy"
+. error
+.endif
+
+# [:] matches never since the ':' starts the next modifier
+# expect+3: while evaluating variable " : :: ": warning: Unfinished character list in pattern '[' of modifier ':M'
+# expect+2: while evaluating variable " : :: ": Unknown modifier "]"
+# expect+1: Malformed conditional (${ ${:U\:} ${:U\:\:} :L:M[:]} != ":")
+.if ${ ${:U\:} ${:U\:\:} :L:M[:]} != ":"
+. error
+.else
+. error
+.endif
+
+
+# 7. Historical bugs
+
+# Before var.c 1.1031 from 2022-08-24, the following expressions caused an
+# out-of-bounds read beyond the indirect ':M' modifiers.
+#
+# The argument to the inner ':U' is unescaped to 'M\'.
+# This 'M\' becomes an # indirect modifier ':M' with the pattern '\'.
+# The pattern '\' never matches.
+.if ${:U:${:UM\\}}
+. error
+.endif
+# The argument to the inner ':U' is unescaped to 'M\:\'.
+# This 'M\:\' becomes an indirect modifier ':M' with the pattern ':\'.
+# The pattern ':\' never matches.
+.if ${:U:${:UM\\\:\\}}
+. error
+.endif
diff --git a/contrib/bmake/unit-tests/varmod-mtime.exp b/contrib/bmake/unit-tests/varmod-mtime.exp
new file mode 100644
index 000000000000..87f405573437
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-mtime.exp
@@ -0,0 +1,14 @@
+make: "varmod-mtime.mk" line 47: while evaluating variable "no/such/file": Invalid argument '123x' for modifier ':mtime'
+make: "varmod-mtime.mk" line 47: Malformed conditional (${no/such/file:L:mtime=123x})
+make: "varmod-mtime.mk" line 70: while evaluating variable "no/such/file1 no/such/file2": Cannot determine mtime for 'no/such/file1': <ENOENT>
+make: "varmod-mtime.mk" line 70: while evaluating variable "no/such/file1 no/such/file2": Cannot determine mtime for 'no/such/file2': <ENOENT>
+make: "varmod-mtime.mk" line 70: Malformed conditional (${no/such/file1 no/such/file2:L:mtime=error})
+make: "varmod-mtime.mk" line 81: while evaluating variable "MAKEFILE": Invalid argument 'errorhandler-no' for modifier ':mtime'
+make: "varmod-mtime.mk" line 81: Malformed conditional (${MAKEFILE:mtime=errorhandler-no} > 0)
+make: "varmod-mtime.mk" line 90: while evaluating variable "MAKEFILE": Invalid argument 'warn' for modifier ':mtime'
+make: "varmod-mtime.mk" line 90: Malformed conditional (${MAKEFILE:mtime=warn} > 0)
+make: "varmod-mtime.mk" line 115: while evaluating variable "anything": Unknown modifier "mtim"
+make: "varmod-mtime.mk" line 115: Malformed conditional (${anything:L:mtim})
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-mtime.mk b/contrib/bmake/unit-tests/varmod-mtime.mk
new file mode 100644
index 000000000000..189bb8cadc9a
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-mtime.mk
@@ -0,0 +1,125 @@
+# $NetBSD: varmod-mtime.mk,v 1.10 2024/04/20 10:18:55 rillig Exp $
+#
+# Tests for the ':mtime' variable modifier, which maps each word of the
+# expression to that file's modification time.
+
+# Note: strftime() uses mktime() for %s and mktime() assumes localtime
+# so this should match time()
+start:= ${%s:L:localtime} # see varmod-gmtime.mk, keyword '%s'
+
+
+# Ensure that this makefile exists and has a modification time. If the file
+# didn't exist, the ':mtime' modifier would return the current time.
+.if ${MAKEFILE:mtime} >= ${start}
+. error
+.endif
+
+
+# For a file that doesn't exist, the ':mtime' modifier returns the current
+# time, without an error or warning message. The returned timestamp differs
+# from the 'now' that is used when updating the timestamps in archives or for
+# touching files using the '-t' option, which is taken once when make is
+# started.
+not_found_mtime:= ${no/such/file:L:mtime}
+.if ${not_found_mtime} < ${start}
+. error
+.endif
+
+
+# The ':mtime' modifier accepts a timestamp in seconds as an optional
+# argument. This timestamp is used as a fallback in case the file's time
+# cannot be determined, without any error or warning message.
+.if ${no/such/file:L:mtime=0} != "0"
+. error
+.endif
+
+
+# The fallback timestamp must start with a digit, and it is interpreted as a
+# decimal integer.
+.if ${no/such/file:L:mtime=00042} != "42"
+. error
+.endif
+
+
+# The fallback timestamp must only be an integer, without trailing characters.
+# expect+2: while evaluating variable "no/such/file": Invalid argument '123x' for modifier ':mtime'
+# expect+1: Malformed conditional (${no/such/file:L:mtime=123x})
+.if ${no/such/file:L:mtime=123x}
+. error
+.else
+. error
+.endif
+
+
+# The timestamp of a newly created file must be at least as great as the
+# timestamp when parsing of this makefile started.
+COOKIE= ${TMPDIR:U/tmp}/varmod-mtime.cookie
+_!= touch ${COOKIE}
+.if ${COOKIE:mtime=0} < ${start}
+. error ${COOKIE:mtime=0} < ${start}
+.endif
+_!= rm -f ${COOKIE}
+
+
+# If the optional argument of the ':mtime' modifier is the word 'error', the
+# modifier fails with an error message, once for each affected file.
+#
+# expect+3: while evaluating variable "no/such/file1 no/such/file2": Cannot determine mtime for 'no/such/file1': <ENOENT>
+# expect+2: while evaluating variable "no/such/file1 no/such/file2": Cannot determine mtime for 'no/such/file2': <ENOENT>
+# expect+1: Malformed conditional (${no/such/file1 no/such/file2:L:mtime=error})
+.if ${no/such/file1 no/such/file2:L:mtime=error}
+. error
+.else
+. error
+.endif
+
+
+# Only the word 'error' is a special argument to the ':mtime' modifier, all
+# other words result in a parse error.
+# expect+2: while evaluating variable "MAKEFILE": Invalid argument 'errorhandler-no' for modifier ':mtime'
+# expect+1: Malformed conditional (${MAKEFILE:mtime=errorhandler-no} > 0)
+.if ${MAKEFILE:mtime=errorhandler-no} > 0
+.else
+. error
+.endif
+
+
+# Only the word 'error' can be used as a fallback argument to the modifier.
+# expect+2: while evaluating variable "MAKEFILE": Invalid argument 'warn' for modifier ':mtime'
+# expect+1: Malformed conditional (${MAKEFILE:mtime=warn} > 0)
+.if ${MAKEFILE:mtime=warn} > 0
+. error
+.else
+. error
+.endif
+
+
+# Ensure that the fallback for a missing modification time is indeed the
+# current time, and not any later time.
+end:= ${%s:L:gmtime}
+.if ${not_found_mtime} > ${end}
+. error
+.endif
+
+
+# If the expression is irrelevant, the ':mtime' modifier is only parsed, it
+# does not perform any filesystem operations.
+.if 0 && ${no/such/file:L:mtime=error}
+. error
+.endif
+
+
+# If there is a typo in the modifier name, it does not match.
+# expect+2: while evaluating variable "anything": Unknown modifier "mtim"
+# expect+1: Malformed conditional (${anything:L:mtim})
+.if ${anything:L:mtim}
+. error
+.else
+. error
+.endif
+
+
+# An empty word list results in an empty mtime list.
+.if ${:U:mtime} != ""
+. error
+.endif
diff --git a/contrib/bmake/unit-tests/varmod-no-match.mk b/contrib/bmake/unit-tests/varmod-no-match.mk
index 2acb27e2e727..c03b4bf94e70 100644
--- a/contrib/bmake/unit-tests/varmod-no-match.mk
+++ b/contrib/bmake/unit-tests/varmod-no-match.mk
@@ -1,9 +1,97 @@
-# $NetBSD: varmod-no-match.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varmod-no-match.mk,v 1.3 2023/02/26 06:08:06 rillig Exp $
#
-# Tests for the :N variable modifier, which filters words that do not match
-# the given pattern.
+# Tests for the expression modifier ':N', which filters words that do not
+# match the given pattern.
+
+
+# Keep all words except for 'two'.
+.if ${:U one two three :Ntwo} != "one three"
+. error
+.endif
+
+# Keep all words except those starting with 't'.
+# See varmod-match.mk for the details of pattern matching.
+.if ${:U one two three four six :Nt*} != "one four six"
+. error
+.endif
+
+
+# Idiom: normalize whitespace
+#
+# The modifier ':N' can be used with an empty pattern. As that pattern never
+# matches a word, the only effect is that the string is split into words and
+# then joined again, thereby normalizing whitespace around and between the
+# words. And even though the 'N' in ':N' might serve as a mnemonic for
+# "normalize whitespace", this idiom is not used in practice, resorting to the
+# much more common ':M*' to "select all words" instead.
+.if ${:U :N} != ""
+. error
+.endif
+.if ${:U one two three :N} != "one two three"
+. error
+.endif
+.if ${:U one two three :M*} != "one two three"
+. error
+.endif
+
+
+# Idiom: single-word expression equals any of several words or patterns
+#
+# If an expression is guaranteed to consist of a single word, the modifier
+# ':N' can be chained to compare the expression to several words or even
+# patterns in a sequence. If one of the patterns matches, the final
+# expression will be the empty string.
+#
+.if ${:U word :None:Ntwo:Nthree} != ""
+# good
+.else
+. error
+.endif
+.if ${:U two :None:Ntwo:Nthree} != ""
+. error
+.else
+# good
+.endif
+#
+# The modifier ':N' is seldom used in general since positive matches with ':M'
+# are easier to grasp. Chaining the ':N' modifier is even more difficult to
+# grasp due to the many negations involved.
+#
+# The final '!= ""' adds to the confusion because at first glance, the
+# condition may look like '${VAR} != ""', which for a single-word variable is
+# always true.
+#
+# The '!= ""' can be omitted if the expression cannot have the numeric value
+# 0, which is common in practice. In that form, each ':N' can be pronounced
+# as 'neither' or 'nor', which makes the expression sound more natural.
+#
+.if ${:U word :None:Ntwo:Nthree}
+# good
+.else
+. error
+.endif
+.if ${:U two :None:Ntwo:Nthree}
+. error
+.else
+# good
+.endif
+#
+# Replacing the '${...} != ""' with '!empty(...)' doesn't improve the
+# situation as the '!' adds another level of negations, and the word 'empty'
+# is a negation on its own, thereby creating a triple negation. Furthermore,
+# due to the '!empty', the expression to be evaluated no longer starts with
+# '$' and is thus more difficult to spot quickly.
+#
+.if !empty(:U word :None:Ntwo:Nthree)
+# good
+.else
+. error
+.endif
+.if !empty(:U two :None:Ntwo:Nthree)
+. error
+.else
+# good
+.endif
-# TODO: Implementation
all:
- @:;
diff --git a/contrib/bmake/unit-tests/var-class.exp b/contrib/bmake/unit-tests/varmod-order-numeric.exp
index 39a9383953dd..39a9383953dd 100644
--- a/contrib/bmake/unit-tests/var-class.exp
+++ b/contrib/bmake/unit-tests/varmod-order-numeric.exp
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..62212bd265ad
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-order-numeric.mk
@@ -0,0 +1,59 @@
+# $NetBSD: varmod-order-numeric.mk,v 1.8 2022/09/27 19:18:45 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 necessarily provide integer types larger than 32 bit.
+# Make uses 'long long' for C99 or later, and 'long' for older C versions.
+#
+# 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 # subsequence of 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 can be sorted as well.
+MIXED_BASE= 0 010 0x7 9
+.if ${MIXED_BASE:On} != "0 0x7 010 9"
+. error ${MIXED_BASE:On}
+.endif
+
+# The measurement units for suffixes are k, M, G, but not T.
+# The string '3T' evaluates to 3, the string 'x' evaluates as '0'.
+.if ${4 3T 2M x:L:On} != "x 3T 4 2M"
+. error
+.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..e9898600355a 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.8 2023/02/26 06:08:06 rillig Exp $
#
# Tests for the :Ox variable modifier, which returns the words of the
# variable, shuffled.
@@ -6,12 +6,13 @@
# The variable modifier :Ox is available since 2005-06-01.
#
# As of 2020-08-16, make uses random(3) seeded by the current time in seconds.
-# This makes the random numbers completely predictable since there is no other
-# part of make that uses random numbers.
+# This makes the random numbers completely predictable since the only other
+# part of make that uses random numbers is the 'randomize-targets' mode, which
+# is off by default.
#
# 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 +21,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..12d0bff75157 100644
--- a/contrib/bmake/unit-tests/varmod-order.exp
+++ b/contrib/bmake/unit-tests/varmod-order.exp
@@ -1,7 +1,26 @@
-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 16: Undefined variable "${WORDS:OX"
+make: Bad modifier ":OxXX" for variable "WORDS"
+make: "varmod-order.mk" line 21: Undefined variable "${WORDS:Ox"
+make: Unclosed expression, expecting '}' for modifier "O" of variable "WORDS" with value "eight five four nine one seven six ten three two"
+make: Unclosed expression, expecting '}' for modifier "On" of variable "NUMBERS" with value "1 2 3 4 5 6 7 8 9 10"
+make: Unclosed expression, expecting '}' for modifier "Onr" of variable "NUMBERS" with value "10 9 8 7 6 5 4 3 2 1"
+make: Bad modifier ":Oxn" for variable "NUMBERS"
+make: "varmod-order.mk" line 33: Malformed conditional (${NUMBERS:Oxn})
+make: Bad modifier ":On_typo" for variable "NUMBERS"
+make: "varmod-order.mk" line 44: Malformed conditional (${NUMBERS:On_typo})
+make: Bad modifier ":Onr_typo" for variable "NUMBERS"
+make: "varmod-order.mk" line 54: Malformed conditional (${NUMBERS:Onr_typo})
+make: Bad modifier ":Orn_typo" for variable "NUMBERS"
+make: "varmod-order.mk" line 64: Malformed conditional (${NUMBERS:Orn_typo})
+make: Bad modifier ":Onn" for variable "NUMBERS"
+make: "varmod-order.mk" line 76: Malformed conditional (${NUMBERS:Onn})
+make: Bad modifier ":Onrr" for variable "NUMBERS"
+make: "varmod-order.mk" line 86: Malformed conditional (${NUMBERS:Onrr})
+make: Bad modifier ":Orrn" for variable "NUMBERS"
+make: "varmod-order.mk" line 96: Malformed conditional (${NUMBERS:Orrn})
+make: Bad modifier ":On=Off" for variable "SWITCH"
+make: "varmod-order.mk" line 111: Malformed conditional (${SWITCH:On=Off} != "Off")
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..67919543a93e 100644
--- a/contrib/bmake/unit-tests/varmod-order.mk
+++ b/contrib/bmake/unit-tests/varmod-order.mk
@@ -1,19 +1,117 @@
-# $NetBSD: varmod-order.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: varmod-order.mk,v 1.11 2023/06/01 20:56:35 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}
+# FIXME: The error message is wrong.
+# expect+1: Undefined variable "${WORDS:OX"
+_:= ${WORDS:OX}
# Unknown modifier "OxXX"
-_:= ${NUMBERS:OxXX}
+# FIXME: The error message is wrong.
+# expect+1: Undefined variable "${WORDS:Ox"
+_:= ${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: make: 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: make: Bad modifier ":On_typo" for variable "NUMBERS"
+# expect+1: Malformed conditional (${NUMBERS:On_typo})
+.if ${NUMBERS:On_typo}
+. error
+.else
+. error
+.endif
+
+# Extra characters after ':Onr' are detected and diagnosed.
+#
+# expect: make: Bad modifier ":Onr_typo" for variable "NUMBERS"
+# expect+1: Malformed conditional (${NUMBERS:Onr_typo})
+.if ${NUMBERS:Onr_typo}
+. error
+.else
+. error
+.endif
+
+# Extra characters after ':Orn' are detected and diagnosed.
+#
+# expect: make: Bad modifier ":Orn_typo" for variable "NUMBERS"
+# expect+1: Malformed conditional (${NUMBERS:Orn_typo})
+.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: make: Bad modifier ":Onn" for variable "NUMBERS"
+# expect+1: Malformed conditional (${NUMBERS:Onn})
+.if ${NUMBERS:Onn}
+. error
+.else
+. error
+.endif
+
+# Repeating the 'r' is not supported as well, for the same reasons as above.
+#
+# expect: make: Bad modifier ":Onrr" for variable "NUMBERS"
+# expect+1: Malformed conditional (${NUMBERS:Onrr})
+.if ${NUMBERS:Onrr}
+. error
+.else
+. error
+.endif
+
+# Repeating the 'r' is not supported as well, for the same reasons as above.
+#
+# expect: make: Bad modifier ":Orrn" for variable "NUMBERS"
+# expect+1: Malformed conditional (${NUMBERS:Orrn})
+.if ${NUMBERS:Orrn}
+. error
+.else
+. error
+.endif
+
+
+# If a modifier that starts with ':O' is not one of the known sort or shuffle
+# forms, it is a parse error. Several other modifiers such as ':H' or ':u'
+# fall back to the SysV modifier, for example, ':H=new' is not the standard
+# ':H' modifier but instead replaces a trailing 'H' with 'new' in each word.
+# There is no such fallback for the ':O' modifiers.
+SWITCH= On
+# expect: make: Bad modifier ":On=Off" for variable "SWITCH"
+# expect+1: Malformed conditional (${SWITCH:On=Off} != "Off")
+.if ${SWITCH:On=Off} != "Off"
+. error
+.else
+. error
+.endif
all:
- @:;
diff --git a/contrib/bmake/unit-tests/varmod-path.mk b/contrib/bmake/unit-tests/varmod-path.mk
index ebbf755ddbec..25d4e3899b99 100644
--- a/contrib/bmake/unit-tests/varmod-path.mk
+++ b/contrib/bmake/unit-tests/varmod-path.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-path.mk,v 1.3 2020/08/23 08:10:49 rillig Exp $
+# $NetBSD: varmod-path.mk,v 1.4 2023/05/10 15:53:32 rillig Exp $
#
# Tests for the :P variable modifier, which looks up the path for a given
# target.
@@ -7,11 +7,12 @@
# as of 2020-08-23 it is nevertheless resolved to a path. This is probably
# unintended.
#
-# The real target is located in a subdirectory, and its full path is returned.
-# If it had been in the current directory, the difference between its path and
-# its name would not be visible.
+# In this test, the real target is located in a subdirectory, and its full
+# path is returned. If it had been in the current directory, the difference
+# between its path and its name would not be visible.
#
-# The enoent target does not exist, therefore the target name is returned.
+# The enoent target does not exist, therefore the plain name of the target
+# is returned.
.MAIN: all
@@ -20,7 +21,8 @@ _!= mkdir varmod-path.subdir
_!= > varmod-path.subdir/varmod-path.phony
_!= > varmod-path.subdir/varmod-path.real
-# To have an effect, this .PATH declaration must be after the directory is created.
+# To have an effect, this .PATH declaration must be processed after the
+# directory has been created.
.PATH: varmod-path.subdir
varmod-path.phony: .PHONY
diff --git a/contrib/bmake/unit-tests/varmod-quote-dollar.exp b/contrib/bmake/unit-tests/varmod-quote-dollar.exp
index 39a9383953dd..f12b0280e6f7 100644
--- a/contrib/bmake/unit-tests/varmod-quote-dollar.exp
+++ b/contrib/bmake/unit-tests/varmod-quote-dollar.exp
@@ -1 +1,4 @@
+!"#$$%&'()*+,-./09:;<=>?@AZ[\]^_`az{|}~
+!"#$$%&'()*+,-./09:;<=>?@AZ[\]^_`az{|}~
+ !"#$$%&'()*+,-./09:;<=>?@AZ[\]^_`az{|}~
exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-quote-dollar.mk b/contrib/bmake/unit-tests/varmod-quote-dollar.mk
index fedbe8a10f4b..8e68282c536d 100644
--- a/contrib/bmake/unit-tests/varmod-quote-dollar.mk
+++ b/contrib/bmake/unit-tests/varmod-quote-dollar.mk
@@ -1,10 +1,17 @@
-# $NetBSD: varmod-quote-dollar.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varmod-quote-dollar.mk,v 1.4 2022/05/08 10:14:40 rillig Exp $
#
# Tests for the :q variable modifier, which quotes the string for the shell
# and doubles dollar signs, to prevent them from being interpreted by a
# child process of make.
-# TODO: Implementation
+# The newline and space characters at the beginning of this string are passed
+# to the child make. When the child make parses the variable assignment, it
+# discards the leading space characters.
+ASCII_CHARS= ${.newline} !"\#$$%&'()*+,-./09:;<=>?@AZ[\]^_`az{|}~
all:
- @:;
+ @${MAKE} -r -f /dev/null \
+ CHARS=${ASCII_CHARS:q} \
+ TWICE=${ASCII_CHARS:q}${ASCII_CHARS:q} \
+ -V CHARS \
+ -V TWICE
diff --git a/contrib/bmake/unit-tests/varmod-range.exp b/contrib/bmake/unit-tests/varmod-range.exp
index f4ada11ebde6..d9848c2ef4f6 100644
--- a/contrib/bmake/unit-tests/varmod-range.exp
+++ b/contrib/bmake/unit-tests/varmod-range.exp
@@ -1,13 +1,14 @@
-make: "varmod-range.mk" line 53: Invalid number "x}Rest" != "Rest"" for ':range' modifier
-make: "varmod-range.mk" line 53: Malformed conditional ("${:U:range=x}Rest" != "Rest")
-make: "varmod-range.mk" line 62: Unknown modifier "x0"
-make: "varmod-range.mk" line 62: Malformed conditional ("${:U:range=0x0}Rest" != "Rest")
-make: "varmod-range.mk" line 78: Unknown modifier "rang"
-make: "varmod-range.mk" line 78: Malformed conditional ("${a b c:L:rang}Rest" != "Rest")
-make: "varmod-range.mk" line 85: Unknown modifier "rango"
-make: "varmod-range.mk" line 85: Malformed conditional ("${a b c:L:rango}Rest" != "Rest")
-make: "varmod-range.mk" line 92: Unknown modifier "ranger"
-make: "varmod-range.mk" line 92: Malformed conditional ("${a b c:L:ranger}Rest" != "Rest")
+make: "varmod-range.mk" line 43: Malformed conditional (${:range=5} != "")
+make: "varmod-range.mk" line 67: while evaluating "${:U:range=x}Rest" != "Rest"": Invalid number "x}Rest" != "Rest"" for ':range' modifier
+make: "varmod-range.mk" line 67: Malformed conditional ("${:U:range=x}Rest" != "Rest")
+make: "varmod-range.mk" line 78: while evaluating "${:U:range=0x0}Rest" != "Rest"": Unknown modifier "x0"
+make: "varmod-range.mk" line 78: Malformed conditional ("${:U:range=0x0}Rest" != "Rest")
+make: "varmod-range.mk" line 96: while evaluating variable "a b c": Unknown modifier "rang"
+make: "varmod-range.mk" line 96: Malformed conditional ("${a b c:L:rang}Rest" != "Rest")
+make: "varmod-range.mk" line 105: while evaluating variable "a b c": Unknown modifier "rango"
+make: "varmod-range.mk" line 105: Malformed conditional ("${a b c:L:rango}Rest" != "Rest")
+make: "varmod-range.mk" line 114: while evaluating variable "a b c": Unknown modifier "ranger"
+make: "varmod-range.mk" line 114: Malformed conditional ("${a b c:L:ranger}Rest" != "Rest")
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-range.mk b/contrib/bmake/unit-tests/varmod-range.mk
index d63525248e58..2915100bfb79 100644
--- a/contrib/bmake/unit-tests/varmod-range.mk
+++ b/contrib/bmake/unit-tests/varmod-range.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-range.mk,v 1.7 2020/11/01 14:36:25 rillig Exp $
+# $NetBSD: varmod-range.mk,v 1.11 2024/04/20 10:18:55 rillig Exp $
#
# Tests for the :range variable modifier, which generates sequences
# of integers from the given range.
@@ -7,7 +7,7 @@
# modword.mk
# The :range modifier generates a sequence of integers, one number per
-# word of the variable expression's value.
+# word of the expression's value.
.if ${a b c:L:range} != "1 2 3"
. error
.endif
@@ -19,20 +19,32 @@
.endif
# The :range modifier takes the number of words from the value of the
-# variable expression. If that expression is undefined, the range is
+# expression. If that expression is undefined, the range is
# undefined as well. This should not come as a surprise.
.if "${:range}" != ""
. error
.endif
+# An empty expression results in a sequence of a single number, even though
+# the expression contains 0 words.
+.if ${:U:range} != "1"
+. error
+.endif
+
# The :range modifier can be given a parameter, which makes the generated
-# range independent from the value or the name of the variable expression.
-#
-# XXX: As of 2020-09-27, the :range=... modifier does not turn the undefined
-# expression into a defined one. This looks like an oversight.
+# range independent from the value or the name of the expression.
.if "${:range=5}" != ""
. error
.endif
+# XXX: As of 2023-12-17, the ':range=n' modifier does not turn the undefined
+# expression into a defined one, even though it does not depend on the value
+# of the expression. This looks like an oversight.
+# expect+1: Malformed conditional (${:range=5} != "")
+.if ${:range=5} != ""
+. error
+.else
+. error
+.endif
# Negative ranges don't make sense.
# As of 2020-11-01, they are accepted though, using up all available memory.
@@ -50,6 +62,8 @@
#
# Since 2020-11-01, the parser issues a more precise "Invalid number" error
# instead.
+# expect+2: while evaluating "${:U:range=x}Rest" != "Rest"": Invalid number "x}Rest" != "Rest"" for ':range' modifier
+# expect+1: Malformed conditional ("${:U:range=x}Rest" != "Rest")
.if "${:U:range=x}Rest" != "Rest"
. error
.else
@@ -59,6 +73,8 @@
# The upper limit of the range must always be given in decimal.
# This parse error stops at the 'x', trying to parse it as a variable
# modifier.
+# expect+2: while evaluating "${:U:range=0x0}Rest" != "Rest"": Unknown modifier "x0"
+# expect+1: Malformed conditional ("${:U:range=0x0}Rest" != "Rest")
.if "${:U:range=0x0}Rest" != "Rest"
. error
.else
@@ -75,6 +91,8 @@
#.endif
# modifier name too short
+# expect+2: while evaluating variable "a b c": Unknown modifier "rang"
+# expect+1: Malformed conditional ("${a b c:L:rang}Rest" != "Rest")
.if "${a b c:L:rang}Rest" != "Rest"
. error
.else
@@ -82,6 +100,8 @@
.endif
# misspelled modifier name
+# expect+2: while evaluating variable "a b c": Unknown modifier "rango"
+# expect+1: Malformed conditional ("${a b c:L:rango}Rest" != "Rest")
.if "${a b c:L:rango}Rest" != "Rest"
. error
.else
@@ -89,6 +109,8 @@
.endif
# modifier name too long
+# expect+2: while evaluating variable "a b c": Unknown modifier "ranger"
+# expect+1: Malformed conditional ("${a b c:L:ranger}Rest" != "Rest")
.if "${a b c:L:ranger}Rest" != "Rest"
. error
.else
diff --git a/contrib/bmake/unit-tests/varmod-remember.mk b/contrib/bmake/unit-tests/varmod-remember.mk
index 403811759672..e92b2d2c4012 100644
--- a/contrib/bmake/unit-tests/varmod-remember.mk
+++ b/contrib/bmake/unit-tests/varmod-remember.mk
@@ -1,30 +1,62 @@
-# $NetBSD: varmod-remember.mk,v 1.6 2021/03/14 17:27:27 rillig Exp $
+# $NetBSD: varmod-remember.mk,v 1.9 2023/02/09 22:21:57 rillig Exp $
#
-# Tests for the :_ modifier, which saves the current variable value
+# Tests for the :_ modifier, which saves the current expression value
# in the _ variable or another, to be used later again.
-.if ${1 2 3:L:_:@var@${_}@} != "1 2 3 1 2 3 1 2 3"
+
+# The ':_' modifier is typically used in situations where the value of an
+# expression is needed at the same time as a sequence of numbers. In these
+# cases, the value of the expression is saved in the temporary variable '_',
+# from where it is taken later in the same expression.
+ABC= ${A B C:L:_:range:@i@$i=${_:[$i]}@}
+DEF= ${D E F:L:_:range:@i@$i=${_:[$i]}@}
+GHI= ${G H I:L:_:range:@i@$i=${_:[$i]}@}
+
+ABC.global:= ${ABC} # is evaluated in the global scope
+.if ${ABC.global} != "1=A 2=B 3=C"
+. error
+.endif
+
+.if ${DEF} != "1=D 2=E 3=F" # is evaluated in the command line scope
+. error
+.endif
+
+# Before var.c 1.1040 from 2023-02-09, the temporary variable '_' was placed
+# in the scope of the current evaluation, which meant that after the first
+# ':_' modifier had been evaluated in command line scope, all further
+# evaluations in global scope could not overwrite the variable '_' anymore,
+# as the command line scope takes precedence over the global scope.
+# The expression ${GHI} therefore evaluated to '1=D 2=E 3=F', reusing the
+# value of '_' from the previous evaluation in command line scope.
+GHI.global:= ${GHI} # is evaluated in the global scope
+.if ${GHI.global} != "1=G 2=H 3=I"
. error
.endif
+
# In the parameterized form, having the variable name on the right side of
-# the = assignment operator is confusing. In almost all other situations
-# the variable name is on the left-hand side of the = operator. Luckily
-# this modifier is only rarely needed.
+# the = assignment operator looks confusing. In almost all other situations,
+# the variable name is on the left-hand side of the = operator, therefore
+# '_=SAVED' looks like it would copy 'SAVED' to '_'. Luckily, this modifier
+# is only rarely needed.
.if ${1 2 3:L:@var@${var:_=SAVED:}@} != "1 2 3"
. error
.elif ${SAVED} != "3"
. error
.endif
-# The ':_' modifier takes a variable name as optional argument. This variable
-# name can refer to other variables, though this was rather an implementation
-# oversight than an intended feature. The variable name stops at the first
-# '}' or ')' and thus cannot use the usual form ${VARNAME} of long variable
-# names.
+
+# The ':_' modifier takes a variable name as optional argument. Before var.c
+# 1.867 from 2021-03-14, this variable name could refer to other variables,
+# such as in 'VAR.$p'. It was not possible to refer to 'VAR.${param}' though,
+# as that form caused a parse error. The cause for the parse error in
+# '${...:_=VAR.${param}}' is that the variable name is parsed in an ad-hoc
+# manner, stopping at the first ':', ')' or '}', without taking any nested
+# expressions into account. Due to this inconsistency that short expressions
+# are possible but long expressions aren't, the name of the temporary variable
+# is no longer expanded.
#
-# Because of all these edge-casey conditions, this "feature" has been removed
-# in var.c 1.867 from 2021-03-14.
+# TODO: Warn about the unusual variable name '$S'.
S= INDIRECT_VARNAME
.if ${value:L:@var@${var:_=$S}@} != "value"
. error
@@ -32,4 +64,15 @@ S= INDIRECT_VARNAME
. error
.endif
+
+# When a variable using ':_' refers to another variable that also uses ':_',
+# the value of the temporary variable '_' from the inner expression leaks into
+# the evaluation of the outer expression. If the expressions were evaluated
+# independently, the last word of the result would be outer_='outer' instead.
+INNER= ${inner:L:_:@i@$i inner_='$_'@}
+OUTER= ${outer:L:_:@o@$o ${INNER} outer_='$_'@}
+.if ${OUTER} != "outer inner inner_='inner' outer_='inner'"
+.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.exp b/contrib/bmake/unit-tests/varmod-select-words.exp
index 39a9383953dd..02e9974c02d6 100644
--- a/contrib/bmake/unit-tests/varmod-select-words.exp
+++ b/contrib/bmake/unit-tests/varmod-select-words.exp
@@ -1 +1,126 @@
+make: Bad modifier ":[]" for variable "LIST"
+LIST:[]="" is an error
+LIST:[0]="one two three four five six"
+LIST:[0x0]="one two three four five six"
+LIST:[000]="one two three four five six"
+LIST:[*]="one two three four five six"
+LIST:[@]="one two three four five six"
+LIST:[0]:C/ /,/="one,two three four five six"
+LIST:[0]:C/ /,/g="one,two,three,four,five,six"
+LIST:[0]:C/ /,/1g="one,two,three,four,five,six"
+LIST:[*]:C/ /,/="one,two three four five six"
+LIST:[*]:C/ /,/g="one,two,three,four,five,six"
+LIST:[*]:C/ /,/1g="one,two,three,four,five,six"
+LIST:[@]:C/ /,/="one two three four five six"
+LIST:[@]:C/ /,/g="one two three four five six"
+LIST:[@]:C/ /,/1g="one two three four five six"
+LIST:[@]:[0]:C/ /,/="one,two three four five six"
+LIST:[0]:[@]:C/ /,/="one two three four five six"
+LIST:[@]:[*]:C/ /,/="one,two three four five six"
+LIST:[*]:[@]:C/ /,/="one two three four five six"
+EMPTY=""
+EMPTY:[#]="1" == 1 ?
+ESCAPEDSPACE="\ "
+ESCAPEDSPACE:[#]="1" == 1 ?
+REALLYSPACE=" "
+REALLYSPACE:[#]="1" == 1 ?
+LIST:[#]="6"
+LIST:[0]:[#]="1" == 1 ?
+LIST:[*]:[#]="1" == 1 ?
+LIST:[@]:[#]="6"
+LIST:[1]:[#]="1"
+LIST:[1..3]:[#]="3"
+EMPTY:[1]=""
+ESCAPEDSPACE="\ "
+ESCAPEDSPACE:[1]="\ "
+REALLYSPACE=" "
+REALLYSPACE:[1]="" == "" ?
+REALLYSPACE:[*]:[1]=" " == " " ?
+LIST:[1]="one"
+make: Bad modifier ":[1.]" for variable "LIST"
+LIST:[1.]="" is an error
+make: Bad modifier ":[1]." for variable "LIST"
+LIST:[1].="}" is an error
+LIST:[2]="two"
+LIST:[6]="six"
+LIST:[7]=""
+LIST:[999]=""
+make: Bad modifier ":[-]" for variable "LIST"
+LIST:[-]="" is an error
+make: Bad modifier ":[--]" for variable "LIST"
+LIST:[--]="" is an error
+LIST:[-1]="six"
+LIST:[-2]="five"
+LIST:[-6]="one"
+LIST:[-7]=""
+LIST:[-999]=""
+LONGLIST:[17]="17"
+LONGLIST:[0x11]="17"
+LONGLIST:[021]="17"
+LIST:[0]:[1]="one two three four five six"
+LIST:[*]:[1]="one two three four five six"
+LIST:[@]:[1]="one"
+LIST:[0]:[2]=""
+LIST:[*]:[2]=""
+LIST:[@]:[2]="two"
+LIST:[*]:C/ /,/:[2]=""
+LIST:[*]:C/ /,/:[*]:[2]=""
+LIST:[*]:C/ /,/:[@]:[2]="three"
+LONGLIST:[012..0x12]="10 11 12 13 14 15 16 17 18"
+make: Bad modifier ":[1.]" for variable "LIST"
+LIST:[1.]="" is an error
+make: Bad modifier ":[1..]" for variable "LIST"
+LIST:[1..]="" is an error
+make: Bad modifier ":[1.. ]" for variable "LIST"
+LIST:[1.. ]="" is an error
+LIST:[1..1]="one"
+make: Bad modifier ":[1..1.]" for variable "LIST"
+LIST:[1..1.]="" is an error
+LIST:[1..2]="one two"
+LIST:[2..1]="two one"
+LIST:[3..-2]="three four five"
+LIST:[-4..4]="three four"
+make: Bad modifier ":[0..1]" for variable "LIST"
+LIST:[0..1]="" is an error
+make: Bad modifier ":[-1..0]" for variable "LIST"
+LIST:[-1..0]="" is an error
+LIST:[-1..1]="six five four three two one"
+LIST:[0..0]="one two three four five six"
+LIST:[3..99]="three four five six"
+LIST:[-3..-99]="four three two one"
+LIST:[-99..-3]="one two three four"
+HASH="#" == "#" ?
+LIST:[${HASH}]="6"
+LIST:[${ZERO}]="one two three four five six"
+LIST:[${ZERO}x${ONE}]="one"
+LIST:[${ONE}]="one"
+LIST:[${MINUSONE}]="six"
+LIST:[${STAR}]="one two three four five six"
+LIST:[${AT}]="one two three four five six"
+make: Bad modifier ":[${EMPTY" for variable "LIST"
+LIST:[${EMPTY}]="" is an error
+LIST:[${LONGLIST:[21]:S/2//}]="one"
+LIST:[${LIST:[#]}]="six"
+LIST:[${LIST:[${HASH}]}]="six"
+LIST:[ -1.. +3]="six five four three"
+LIST:S/ /,/="one two three four five six"
+LIST:S/ /,/W="one,two three four five six"
+LIST:S/ /,/gW="one,two,three,four,five,six"
+EMPTY:S/^/,/=","
+EMPTY:S/^/,/W=","
+LIST:C/ /,/="one two three four five six"
+LIST:C/ /,/W="one,two three four five six"
+LIST:C/ /,/gW="one,two,three,four,five,six"
+EMPTY:C/^/,/=","
+EMPTY:C/^/,/W=","
+LIST:tW="one two three four five six"
+LIST:tw="one two three four five six"
+LIST:tW:C/ /,/="one,two three four five six"
+LIST:tW:C/ /,/g="one,two,three,four,five,six"
+LIST:tW:C/ /,/1g="one,two,three,four,five,six"
+LIST:tw:C/ /,/="one two three four five six"
+LIST:tw:C/ /,/g="one two three four five six"
+LIST:tw:C/ /,/1g="one two three four five six"
+LIST:tw:tW:C/ /,/="one,two three four five six"
+LIST:tW:tw:C/ /,/="one two three four five six"
exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-select-words.mk b/contrib/bmake/unit-tests/varmod-select-words.mk
index a9df25f9ff32..910b67a24e39 100644
--- a/contrib/bmake/unit-tests/varmod-select-words.mk
+++ b/contrib/bmake/unit-tests/varmod-select-words.mk
@@ -1,9 +1,166 @@
-# $NetBSD: varmod-select-words.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varmod-select-words.mk,v 1.4 2022/01/23 16:09:38 rillig Exp $
#
# Tests for the :[...] variable modifier, which selects a single word
# or a range of words from a variable.
+#
+# History:
+# The variable modifier ':[...]' was added on 2003-09-27.
+#
+# See also:
+# modword.mk (should be migrated here)
+
+all: mod-squarebrackets mod-S-W mod-C-W mod-tW-tw
+
+LIST= one two three four five six
+LONGLIST= 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
+
+EMPTY= # the space should be ignored
+ESCAPEDSPACE= \ # escaped space before the '#', the actual value is '\ '
+REALLYSPACE:= ${:U }
+HASH= \#
+AT= @
+STAR= *
+ZERO= 0
+ONE= 1
+MINUSONE= -1
+
+mod-squarebrackets: mod-squarebrackets-0-star-at \
+ mod-squarebrackets-hash \
+ mod-squarebrackets-n \
+ mod-squarebrackets-start-end \
+ mod-squarebrackets-nested \
+ mod-squarebrackets-space
+
+mod-squarebrackets-0-star-at:
+ @echo 'LIST:[]="${LIST:[]}" is an error'
+ @echo 'LIST:[0]="${LIST:[0]}"'
+ @echo 'LIST:[0x0]="${LIST:[0x0]}"'
+ @echo 'LIST:[000]="${LIST:[000]}"'
+ @echo 'LIST:[*]="${LIST:[*]}"'
+ @echo 'LIST:[@]="${LIST:[@]}"'
+ @echo 'LIST:[0]:C/ /,/="${LIST:[0]:C/ /,/}"'
+ @echo 'LIST:[0]:C/ /,/g="${LIST:[0]:C/ /,/g}"'
+ @echo 'LIST:[0]:C/ /,/1g="${LIST:[0]:C/ /,/1g}"'
+ @echo 'LIST:[*]:C/ /,/="${LIST:[*]:C/ /,/}"'
+ @echo 'LIST:[*]:C/ /,/g="${LIST:[*]:C/ /,/g}"'
+ @echo 'LIST:[*]:C/ /,/1g="${LIST:[*]:C/ /,/1g}"'
+ @echo 'LIST:[@]:C/ /,/="${LIST:[@]:C/ /,/}"'
+ @echo 'LIST:[@]:C/ /,/g="${LIST:[@]:C/ /,/g}"'
+ @echo 'LIST:[@]:C/ /,/1g="${LIST:[@]:C/ /,/1g}"'
+ @echo 'LIST:[@]:[0]:C/ /,/="${LIST:[@]:[0]:C/ /,/}"'
+ @echo 'LIST:[0]:[@]:C/ /,/="${LIST:[0]:[@]:C/ /,/}"'
+ @echo 'LIST:[@]:[*]:C/ /,/="${LIST:[@]:[*]:C/ /,/}"'
+ @echo 'LIST:[*]:[@]:C/ /,/="${LIST:[*]:[@]:C/ /,/}"'
+
+mod-squarebrackets-hash:
+ @echo 'EMPTY="${EMPTY}"'
+ @echo 'EMPTY:[#]="${EMPTY:[#]}" == 1 ?'
+ @echo 'ESCAPEDSPACE="${ESCAPEDSPACE}"'
+ @echo 'ESCAPEDSPACE:[#]="${ESCAPEDSPACE:[#]}" == 1 ?'
+ @echo 'REALLYSPACE="${REALLYSPACE}"'
+ @echo 'REALLYSPACE:[#]="${REALLYSPACE:[#]}" == 1 ?'
+ @echo 'LIST:[#]="${LIST:[#]}"'
+ @echo 'LIST:[0]:[#]="${LIST:[0]:[#]}" == 1 ?'
+ @echo 'LIST:[*]:[#]="${LIST:[*]:[#]}" == 1 ?'
+ @echo 'LIST:[@]:[#]="${LIST:[@]:[#]}"'
+ @echo 'LIST:[1]:[#]="${LIST:[1]:[#]}"'
+ @echo 'LIST:[1..3]:[#]="${LIST:[1..3]:[#]}"'
+
+mod-squarebrackets-n:
+ @echo 'EMPTY:[1]="${EMPTY:[1]}"'
+ @echo 'ESCAPEDSPACE="${ESCAPEDSPACE}"'
+ @echo 'ESCAPEDSPACE:[1]="${ESCAPEDSPACE:[1]}"'
+ @echo 'REALLYSPACE="${REALLYSPACE}"'
+ @echo 'REALLYSPACE:[1]="${REALLYSPACE:[1]}" == "" ?'
+ @echo 'REALLYSPACE:[*]:[1]="${REALLYSPACE:[*]:[1]}" == " " ?'
+ @echo 'LIST:[1]="${LIST:[1]}"'
+ @echo 'LIST:[1.]="${LIST:[1.]}" is an error'
+ @echo 'LIST:[1].="${LIST:[1].}" is an error'
+ @echo 'LIST:[2]="${LIST:[2]}"'
+ @echo 'LIST:[6]="${LIST:[6]}"'
+ @echo 'LIST:[7]="${LIST:[7]}"'
+ @echo 'LIST:[999]="${LIST:[999]}"'
+ @echo 'LIST:[-]="${LIST:[-]}" is an error'
+ @echo 'LIST:[--]="${LIST:[--]}" is an error'
+ @echo 'LIST:[-1]="${LIST:[-1]}"'
+ @echo 'LIST:[-2]="${LIST:[-2]}"'
+ @echo 'LIST:[-6]="${LIST:[-6]}"'
+ @echo 'LIST:[-7]="${LIST:[-7]}"'
+ @echo 'LIST:[-999]="${LIST:[-999]}"'
+ @echo 'LONGLIST:[17]="${LONGLIST:[17]}"'
+ @echo 'LONGLIST:[0x11]="${LONGLIST:[0x11]}"'
+ @echo 'LONGLIST:[021]="${LONGLIST:[021]}"'
+ @echo 'LIST:[0]:[1]="${LIST:[0]:[1]}"'
+ @echo 'LIST:[*]:[1]="${LIST:[*]:[1]}"'
+ @echo 'LIST:[@]:[1]="${LIST:[@]:[1]}"'
+ @echo 'LIST:[0]:[2]="${LIST:[0]:[2]}"'
+ @echo 'LIST:[*]:[2]="${LIST:[*]:[2]}"'
+ @echo 'LIST:[@]:[2]="${LIST:[@]:[2]}"'
+ @echo 'LIST:[*]:C/ /,/:[2]="${LIST:[*]:C/ /,/:[2]}"'
+ @echo 'LIST:[*]:C/ /,/:[*]:[2]="${LIST:[*]:C/ /,/:[*]:[2]}"'
+ @echo 'LIST:[*]:C/ /,/:[@]:[2]="${LIST:[*]:C/ /,/:[@]:[2]}"'
+ @echo 'LONGLIST:[012..0x12]="${LONGLIST:[012..0x12]}"'
+
+mod-squarebrackets-start-end:
+ @echo 'LIST:[1.]="${LIST:[1.]}" is an error'
+ @echo 'LIST:[1..]="${LIST:[1..]}" is an error'
+ @echo 'LIST:[1.. ]="${LIST:[1.. ]}" is an error'
+ @echo 'LIST:[1..1]="${LIST:[1..1]}"'
+ @echo 'LIST:[1..1.]="${LIST:[1..1.]}" is an error'
+ @echo 'LIST:[1..2]="${LIST:[1..2]}"'
+ @echo 'LIST:[2..1]="${LIST:[2..1]}"'
+ @echo 'LIST:[3..-2]="${LIST:[3..-2]}"'
+ @echo 'LIST:[-4..4]="${LIST:[-4..4]}"'
+ @echo 'LIST:[0..1]="${LIST:[0..1]}" is an error'
+ @echo 'LIST:[-1..0]="${LIST:[-1..0]}" is an error'
+ @echo 'LIST:[-1..1]="${LIST:[-1..1]}"'
+ @echo 'LIST:[0..0]="${LIST:[0..0]}"'
+ @echo 'LIST:[3..99]="${LIST:[3..99]}"'
+ @echo 'LIST:[-3..-99]="${LIST:[-3..-99]}"'
+ @echo 'LIST:[-99..-3]="${LIST:[-99..-3]}"'
+
+mod-squarebrackets-nested:
+ @echo 'HASH="${HASH}" == "#" ?'
+ @echo 'LIST:[$${HASH}]="${LIST:[${HASH}]}"'
+ @echo 'LIST:[$${ZERO}]="${LIST:[${ZERO}]}"'
+ @echo 'LIST:[$${ZERO}x$${ONE}]="${LIST:[${ZERO}x${ONE}]}"'
+ @echo 'LIST:[$${ONE}]="${LIST:[${ONE}]}"'
+ @echo 'LIST:[$${MINUSONE}]="${LIST:[${MINUSONE}]}"'
+ @echo 'LIST:[$${STAR}]="${LIST:[${STAR}]}"'
+ @echo 'LIST:[$${AT}]="${LIST:[${AT}]}"'
+ @echo 'LIST:[$${EMPTY}]="${LIST:[${EMPTY}]}" is an error'
+ @echo 'LIST:[$${LONGLIST:[21]:S/2//}]="${LIST:[${LONGLIST:[21]:S/2//}]}"'
+ @echo 'LIST:[$${LIST:[#]}]="${LIST:[${LIST:[#]}]}"'
+ @echo 'LIST:[$${LIST:[$${HASH}]}]="${LIST:[${LIST:[${HASH}]}]}"'
+
+mod-squarebrackets-space:
+ # As of 2020-11-01, it is possible to have spaces before the numbers
+ # but not after them. This is an unintended side-effect of using
+ # strtol for parsing the numbers.
+ @echo 'LIST:[ -1.. +3]="${LIST:[ -1.. +3]}"'
+
+mod-C-W:
+ @echo 'LIST:C/ /,/="${LIST:C/ /,/}"'
+ @echo 'LIST:C/ /,/W="${LIST:C/ /,/W}"'
+ @echo 'LIST:C/ /,/gW="${LIST:C/ /,/gW}"'
+ @echo 'EMPTY:C/^/,/="${EMPTY:C/^/,/}"'
+ @echo 'EMPTY:C/^/,/W="${EMPTY:C/^/,/W}"'
-# TODO: Implementation
+mod-S-W:
+ @echo 'LIST:S/ /,/="${LIST:S/ /,/}"'
+ @echo 'LIST:S/ /,/W="${LIST:S/ /,/W}"'
+ @echo 'LIST:S/ /,/gW="${LIST:S/ /,/gW}"'
+ @echo 'EMPTY:S/^/,/="${EMPTY:S/^/,/}"'
+ @echo 'EMPTY:S/^/,/W="${EMPTY:S/^/,/W}"'
-all:
- @:;
+mod-tW-tw:
+ @echo 'LIST:tW="${LIST:tW}"'
+ @echo 'LIST:tw="${LIST:tw}"'
+ @echo 'LIST:tW:C/ /,/="${LIST:tW:C/ /,/}"'
+ @echo 'LIST:tW:C/ /,/g="${LIST:tW:C/ /,/g}"'
+ @echo 'LIST:tW:C/ /,/1g="${LIST:tW:C/ /,/1g}"'
+ @echo 'LIST:tw:C/ /,/="${LIST:tw:C/ /,/}"'
+ @echo 'LIST:tw:C/ /,/g="${LIST:tw:C/ /,/g}"'
+ @echo 'LIST:tw:C/ /,/1g="${LIST:tw:C/ /,/1g}"'
+ @echo 'LIST:tw:tW:C/ /,/="${LIST:tw:tW:C/ /,/}"'
+ @echo 'LIST:tW:tw:C/ /,/="${LIST:tW:tw:C/ /,/}"'
diff --git a/contrib/bmake/unit-tests/varmod-shell.exp b/contrib/bmake/unit-tests/varmod-shell.exp
index 9aef0c9e5acc..208ef953728b 100644
--- a/contrib/bmake/unit-tests/varmod-shell.exp
+++ b/contrib/bmake/unit-tests/varmod-shell.exp
@@ -1,3 +1,13 @@
make: "echo word; false" returned non-zero status
make: "echo word; false" returned non-zero status
+Global: _ = # (empty)
+Var_Parse: ${:!echo word; ${:Ufalse}!} (eval-keep-dollar-and-undefined)
+Evaluating modifier ${:!...} on value "" (eval-keep-dollar-and-undefined, undefined)
+Modifier part: "echo word; false"
+Capturing the output of command "echo word; false"
+make: "echo word; false" returned non-zero status
+Result of ${:!echo word; ${:Ufalse}!} is "word" (eval-keep-dollar-and-undefined, defined)
+Global: _ = word
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-shell.mk b/contrib/bmake/unit-tests/varmod-shell.mk
index c736042f80a0..d449709cee0f 100644
--- a/contrib/bmake/unit-tests/varmod-shell.mk
+++ b/contrib/bmake/unit-tests/varmod-shell.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-shell.mk,v 1.6 2021/02/14 20:16:17 rillig Exp $
+# $NetBSD: varmod-shell.mk,v 1.7 2022/01/10 20:32:29 rillig Exp $
#
# Tests for the ':!cmd!' variable modifier, which runs the shell command
# given by the variable modifier and returns its output.
@@ -20,8 +20,7 @@
#
# Between 2000-04-29 and 2020-11-17, the error message mentioned the previous
# value of the expression (which is usually an empty string) instead of the
-# command that was executed. It's strange that such a simple bug could
-# survive such a long time.
+# command that was executed.
.if ${:!echo word; false!} != "word"
. error
.endif
@@ -29,4 +28,9 @@
. error
.endif
+
+.MAKEFLAGS: -dv # to see the actual command
+_:= ${:!echo word; ${:Ufalse}!}
+.MAKEFLAGS: -d0
+
all:
diff --git a/contrib/bmake/unit-tests/varmod-subst-regex.exp b/contrib/bmake/unit-tests/varmod-subst-regex.exp
index a09046ef764c..25626ba18508 100644
--- a/contrib/bmake/unit-tests/varmod-subst-regex.exp
+++ b/contrib/bmake/unit-tests/varmod-subst-regex.exp
@@ -20,7 +20,7 @@ mod-regex-limits:22-ok:1 33 556
mod-regex-limits:capture:ihgfedcbaabcdefghijABCDEFGHIJa0a1a2rest
make: Regex compilation error: (details omitted)
mod-regex-errors:
-make: Unknown modifier "Z"
+make: in target "mod-regex-errors": while evaluating variable "word": while evaluating "${:U:Z}y,W}": Unknown modifier "Z"
mod-regex-errors: xy
unmatched-subexpression.ok: one one 2 3 5 8 one3 2one 34
make: No match for subexpression \2
diff --git a/contrib/bmake/unit-tests/varmod-subst-regex.mk b/contrib/bmake/unit-tests/varmod-subst-regex.mk
index 197691d73aad..c1ffc4580d3a 100644
--- a/contrib/bmake/unit-tests/varmod-subst-regex.mk
+++ b/contrib/bmake/unit-tests/varmod-subst-regex.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-subst-regex.mk,v 1.7 2021/06/21 08:17:39 rillig Exp $
+# $NetBSD: varmod-subst-regex.mk,v 1.11 2023/12/18 11:13:51 rillig Exp $
#
# Tests for the :C,from,to, variable modifier.
@@ -10,7 +10,7 @@ all: mod-regex-limits
all: mod-regex-errors
all: unmatched-subexpression
-# The variable expression expands to 4 words. Of these words, none matches
+# The expression expands to 4 words. Of these words, none matches
# the regular expression "a b" since these words don't contain any
# whitespace.
.if ${:Ua b b c:C,a b,,} != "a b b c"
@@ -18,7 +18,7 @@ all: unmatched-subexpression
.endif
# Using the '1' modifier does not change anything. The '1' modifier just
-# means to apply at most 1 replacement in the whole variable expression.
+# means to apply at most 1 replacement in the whole expression.
.if ${:Ua b b c:C,a b,,1} != "a b b c"
. error
.endif
@@ -84,9 +84,54 @@ all: unmatched-subexpression
. error
.endif
+
+# Like the ':S' modifier, the ':C' modifier matches on an expression
+# that contains no words at all, but only if the regular expression matches an
+# empty string, for example, when the regular expression is anchored at the
+# beginning or the end of the word. An unanchored regular expression that
+# matches the empty string is uncommon in practice, as it would match before
+# each character of the word.
+.if "<${:U:S,,unanchored,}> <${:U:C,.?,unanchored,}>" != "<> <unanchored>"
+. error
+.endif
+.if "<${:U:S,^,prefix,}> <${:U:C,^,prefix,}>" != "<prefix> <prefix>"
+. error
+.endif
+.if "<${:U:S,$,suffix,}> <${:U:C,$,suffix,}>" != "<suffix> <suffix>"
+. error
+.endif
+.if "<${:U:S,^$,whole,}> <${:U:C,^$,whole,}>" != "<whole> <whole>"
+. error
+.endif
+.if "<${:U:S,,unanchored,g}> <${:U:C,.?,unanchored,g}>" != "<> <unanchored>"
+. error
+.endif
+.if "<${:U:S,^,prefix,g}> <${:U:C,^,prefix,g}>" != "<prefix> <prefix>"
+. error
+.endif
+.if "<${:U:S,$,suffix,g}> <${:U:C,$,suffix,g}>" != "<suffix> <suffix>"
+. error
+.endif
+.if "<${:U:S,^$,whole,g}> <${:U:C,^$,whole,g}>" != "<whole> <whole>"
+. error
+.endif
+.if "<${:U:S,,unanchored,W}> <${:U:C,.?,unanchored,W}>" != "<> <unanchored>"
+. error
+.endif
+.if "<${:U:S,^,prefix,W}> <${:U:C,^,prefix,W}>" != "<prefix> <prefix>"
+. error
+.endif
+.if "<${:U:S,$,suffix,W}> <${:U:C,$,suffix,W}>" != "<suffix> <suffix>"
+. error
+.endif
+.if "<${:U:S,^$,whole,W}> <${:U:C,^$,whole,W}>" != "<whole> <whole>"
+. error
+.endif
+
+
# Multiple asterisks form an invalid regular expression. This produces an
# error message and (as of 2020-08-28) stops parsing in the middle of the
-# variable expression. The unparsed part of the expression is then copied
+# expression. The unparsed part of the expression is then copied
# verbatim to the output, which is unexpected and can lead to strange shell
# commands being run.
mod-regex-compile-error:
@@ -101,7 +146,7 @@ mod-regex-limits:
@echo $@:22-missing:${:U1 23 456:C,(.).,\2\2,:Q}
@echo $@:22-ok:${:U1 23 456:C,(.)(.),\2\2,:Q}
# The :C modifier only handles single-digit capturing groups,
- # which is more than enough for daily use.
+ # which is enough for all practical use cases.
@echo $@:capture:${:UabcdefghijABCDEFGHIJrest:C,(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.),\9\8\7\6\5\4\3\2\1\0\10\11\12,}
mod-regex-errors:
@@ -109,7 +154,7 @@ mod-regex-errors:
# If the replacement pattern produces a parse error because of an
# unknown modifier, the parse error is ignored in ParseModifierPart
- # and the faulty variable expression expands to "".
+ # and the faulty expression expands to "".
@echo $@: ${word:L:C,.*,x${:U:Z}y,W}
# In regular expressions with alternatives, not all capturing groups are
diff --git a/contrib/bmake/unit-tests/varmod-subst.exp b/contrib/bmake/unit-tests/varmod-subst.exp
index 97fa2e4f1491..9d88b3d6fa6f 100644
--- a/contrib/bmake/unit-tests/varmod-subst.exp
+++ b/contrib/bmake/unit-tests/varmod-subst.exp
@@ -45,7 +45,7 @@ mod-subst-delimiter:
1 two 3 tilde
mod-subst-chain:
A B c.
-make: Unknown modifier "i"
+make: in target "mod-subst-chain": while evaluating "${:Uvalue:S,a,x,i}.": Unknown modifier "i"
.
mod-subst-dollar:$1:
mod-subst-dollar:$2:
diff --git a/contrib/bmake/unit-tests/varmod-subst.mk b/contrib/bmake/unit-tests/varmod-subst.mk
index 85f41e499ab7..2903d36445f8 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.14 2023/12/18 11:13:51 rillig Exp $
#
# Tests for the :S,from,to, variable modifier.
@@ -9,83 +9,180 @@ all: mod-subst-dollar
WORDS= sequences of letters
+# The empty pattern never matches anything, except if it is anchored at the
+# beginning or the end of the word.
.if ${WORDS:S,,,} != ${WORDS}
-. warning The empty pattern matches something.
+. error
.endif
+# The :S modifier flag '1' is applied exactly once.
.if ${WORDS:S,e,*,1} != "s*quences of letters"
-. warning The :S modifier flag '1' is not applied exactly once.
+. error
.endif
+# The :S modifier flag '1' is applied to the first occurrence, no matter if
+# the occurrence is in the first word or not.
.if ${WORDS:S,f,*,1} != "sequences o* letters"
-. warning The :S modifier flag '1' is only applied to the first word,\
- not to the first occurrence.
+. error
.endif
+# The :S modifier replaces every first match per word.
.if ${WORDS:S,e,*,} != "s*quences of l*tters"
-. warning The :S modifier does not replace every first match per word.
+. error
.endif
+# The :S modifier flag 'g' replaces every occurrence.
.if ${WORDS:S,e,*,g} != "s*qu*nc*s of l*tt*rs"
-. warning The :S modifier flag 'g' does not replace every occurrence.
+. error
.endif
+# The '^' in the search pattern anchors the pattern at the beginning of each
+# word, thereby matching a prefix.
.if ${WORDS:S,^sequ,occurr,} != "occurrences of letters"
-. warning The :S modifier fails for a short match anchored at the start.
+. error
.endif
+# The :S modifier with a '^' anchor replaces the whole word if that word is
+# exactly the pattern.
.if ${WORDS:S,^of,with,} != "sequences with letters"
-. warning The :S modifier fails for an exact match anchored at the start.
+. error
.endif
+# The :S modifier does not match if the pattern is longer than the word.
.if ${WORDS:S,^office,does not match,} != ${WORDS}
-. warning The :S modifier matches a too long pattern anchored at the start.
+. warning
.endif
+# The '$' in the search pattern anchors the pattern at the end of each word,
+# thereby matching a suffix.
.if ${WORDS:S,f$,r,} != "sequences or letters"
-. warning The :S modifier fails for a short match anchored at the end.
+. error
.endif
+# The :S modifier with a '$' anchor replaces at most one occurrence per word.
.if ${WORDS:S,s$,,} != "sequence of letter"
-. warning The :S modifier fails to replace one occurrence per word.
+. error
.endif
+# The :S modifier with a '$' anchor replaces the whole word if that word is
+# exactly the pattern.
.if ${WORDS:S,of$,,} != "sequences letters"
-. warning The :S modifier fails for an exact match anchored at the end.
+. error
.endif
+# The :S modifier with a '$' anchor and a pattern that is longer than a word
+# cannot match that word.
.if ${WORDS:S,eof$,,} != ${WORDS}
-. warning The :S modifier matches a too long pattern anchored at the end.
+. warning
.endif
+# The :S modifier with the '^' and '$' anchors matches an exact word.
.if ${WORDS:S,^of$,,} != "sequences letters"
-. warning The :S modifier does not match a word anchored at both ends.
+. error
.endif
+# The :S modifier with the '^' and '$' anchors does not match a word that
+# starts with the pattern but is longer than the pattern.
.if ${WORDS:S,^o$,,} != ${WORDS}
-. warning The :S modifier matches a prefix anchored at both ends.
+. error
.endif
+# The :S modifier with the '^' and '$' anchors does not match a word that ends
+# with the pattern but is longer than the pattern.
.if ${WORDS:S,^f$,,} != ${WORDS}
-. warning The :S modifier matches a suffix anchored at both ends.
+. error
.endif
+# The :S modifier with the '^' and '$' anchors does not match a word if the
+# pattern ends with the word but is longer than the word.
.if ${WORDS:S,^eof$,,} != ${WORDS}
-. warning The :S modifier matches a too long prefix anchored at both ends.
+. error
.endif
+# The :S modifier with the '^' and '$' anchors does not match a word if the
+# pattern starts with the word but is longer than the word.
.if ${WORDS:S,^office$,,} != ${WORDS}
-. warning The :S modifier matches a too long suffix anchored at both ends.
+. error
.endif
+# Except for the '^' and '$' anchors, the pattern does not contain any special
+# characters, so the '*' from the pattern would only match a literal '*' in a
+# word.
.if ${WORDS:S,*,replacement,} != ${WORDS}
-. error The '*' seems to be interpreted as a wildcard of some kind.
+. error
.endif
+# Except for the '^' and '$' anchors, the pattern does not contain any special
+# characters, so the '.' from the pattern would only match a literal '.' in a
+# word.
.if ${WORDS:S,.,replacement,} != ${WORDS}
-. error The '.' seems to be interpreted as a wildcard of some kind.
+. error
.endif
+# The '&' in the replacement is a placeholder for the text matched by the
+# pattern.
+.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
+
+
+# When a word is replaced with nothing, the remaining words are separated by a
+# single space, not two.
+.if ${1 2 3:L:S,2,,} != "1 3"
+. error
+.endif
+
+
+# In an empty expression, the ':S' modifier matches a single time, but only if
+# the search string is empty and anchored at either the beginning or the end
+# of the word.
+.if ${:U:S,,out-of-nothing,} != ""
+. error
+.endif
+.if ${:U:S,^,out-of-nothing,} != "out-of-nothing"
+. error
+.endif
+.if ${:U:S,$,out-of-nothing,} != "out-of-nothing"
+. error
+.endif
+.if ${:U:S,^$,out-of-nothing,} != "out-of-nothing"
+. error
+.endif
+.if ${:U:S,,out-of-nothing,g} != ""
+. error
+.endif
+.if ${:U:S,^,out-of-nothing,g} != "out-of-nothing"
+. error
+.endif
+.if ${:U:S,$,out-of-nothing,g} != "out-of-nothing"
+. error
+.endif
+.if ${:U:S,^$,out-of-nothing,g} != "out-of-nothing"
+. error
+.endif
+.if ${:U:S,,out-of-nothing,W} != ""
+. error
+.endif
+.if ${:U:S,^,out-of-nothing,W} != "out-of-nothing"
+. error
+.endif
+.if ${:U:S,$,out-of-nothing,W} != "out-of-nothing"
+. error
+.endif
+.if ${:U:S,^$,out-of-nothing,W} != "out-of-nothing"
+. error
+.endif
+
+
mod-subst:
@echo $@:
@echo :${:Ua b b c:S,a b,,:Q}:
diff --git a/contrib/bmake/unit-tests/varmod-sun-shell.exp b/contrib/bmake/unit-tests/varmod-sun-shell.exp
index 5087bc66d943..7f661ff6e79e 100644
--- a/contrib/bmake/unit-tests/varmod-sun-shell.exp
+++ b/contrib/bmake/unit-tests/varmod-sun-shell.exp
@@ -1,2 +1,13 @@
make: "echo word; false" returned non-zero status
+Global: _ = # (empty)
+Var_Parse: ${echo word; ${:Ufalse}:L:sh} (eval-keep-dollar-and-undefined)
+Evaluating modifier ${echo word; false:L} on value "" (eval-keep-dollar-and-undefined, undefined)
+Result of ${echo word; false:L} is "echo word; false" (eval-keep-dollar-and-undefined, defined)
+Evaluating modifier ${echo word; false:s...} on value "echo word; false" (eval-keep-dollar-and-undefined, defined)
+Capturing the output of command "echo word; false"
+make: "echo word; false" returned non-zero status
+Result of ${echo word; false:sh} is "word" (eval-keep-dollar-and-undefined, defined)
+Global: _ = word
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-sun-shell.mk b/contrib/bmake/unit-tests/varmod-sun-shell.mk
index 712b36bc7030..97acc5bd8c0f 100644
--- a/contrib/bmake/unit-tests/varmod-sun-shell.mk
+++ b/contrib/bmake/unit-tests/varmod-sun-shell.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-sun-shell.mk,v 1.1 2021/02/14 20:16:17 rillig Exp $
+# $NetBSD: varmod-sun-shell.mk,v 1.2 2022/01/10 20:32:29 rillig Exp $
#
# Tests for the :sh variable modifier, which runs the shell command
# given by the variable value and returns its output.
@@ -18,4 +18,9 @@
. error
.endif
+
+.MAKEFLAGS: -dv # to see the actual command
+_:= ${echo word; ${:Ufalse}:L:sh}
+.MAKEFLAGS: -d0
+
all:
diff --git a/contrib/bmake/unit-tests/varmod-sysv.exp b/contrib/bmake/unit-tests/varmod-sysv.exp
index 59275857f98a..6a3728c3c0fc 100644
--- a/contrib/bmake/unit-tests/varmod-sysv.exp
+++ b/contrib/bmake/unit-tests/varmod-sysv.exp
@@ -1,5 +1,5 @@
make: Unfinished modifier for "word214" ('=' missing)
-make: "varmod-sysv.mk" line 214: Malformed conditional (${word214:L:from${:D=}to})
+make: "varmod-sysv.mk" line 215: Malformed conditional (${word214:L:from${:D=}to})
word modifier result
'' = ""
suffix = "suffix"
diff --git a/contrib/bmake/unit-tests/varmod-sysv.mk b/contrib/bmake/unit-tests/varmod-sysv.mk
index 712c1731717b..0f92e1df7032 100644
--- a/contrib/bmake/unit-tests/varmod-sysv.mk
+++ b/contrib/bmake/unit-tests/varmod-sysv.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-sysv.mk,v 1.14 2021/04/12 16:09:57 rillig Exp $
+# $NetBSD: varmod-sysv.mk,v 1.16 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the variable modifier ':from=to', which replaces the suffix
# "from" with "to". It can also use '%' as a wildcard.
@@ -49,7 +49,7 @@
. error
.endif
-# In the modifier ':from=to', both parts can contain variable expressions.
+# In the modifier ':from=to', both parts can contain expressions.
.if ${one two:L:${:Uone}=${:U1}} != "1 two"
. error
.endif
@@ -69,7 +69,7 @@
.endif
# The replacement string can contain spaces, thereby changing the number
-# of words in the variable expression.
+# of words in the expression.
.if ${In:L:%=% ${:Uthe Sun}} != "In the Sun"
. error
.endif
@@ -206,11 +206,12 @@
. error
.endif
-# This is not a SysV modifier since the nested variable expression expands
+# This is not a SysV modifier since the nested expression expands
# to an empty string. The '=' in it should be irrelevant during parsing.
# XXX: As of 2020-12-05, this expression generates an "Unfinished modifier"
# error, while the correct error message would be "Unknown modifier" since
# there is no modifier named "fromto".
+# expect+1: Malformed conditional (${word214:L:from${:D=}to})
.if ${word214:L:from${:D=}to}
. error
.endif
@@ -220,7 +221,7 @@
# "fromto}...". The next modifier is a SysV modifier. ApplyModifier_SysV
# parses the modifier as "from${:D=}to", ending at the '}'. Next, the two
# parts of the modifier are parsed using ParseModifierPart, which scans
-# differently, properly handling nested variable expressions. The two parts
+# differently, properly handling nested expressions. The two parts
# are now "fromto}..." and "replaced".
.if "${:Ufromto\}...:from${:D=}to}...=replaced}" != "replaced"
. error
diff --git a/contrib/bmake/unit-tests/varmod-to-abs.exp b/contrib/bmake/unit-tests/varmod-to-abs.exp
index 426b4d39744f..4506d5ea8175 100644
--- a/contrib/bmake/unit-tests/varmod-to-abs.exp
+++ b/contrib/bmake/unit-tests/varmod-to-abs.exp
@@ -1,5 +1,5 @@
-make: "varmod-to-abs.mk" line 18: does-not-exist.c
make: "varmod-to-abs.mk" line 19: does-not-exist.c
+make: "varmod-to-abs.mk" line 21: does-not-exist.c
cached_realpath: varmod-to-abs.mk -> varmod-to-abs.mk
-make: "varmod-to-abs.mk" line 23: varmod-to-abs.mk
+make: "varmod-to-abs.mk" line 26: varmod-to-abs.mk
exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-to-abs.mk b/contrib/bmake/unit-tests/varmod-to-abs.mk
index 7f23318487e3..bc4722068988 100644
--- a/contrib/bmake/unit-tests/varmod-to-abs.mk
+++ b/contrib/bmake/unit-tests/varmod-to-abs.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-to-abs.mk,v 1.5 2020/11/15 05:48:17 rillig Exp $
+# $NetBSD: varmod-to-abs.mk,v 1.6 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the :tA variable modifier, which returns the absolute path for
# each of the words in the variable value.
@@ -15,11 +15,14 @@
# keeping the cache, just like the GNode for global variables.
.MAKEFLAGS: -dd
does-not-exist.c= /dev/null
+# expect+1: does-not-exist.c
.info ${does-not-exist.c:L:tA}
+# expect+1: does-not-exist.c
.info ${does-not-exist.c:L:tA}
# The output of the following line is modified by the global _SED_CMDS in
# unit-tests/Makefile. See the .rawout file for the truth.
+# expect+1: varmod-to-abs.mk
.info ${MAKEFILE:tA}
.MAKEFLAGS: -d0
diff --git a/contrib/bmake/unit-tests/varmod-to-lower.mk b/contrib/bmake/unit-tests/varmod-to-lower.mk
index 19d3406054b7..44116fd3eee2 100644
--- a/contrib/bmake/unit-tests/varmod-to-lower.mk
+++ b/contrib/bmake/unit-tests/varmod-to-lower.mk
@@ -1,7 +1,7 @@
-# $NetBSD: varmod-to-lower.mk,v 1.5 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: varmod-to-lower.mk,v 1.6 2022/11/29 23:54:55 rillig Exp $
#
-# Tests for the :tl variable modifier, which returns the words in the
-# variable value, converted to lowercase.
+# Tests for the :tl variable modifier, which converts the expression value
+# to lowercase.
#
# TODO: What about non-ASCII characters? ISO-8859-1, UTF-8?
@@ -17,5 +17,10 @@
. error
.endif
-all:
- @:;
+# The ':tl' modifier works on the whole string, without splitting it into
+# words.
+.if ${:Umultiple spaces:tl} != "multiple spaces"
+. error
+.endif
+
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/varmod-to-separator.exp b/contrib/bmake/unit-tests/varmod-to-separator.exp
index c6e8ce98a21a..6bdd6f0c2d43 100644
--- a/contrib/bmake/unit-tests/varmod-to-separator.exp
+++ b/contrib/bmake/unit-tests/varmod-to-separator.exp
@@ -1,19 +1,25 @@
-make: "varmod-to-separator.mk" line 107: Invalid character number: 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: Malformed conditional (${WORDS:[1..3]:ts\x100:tu})
+make: "varmod-to-separator.mk" line 155: while evaluating variable "WORDS": Invalid character number at "400:tu}"
+make: "varmod-to-separator.mk" line 155: Malformed conditional (${WORDS:[1..3]:ts\400:tu})
+make: "varmod-to-separator.mk" line 171: while evaluating variable "WORDS": Invalid character number at "100:tu}"
+make: "varmod-to-separator.mk" line 171: 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})
+make: "varmod-to-separator.mk" line 179: Malformed conditional (${WORDS:[1..3]:ts\-300:tu})
make: Bad modifier ":ts\8" for variable "1 2 3"
-make: "varmod-to-separator.mk" line 136: Malformed conditional (${1 2 3:L:ts\8:tu})
+make: "varmod-to-separator.mk" line 188: Malformed conditional (${1 2 3:L:ts\8:tu})
make: Bad modifier ":ts\100L" for variable "1 2 3"
-make: "varmod-to-separator.mk" line 143: Malformed conditional (${1 2 3:L:ts\100L})
+make: "varmod-to-separator.mk" line 196: Malformed conditional (${1 2 3:L:ts\100L})
make: Bad modifier ":ts\x40g" for variable "1 2 3"
-make: "varmod-to-separator.mk" line 150: Malformed conditional (${1 2 3:L:ts\x40g})
+make: "varmod-to-separator.mk" line 204: Malformed conditional (${1 2 3:L:ts\x40g})
make: Bad modifier ":tx" for variable "WORDS"
-make: "varmod-to-separator.mk" line 158: Malformed conditional (${WORDS:tx} != "anything")
+make: "varmod-to-separator.mk" line 214: Malformed conditional (${WORDS:tx})
+make: Bad modifier ":ts\X" for variable "WORDS"
+make: "varmod-to-separator.mk" line 223: Malformed conditional (${WORDS:ts\X})
make: Bad modifier ":t\X" for variable "WORDS"
-make: "varmod-to-separator.mk" line 165: Malformed conditional (${WORDS:t\X} != "anything")
+make: "varmod-to-separator.mk" line 232: Malformed conditional (${WORDS:t\X} != "anything")
+make: Bad modifier ":ts\69" for variable ""
+make: "varmod-to-separator.mk" line 249: Malformed conditional (${:Ua b:ts\69})
+make: "varmod-to-separator.mk" line 258: while evaluating "${:Ua b:ts\x1F60E}": Invalid character number at "1F60E}"
+make: "varmod-to-separator.mk" line 258: Malformed conditional (${:Ua b:ts\x1F60E})
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-to-separator.mk b/contrib/bmake/unit-tests/varmod-to-separator.mk
index 08c6126ecc68..8b8aeb4d3c33 100644
--- a/contrib/bmake/unit-tests/varmod-to-separator.mk
+++ b/contrib/bmake/unit-tests/varmod-to-separator.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-to-separator.mk,v 1.7 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: varmod-to-separator.mk,v 1.14 2024/04/20 10:18:55 rillig Exp $
#
# Tests for the :ts variable modifier, which joins the words of the variable
# using an arbitrary character as word separator.
@@ -39,7 +39,7 @@ WORDS= one two three four five six
# quote though, or other special characters like dollar or backslash.
#
# This example also demonstrates that the closing brace is not interpreted
-# as a separator, but as the closing delimiter of the whole variable
+# as a separator, but as the closing delimiter of the whole
# expression.
.if ${WORDS:tu:ts} != "ONETWOTHREEFOURFIVESIX"
. warning Colon as separator does not work.
@@ -80,6 +80,52 @@ WORDS= one two three four five six
. warning The :ts modifier followed by an :S modifier does not work.
.endif
+# After the modifier ':ts/', the expression value is a single word since all
+# spaces have been replaced with '/'. This single word does not start with
+# 'two', which makes the modifier ':S' a no-op.
+.if ${WORDS:ts/:S/^two/2/} != "one/two/three/four/five/six"
+. error
+.endif
+
+# After the :ts modifier, the whole string is interpreted as a single
+# word since all spaces have been replaced with x. Because of this single
+# word, only the first 'b' is replaced with 'B'.
+.if ${aa bb aa bb aa bb:L:tsx:S,b,B,} != "aaxBbxaaxbbxaaxbb"
+. error
+.endif
+
+# The :ts modifier also applies to word separators that are added
+# afterwards. First, the modifier ':tsx' joins the 3 words, then the modifier
+# ':S' replaces the 2 'b's with spaces. These spaces are part of the word,
+# so when the words are joined at the end of the modifier ':S', there is only
+# a single word, and the custom separator from the modifier ':tsx' has no
+# effect.
+.if ${a ababa c:L:tsx:S,b, ,g} != "axa a axc"
+. error
+.endif
+
+# Adding the modifier ':M*' at the end of the above chain splits the
+# expression value and then joins it again. At this point of splitting, the
+# newly added spaces are treated as word separators, resulting in 3 words.
+# When these 3 words are joined, the separator from the modifier ':tsx' is
+# used.
+.if ${a ababa c:L:tsx:S,b, ,g:M*} != "axaxaxaxc"
+. error
+.endif
+
+# Not all modifiers use the separator from the previous modifier ':ts' though.
+# The modifier ':@' always uses a space as word separator instead. This has
+# probably been an oversight during implementation. For consistency, the
+# result should rather be "axaxaxaxc", as in the previous example.
+.if ${a ababa c:L:tsx:S,b, ,g:@v@$v@} != "axa a axc"
+. error
+.endif
+
+# Adding a final :M* modifier applies the :ts separator again, though.
+.if ${a ababa c:L:tsx:S,b, ,g:@v@${v}@:M*} != "axaxaxaxc"
+. error
+.endif
+
# The separator can be \n, which is a newline.
.if ${WORDS:[1..3]:ts\n} != "one${.newline}two${.newline}three"
. warning The separator \n does not produce a newline.
@@ -104,6 +150,8 @@ WORDS= one two three four five six
# for an unsigned character though.
#
# Since 2020-11-01, these out-of-bounds values are rejected.
+# expect+2: while evaluating variable "WORDS": Invalid character number at "400:tu}"
+# expect+1: Malformed conditional (${WORDS:[1..3]:ts\400:tu})
.if ${WORDS:[1..3]:ts\400:tu}
. warning The separator \400 is accepted even though it is out of bounds.
.else
@@ -118,6 +166,8 @@ WORDS= one two three four five six
# The hexadecimal number must be in the range of an unsigned char.
#
# Since 2020-11-01, these out-of-bounds values are rejected.
+# expect+2: while evaluating variable "WORDS": Invalid character number at "100:tu}"
+# expect+1: Malformed conditional (${WORDS:[1..3]:ts\x100:tu})
.if ${WORDS:[1..3]:ts\x100:tu}
. warning The separator \x100 is accepted even though it is out of bounds.
.else
@@ -125,6 +175,7 @@ WORDS= one two three four five six
.endif
# Negative numbers are not allowed for the separator character.
+# expect+1: Malformed conditional (${WORDS:[1..3]:ts\-300:tu})
.if ${WORDS:[1..3]:ts\-300:tu}
. warning The separator \-300 is accepted even though it is negative.
.else
@@ -133,6 +184,7 @@ WORDS= one two three four five six
# The character number is interpreted as octal number by default.
# The digit '8' is not an octal digit though.
+# expect+1: Malformed conditional (${1 2 3:L:ts\8:tu})
.if ${1 2 3:L:ts\8:tu}
. warning The separator \8 is accepted even though it is not octal.
.else
@@ -140,6 +192,7 @@ WORDS= one two three four five six
.endif
# Trailing characters after the octal character number are rejected.
+# expect+1: Malformed conditional (${1 2 3:L:ts\100L})
.if ${1 2 3:L:ts\100L}
. warning The separator \100L is accepted even though it contains an 'L'.
.else
@@ -147,6 +200,7 @@ WORDS= one two three four five six
.endif
# Trailing characters after the hexadecimal character number are rejected.
+# expect+1: Malformed conditional (${1 2 3:L:ts\x40g})
.if ${1 2 3:L:ts\x40g}
. warning The separator \x40g is accepted even though it contains a 'g'.
.else
@@ -155,21 +209,54 @@ WORDS= one two three four five six
# In the :t modifier, the :t must be followed by any of A, l, s, u.
-.if ${WORDS:tx} != "anything"
-. info This line is not reached because of the malformed condition.
-. info If this line were reached, it would be visible in the -dcpv log.
+# expect: make: Bad modifier ":tx" for variable "WORDS"
+# expect+1: Malformed conditional (${WORDS:tx})
+.if ${WORDS:tx}
+. error
+.else
+. error
+.endif
+
+# The word separator can only be a single character.
+# expect: make: Bad modifier ":ts\X" for variable "WORDS"
+# expect+1: Malformed conditional (${WORDS:ts\X})
+.if ${WORDS:ts\X}
+. error
+.else
+. error
.endif
# After the backslash, only n, t, an octal number, or x and a hexadecimal
# number are allowed.
+# expect+1: Malformed conditional (${WORDS:t\X} != "anything")
.if ${WORDS:t\X} != "anything"
. info This line is not reached.
.endif
-# TODO: This modifier used to accept decimal numbers as well, in the form
-# ':ts\120'. When has this been changed to octal, and what happens now
-# for ':ts\90' ('Z' in decimal ASCII, undefined in octal)?
-# TODO: :ts\x1F600
+# Since 2003.07.23.18.06.46 and before 2016.03.07.20.20.35, the modifier ':ts'
+# interpreted an "octal escape" as decimal if the first digit was not '0'.
+.if ${:Ua b:ts\61} != "a1b" # decimal would have been "a=b"
+. error
+.endif
-all:
+# Since the character escape is always interpreted as octal, let's see what
+# happens for non-octal digits. From 2003.07.23.18.06.46 to
+# 2016.02.27.16.20.06, the result was '1E2', since 2016.03.07.20.20.35 make no
+# longer accepts this escape and complains.
+# expect: make: Bad modifier ":ts\69" for variable ""
+# expect+1: Malformed conditional (${:Ua b:ts\69})
+.if ${:Ua b:ts\69}
+. error
+.else
+. error
+.endif
+
+# Try whether bmake is Unicode-ready.
+# expect+2: while evaluating "${:Ua b:ts\x1F60E}": Invalid character number at "1F60E}"
+# expect+1: Malformed conditional (${:Ua b:ts\x1F60E})
+.if ${:Ua b:ts\x1F60E} # U+1F60E "smiling face with sunglasses"
+. error
+.else
+. error
+.endif
diff --git a/contrib/bmake/unit-tests/varmod-undefined.mk b/contrib/bmake/unit-tests/varmod-undefined.mk
index e06fc73244ab..fd56ffd35e30 100644
--- a/contrib/bmake/unit-tests/varmod-undefined.mk
+++ b/contrib/bmake/unit-tests/varmod-undefined.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-undefined.mk,v 1.7 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: varmod-undefined.mk,v 1.9 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the :U variable modifier, which returns the given string
# if the variable is undefined.
@@ -19,17 +19,17 @@
.endif
# .endfor
-# The variable expressions in the text of the :U modifier may be arbitrarily
+# The expressions in the text of the :U modifier may be arbitrarily
# nested.
.if ${:U${:Unested}${${${:Udeeply}}}} != nested
. error
.endif
-# The nested variable expressions may contain braces, and these braces don't
+# The nested expressions may contain braces, and these braces don't
# need to match pairwise. In the following example, the :S modifier uses '{'
# as delimiter, which confuses both editors and humans because the opening
-# and # closing braces don't match anymore. It's syntactically valid though.
+# and closing braces don't match anymore. It's syntactically valid though.
# For more similar examples, see varmod-subst.mk, mod-subst-delimiter.
.if ${:U${:Uvalue:S{a{X{}} != vXlue
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/varmod.exp b/contrib/bmake/unit-tests/varmod.exp
index e36c4ded9b47..22a7019dfc1a 100644
--- a/contrib/bmake/unit-tests/varmod.exp
+++ b/contrib/bmake/unit-tests/varmod.exp
@@ -1,8 +1,8 @@
-make: "varmod.mk" line 42: To escape a dollar, use \$, not $$, at "$$:L} != """
-make: "varmod.mk" line 42: Invalid variable name ':', at "$:L} != """
-make: "varmod.mk" line 47: Dollar followed by nothing
-make: "varmod.mk" line 56: Missing delimiter ':' after modifier "P"
-make: "varmod.mk" line 57: Missing argument for ".error"
+make: "varmod.mk" line 101: To escape a dollar, use \$, not $$, at "$$:L} != """
+make: "varmod.mk" line 101: Invalid variable name ':', at "$:L} != """
+make: "varmod.mk" line 107: while evaluating "${:Uword:@word@${word}$@} != "word"": Dollar followed by nothing
+make: "varmod.mk" line 117: while evaluating variable "VAR": Missing delimiter ':' after modifier "P"
+make: "varmod.mk" line 119: Missing argument for ".error"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varmod.mk b/contrib/bmake/unit-tests/varmod.mk
index 21ddf9103251..c749dfb9659d 100644
--- a/contrib/bmake/unit-tests/varmod.mk
+++ b/contrib/bmake/unit-tests/varmod.mk
@@ -1,11 +1,68 @@
-# $NetBSD: varmod.mk,v 1.5 2020/12/19 22:33:11 rillig Exp $
+# $NetBSD: varmod.mk,v 1.11 2024/04/20 10:18:56 rillig Exp $
#
# Tests for variable modifiers, such as :Q, :S,from,to or :Ufallback.
+#
+# See also:
+# varparse-errors.mk
+
+# As of 2022-08-06, the possible behaviors during parsing are:
+#
+# * `strict`: the parsing style used by most modifiers:
+# * either uses `ParseModifierPart` or parses the modifier literal
+# * other modifiers may follow, separated by a ':'
+#
+# * `greedy`: calls `ParseModifierPart` with `ch->endc`; this means
+# that no further modifiers are parsed in that expression.
+#
+# * `no-colon`: after parsing this modifier, the following modifier
+# does not need to be separated by a colon.
+# Omitting this colon is bad style.
+#
+# * `individual`: parsing this modifier does not follow the common
+# pattern of calling `ParseModifierPart`.
+#
+# The SysV column says whether a parse error in the modifier falls back
+# trying the `:from=to` System V modifier.
+#
+# | **Operator** | **Behavior** | **Remarks** | **SysV** |
+# |--------------|--------------|--------------------|----------|
+# | `!` | no-colon | | no |
+# | `:=` | greedy | | yes |
+# | `?:` | greedy | | no |
+# | `@` | no-colon | | no |
+# | `C` | no-colon | | no |
+# | `D` | individual | custom parser | N/A |
+# | `E` | strict | | yes |
+# | `H` | strict | | yes |
+# | `L` | no-colon | | N/A |
+# | `M` | individual | custom parser | N/A |
+# | `N` | individual | custom parser | N/A |
+# | `O` | strict | only literal value | no |
+# | `P` | no-colon | | N/A |
+# | `Q` | strict | | yes |
+# | `R` | strict | | yes |
+# | `S` | no-colon | | N/A |
+# | `T` | strict | | N/A |
+# | `U` | individual | custom parser | N/A |
+# | `[` | strict | | no |
+# | `_` | individual | strcspn | yes |
+# | `gmtime` | strict | only literal value | yes |
+# | `hash` | strict | | N/A |
+# | `localtime` | strict | only literal value | yes |
+# | `q` | strict | | yes |
+# | `range` | strict | | N/A |
+# | `sh` | strict | | N/A |
+# | `t` | strict | | no |
+# | `u` | strict | | yes |
+# | `from=to` | greedy | SysV, fallback | N/A |
+
+# These tests assume
+.MAKE.SAVE_DOLLARS = yes
DOLLAR1= $$
DOLLAR2= ${:U\$}
-# To get a single '$' sign in the value of a variable expression, it has to
+# To get a single '$' sign in the value of an expression, it has to
# be written as '$$' in a literal variable value.
#
# See Var_Parse, where it calls Var_Subst.
@@ -39,21 +96,26 @@ DOLLAR2= ${:U\$}
# For compatibility, make does not print these error messages in normal mode.
# Should it?
.MAKEFLAGS: -dL
+# expect+2: To escape a dollar, use \$, not $$, at "$$:L} != """
+# expect+1: Invalid variable name ':', at "$:L} != """
.if ${$$:L} != ""
. error
.endif
# A '$' followed by nothing is an error as well.
+# expect+1: while evaluating "${:Uword:@word@${word}$@} != "word"": Dollar followed by nothing
.if ${:Uword:@word@${word}$@} != "word"
. error
.endif
# The variable modifier :P does not fall back to the SysV modifier.
# Therefore the modifier :P=RE generates a parse error.
-# XXX: The .error should not be reached since the variable expression is
+# XXX: The .error should not be reached since the expression is
# malformed, and this error should be propagated up to Cond_EvalLine.
VAR= STOP
+# expect+1: while evaluating variable "VAR": Missing delimiter ':' after modifier "P"
.if ${VAR:P=RE} != "STORE"
+# expect+1: Missing argument for ".error"
. error
.endif
diff --git a/contrib/bmake/unit-tests/varname-dollar.exp b/contrib/bmake/unit-tests/varname-dollar.exp
index c880e82f0170..4f7c2ebbdf12 100644
--- a/contrib/bmake/unit-tests/varname-dollar.exp
+++ b/contrib/bmake/unit-tests/varname-dollar.exp
@@ -1,5 +1,5 @@
-make: "varname-dollar.mk" line 16: dollar is $.
-make: "varname-dollar.mk" line 17: dollar in braces is .
-make: "varname-dollar.mk" line 25: dollar is $.
-make: "varname-dollar.mk" line 26: dollar in braces is dollar.
+make: "varname-dollar.mk" line 17: dollar is $.
+make: "varname-dollar.mk" line 19: dollar in braces is .
+make: "varname-dollar.mk" line 28: dollar is $.
+make: "varname-dollar.mk" line 30: dollar in braces is dollar.
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dollar.mk b/contrib/bmake/unit-tests/varname-dollar.mk
index d1db9f833306..e60c9dd43e8f 100644
--- a/contrib/bmake/unit-tests/varname-dollar.mk
+++ b/contrib/bmake/unit-tests/varname-dollar.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varname-dollar.mk,v 1.3 2020/08/19 05:40:06 rillig Exp $
+# $NetBSD: varname-dollar.mk,v 1.4 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the expression "$$", which looks as if it referred to a variable,
# but simply expands to a single '$' sign.
@@ -13,7 +13,9 @@ DOLLAR= $$
# At this point, the variable '$' is not defined. Therefore the second line
# returns an empty string.
+# expect+1: dollar is $.
.info dollar is $$.
+# expect+1: dollar in braces is .
.info dollar in braces is ${${DOLLAR}}.
# Now overwrite the '$' variable to see whether '$$' really expands to that
@@ -22,7 +24,9 @@ ${DOLLAR}= dollar
# At this point, the variable '$' is defined, therefore its value is printed
# in the second .info directive.
+# expect+1: dollar is $.
.info dollar is $$.
+# expect+1: dollar in braces is dollar.
.info dollar in braces is ${${DOLLAR}}.
all:
diff --git a/contrib/bmake/unit-tests/varname-dot-make-jobs.exp b/contrib/bmake/unit-tests/varname-dot-make-jobs.exp
index 39a9383953dd..a899c5d38418 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-jobs.exp
+++ b/contrib/bmake/unit-tests/varname-dot-make-jobs.exp
@@ -1 +1,8 @@
+undefined
+1
+--- echo ---
+5
+--- echo ---
+20
+1
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-jobs.mk b/contrib/bmake/unit-tests/varname-dot-make-jobs.mk
index 1e99b3d28ea8..5ed62c180eed 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-jobs.mk
+++ b/contrib/bmake/unit-tests/varname-dot-make-jobs.mk
@@ -1,8 +1,43 @@
-# $NetBSD: varname-dot-make-jobs.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varname-dot-make-jobs.mk,v 1.5 2023/09/10 16:25:32 sjg Exp $
#
-# Tests for the special .MAKE.JOBS variable.
+# Tests for the special .MAKE.JOBS variable, which is defined in jobs mode
+# only. There it contains the number of jobs that may run in parallel.
-# TODO: Implementation
+.MAIN: all
+
+echo: .PHONY
+ @echo ${.MAKE.JOBS:Uundefined}
all:
- @:;
+ @${MAKE} -r -f ${MAKEFILE} echo
+ @${MAKE} -r -f ${MAKEFILE} echo -j1
+ @${MAKE} -r -f ${MAKEFILE} echo -j5
+ @${MAKE} -r -f ${MAKEFILE} echo -j20
+ @${MAKE} -r -f ${MAKEFILE} echo -j00000000000000000000000000000001
+
+.if !make(echo) && ${.MAKE.JOBS.C} == "yes"
+# These results will not be static, we need NCPU
+# to compute expected results.
+all: jC
+
+NCPU!= ${MAKE} -r -f /dev/null -jC -V .MAKE.JOBS
+
+# If -j arg is floating point or ends in C;
+# .MAKE.JOBS is a multiple of _SC_NPROCESSORS_ONLN
+# No news is good news here.
+jCvals ?= 1 1.2 2
+
+jC:
+ @for j in ${jCvals}; do \
+ e=`echo "${NCPU} * $$j" | bc | sed 's/\.[0-9]*//'`; \
+ g=`${MAKE} -r -f /dev/null -V .MAKE.JOBS -j$${j}C`; \
+ test $$g = $$e || echo "$$g != $$e"; \
+ done
+
+.endif
+
+# expect: undefined
+# expect: 1
+# expect: 5
+# expect: 20
+# expect: 1
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp
index 39a9383953dd..045d26dc080e 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp
@@ -1 +1,11 @@
+Initialize check-ignore.meta
+Building <tmpdir>/obj/check-ignore
+Skipping meta for .END: .SPECIAL
+Use .MAKE.META.IGNORE_FILTER - check-ignore is up to date
+`check-ignore' is up to date.
+Skipping meta for .END: .SPECIAL
+Skip .MAKE.META.IGNORE_FILTER - check-ignore is out of date
+<tmpdir>/obj/check-ignore.meta: <line>: file '<tmpdir>/ignore/check' is newer than the target...
+Building <tmpdir>/obj/check-ignore
+Skipping meta for .END: .SPECIAL
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk
index c41aec4acdf8..0adf6a202857 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk
@@ -1,8 +1,5 @@
-# $NetBSD: varname-dot-make-meta-ignore_filter.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varname-dot-make-meta-ignore_filter.mk,v 1.3 2023/02/23 05:20:45 sjg Exp $
#
# Tests for the special .MAKE.META.IGNORE_FILTER variable.
-# TODO: Implementation
-
-all:
- @:;
+.include "meta-ignore.inc"
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp
index 39a9383953dd..161d09c2eff4 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp
@@ -1 +1,11 @@
+Initialize check-ignore.meta
+Building <tmpdir>/obj/check-ignore
+Skipping meta for .END: .SPECIAL
+Use .MAKE.META.IGNORE_PATHS - check-ignore is up to date
+`check-ignore' is up to date.
+Skipping meta for .END: .SPECIAL
+Skip .MAKE.META.IGNORE_PATHS - check-ignore is out of date
+<tmpdir>/obj/check-ignore.meta: <line>: file '<tmpdir>/ignore/check' is newer than the target...
+Building <tmpdir>/obj/check-ignore
+Skipping meta for .END: .SPECIAL
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk
index 4ae34f51608b..2c58849af0be 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk
@@ -1,8 +1,5 @@
-# $NetBSD: varname-dot-make-meta-ignore_paths.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varname-dot-make-meta-ignore_paths.mk,v 1.3 2023/02/23 05:20:45 sjg Exp $
#
# Tests for the special .MAKE.META.IGNORE_PATHS variable.
-# TODO: Implementation
-
-all:
- @:;
+.include "meta-ignore.inc"
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp
index 39a9383953dd..ebc1c0c6261c 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp
@@ -1 +1,11 @@
+Initialize check-ignore.meta
+Building <tmpdir>/obj/check-ignore
+Skipping meta for .END: .SPECIAL
+Use .MAKE.META.IGNORE_PATTERNS - check-ignore is up to date
+`check-ignore' is up to date.
+Skipping meta for .END: .SPECIAL
+Skip .MAKE.META.IGNORE_PATTERNS - check-ignore is out of date
+<tmpdir>/obj/check-ignore.meta: <line>: file '<tmpdir>/ignore/check' is newer than the target...
+Building <tmpdir>/obj/check-ignore
+Skipping meta for .END: .SPECIAL
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk
index ea9fc49f1718..d3d6e065857d 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk
+++ b/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk
@@ -1,8 +1,5 @@
-# $NetBSD: varname-dot-make-meta-ignore_patterns.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varname-dot-make-meta-ignore_patterns.mk,v 1.3 2023/02/23 05:20:45 sjg Exp $
#
# Tests for the special .MAKE.META.IGNORE_PATTERNS variable.
-# TODO: Implementation
-
-all:
- @:;
+.include "meta-ignore.inc"
diff --git a/contrib/bmake/unit-tests/varname-dot-make-mode.exp b/contrib/bmake/unit-tests/varname-dot-make-mode.exp
index 39a9383953dd..fa033e718b0c 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-mode.exp
+++ b/contrib/bmake/unit-tests/varname-dot-make-mode.exp
@@ -1 +1,31 @@
+randomize compat mode:
+: Making a
+: Making a
+: Making a
+: Making b
+: Making b
+: Making b
+: Making c
+: Making c
+: Making c
+randomize jobs mode (-j1):
+: Making a
+: Making a
+: Making a
+: Making b
+: Making b
+: Making b
+: Making c
+: Making c
+: Making c
+randomize jobs mode (-j5):
+: Making a
+: Making a
+: Making a
+: Making b
+: Making b
+: Making b
+: Making c
+: Making c
+: Making c
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-make-mode.mk b/contrib/bmake/unit-tests/varname-dot-make-mode.mk
index ee75a54ebd74..169aa17cdc36 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-mode.mk
+++ b/contrib/bmake/unit-tests/varname-dot-make-mode.mk
@@ -1,8 +1,41 @@
-# $NetBSD: varname-dot-make-mode.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varname-dot-make-mode.mk,v 1.3 2022/05/07 17:49:47 rillig Exp $
#
# Tests for the special .MAKE.MODE variable.
-# TODO: Implementation
+# TODO: test .MAKE.MODE "meta", or see meta mode tests.
+# TODO: test .MAKE.MODE "compat"
-all:
- @:;
+
+# See Makefile, POSTPROC for the postprocessing that takes place.
+# See the .rawout file for the raw output before stripping the digits.
+all: .PHONY make-mode-randomize-targets
+
+
+# By adding the word "randomize-targets" to the variable .MAKE.MODE, the
+# targets are not made in declaration order, but rather in random order. This
+# mode helps to find undeclared dependencies between files.
+#
+# History
+# Added on 2022-05-07.
+#
+# See also
+# https://gnats.netbsd.org/45226
+make-mode-randomize-targets: .PHONY
+ @echo "randomize compat mode:"
+ @${MAKE} -r -f ${MAKEFILE} randomize-targets
+
+ @echo "randomize jobs mode (-j1):"
+ @${MAKE} -r -f ${MAKEFILE} -j1 randomize-targets
+
+ @echo "randomize jobs mode (-j5):"
+ @${MAKE} -r -f ${MAKEFILE} -j5 randomize-targets | grep '^:'
+
+.if make(randomize-targets)
+randomize-targets: .WAIT a1 a2 a3 .WAIT b1 b2 b3 .WAIT c1 c2 c3 .WAIT
+a1 a2 a3 b1 b2 b3 c1 c2 c3:
+ : Making ${.TARGET}
+
+# .MAKE.MODE is evaluated after parsing all files, so it suffices to switch
+# the mode after defining the targets.
+.MAKE.MODE+= randomize-targets
+.endif
diff --git a/contrib/bmake/unit-tests/varname-dot-make-pid.mk b/contrib/bmake/unit-tests/varname-dot-make-pid.mk
index bea114d33547..d7ef5bfd5c44 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-pid.mk
+++ b/contrib/bmake/unit-tests/varname-dot-make-pid.mk
@@ -1,8 +1,16 @@
-# $NetBSD: varname-dot-make-pid.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varname-dot-make-pid.mk,v 1.3 2022/01/23 21:48:59 rillig Exp $
#
-# Tests for the special .MAKE.PID variable.
+# Tests for the special .MAKE.PID variable, which contains the process ID of
+# the make process itself.
-# TODO: Implementation
+# The process ID must be a positive integer.
+.if ${.MAKE.PID:C,[0-9],,g} != ""
+. error
+.elif !(${.MAKE.PID} > 0)
+. error
+.endif
-all:
- @:;
+# Ensure that the process exists.
+_!= kill -0 ${.MAKE.PID}
+
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/varname-dot-make-ppid.mk b/contrib/bmake/unit-tests/varname-dot-make-ppid.mk
index c9471542ca35..91f13fd2feec 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-ppid.mk
+++ b/contrib/bmake/unit-tests/varname-dot-make-ppid.mk
@@ -1,8 +1,23 @@
-# $NetBSD: varname-dot-make-ppid.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varname-dot-make-ppid.mk,v 1.3 2022/01/23 21:48:59 rillig Exp $
#
-# Tests for the special .MAKE.PPID variable.
+# Tests for the special .MAKE.PPID variable, which contains the process ID of
+# make's parent process.
-# TODO: Implementation
+# The parent process ID must be a positive integer.
+.if ${.MAKE.PPID:C,[0-9],,g} != ""
+. error
+.elif !(${.MAKE.PPID} > 0)
+. error
+.endif
-all:
- @:;
+# Ensure that the process exists.
+_!= kill -0 ${.MAKE.PPID}
+
+# The parent process ID must be different from the process ID. If they were
+# the same, make would run as process 1, which is not a good idea because make
+# is not prepared to clean up after other processes.
+.if ${.MAKE.PPID} == ${.MAKE.PID}
+. error
+.endif
+
+all: .PHONY
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-makeflags.exp b/contrib/bmake/unit-tests/varname-dot-makeflags.exp
index dbf96469f86b..8004ab40a363 100644
--- a/contrib/bmake/unit-tests/varname-dot-makeflags.exp
+++ b/contrib/bmake/unit-tests/varname-dot-makeflags.exp
@@ -1,3 +1,10 @@
-echo "$MAKEFLAGS"
- -r -k -d 00000 -D VARNAME WITH SPACES
+make: "varname-dot-makeflags.mk" line 11: MAKEFLAGS=<undefined>
+make: "varname-dot-makeflags.mk" line 13: .MAKEFLAGS=< -r -k>
+make: "varname-dot-makeflags.mk" line 15: .MAKEOVERRIDES=<>
+make: "varname-dot-makeflags.mk" line 21: MAKEFLAGS=<undefined>
+make: "varname-dot-makeflags.mk" line 23: .MAKEFLAGS=< -r -k -D VARNAME -r>
+make: "varname-dot-makeflags.mk" line 25: .MAKEOVERRIDES=< VAR>
+runtime: MAKEFLAGS=< -r -k -D VARNAME -r VAR=value>
+runtime: .MAKEFLAGS=< -r -k -D VARNAME -r>
+runtime: .MAKEOVERRIDES=< VAR>
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-makeflags.mk b/contrib/bmake/unit-tests/varname-dot-makeflags.mk
index 10d1903022cb..ffb09decb70e 100644
--- a/contrib/bmake/unit-tests/varname-dot-makeflags.mk
+++ b/contrib/bmake/unit-tests/varname-dot-makeflags.mk
@@ -1,15 +1,39 @@
-# $NetBSD: varname-dot-makeflags.mk,v 1.1 2020/12/01 20:37:30 rillig Exp $
+# $NetBSD: varname-dot-makeflags.mk,v 1.8 2023/06/01 20:56:35 rillig Exp $
#
# Tests for the special .MAKEFLAGS variable, which collects almost all
# command line arguments and passes them on to any child processes via
# the environment variable MAKEFLAGS (without leading '.').
+#
+# See also:
+# varname-dot-makeoverrides.mk
+
+# expect+1: MAKEFLAGS=<undefined>
+.info MAKEFLAGS=<${MAKEFLAGS:Uundefined}>
+# expect+1: .MAKEFLAGS=< -r -k>
+.info .MAKEFLAGS=<${.MAKEFLAGS}>
+# expect+1: .MAKEOVERRIDES=<>
+.info .MAKEOVERRIDES=<${.MAKEOVERRIDES:Uundefined}>
+
+# Append an option with argument, a plain option and a variable assignment.
+.MAKEFLAGS: -DVARNAME -r VAR=value
+
+# expect+1: MAKEFLAGS=<undefined>
+.info MAKEFLAGS=<${MAKEFLAGS:Uundefined}>
+# expect+1: .MAKEFLAGS=< -r -k -D VARNAME -r>
+.info .MAKEFLAGS=<${.MAKEFLAGS}>
+# expect+1: .MAKEOVERRIDES=< VAR>
+.info .MAKEOVERRIDES=<${.MAKEOVERRIDES}>
-# When options are parsed, the option and its argument are appended as
-# separate words to .MAKEFLAGS. Special characters in the option argument
-# are not quoted though. It seems to have not been necessary at least from
-# 1993 until 2020.
-.MAKEFLAGS: -d00000 -D"VARNAME WITH SPACES"
+# The environment variable 'MAKEFLAGS' is not available to child processes
+# when parsing the makefiles. This is different from exported variables,
+# which are already available during parse time.
+.if ${:!echo "\${MAKEFLAGS-undef}"!} != "undef"
+. error
+.endif
-all:
- echo "$$MAKEFLAGS"
- @:;
+# After parsing, the environment variable 'MAKEFLAGS' is set based on the
+# special variables '.MAKEFLAGS' and '.MAKEOVERRIDES'.
+runtime:
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+ @echo '$@: .MAKEFLAGS=<'${.MAKEFLAGS:Q}'>'
+ @echo '$@: .MAKEOVERRIDES=<'${.MAKEOVERRIDES:Q}'>'
diff --git a/contrib/bmake/unit-tests/varname-dot-makeoverrides.exp b/contrib/bmake/unit-tests/varname-dot-makeoverrides.exp
index 39a9383953dd..21942ffdc96d 100644
--- a/contrib/bmake/unit-tests/varname-dot-makeoverrides.exp
+++ b/contrib/bmake/unit-tests/varname-dot-makeoverrides.exp
@@ -1 +1,8 @@
+all: overrides=<>
+make -f varname-dot-makeoverrides.mk stage_1 VAR=value
+stage_1: overrides=< VAR>
+make -f varname-dot-makeoverrides.mk stage_2 .VAR=too
+stage_2: overrides=< VAR .VAR>
+make -f varname-dot-makeoverrides.mk stage_3
+stage_3: overrides=< .VAR VAR>
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk b/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk
index a897f4667175..966fcb7e0ec9 100644
--- a/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk
+++ b/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk
@@ -1,8 +1,23 @@
-# $NetBSD: varname-dot-makeoverrides.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varname-dot-makeoverrides.mk,v 1.6 2024/05/07 18:26:22 sjg Exp $
#
-# Tests for the special .MAKE.MAKEOVERRIDES variable.
-
-# TODO: Implementation
+# Tests for the special .MAKEOVERRIDES variable, which lists the names of the
+# variables that are passed on to child processes via the MAKEFLAGS
+# environment variable.
+#
+# See also:
+# varname-dot-makeflags.mk
all:
- @:;
+ @echo '$@: overrides=<'${.MAKEOVERRIDES:Uundefined:Q}'>'
+ ${MAKE} -f ${MAKEFILE} stage_1 VAR=value
+
+stage_1:
+ @echo '$@: overrides=<'${.MAKEOVERRIDES:Q}'>'
+ ${MAKE} -f ${MAKEFILE} stage_2 .VAR=too
+
+stage_2:
+ @echo '$@: overrides=<'${.MAKEOVERRIDES:Q}'>'
+ ${MAKE} -f ${MAKEFILE} stage_3
+
+stage_3:
+ @echo '$@: overrides=<'${.MAKEOVERRIDES:Q}'>'
diff --git a/contrib/bmake/unit-tests/varname-dot-newline.exp b/contrib/bmake/unit-tests/varname-dot-newline.exp
index 13943539bf3b..684fb5799752 100644
--- a/contrib/bmake/unit-tests/varname-dot-newline.exp
+++ b/contrib/bmake/unit-tests/varname-dot-newline.exp
@@ -1,4 +1,5 @@
-make: "varname-dot-newline.mk" line 16: The .newline variable can be overwritten. Just don't do that.
first
second
+backslash newline: <\
+>
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-newline.mk b/contrib/bmake/unit-tests/varname-dot-newline.mk
index 0565d244f298..1940dc2a990d 100644
--- a/contrib/bmake/unit-tests/varname-dot-newline.mk
+++ b/contrib/bmake/unit-tests/varname-dot-newline.mk
@@ -1,23 +1,31 @@
-# $NetBSD: varname-dot-newline.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: varname-dot-newline.mk,v 1.6 2023/01/26 20:48:18 sjg Exp $
#
-# Tests for the special .newline variable.
+# Tests for the special .newline variable, which contains a single newline
+# character (U+000A).
+
+
+# https://austingroupbugs.net/view.php?id=1549 proposes:
+# > After all macro expansion is complete, when an escaped <newline> is
+# > found in a command line in a makefile, the command line that is executed
+# > shall contain the <backslash>, the <newline>, and the next line, except
+# > that the first character of the next line shall not be included if it is
+# > a <tab>.
#
-# Contrary to the special variable named "" that is used in expressions like
-# ${:Usome-value}, the variable ".newline" is not protected against
-# modification. Nobody exploits that though.
+# The above quote assumes that each resulting <newline> character has a "next
+# line", but that's not how the .newline variable works.
+BACKSLASH_NEWLINE:= \${.newline}
+
+
+# Check that .newline is read-only
NEWLINE:= ${.newline}
.newline= overwritten
-.if ${.newline} == ${NEWLINE}
-. info The .newline variable cannot be overwritten. Good.
-.else
-. info The .newline variable can be overwritten. Just don't do that.
+.if ${.newline} != ${NEWLINE}
+. error The .newline variable can be overwritten. It should be read-only.
.endif
-# Restore the original value.
-.newline= ${NEWLINE}
-
all:
@echo 'first${.newline}second'
+ @echo 'backslash newline: <${BACKSLASH_NEWLINE}>'
diff --git a/contrib/bmake/unit-tests/varname-dot-parsedir.exp b/contrib/bmake/unit-tests/varname-dot-parsedir.exp
index c0bc56f41d6e..4ac953c813cd 100644
--- a/contrib/bmake/unit-tests/varname-dot-parsedir.exp
+++ b/contrib/bmake/unit-tests/varname-dot-parsedir.exp
@@ -1,5 +1,5 @@
-make: "varname-dot-parsedir.mk" line 28: At this point, .PARSEDIR is undefined.
-make: "<normalized>" line 34: The location can be faked in some cases.
-make: "varname-dot-parsedir.mk" line 38: The location is no longer fake.
+make: "varname-dot-parsedir.mk" line 37: At this point, .PARSEDIR is undefined.
+make: "<normalized>" line 43: The location can be faked in some cases.
+make: "varname-dot-parsedir.mk" line 48: The location is no longer fake.
At run time, .PARSEDIR is undefined.
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-parsedir.mk b/contrib/bmake/unit-tests/varname-dot-parsedir.mk
index 7c74419ddd10..525fdbd5636f 100644
--- a/contrib/bmake/unit-tests/varname-dot-parsedir.mk
+++ b/contrib/bmake/unit-tests/varname-dot-parsedir.mk
@@ -1,7 +1,15 @@
-# $NetBSD: varname-dot-parsedir.mk,v 1.6 2020/10/24 08:50:17 rillig Exp $
+# $NetBSD: varname-dot-parsedir.mk,v 1.8 2023/06/21 07:30:50 rillig Exp $
#
# Tests for the special .PARSEDIR variable, which contains the directory part
# of the file that is currently parsed.
+#
+# See also
+# varname-dot-includedfromdir.mk
+# varname-dot-includedfromfile.mk
+# varname-dot-parsefile.mk
+#
+# History
+# .PARSEDIR and .PARSEFILE were added on 1999-08-09.
# The .PARSEDIR may be absolute or relative, therefore there is not much that
# can be tested here.
@@ -25,6 +33,7 @@
#
# The .rawout file contains the full path to the current directory.
# In the .out file, it is filtered out.
+# expect+1: At this point, .PARSEDIR is undefined.
.info At this point, .PARSEDIR is undefined.
# There is absolutely no point in faking the location of the file that is
@@ -35,6 +44,7 @@
# After including another file, .PARSEDIR is reset.
.include "/dev/null"
+# expect+1: The location is no longer fake.
.info The location is no longer fake.
all:
diff --git a/contrib/bmake/unit-tests/varname-dot-parsefile.exp b/contrib/bmake/unit-tests/varname-dot-parsefile.exp
index b61f01c01ab7..651666ebcf24 100644
--- a/contrib/bmake/unit-tests/varname-dot-parsefile.exp
+++ b/contrib/bmake/unit-tests/varname-dot-parsefile.exp
@@ -1,5 +1,5 @@
-make: "varname-dot-parsefile.mk" line 23: At this point, .PARSEFILE is undefined.
-make: "<normalized>" line 29: The location can be faked in some cases.
-make: "varname-dot-parsefile.mk" line 33: The location is no longer fake.
+make: "varname-dot-parsefile.mk" line 32: At this point, .PARSEFILE is undefined.
+make: "<normalized>" line 38: The location can be faked in some cases.
+make: "varname-dot-parsefile.mk" line 43: The location is no longer fake.
At run time, .PARSEFILE is undefined.
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-parsefile.mk b/contrib/bmake/unit-tests/varname-dot-parsefile.mk
index 17b48a5f77ec..0a6ed3c378e0 100644
--- a/contrib/bmake/unit-tests/varname-dot-parsefile.mk
+++ b/contrib/bmake/unit-tests/varname-dot-parsefile.mk
@@ -1,7 +1,15 @@
-# $NetBSD: varname-dot-parsefile.mk,v 1.5 2020/10/24 08:50:17 rillig Exp $
+# $NetBSD: varname-dot-parsefile.mk,v 1.7 2023/06/21 07:30:50 rillig Exp $
#
# Tests for the special .PARSEFILE variable, which contains the basename part
# of the file that is currently parsed.
+#
+# See also
+# varname-dot-includedfromdir.mk
+# varname-dot-includedfromfile.mk
+# varname-dot-parsedir.mk
+#
+# History
+# .PARSEDIR and .PARSEFILE were added on 1999-08-09.
.if ${.PARSEFILE} != "varname-dot-parsefile.mk"
. error
@@ -20,6 +28,7 @@
# The variable .PARSEFILE is indirectly used by the .info directive,
# via PrintLocation.
+# expect+1: At this point, .PARSEFILE is undefined.
.info At this point, .PARSEFILE is undefined.
# There is absolutely no point in faking the location of the file that is
@@ -30,6 +39,7 @@
# After including another file, .PARSEFILE is reset.
.include "/dev/null"
+# expect+1: The location is no longer fake.
.info The location is no longer fake.
all:
diff --git a/contrib/bmake/unit-tests/varname-dot-shell.exp b/contrib/bmake/unit-tests/varname-dot-shell.exp
index bfbcfc960182..7d481fdf6ff1 100755
--- a/contrib/bmake/unit-tests/varname-dot-shell.exp
+++ b/contrib/bmake/unit-tests/varname-dot-shell.exp
@@ -1,31 +1,34 @@
-ParseReadLine (10): 'ORIG_SHELL:= ${.SHELL}'
-Global: ORIG_SHELL =
+Parsing line 10: ORIG_SHELL:= ${.SHELL}
+Global: ORIG_SHELL = # (empty)
Var_Parse: ${.SHELL} (eval-keep-dollar-and-undefined)
-Global:delete .SHELL (not found)
+Global: ignoring delete '.SHELL' as it is not found
Command: .SHELL = (details omitted)
Global: ORIG_SHELL = (details omitted)
-ParseReadLine (12): '.SHELL= overwritten'
-Global: .SHELL = overwritten
+Parsing line 12: .SHELL= overwritten
+Global: ignoring '.SHELL = overwritten' due to a command line variable of the same name
+Parsing line 13: .if ${.SHELL} != ${ORIG_SHELL}
CondParser_Eval: ${.SHELL} != ${ORIG_SHELL}
Var_Parse: ${.SHELL} != ${ORIG_SHELL} (eval-defined)
Var_Parse: ${ORIG_SHELL} (eval-defined)
-lhs = "(details omitted)", rhs = "(details omitted)", op = !=
-ParseReadLine (19): '.MAKEFLAGS: .SHELL+=appended'
+Comparing "(details omitted)" != "(details omitted)"
+Parsing line 19: .MAKEFLAGS: .SHELL+=appended
ParseDependency(.MAKEFLAGS: .SHELL+=appended)
-Ignoring append to .SHELL since it is read-only
+Command: ignoring '.SHELL += appended' as it is read-only
+Parsing line 20: .if ${.SHELL} != ${ORIG_SHELL}
CondParser_Eval: ${.SHELL} != ${ORIG_SHELL}
Var_Parse: ${.SHELL} != ${ORIG_SHELL} (eval-defined)
Var_Parse: ${ORIG_SHELL} (eval-defined)
-lhs = "(details omitted)", rhs = "(details omitted)", op = !=
-ParseReadLine (27): '.undef .SHELL'
-Global:delete .SHELL
-ParseReadLine (28): '.SHELL= newly overwritten'
-Global: .SHELL = newly overwritten
+Comparing "(details omitted)" != "(details omitted)"
+Parsing line 27: .undef .SHELL
+Global: ignoring delete '.SHELL' as it is not found
+Parsing line 28: .SHELL= newly overwritten
+Global: ignoring '.SHELL = newly overwritten' due to a command line variable of the same name
+Parsing line 29: .if ${.SHELL} != ${ORIG_SHELL}
CondParser_Eval: ${.SHELL} != ${ORIG_SHELL}
Var_Parse: ${.SHELL} != ${ORIG_SHELL} (eval-defined)
Var_Parse: ${ORIG_SHELL} (eval-defined)
-lhs = "(details omitted)", rhs = "(details omitted)", op = !=
-ParseReadLine (33): '.MAKEFLAGS: -d0'
+Comparing "(details omitted)" != "(details omitted)"
+Parsing line 33: .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/varname-dot-suffixes.exp b/contrib/bmake/unit-tests/varname-dot-suffixes.exp
new file mode 100644
index 000000000000..230ba36d56ed
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-suffixes.exp
@@ -0,0 +1,39 @@
+Global: ignoring delete '.SUFFIXES' as it is not found
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
+Global: ignoring '.SUFFIXES = set' as it is read-only
+Global: ignoring '.SUFFIXES = append' as it is read-only
+Global: _ = # (empty)
+Var_Parse: ${.SUFFIXES::=assign} (eval-keep-dollar-and-undefined)
+Evaluating modifier ${.SUFFIXES::...} on value ".c .o .1 .err .tar.gz" (eval-keep-dollar-and-undefined, regular)
+Modifier part: "assign"
+Global: ignoring '.SUFFIXES = assign' as it is read-only
+Result of ${.SUFFIXES::=assign} is "" (eval-keep-dollar-and-undefined, regular)
+Global: _ = # (empty)
+Var_Parse: ${preserve:L:_=.SUFFIXES} (eval-keep-dollar-and-undefined)
+Evaluating modifier ${preserve:L} on value "" (eval-keep-dollar-and-undefined, undefined)
+Result of ${preserve:L} is "preserve" (eval-keep-dollar-and-undefined, defined)
+Evaluating modifier ${preserve:_...} on value "preserve" (eval-keep-dollar-and-undefined, defined)
+Global: ignoring '.SUFFIXES = preserve' as it is read-only
+Result of ${preserve:_=.SUFFIXES} is "preserve" (eval-keep-dollar-and-undefined, defined)
+Global: _ = preserve
+Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d
+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: ignoring '.SUFFIXES = 1' as it is read-only
+Var_Parse: ${.SUFFIXES} (eval-defined)
+ModifyWord_Loop: expand "${.SUFFIXES}" to ".c .o .1 .err .tar.gz"
+Command: ignoring '.SUFFIXES = 2' as it is read-only
+Var_Parse: ${.SUFFIXES} (eval-defined)
+ModifyWord_Loop: expand "${.SUFFIXES}" to ".c .o .1 .err .tar.gz"
+Command: ignoring delete '.SUFFIXES' as it is not found
+Result of ${1 2:@.SUFFIXES@${.SUFFIXES}@} is ".c .o .1 .err .tar.gz .c .o .1 .err .tar.gz" (eval-defined, defined)
+Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d 0 -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d 0 -d v -d 0
+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..27521f621cb0
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-suffixes.mk
@@ -0,0 +1,105 @@
+# $NetBSD: varname-dot-suffixes.mk,v 1.5 2023/12/20 09:03:09 rillig Exp $
+#
+# Tests for the special "variable" .SUFFIXES, which lists the suffixes that
+# have been registered for use in suffix transformation rules. Suffixes are
+# 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: ignoring delete '.SUFFIXES' as it is 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: ignoring '.SUFFIXES = set' as it is read-only
+.SUFFIXES= set
+# expect: Global: ignoring '.SUFFIXES = append' as it is read-only
+.SUFFIXES+= append
+# expect: Global: ignoring '.SUFFIXES = assign' as it is read-only
+_:= ${.SUFFIXES::=assign}
+# expect: Global: ignoring '.SUFFIXES = preserve' as it is 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: ignoring '.SUFFIXES = 1' as it is read-only
+# expect: Command: ignoring '.SUFFIXES = 2' as it is read-only
+# expect: Command: ignoring delete '.SUFFIXES' as it is not found
+.if ${1 2:L:@.SUFFIXES@${.SUFFIXES}@} != ".c .o .1 .err .tar.gz .c .o .1 .err .tar.gz"
+. error
+.endif
+.MAKEFLAGS: -d0
+
+all:
diff --git a/contrib/bmake/unit-tests/varname-empty.exp b/contrib/bmake/unit-tests/varname-empty.exp
index ec225c6973c8..2165784933e4 100644
--- a/contrib/bmake/unit-tests/varname-empty.exp
+++ b/contrib/bmake/unit-tests/varname-empty.exp
@@ -1,25 +1,22 @@
-Var_SetExpand: variable name "${:U}" expands to empty string, with value "cmdline-u" - ignored
-Var_SetExpand: variable name "" expands to empty string, with value "cmdline-plain" - ignored
+Command: ignoring ' = cmdline-u' as the variable name '${:U}' expands to empty
+Command: ignoring ' = cmdline-plain' as the variable name '' expands to empty
Global: .CURDIR = <curdir>
Var_Parse: ${MAKE_OBJDIR_CHECK_WRITABLE} (eval)
-Global: .OBJDIR = <curdir>
-Global:delete .PATH (not found)
-Global: .PATH = .
-Global: .PATH = . <curdir>
-Global: .TARGETS =
+Global: .TARGETS = # (empty)
Internal: MAKEFILE = varname-empty.mk
Global: .MAKE.MAKEFILES = varname-empty.mk
Global: .PARSEFILE = varname-empty.mk
-Global:delete .INCLUDEDFROMDIR (not found)
-Global:delete .INCLUDEDFROMFILE (not found)
-Var_SetExpand: variable name "" expands to empty string, with value "default" - ignored
-Var_SetExpand: variable name "" expands to empty string, with value "assigned" - ignored
-SetVar: variable name is empty - ignored
-Var_SetExpand: variable name "" expands to empty string, with value "" - ignored
-Var_SetExpand: variable name "" expands to empty string, with value "subst" - ignored
-Var_SetExpand: variable name "" expands to empty string, with value "shell-output" - ignored
-Var_SetExpand: variable name "${:U}" expands to empty string, with value "assigned indirectly" - ignored
-Var_AppendExpand: variable name "${:U}" expands to empty string, with value "appended indirectly" - ignored
+Global: ignoring delete '.INCLUDEDFROMDIR' as it is not found
+Global: ignoring delete '.INCLUDEDFROMFILE' as it is not found
+Global: ignoring ' = default' as the variable name '' expands to empty
+Global: ignoring ' = assigned' as the variable name '' expands to empty
+Global: ignoring ' = appended' as the variable name is empty
+Global: ignoring ' = ' as the variable name '' expands to empty
+Global: ignoring ' = subst' as the variable name '' expands to empty
+Capturing the output of command "echo 'shell-output'"
+Global: ignoring ' = shell-output' as the variable name '' expands to empty
+Global: ignoring ' = assigned indirectly' as the variable name '${:U}' expands to empty
+Global: ignoring ' += appended indirectly' as the variable name '${:U}' expands to empty
Global: .MAKEFLAGS = -r -d v -d
Global: .MAKEFLAGS = -r -d v -d 0
out: fallback
diff --git a/contrib/bmake/unit-tests/varname-empty.mk b/contrib/bmake/unit-tests/varname-empty.mk
index f077d2ec07b4..e018a5d44894 100755
--- a/contrib/bmake/unit-tests/varname-empty.mk
+++ b/contrib/bmake/unit-tests/varname-empty.mk
@@ -1,9 +1,9 @@
-# $NetBSD: varname-empty.mk,v 1.9 2021/04/04 10:13:09 rillig Exp $
+# $NetBSD: varname-empty.mk,v 1.10 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the special variable with the empty name.
#
# There is no variable named "" at all, and this fact is used a lot in
-# variable expressions of the form ${:Ufallback}. These expressions are
+# expressions of the form ${:Ufallback}. These expressions are
# based on the variable named "" and use the :U modifier to assign a
# fallback value to the expression (but not to the variable).
#
diff --git a/contrib/bmake/unit-tests/varname-make_print_var_on_error-jobs.mk b/contrib/bmake/unit-tests/varname-make_print_var_on_error-jobs.mk
index 10a9647fbd1e..b422f25ff12e 100644
--- a/contrib/bmake/unit-tests/varname-make_print_var_on_error-jobs.mk
+++ b/contrib/bmake/unit-tests/varname-make_print_var_on_error-jobs.mk
@@ -1,9 +1,9 @@
-# $NetBSD: varname-make_print_var_on_error-jobs.mk,v 1.3 2021/02/04 21:33:14 rillig Exp $
+# $NetBSD: varname-make_print_var_on_error-jobs.mk,v 1.4 2023/11/19 22:32:44 rillig Exp $
#
# Tests for the special MAKE_PRINT_VAR_ON_ERROR variable, which prints the
# values of selected variables on error.
#
-# The variable .ERROR_CMD contains all commands of the target, with variable
+# The variable .ERROR_CMD contains all commands of the target, with
# expressions expanded, just as they were printed to the shell command file.
#
# The commands in .ERROR_CMD are space-separated. Since each command usually
diff --git a/contrib/bmake/unit-tests/varname-makeflags.exp b/contrib/bmake/unit-tests/varname-makeflags.exp
index 39a9383953dd..c1354177ca47 100644
--- a/contrib/bmake/unit-tests/varname-makeflags.exp
+++ b/contrib/bmake/unit-tests/varname-makeflags.exp
@@ -1 +1,21 @@
+spaces_stage_0: MAKEFLAGS=< -r -k >
+spaces_stage_0: env MAKEFLAGS=< -r -k >
+spaces_stage_1: MAKEFLAGS=< -r -k -d 00000 -D VARNAME WITH SPACES >
+spaces_stage_1: env MAKEFLAGS=< -r -k -d 00000 -D VARNAME WITH SPACES >
+dollars_stage_0: MAKEFLAGS=< -r -k >
+dollars_stage_1: env MAKEFLAGS=< -r -k DOLLARS=\$\{varname\}>
+dollars_stage_1: MAKEFLAGS=< -r -k DOLLARS=\{varname\}>
+dollars_stage_1: MAKEFLAGS:q=< -r -k DOLLARS=\{varname\}>
+dollars_stage_2: env MAKEFLAGS=< -r -k DOLLARS=>
+dollars_stage_2: dollars=<>
+dollars_stage_2: MAKEFLAGS=< -r -k DOLLARS=>
+dollars_stage_3: env MAKEFLAGS=< -r -k DOLLARS=>
+dollars_stage_3: dollars=<>
+dollars_stage_3: MAKEFLAGS=< -r -k DOLLARS=>
+append_stage_0: MAKEFLAGS=< -r -k >
+append_stage_1: MAKEFLAGS=< -r -k -D before-0 -D after-0 VAR0=value>
+append_stage_2: MAKEFLAGS=< -r -k -D before-0 -D after-0 -D before-1 -D after-1 VAR0=value VAR1=value>
+append_stage_3: MAKEFLAGS=< -r -k -D before-0 -D after-0 -D before-1 -D after-1 -D before-2 -D after-2 VAR0=value VAR1=value VAR2=value>
+override_stage_1: run MAKEFLAGS=< -r -k STAGE=1 VAR=value>
+override_stage_2: STAGE=<2> VAR=<value>
exit status 0
diff --git a/contrib/bmake/unit-tests/varname-makeflags.mk b/contrib/bmake/unit-tests/varname-makeflags.mk
index 3b4fd91c3f57..4173c5a92095 100644
--- a/contrib/bmake/unit-tests/varname-makeflags.mk
+++ b/contrib/bmake/unit-tests/varname-makeflags.mk
@@ -1,26 +1,182 @@
-# $NetBSD: varname-makeflags.mk,v 1.3 2020/12/01 20:37:30 rillig Exp $
+# $NetBSD: varname-makeflags.mk,v 1.8 2023/06/01 07:27:30 rillig Exp $
#
-# Tests for the special MAKEFLAGS variable, which is basically just a normal
-# environment variable. It is closely related to .MAKEFLAGS but captures the
-# state of .MAKEFLAGS at the very beginning of make, before any makefiles are
-# read.
+# Tests for the environment variable 'MAKEFLAGS', from which additional
+# command line arguments are read before the actual command line arguments.
+#
+# After reading the makefiles and before making the targets, the arguments
+# that were collected in '.MAKEFLAGS' and '.MAKEOVERRIDES' are written back to
+# the environment variable 'MAKEFLAGS'.
+
+all: spaces_stage_0 dollars_stage_0 append_stage_0 override_stage_0
-# TODO: Implementation
-.MAKEFLAGS: -d0
+.if !make(*stage*)
# The unit tests are run with an almost empty environment. In particular,
-# the variable MAKEFLAGS is not set. The '.MAKEFLAGS:' above also doesn't
-# influence the environment variable MAKEFLAGS, therefore it is still
-# undefined at this point.
-.if ${MAKEFLAGS:Uundefined} != "undefined"
-. error
-.endif
+# the variable MAKEFLAGS is not set.
+. if ${MAKEFLAGS:Uundefined} != "undefined"
+. error
+. endif
# The special variable .MAKEFLAGS is influenced though.
# See varname-dot-makeflags.mk for more details.
-.if ${.MAKEFLAGS} != " -r -k -d 0"
-. error
+. if ${.MAKEFLAGS} != " -r -k"
+. error
+. endif
+
+
+# In POSIX mode, the environment variable MAKEFLAGS can contain letters only,
+# for compatibility. These letters are exploded to form regular options.
+OUTPUT!= env MAKEFLAGS=ikrs ${MAKE} -f /dev/null -v .MAKEFLAGS
+. if ${OUTPUT} != " -i -k -r -s -V .MAKEFLAGS"
+. error
+. endif
+
+# As soon as there is a single non-alphabetic character in the environment
+# variable MAKEFLAGS, it is no longer split. In this example, the word
+# "d0ikrs" is treated as a target, but the option '-v' prevents any targets
+# from being built.
+OUTPUT!= env MAKEFLAGS=d0ikrs ${MAKE} -r -f /dev/null -v .MAKEFLAGS
+. if ${OUTPUT} != " -r -V .MAKEFLAGS"
+. error ${OUTPUT}
+. endif
+
+.endif
+
+
+# When options are parsed, the option and its argument are appended as
+# separate words to the MAKEFLAGS for the child processes. Special characters
+# in the option arguments are not quoted though.
+spaces_stage_0:
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+ @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"
+ @${MAKE} -f ${MAKEFILE} spaces_stage_1 -d00000 -D"VARNAME WITH SPACES"
+
+# At this point, the 'VARNAME WITH SPACES' is no longer recognizable as a
+# single command line argument. In practice, variable names don't contain
+# spaces.
+spaces_stage_1:
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+ @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"
+
+
+# Demonstrate that '$' characters are altered when they are passed on to child
+# make processes via MAKEFLAGS.
+dollars_stage_0:
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+
+ # The '$$$$' becomes a literal '$$' when building the '${MAKE}'
+ # command line, making the actual argument 'DOLLARS=$${varname}'.
+ # At this stage, MAKEFLAGS is not yet involved.
+ @${MAKE} -f ${MAKEFILE} dollars_stage_1 DOLLARS='$$$${varname}'
+
+.if make(dollars_stage_1)
+# At this point, the variable 'DOLLARS' contains '$${varname}', which
+# evaluates to a literal '$' followed by '{varname}'.
+. if ${DOLLARS} != "\${varname}"
+. error
+. endif
+.endif
+dollars_stage_1:
+ # At this point, the stage 1 make provides the environment variable
+ # 'MAKEFLAGS' to its child processes, even if the child process is not
+ # another make.
+ #
+ # expect: dollars_stage_1: env MAKEFLAGS=< -r -k DOLLARS=\$\{varname\}>
+ #
+ # The 'DOLLARS=\$\{varname\}' assignment is escaped so that the stage
+ # 2 make will see it as a single word.
+ @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"
+
+ # At this point, evaluating the environment variable 'MAKEFLAGS' leads
+ # to strange side effects as the string '\$\{varname\}' is interpreted
+ # as:
+ #
+ # \ a literal string of a single backslash
+ # $\ the value of the variable named '\'
+ # {varname\} a literal string
+ #
+ # Since the variable named '\' is not defined, the resulting value is
+ # '\{varname\}'. Make doesn't handle isolated '$' characters in
+ # strings well, instead each '$' has to be part of a '$$' or be part
+ # of a subexpression like '${VAR}'.
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+
+ # The modifier ':q' preserves a '$$' in an expression value instead of
+ # expanding it to a single '$', but it's already too late, as that
+ # modifier applies after the expression has been evaluated. Except
+ # for debug logging, there is no way to process strings that contain
+ # isolated '$'.
+ @echo '$@: MAKEFLAGS:q=<'${MAKEFLAGS:q}'>'
+
+ @${MAKE} -f ${MAKEFILE} dollars_stage_2
+
+.if make(dollars_stage_2)
+# At this point, the variable 'DOLLARS' contains '${varname}', and since
+# 'varname' is undefined, that expression evaluates to an empty string.
+. if ${DOLLARS} != ""
+. error
+. endif
+varname= varvalue
+. if ${DOLLARS} != "varvalue"
+. error
+. endif
+. undef varname
+.endif
+dollars_stage_2:
+ @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"
+ @echo '$@: dollars=<'${DOLLARS:Q}'>'
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+ @${MAKE} -f ${MAKEFILE} dollars_stage_3
+
+dollars_stage_3:
+ @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"
+ @echo '$@: dollars=<'${DOLLARS:Uundefined:Q}'>'
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+
+
+# Demonstrates in which exact order the MAKEFLAGS are built from the parent
+# MAKEFLAGS and the flags from the command line, in particular that variable
+# assignments are passed at the end, after the options.
+append_stage_0:
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+ @${MAKE} -Dbefore-0 -f ${MAKEFILE} append_stage_1 VAR0=value -Dafter-0
+
+append_stage_1:
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+ @${MAKE} -Dbefore-1 -f ${MAKEFILE} append_stage_2 VAR1=value -Dafter-1
+
+append_stage_2:
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+ @${MAKE} -Dbefore-2 -f ${MAKEFILE} append_stage_3 VAR2=value -Dafter-2
+
+append_stage_3:
+ @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+
+
+# Demonstrates the implementation details of 'MAKEFLAGS', in particular that
+# it is an environment variable rather than a global variable.
+override_stage_0:
+ @${MAKE} -f ${MAKEFILE} STAGE=1 VAR=value override_stage_1
+
+.if make(override_stage_1)
+# While parsing the makefiles, 'MAKEFLAGS' is the value of the environment
+# variable, in this case provided by stage 0.
+. if ${MAKEFLAGS:M*} != "-r -k"
+. error
+. endif
+MAKEFLAGS= overridden # temporarily override it
+. if ${MAKEFLAGS} != "overridden"
+. error
+. endif
+.undef MAKEFLAGS # make the environment variable visible again
+. if ${MAKEFLAGS:M*} != "-r -k"
+. error
+. endif
.endif
+override_stage_1:
+ @echo '$@: run MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
+ @${MAKE} -f ${MAKEFILE} STAGE=2 override_stage_2
-all:
+override_stage_2:
+ @echo '$@: STAGE=<${STAGE}> VAR=<${VAR}>'
diff --git a/contrib/bmake/unit-tests/varname.exp b/contrib/bmake/unit-tests/varname.exp
index 942532b654d5..640f228f8a51 100644
--- a/contrib/bmake/unit-tests/varname.exp
+++ b/contrib/bmake/unit-tests/varname.exp
@@ -5,13 +5,13 @@ Var_Parse: ${VARNAME} (eval)
Global: VAR((( = 3 open parentheses
Var_Parse: ${VAR(((}}}}" != "3 open parentheses}}}" (eval)
Global: .ALLTARGETS = VAR(((=)
-make: "varname.mk" line 30: No closing parenthesis in archive specification
-make: "varname.mk" line 30: Error in archive specification: "VAR"
+make: "varname.mk" line 32: No closing parenthesis in archive specification
+make: "varname.mk" line 32: Error in archive specification: "VAR"
Var_Parse: ${:UVAR\(\(\(}= try2 (eval-defined)
Evaluating modifier ${:U...} on value "" (eval-defined, undefined)
Result of ${:UVAR\(\(\(} is "VAR\(\(\(" (eval-defined, defined)
Global: .ALLTARGETS = VAR(((=) VAR\(\(\(=
-make: "varname.mk" line 35: Invalid line type
+make: "varname.mk" line 38: Invalid line '${:UVAR\(\(\(}= try2', expanded to 'VAR\(\(\(= try2'
Var_Parse: ${VARNAME} (eval)
Global: VAR((( = try3
Global: .MAKEFLAGS = -r -k -d v -d
diff --git a/contrib/bmake/unit-tests/varname.mk b/contrib/bmake/unit-tests/varname.mk
index f586c7602cb7..cad0a10fe563 100644
--- a/contrib/bmake/unit-tests/varname.mk
+++ b/contrib/bmake/unit-tests/varname.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varname.mk,v 1.8 2020/11/02 22:59:48 rillig Exp $
+# $NetBSD: varname.mk,v 1.14 2023/11/19 21:47:52 rillig Exp $
#
# Tests for special variables, such as .MAKE or .PARSEDIR.
# And for variable names in general.
@@ -12,7 +12,7 @@ VAR{{{}}}= 3 braces
. error
.endif
-# In variable expressions, the parser works differently. It doesn't treat
+# In expressions, the parser works differently. It doesn't treat
# braces and parentheses equally, therefore the first closing brace already
# marks the end of the variable name.
VARNAME= VAR(((
@@ -27,11 +27,14 @@ ${VARNAME}= 3 open parentheses
# This is not a variable assignment since the parentheses and braces are not
# balanced. At the end of the line, there are still 3 levels open, which
# means the variable name is not finished.
+# expect+2: Error in archive specification: "VAR"
+# expect+1: No closing parenthesis in archive specification
${:UVAR(((}= try1
# On the left-hand side of a variable assignments, the backslash is not parsed
# as an escape character, therefore the parentheses still count to the nesting
# level, which at the end of the line is still 3. Therefore this is not a
# variable assignment as well.
+# expect+1: Invalid line '${:UVAR\(\(\(}= try2', expanded to 'VAR\(\(\(= try2'
${:UVAR\(\(\(}= try2
# To assign to a variable with an arbitrary name, the variable name has to
# come from an external source, not the text that is parsed in the assignment
@@ -41,4 +44,46 @@ ${VARNAME}= try3
.MAKEFLAGS: -d0
+# All variable names of a scope are stored in the same hash table, using a
+# simple hash function. Ensure that HashTable_Find handles collisions
+# correctly and that the correct variable is looked up. The strings "0x" and
+# "1Y" have the same hash code, as 31 * '0' + 'x' == 31 * '1' + 'Y'.
+V.0x= 0x
+V.1Y= 1Y
+.if ${V.0x} != "0x" || ${V.1Y} != "1Y"
+. error
+.endif
+
+# The string "ASDZguv", when used as a prefix of a variable name, keeps the
+# hash code unchanged, its own hash code is 0.
+ASDZguvV.0x= 0x
+ASDZguvV.1Y= 1Y
+.if ${ASDZguvV.0x} != "0x"
+. error
+.elif ${ASDZguvV.1Y} != "1Y"
+. error
+.endif
+
+# Ensure that variables with the same hash code whose name is a prefix of the
+# other can be accessed. In this case, the shorter variable name is defined
+# first to make it appear later in the bucket of the hash table.
+ASDZguv= once
+ASDZguvASDZguv= twice
+.if ${ASDZguv} != "once"
+. error
+.elif ${ASDZguvASDZguv} != "twice"
+. error
+.endif
+
+# Ensure that variables with the same hash code whose name is a prefix of the
+# other can be accessed. In this case, the longer variable name is defined
+# first to make it appear later in the bucket of the hash table.
+ASDZguvASDZguv.param= twice
+ASDZguv.param= once
+.if ${ASDZguv.param} != "once"
+. error
+.elif ${ASDZguvASDZguv.param} != "twice"
+. error
+.endif
+
all:
diff --git a/contrib/bmake/unit-tests/varparse-dynamic.exp b/contrib/bmake/unit-tests/varparse-dynamic.exp
index a2ff29413167..caf8424fa91a 100644
--- a/contrib/bmake/unit-tests/varparse-dynamic.exp
+++ b/contrib/bmake/unit-tests/varparse-dynamic.exp
@@ -1,5 +1,5 @@
-make: "varparse-dynamic.mk" line 8: Malformed conditional (${.TARGEX})
-make: "varparse-dynamic.mk" line 10: Malformed conditional (${.TARGXX})
+make: "varparse-dynamic.mk" line 9: Malformed conditional (${.TARGEX})
+make: "varparse-dynamic.mk" line 12: Malformed conditional (${.TARGXX})
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varparse-dynamic.mk b/contrib/bmake/unit-tests/varparse-dynamic.mk
index d4d165017a7f..40f43b049b13 100644
--- a/contrib/bmake/unit-tests/varparse-dynamic.mk
+++ b/contrib/bmake/unit-tests/varparse-dynamic.mk
@@ -1,12 +1,14 @@
-# $NetBSD: varparse-dynamic.mk,v 1.5 2021/02/22 20:38:55 rillig Exp $
+# $NetBSD: varparse-dynamic.mk,v 1.8 2023/11/19 22:32:44 rillig Exp $
# Before 2020-07-27, there was an off-by-one error in Var_Parse that skipped
# the last character in the variable name.
-# To trigger the bug, the variable must not be defined.
+# To trigger the bug, the variable had to be undefined.
.if ${.TARGET} # exact match, may be undefined
.endif
+# expect+1: Malformed conditional (${.TARGEX})
.if ${.TARGEX} # 1 character difference, must be defined
.endif
+# expect+1: Malformed conditional (${.TARGXX})
.if ${.TARGXX} # 2 characters difference, must be defined
.endif
@@ -22,7 +24,7 @@
.endif
# If a dynamic variable is expanded in a non-local scope, the expression
-# based on this variable is not expanded. But there may be nested variable
+# based on this variable is not expanded. But there may be nested
# expressions in the modifiers, and these are kept unexpanded as well.
.if ${.TARGET:M${:Ufallback}} != "\${.TARGET:M\${:Ufallback}}"
. error
@@ -30,6 +32,3 @@
.if ${.TARGET:M${UNDEF}} != "\${.TARGET:M\${UNDEF}}"
. error
.endif
-
-all:
- @:
diff --git a/contrib/bmake/unit-tests/varparse-errors.exp b/contrib/bmake/unit-tests/varparse-errors.exp
index 27589e0b21af..20fee23bddae 100644
--- a/contrib/bmake/unit-tests/varparse-errors.exp
+++ b/contrib/bmake/unit-tests/varparse-errors.exp
@@ -1,5 +1,25 @@
-make: "varparse-errors.mk" line 38: Unknown modifier "Z"
-make: "varparse-errors.mk" line 46: Unknown modifier "Z"
+make: "varparse-errors.mk" line 38: while evaluating "${:U:Z}": Unknown modifier "Z"
+make: "varparse-errors.mk" line 47: while evaluating "${:U:Z}post": Unknown modifier "Z"
+make: Bad modifier ":OX" for variable ""
+make: "varparse-errors.mk" line 71: Undefined variable "${:U:OX"
+make: Bad modifier ":OX" for variable ""
+make: Bad modifier ":OX" for variable ""
+make: "varparse-errors.mk" line 71: Undefined variable "${:U:OX"
+make: Bad modifier ":OX" for variable ""
+make: Unclosed expression, expecting '}' for modifier "Q" of variable "" with value ""
+make: Unclosed expression, expecting '}' for modifier "sh" of variable "" with value ""
+make: Unclosed expression, expecting '}' for modifier "tA" of variable "" with value ""
+make: Unclosed expression, expecting '}' for modifier "tsX" of variable "" with value ""
+make: Unclosed expression, expecting '}' for modifier "ts" of variable "" with value ""
+make: Unclosed expression, expecting '}' for modifier "ts\040" of variable "" with value ""
+make: Unclosed expression, expecting '}' for modifier "u" of variable "" with value ""
+make: Unclosed expression, expecting '}' for modifier "H" of variable "" with value "."
+make: Unclosed expression, expecting '}' for modifier "[1]" of variable "" with value ""
+make: Unclosed expression, expecting '}' for modifier "hash" of variable "" with value "b2af338b"
+make: Unclosed expression, expecting '}' for modifier "range" of variable "" with value "1"
+make: Unclosed expression, expecting '}' for modifier "_" of variable "" with value ""
+make: Unclosed expression, expecting '}' for modifier "gmtime" of variable "" with value "<timestamp>"
+make: Unclosed expression, expecting '}' for modifier "localtime" of variable "" with value "<timestamp>"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varparse-errors.mk b/contrib/bmake/unit-tests/varparse-errors.mk
index f0947bb9410a..edb02ef0b957 100644
--- a/contrib/bmake/unit-tests/varparse-errors.mk
+++ b/contrib/bmake/unit-tests/varparse-errors.mk
@@ -1,12 +1,11 @@
-# $NetBSD: varparse-errors.mk,v 1.4 2021/03/15 12:15:03 rillig Exp $
+# $NetBSD: varparse-errors.mk,v 1.12 2024/04/20 10:18:56 rillig Exp $
-# Tests for parsing and evaluating all kinds of variable expressions.
+# Tests for parsing and evaluating all kinds of expressions.
#
# This is the basis for redesigning the error handling in Var_Parse and
# Var_Subst, collecting typical and not so typical use cases.
#
# See also:
-# VarParseResult
# Var_Parse
# Var_Subst
@@ -18,13 +17,13 @@ INDIRECT= An ${:Uindirect} value.
REF_UNDEF= A reference to an ${UNDEF}undefined variable.
-ERR_UNCLOSED= An ${UNCLOSED variable expression.
+ERR_UNCLOSED= An ${UNCLOSED expression.
ERR_BAD_MOD= An ${:Uindirect:Z} expression with an unknown modifier.
ERR_EVAL= An evaluation error ${:Uvalue:C,.,\3,}.
-# In a conditional, a variable expression that is not enclosed in quotes is
+# In a conditional, an expression that is not enclosed in quotes is
# expanded using the mode VARE_UNDEFERR.
# The variable itself must be defined.
# It may refer to undefined variables though.
@@ -35,6 +34,7 @@ ERR_EVAL= An evaluation error ${:Uvalue:C,.,\3,}.
# As of 2020-12-01, errors in the variable name are silently ignored.
# Since var.c 1.754 from 2020-12-20, unknown modifiers at parse time result
# in an error message and a non-zero exit status.
+# expect+1: while evaluating "${:U:Z}": Unknown modifier "Z"
VAR.${:U:Z}= unknown modifier in the variable name
.if ${VAR.} != "unknown modifier in the variable name"
. error
@@ -43,9 +43,49 @@ VAR.${:U:Z}= unknown modifier in the variable name
# As of 2020-12-01, errors in the variable name are silently ignored.
# Since var.c 1.754 from 2020-12-20, unknown modifiers at parse time result
# in an error message and a non-zero exit status.
+# expect+1: while evaluating "${:U:Z}post": Unknown modifier "Z"
VAR.${:U:Z}post= unknown modifier with text in the variable name
.if ${VAR.post} != "unknown modifier with text in the variable name"
. error
.endif
-all:
+# Demonstrate an edge case in which the 'static' for 'errorReported' in
+# Var_Subst actually makes a difference, preventing "a plethora of messages".
+# Given that this is an edge case and the error message is wrong and thus
+# misleading anyway, that piece of code is probably not necessary. The wrong
+# condition was added in var.c 1.185 from 2014-05-19.
+#
+# To trigger this difference, the variable assignment must use the assignment
+# operator ':=' to make VarEvalMode_ShouldKeepUndef return true. There must
+# be 2 expressions that create a parse error, which in this case is ':OX'.
+# These expressions must be nested in some way. The below expressions are
+# minimal, that is, removing any part of it destroys the effect.
+#
+# Without the 'static', there would be one more message like this:
+# Undefined variable "${:U:OX"
+#
+#.MAKEFLAGS: -dv
+IND= ${:OX}
+# expect+2: Undefined variable "${:U:OX"
+# expect+1: Undefined variable "${:U:OX"
+_:= ${:U:OX:U${IND}} ${:U:OX:U${IND}}
+#.MAKEFLAGS: -d0
+
+
+# Before var.c 1.032 from 2022-08-24, make complained about 'Unknown modifier'
+# or 'Bad modifier' when in fact the modifier was entirely correct, it was
+# just not delimited by either ':' or '}' but instead by '\0'.
+UNCLOSED:= ${:U:Q
+UNCLOSED:= ${:U:sh
+UNCLOSED:= ${:U:tA
+UNCLOSED:= ${:U:tsX
+UNCLOSED:= ${:U:ts
+UNCLOSED:= ${:U:ts\040
+UNCLOSED:= ${:U:u
+UNCLOSED:= ${:U:H
+UNCLOSED:= ${:U:[1]
+UNCLOSED:= ${:U:hash
+UNCLOSED:= ${:U:range
+UNCLOSED:= ${:U:_
+UNCLOSED:= ${:U:gmtime
+UNCLOSED:= ${:U:localtime
diff --git a/contrib/bmake/unit-tests/varparse-mod.mk b/contrib/bmake/unit-tests/varparse-mod.mk
index 0b4cbf6ca40a..c5fa6f5ece71 100644
--- a/contrib/bmake/unit-tests/varparse-mod.mk
+++ b/contrib/bmake/unit-tests/varparse-mod.mk
@@ -1,6 +1,6 @@
-# $NetBSD: varparse-mod.mk,v 1.1 2020/10/02 20:34:59 rillig Exp $
+# $NetBSD: varparse-mod.mk,v 1.2 2023/11/19 21:47:52 rillig Exp $
-# Tests for parsing variable expressions with modifiers.
+# Tests for parsing expressions with modifiers.
# As of 2020-10-02, the below condition does not result in a parse error.
# The condition contains two separate mistakes. The first mistake is that
@@ -8,7 +8,7 @@
# there is a stray '}' at the end of the whole condition.
#
# As of 2020-10-02, the actual parse result of this condition is a single
-# variable expression with 2 modifiers. The first modifier is
+# expression with 2 modifiers. The first modifier is
# ":!echo "\$VAR"} !". Afterwards, the parser optionally skips a ':' (at the
# bottom of ApplyModifiers) and continues with the next modifier, in this case
# "= "value"", which is interpreted as a SysV substitution modifier with an
diff --git a/contrib/bmake/unit-tests/varparse-undef-partial.mk b/contrib/bmake/unit-tests/varparse-undef-partial.mk
index 27f44d79b31a..9a5704265086 100644
--- a/contrib/bmake/unit-tests/varparse-undef-partial.mk
+++ b/contrib/bmake/unit-tests/varparse-undef-partial.mk
@@ -1,7 +1,7 @@
-# $NetBSD: varparse-undef-partial.mk,v 1.3 2020/11/04 05:10:01 rillig Exp $
+# $NetBSD: varparse-undef-partial.mk,v 1.5 2024/01/07 11:39:04 rillig Exp $
# When an undefined variable is expanded in a ':=' assignment, only the
-# initial '$' of the variable expression is skipped by the parser, while
+# initial '$' of the expression is skipped by the parser, while
# the remaining expression is evaluated. In edge cases this can lead to
# a completely different interpretation of the partially expanded text.
@@ -11,11 +11,10 @@ PARAM= :Q
# The expression ${VAR.${PARAM}} refers to the variable named "VAR.:Q",
# with the ":Q" being part of the name. This variable is not defined,
-# therefore the initial '$' of that whole expression is skipped by the
-# parser (see Var_Subst, the Buf_AddByte in the else branch) and the rest
-# of the expression is expanded as usual.
+# therefore the initial '$' of that whole expression is skipped by the parser
+# (see VarSubstExpr) and the rest of the expression is expanded as usual.
#
-# The resulting variable expression is ${VAR.:Q}, which means that the
+# The resulting expression is ${VAR.:Q}, which means that the
# interpretation of the ":Q" has changed from being part of the variable
# name to being a variable modifier. This is a classical code injection.
EVAL:= ${LIST}
@@ -37,7 +36,7 @@ ${:UVAR.\:Q}= var-dot with parameter :Q
# In contrast to the previous line, evaluating the original LIST again now
# produces a different result since the variable named "VAR.:Q" is now
# defined. It is expanded as usual, interpreting the ":Q" as part of the
-# variable name, as would be expected from reading the variable expression.
+# variable name, as would be expected from reading the expression.
EVAL:= ${LIST}
.if ${EVAL} != "defined var-dot with parameter :Q end"
. error ${EVAL}
diff --git a/contrib/bmake/unit-tests/varquote.exp b/contrib/bmake/unit-tests/varquote.exp
deleted file mode 100644
index 63107bfd34f5..000000000000
--- a/contrib/bmake/unit-tests/varquote.exp
+++ /dev/null
@@ -1,3 +0,0 @@
--fdebug-prefix-map=$NETBSDSRCDIR=/usr/src -fdebug-regex-map=/usr/src/(.*)/obj$=/usr/obj/\1
--fdebug-prefix-map=$NETBSDSRCDIR=/usr/src -fdebug-regex-map=/usr/src/(.*)/obj$=/usr/obj/\1
-exit status 0
diff --git a/contrib/bmake/unit-tests/varquote.mk b/contrib/bmake/unit-tests/varquote.mk
deleted file mode 100644
index fb8b1066ac15..000000000000
--- a/contrib/bmake/unit-tests/varquote.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-# $NetBSD: varquote.mk,v 1.4 2018/12/16 18:53:34 christos Exp $
-#
-# Test VAR:q modifier
-
-.if !defined(REPROFLAGS)
-REPROFLAGS+= -fdebug-prefix-map=\$$NETBSDSRCDIR=/usr/src
-REPROFLAGS+= -fdebug-regex-map='/usr/src/(.*)/obj$$=/usr/obj/\1'
-all:
- @${MAKE} -f ${MAKEFILE} REPROFLAGS=${REPROFLAGS:S/\$/&&/g:Q}
- @${MAKE} -f ${MAKEFILE} REPROFLAGS=${REPROFLAGS:q}
-.else
-all:
- @printf "%s %s\n" ${REPROFLAGS}
-.endif
diff --git a/contrib/bmake/util.c b/contrib/bmake/util.c
index 571d187c5fe1..f660c21a228a 100644
--- a/contrib/bmake/util.c
+++ b/contrib/bmake/util.c
@@ -1,9 +1,9 @@
-/* $NetBSD: util.c,v 1.76 2021/02/03 08:00:36 rillig Exp $ */
+/* $NetBSD: util.c,v 1.78 2021/12/15 12:58:01 rillig Exp $ */
/*
* Missing stuff from OS's
*
- * $Id: util.c,v 1.46 2021/02/05 20:02:29 sjg Exp $
+ * $Id: util.c,v 1.52 2024/01/04 00:27:30 sjg Exp $
*/
#include <sys/param.h>
@@ -13,7 +13,7 @@
#include "make.h"
-MAKE_RCSID("$NetBSD: util.c,v 1.76 2021/02/03 08:00:36 rillig Exp $");
+MAKE_RCSID("$NetBSD: util.c,v 1.78 2021/12/15 12:58:01 rillig Exp $");
#if !defined(MAKE_NATIVE) && !defined(HAVE_STRERROR)
extern int errno, sys_nerr;
@@ -22,12 +22,12 @@ extern char *sys_errlist[];
char *
strerror(int e)
{
- static char buf[100];
- if (e < 0 || e >= sys_nerr) {
- snprintf(buf, sizeof buf, "Unknown error %d", e);
- return buf;
- } else
- return sys_errlist[e];
+ static char buf[100];
+ if (e < 0 || e >= sys_nerr) {
+ snprintf(buf, sizeof buf, "Unknown error %d", e);
+ return buf;
+ } else
+ return sys_errlist[e];
}
#endif
@@ -57,9 +57,9 @@ findenv(const char *name, int *offset)
char *
getenv(const char *name)
{
- int offset;
+ int offset;
- return findenv(name, &offset);
+ return findenv(name, &offset);
}
int
@@ -73,7 +73,7 @@ unsetenv(const char *name)
return -1;
}
- while (findenv(name, &offset)) { /* if set multiple times */
+ while (findenv(name, &offset)) { /* if set multiple times */
for (p = &environ[offset];; p++)
if (!(*p = *(p + 1)))
break;
@@ -94,7 +94,7 @@ setenv(const char *name, const char *value, int rewrite)
return -1;
}
- if (*value == '=') /* no `=' in value */
+ if (*value == '=') /* no `=' in value */
value++;
l_value = strlen(value);
@@ -160,16 +160,15 @@ main(int argc, char *argv[])
static char *
strrcpy(char *ptr, char *str)
{
- int len = strlen(str);
-
- while (len != 0)
- *--ptr = str[--len];
+ int len = strlen(str);
- return ptr;
-} /* end strrcpy */
+ while (len != 0)
+ *--ptr = str[--len];
+ return ptr;
+}
-char *sys_siglist[] = {
+char *sys_siglist[] = {
"Signal 0",
"Hangup", /* SIGHUP */
"Interrupt", /* SIGINT */
@@ -218,7 +217,7 @@ char *sys_siglist[] = {
int
killpg(int pid, int sig)
{
- return kill(-pid, sig);
+ return kill(-pid, sig);
}
#if !defined(BSD) && !defined(d_fileno)
@@ -340,6 +339,10 @@ getcwd(path, sz)
}
#endif
+#if !defined(HAVE_SIGACTION)
+#include "sigact.h"
+#endif
+
/* force posix signals */
SignalProc
bmake_signal(int s, SignalProc a)
@@ -389,7 +392,7 @@ vsnprintf(char *s, size_t n, const char *fmt, va_list args)
fakebuf._cnt++;
putc('\0', &fakebuf);
if (fakebuf._cnt < 0)
- fakebuf._cnt = 0;
+ fakebuf._cnt = 0;
return n - fakebuf._cnt - 1;
#else
#ifndef _PATH_DEVNULL
@@ -428,18 +431,28 @@ snprintf(char *s, size_t n, const char *fmt, ...)
}
#endif
-#if !defined(HAVE_STRFTIME)
+#if !defined(HAVE_STRFTIME) || defined(FORCE_BMAKE_STRFTIME)
+/* we only implement enough to pass our unit-tests */
size_t
strftime(char *buf, size_t len, const char *fmt, const struct tm *tm)
{
- static char months[][4] = {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ static const char *months[] = {
+ "January", "February", "March",
+ "April", "May", "June",
+ "July", "August", "September",
+ "October", "November", "December"
};
-
+ static const char *days[] = {
+ "Sunday", "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday"
+ };
+ int i;
size_t s;
char *b = buf;
+ char *cp;
+ if (fmt == NULL || *fmt == '\0')
+ fmt = "%c";
while (*fmt) {
if (len == 0)
return buf - b;
@@ -448,6 +461,7 @@ strftime(char *buf, size_t len, const char *fmt, const struct tm *tm)
len--;
continue;
}
+ fmt++;
switch (*fmt++) {
case '%':
*buf++ = '%';
@@ -458,26 +472,87 @@ strftime(char *buf, size_t len, const char *fmt, const struct tm *tm)
*buf = '%';
s = 1;
break;
+ case 'A':
+ s = snprintf(buf, len, "%s", days[tm->tm_wday]);
+ break;
+ case 'a':
+ s = snprintf(buf, len, "%.3s", days[tm->tm_wday]);
+ break;
+ case 'B':
+ if (tm->tm_mon >= 12)
+ return buf - b;
+ s = snprintf(buf, len, "%s", months[tm->tm_mon]);
+ break;
+ case 'b':
+ if (tm->tm_mon >= 12)
+ return buf - b;
+ s = snprintf(buf, len, "%.3s", months[tm->tm_mon]);
+ break;
+ case 'c':
+ s = strftime(buf, len, "%a %b %e %H:%M:%S %Y", tm);
+ break;
+ case 'd':
+ s = snprintf(buf, len, "%02d", tm->tm_mday);
+ break;
+ case 'e':
+ s = snprintf(buf, len, "%2d", tm->tm_mday);
+ break;
+ case 'F':
+ s = strftime(buf, len, "%y-%m-%d", tm);
+ break;
+ case 'H':
+ s = snprintf(buf, len, "%02d", tm->tm_hour);
+ break;
+ case 'I':
+ if ((i = tm->tm_hour) == 0)
+ i = 24;
+ s = snprintf(buf, len, "%02d", (i > 12) ? (i - 12) : i);
+ break;
+ case 'j':
+ s = snprintf(buf, len, "%03d", tm->tm_yday + 1);
+ break;
case 'k':
s = snprintf(buf, len, "%d", tm->tm_hour);
break;
case 'M':
s = snprintf(buf, len, "%02d", tm->tm_min);
break;
+ case 'm':
+ s = snprintf(buf, len, "%02d", 1 + tm->tm_mon);
+ break;
case 'S':
s = snprintf(buf, len, "%02d", tm->tm_sec);
break;
- case 'b':
- if (tm->tm_mon >= 12)
- return buf - b;
- s = snprintf(buf, len, "%s", months[tm->tm_mon]);
+ case 's':
+ s = snprintf(buf, len, "%ld", (long)time(NULL));
break;
- case 'd':
- s = snprintf(buf, len, "%02d", tm->tm_mday);
+ case 'T':
+ s = strftime(buf, len, "%H:%M:%S", tm);
+ break;
+ case 'w':
+ s = snprintf(buf, len, "%02d", tm->tm_wday);
break;
case 'Y':
s = snprintf(buf, len, "%d", 1900 + tm->tm_year);
break;
+ case 'y':
+ s = snprintf(buf, len, "%02d", tm->tm_year % 100);
+ break;
+ case 'Z':
+ if ((cp = getenv("TZ")) != NULL) {
+ char tz[20];
+
+ i = snprintf(tz, sizeof(tz), "%s", cp);
+ if (i > 5) {
+ cp = &tz[i - 3];
+ tz[3] = '\0';
+ } else
+ cp = tz;
+ s = snprintf(buf, len, "%s",
+ tm->tm_isdst ? cp : tz);
+ } else
+ s = 0;
+ break;
default:
s = snprintf(buf, len, "Unsupported format %c",
fmt[-1]);
@@ -586,3 +661,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..ec0a23e05a67 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.1109 2024/05/07 18:26:22 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -81,8 +81,7 @@
* Var_End Clean up the module.
*
* Var_Set
- * Var_SetExpand
- * Set the value of the variable, creating it if
+ * Var_SetExpand Set the value of the variable, creating it if
* necessary.
*
* Var_Append
@@ -98,13 +97,11 @@
* Var_Value Return the unexpanded value of a variable, or NULL if
* the variable is undefined.
*
- * Var_Subst Substitute all variable expressions in a string.
+ * Var_Subst Substitute all expressions in a string.
*
- * Var_Parse Parse a variable expression such as ${VAR:Mpattern}.
+ * Var_Parse Parse an expression such as ${VAR:Mpattern}.
*
- * Var_Delete
- * Var_DeleteExpand
- * Delete a variable.
+ * Var_Delete Delete a variable.
*
* Var_ReexportVars
* Export some or even all variables to the environment
@@ -119,23 +116,21 @@
* Var_Stats Print out hashing statistics if in -dh mode.
*
* Var_Dump Print out all variables defined in the given scope.
- *
- * XXX: There's a lot of almost duplicate code in these functions that only
- * differs in subtle details that are not mentioned in the manual page.
*/
#include <sys/stat.h>
#include <sys/types.h>
-#ifndef NO_REGEX
-#include <regex.h>
-#endif
#include "make.h"
#include <errno.h>
+#ifdef HAVE_REGEX_H
+#include <regex.h>
+#endif
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
-#elif defined(HAVE_STDINT_H)
+#endif
+#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#ifdef HAVE_LIMITS_H
@@ -148,7 +143,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.1109 2024/05/07 18:26:22 sjg Exp $");
/*
* Variables are defined using one of the VAR=value assignments. Their
@@ -158,15 +153,16 @@ MAKE_RCSID("$NetBSD: var.c,v 1.934 2021/06/21 08:40:44 rillig Exp $");
* There are 3 kinds of variables: scope variables, environment variables,
* undefined variables.
*
- * Scope variables are stored in a GNode.scope. The only way to undefine
+ * Scope variables are stored in GNode.vars. The only way to undefine
* a scope variable is using the .undef directive. In particular, it must
* not be possible to undefine a variable during the evaluation of an
- * expression, or Var.name might point nowhere.
+ * expression, or Var.name might point nowhere. (There is another,
+ * unintended way to undefine a scope variable, see varmod-loop-delete.mk.)
*
- * Environment variables are temporary. They are returned by VarFind, and
- * after using them, they must be freed using VarFreeEnv.
+ * Environment variables are short-lived. They are returned by VarFind, and
+ * after using them, they must be freed using VarFreeShortLived.
*
- * Undefined variables occur during evaluation of variable expressions such
+ * Undefined variables occur during evaluation of expressions such
* as ${UNDEF:Ufallback} in Var_Parse and ApplyModifiers.
*/
typedef struct Var {
@@ -181,51 +177,55 @@ typedef struct Var {
Buffer val;
/* The variable came from the command line. */
- bool fromCmd: 1;
+ bool fromCmd:1;
/*
- * The variable comes from the environment.
+ * The variable is short-lived.
* These variables are not registered in any GNode, therefore they
- * must be freed as soon as they are not used anymore.
+ * must be freed after use.
+ */
+ bool shortLived:1;
+
+ /*
+ * The variable comes from the environment.
+ * Appending to its value depends on the scope, see var-op-append.mk.
*/
- bool fromEnv: 1;
+ bool fromEnvironment:1;
/*
* The variable value cannot be changed anymore, and the variable
* cannot be deleted. Any attempts to do so are silently ignored,
* they are logged with -dv though.
+ * Use .[NO]READONLY: to adjust.
*
* See VAR_SET_READONLY.
*/
- bool readOnly: 1;
+ bool readOnly:1;
/*
- * The variable's value is currently being used by Var_Parse or
- * Var_Subst. This marker is used to avoid endless recursion.
- */
- bool inUse: 1;
+ * The variable is currently being accessed by Var_Parse or Var_Subst.
+ * This temporary marker is used to avoid endless recursion.
+ */
+ bool inUse:1;
/*
* The variable is exported to the environment, to be used by child
* processes.
*/
- bool exported: 1;
+ bool exported:1;
/*
* At the point where this variable was exported, it contained an
* unresolved reference to another variable. Before any child
- * process is started, it needs to be exported again, in the hope
- * that the referenced variable can then be resolved.
+ * process is started, it needs to be actually exported, resolving
+ * the referenced variable just in time.
*/
- bool reexport: 1;
+ bool reexport:1;
} Var;
/*
* Exporting variables is expensive and may leak memory, so skip it if we
* can.
- *
- * To avoid this, it might be worth encapsulating the environment variables
- * in a separate data structure called EnvVars.
*/
typedef enum VarExportedMode {
VAR_EXPORTED_NONE,
@@ -250,10 +250,10 @@ typedef enum UnexportWhat {
/* Flags for pattern matching in the :S and :C modifiers */
typedef struct PatternFlags {
- bool subGlobal: 1; /* 'g': replace as often as possible */
- bool subOnce: 1; /* '1': replace only once */
- bool anchorStart: 1; /* '^': match only at start of word */
- bool anchorEnd: 1; /* '$': match only at end of word */
+ bool subGlobal:1; /* 'g': replace as often as possible */
+ bool subOnce:1; /* '1': replace only once */
+ bool anchorStart:1; /* '^': match only at start of word */
+ bool anchorEnd:1; /* '$': match only at end of word */
} PatternFlags;
/* SepBuf builds a string from words interleaved with separators. */
@@ -264,11 +264,20 @@ typedef struct SepBuf {
char sep;
} SepBuf;
+typedef struct {
+ const char *target;
+ const char *varname;
+ const char *expr;
+} EvalStackElement;
-/*
- * This lets us tell if we have replaced the original environ
- * (which we cannot free).
- */
+typedef struct {
+ EvalStackElement *elems;
+ size_t len;
+ size_t cap;
+ Buffer details;
+} EvalStack;
+
+/* Whether we have replaced the original environ (which we cannot free). */
char **savedEnv = NULL;
/*
@@ -312,12 +321,11 @@ static bool save_dollars = false;
* override variables from SCOPE_GLOBAL.
*
* There is no scope for environment variables, these are generated on-the-fly
- * whenever they are referenced. If there were such a scope, each change to
- * environment variables would have to be reflected in that scope, which may
- * be simpler or more complex than the current implementation.
+ * whenever they are referenced.
*
* Each target has its own scope, containing the 7 target-local variables
- * .TARGET, .ALLSRC, etc. No other variables are in these scopes.
+ * .TARGET, .ALLSRC, etc. Variables set on dependency lines also go in
+ * this scope.
*/
GNode *SCOPE_CMDLINE;
@@ -326,8 +334,9 @@ GNode *SCOPE_INTERNAL;
static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE;
-static const char *VarEvalMode_Name[] = {
+static const char VarEvalMode_Name[][32] = {
"parse-only",
+ "parse-balanced",
"eval",
"eval-defined",
"eval-keep-dollar",
@@ -335,9 +344,62 @@ static const char *VarEvalMode_Name[] = {
"eval-keep-dollar-and-undefined",
};
+static EvalStack evalStack;
+
+
+void
+EvalStack_Push(const char *target, const char *expr, const char *varname)
+{
+ if (evalStack.len >= evalStack.cap) {
+ evalStack.cap = 16 + 2 * evalStack.cap;
+ evalStack.elems = bmake_realloc(evalStack.elems,
+ evalStack.cap * sizeof(*evalStack.elems));
+ }
+ evalStack.elems[evalStack.len].target = target;
+ evalStack.elems[evalStack.len].expr = expr;
+ evalStack.elems[evalStack.len].varname = varname;
+ evalStack.len++;
+}
+
+void
+EvalStack_Pop(void)
+{
+ assert(evalStack.len > 0);
+ evalStack.len--;
+}
+
+const char *
+EvalStack_Details(void)
+{
+ size_t i;
+ Buffer *buf = &evalStack.details;
+
+
+ buf->len = 0;
+ for (i = 0; i < evalStack.len; i++) {
+ EvalStackElement *elem = evalStack.elems + i;
+ if (elem->target != NULL) {
+ Buf_AddStr(buf, "in target \"");
+ Buf_AddStr(buf, elem->target);
+ Buf_AddStr(buf, "\": ");
+ }
+ if (elem->expr != NULL) {
+ Buf_AddStr(buf, "while evaluating \"");
+ Buf_AddStr(buf, elem->expr);
+ Buf_AddStr(buf, "\": ");
+ }
+ if (elem->varname != NULL) {
+ Buf_AddStr(buf, "while evaluating variable \"");
+ Buf_AddStr(buf, elem->varname);
+ Buf_AddStr(buf, "\": ");
+ }
+ }
+ return buf->len > 0 ? buf->data : "";
+}
static Var *
-VarNew(FStr name, const char *value, bool fromEnv, bool readOnly)
+VarNew(FStr name, const char *value,
+ bool shortLived, bool fromEnvironment, bool readOnly)
{
size_t value_len = strlen(value);
Var *var = bmake_malloc(sizeof *var);
@@ -345,7 +407,8 @@ VarNew(FStr name, const char *value, bool fromEnv, bool readOnly)
Buf_InitSize(&var->val, value_len + 1);
Buf_AddBytes(&var->val, value, value_len);
var->fromCmd = false;
- var->fromEnv = fromEnv;
+ var->shortLived = shortLived;
+ var->fromEnvironment = fromEnvironment;
var->readOnly = readOnly;
var->inUse = false;
var->exported = false;
@@ -375,11 +438,11 @@ CanonicalVarname(Substring name)
if (Substring_Equals(name, ".TARGET"))
return Substring_InitStr(TARGET);
+ /* GNU make has an additional alias $^ == ${.ALLSRC}. */
+
if (Substring_Equals(name, ".SHELL") && shellPath == NULL)
Shell_Init();
- /* GNU make has an additional alias $^ == ${.ALLSRC}. */
-
return name;
}
@@ -399,8 +462,8 @@ GNode_FindVar(GNode *scope, Substring varname, unsigned int hash)
*
* Results:
* The found variable, or NULL if the variable does not exist.
- * If the variable is an environment variable, it must be freed using
- * VarFreeEnv after use.
+ * If the variable is short-lived (such as environment variables), it
+ * must be freed using VarFreeShortLived after use.
*/
static Var *
VarFindSubstring(Substring name, GNode *scope, bool elsewhere)
@@ -428,18 +491,10 @@ VarFindSubstring(Substring name, GNode *scope, bool elsewhere)
}
if (var == NULL) {
- FStr envName;
- const char *envValue;
-
- /*
- * TODO: try setting an environment variable with the empty
- * name, which should be technically possible, just to see
- * how make reacts. All .for loops should be broken then.
- */
- envName = Substring_Str(name);
- envValue = getenv(envName.str);
+ FStr envName = Substring_Str(name);
+ const char *envValue = getenv(envName.str);
if (envValue != NULL)
- return VarNew(envName, envValue, true, false);
+ return VarNew(envName, envValue, true, true, false);
FStr_Done(&envName);
if (opts.checkEnvFirst && scope != SCOPE_GLOBAL) {
@@ -456,18 +511,17 @@ VarFindSubstring(Substring name, GNode *scope, bool elsewhere)
return var;
}
-/* TODO: Replace these calls with VarFindSubstring, as far as possible. */
static Var *
VarFind(const char *name, GNode *scope, bool elsewhere)
{
return VarFindSubstring(Substring_InitStr(name), scope, elsewhere);
}
-/* If the variable is an environment variable, free it, including its value. */
+/* If the variable is short-lived, free it, including its value. */
static void
-VarFreeEnv(Var *v)
+VarFreeShortLived(Var *v)
{
- if (!v->fromEnv)
+ if (!v->shortLived)
return;
FStr_Done(&v->name);
@@ -475,15 +529,26 @@ VarFreeEnv(Var *v)
free(v);
}
+static const char *
+ValueDescription(const char *value)
+{
+ if (value[0] == '\0')
+ return "# (empty)";
+ if (ch_isspace(value[strlen(value) - 1]))
+ return "# (ends with space)";
+ return "";
+}
+
/* Add a new variable of the given name and value to the given scope. */
static Var *
VarAdd(const char *name, const char *value, GNode *scope, VarSetFlags flags)
{
HashEntry *he = HashTable_CreateEntry(&scope->vars, name, NULL);
Var *v = VarNew(FStr_InitRefer(/* aliased to */ he->key), value,
- false, (flags & VAR_SET_READONLY) != 0);
+ false, false, (flags & VAR_SET_READONLY) != 0);
HashEntry_Set(he, v);
- DEBUG3(VAR, "%s: %s = %s\n", scope->name, name, value);
+ DEBUG4(VAR, "%s: %s = %s%s\n",
+ scope->name, name, value, ValueDescription(value));
return v;
}
@@ -498,16 +563,30 @@ Var_Delete(GNode *scope, const char *varname)
Var *v;
if (he == NULL) {
- DEBUG2(VAR, "%s:delete %s (not found)\n", scope->name, varname);
+ DEBUG2(VAR, "%s: ignoring delete '%s' as it is not found\n",
+ scope->name, varname);
return;
}
- DEBUG2(VAR, "%s:delete %s\n", scope->name, varname);
v = he->value;
+ if (v->readOnly) {
+ DEBUG2(VAR, "%s: ignoring delete '%s' as it is read-only\n",
+ scope->name, varname);
+ return;
+ }
+ if (v->inUse) {
+ Parse_Error(PARSE_FATAL,
+ "Cannot delete variable \"%s\" while it is used",
+ v->name.str);
+ return;
+ }
+
+ DEBUG2(VAR, "%s: delete %s\n", scope->name, varname);
if (v->exported)
unsetenv(v->name.str);
- if (strcmp(v->name.str, MAKE_EXPORTED) == 0)
+ if (strcmp(v->name.str, ".MAKE.EXPORTED") == 0)
var_exportedVars = VAR_EXPORTED_NONE;
+
assert(v->name.freeIt == NULL);
HashTable_DeleteEntry(&scope->vars, he);
Buf_Done(&v->val);
@@ -515,34 +594,12 @@ Var_Delete(GNode *scope, const char *varname)
}
/*
- * Remove a variable from a scope, freeing all related memory as well.
- * The variable name is expanded once.
- */
-void
-Var_DeleteExpand(GNode *scope, const char *name)
-{
- FStr varname = FStr_InitRefer(name);
-
- if (strchr(varname.str, '$') != NULL) {
- char *expanded;
- (void)Var_Subst(varname.str, SCOPE_GLOBAL, VARE_WANTRES,
- &expanded);
- /* TODO: handle errors */
- varname = FStr_InitOwn(expanded);
- }
-
- Var_Delete(scope, varname.str);
- FStr_Done(&varname);
-}
-
-/*
* Undefine one or more variables from the global scope.
* The argument is expanded exactly once and then split into words.
*/
void
Var_Undef(const char *arg)
{
- VarParseResult vpr;
char *expanded;
Words varnames;
size_t i;
@@ -553,8 +610,9 @@ Var_Undef(const char *arg)
return;
}
- vpr = Var_Subst(arg, SCOPE_GLOBAL, VARE_WANTRES, &expanded);
- if (vpr != VPR_OK) {
+ expanded = Var_Subst(arg, SCOPE_GLOBAL, VARE_WANTRES);
+ if (expanded == var_Error) {
+ /* TODO: Make this part of the code reachable. */
Parse_Error(PARSE_FATAL,
"Error in variable names to be undefined");
return;
@@ -599,7 +657,7 @@ MayExport(const char *name)
}
static bool
-ExportVarEnv(Var *v)
+ExportVarEnv(Var *v, GNode *scope)
{
const char *name = v->name.str;
char *val = v->val.data;
@@ -614,17 +672,18 @@ ExportVarEnv(Var *v)
return true;
}
- if (v->inUse) {
- /*
- * We recursed while exporting in a child.
- * This isn't going to end well, just skip it.
- */
- return false;
- }
+ if (v->inUse)
+ return false; /* see EMPTY_SHELL in directive-export.mk */
/* XXX: name is injected without escaping it */
expr = str_concat3("${", name, "}");
- (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &val);
+ val = Var_Subst(expr, scope, VARE_WANTRES);
+ if (scope != SCOPE_GLOBAL) {
+ /* we will need to re-export the global version */
+ v = VarFind(name, SCOPE_GLOBAL, false);
+ if (v != NULL)
+ v->exported = false;
+ }
/* TODO: handle errors */
setenv(name, val, 1);
free(val);
@@ -668,22 +727,24 @@ ExportVarLiteral(Var *v)
/*
* Mark a single variable to be exported later for subprocesses.
*
- * Internal variables (those starting with '.') are not exported.
+ * Internal variables are not exported.
*/
static bool
-ExportVar(const char *name, VarExportMode mode)
+ExportVar(const char *name, GNode *scope, VarExportMode mode)
{
Var *v;
if (!MayExport(name))
return false;
- v = VarFind(name, SCOPE_GLOBAL, false);
+ v = VarFind(name, scope, false);
+ if (v == NULL && scope != SCOPE_GLOBAL)
+ v = VarFind(name, SCOPE_GLOBAL, false);
if (v == NULL)
return false;
if (mode == VEM_ENV)
- return ExportVarEnv(v);
+ return ExportVarEnv(v, scope);
else if (mode == VEM_PLAIN)
return ExportVarPlain(v);
else
@@ -695,7 +756,7 @@ ExportVar(const char *name, VarExportMode mode)
* re-exported.
*/
void
-Var_ReexportVars(void)
+Var_ReexportVars(GNode *scope)
{
char *xvarnames;
@@ -705,9 +766,9 @@ Var_ReexportVars(void)
* We allow the makefiles to update MAKELEVEL and ensure
* children see a correctly incremented value.
*/
- char tmp[21];
- snprintf(tmp, sizeof tmp, "%d", makelevel + 1);
- setenv(MAKE_LEVEL_ENV, tmp, 1);
+ char level_buf[21];
+ snprintf(level_buf, sizeof level_buf, "%d", makelevel + 1);
+ setenv(MAKE_LEVEL_ENV, level_buf, 1);
if (var_exportedVars == VAR_EXPORTED_NONE)
return;
@@ -719,20 +780,20 @@ Var_ReexportVars(void)
HashIter_Init(&hi, &SCOPE_GLOBAL->vars);
while (HashIter_Next(&hi) != NULL) {
Var *var = hi.entry->value;
- ExportVar(var->name.str, VEM_ENV);
+ ExportVar(var->name.str, scope, VEM_ENV);
}
return;
}
- (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", SCOPE_GLOBAL, VARE_WANTRES,
- &xvarnames);
+ xvarnames = Var_Subst("${.MAKE.EXPORTED:O:u}", SCOPE_GLOBAL,
+ VARE_WANTRES);
/* TODO: handle errors */
if (xvarnames[0] != '\0') {
Words varnames = Str_Words(xvarnames, false);
size_t i;
for (i = 0; i < varnames.len; i++)
- ExportVar(varnames.words[i], VEM_ENV);
+ ExportVar(varnames.words[i], scope, VEM_ENV);
Words_Free(varnames);
}
free(xvarnames);
@@ -750,14 +811,14 @@ ExportVars(const char *varnames, bool isExport, VarExportMode mode)
for (i = 0; i < words.len; i++) {
const char *varname = words.words[i];
- if (!ExportVar(varname, mode))
+ if (!ExportVar(varname, SCOPE_GLOBAL, mode))
continue;
if (var_exportedVars == VAR_EXPORTED_NONE)
var_exportedVars = VAR_EXPORTED_SOME;
if (isExport && mode == VEM_PLAIN)
- Global_Append(MAKE_EXPORTED, varname);
+ Global_Append(".MAKE.EXPORTED", varname);
}
Words_Free(words);
}
@@ -765,9 +826,7 @@ ExportVars(const char *varnames, bool isExport, VarExportMode mode)
static void
ExportVarsExpand(const char *uvarnames, bool isExport, VarExportMode mode)
{
- char *xvarnames;
-
- (void)Var_Subst(uvarnames, SCOPE_GLOBAL, VARE_WANTRES, &xvarnames);
+ char *xvarnames = Var_Subst(uvarnames, SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
ExportVars(xvarnames, isExport, mode);
free(xvarnames);
@@ -792,15 +851,13 @@ Var_ExportVars(const char *varnames)
}
-extern char **environ;
-
static void
ClearEnv(void)
{
- const char *cp;
+ const char *level;
char **newenv;
- cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */
+ level = getenv(MAKE_LEVEL_ENV); /* we should preserve this */
if (environ == savedEnv) {
/* we have been here before! */
newenv = bmake_realloc(environ, 2 * sizeof(char *));
@@ -816,8 +873,8 @@ ClearEnv(void)
environ = savedEnv = newenv;
newenv[0] = NULL;
newenv[1] = NULL;
- if (cp != NULL && *cp != '\0')
- setenv(MAKE_LEVEL_ENV, cp, 1);
+ if (level != NULL && *level != '\0')
+ setenv(MAKE_LEVEL_ENV, level, 1);
}
static void
@@ -843,10 +900,8 @@ GetVarnamesToUnexport(bool isEnv, const char *arg,
}
if (what != UNEXPORT_NAMED) {
- char *expanded;
- /* Using .MAKE.EXPORTED */
- (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", SCOPE_GLOBAL,
- VARE_WANTRES, &expanded);
+ char *expanded = Var_Subst("${.MAKE.EXPORTED:O:u}",
+ SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
varnames = FStr_InitOwn(expanded);
}
@@ -856,15 +911,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;
@@ -873,13 +930,12 @@ UnexportVar(const char *varname, UnexportWhat what)
if (what == UNEXPORT_NAMED) {
/* Remove the variable names from .MAKE.EXPORTED. */
/* XXX: v->name is injected without escaping it */
- char *expr = str_concat3("${" MAKE_EXPORTED ":N",
- v->name.str, "}");
- char *cp;
- (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &cp);
+ char *expr = str_concat3(
+ "${.MAKE.EXPORTED:N", v->name.str, "}");
+ char *filtered = Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
- Global_Set(MAKE_EXPORTED, cp);
- free(cp);
+ Global_Set(".MAKE.EXPORTED", filtered);
+ free(filtered);
free(expr);
}
}
@@ -888,27 +944,21 @@ 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);
+ Global_Delete(".MAKE.EXPORTED");
}
-/*
- * This is called when .unexport[-env] is seen.
- *
- * str must have the form "unexport[-env] varname...".
- */
+/* Handle the .unexport and .unexport-env directives. */
void
Var_UnExport(bool isEnv, const char *arg)
{
@@ -920,32 +970,6 @@ Var_UnExport(bool isEnv, const char *arg)
FStr_Done(&varnames);
}
-/*
- * When there is a variable of the same name in the command line scope, the
- * global variable would not be visible anywhere. Therefore there is no
- * point in setting it at all.
- *
- * See 'scope == SCOPE_CMDLINE' in Var_SetWithFlags.
- */
-static bool
-ExistsInCmdline(const char *name, const char *val)
-{
- Var *v;
-
- v = VarFind(name, SCOPE_CMDLINE, false);
- if (v == NULL)
- return false;
-
- if (v->fromCmd) {
- DEBUG3(VAR, "%s: %s = %s ignored!\n",
- SCOPE_GLOBAL->name, name, val);
- return true;
- }
-
- VarFreeEnv(v);
- return false;
-}
-
/* Set the variable to the value; the name is not expanded. */
void
Var_SetWithFlags(GNode *scope, const char *name, const char *val,
@@ -955,12 +979,24 @@ Var_SetWithFlags(GNode *scope, const char *name, const char *val,
assert(val != NULL);
if (name[0] == '\0') {
- DEBUG0(VAR, "SetVar: variable name is empty - ignored\n");
+ DEBUG3(VAR,
+ "%s: ignoring '%s = %s' as the variable name is empty\n",
+ scope->name, name, val);
return;
}
- if (scope == SCOPE_GLOBAL && ExistsInCmdline(name, val))
+ if (scope == SCOPE_GLOBAL
+ && VarFind(name, SCOPE_CMDLINE, false) != NULL) {
+ /*
+ * The global variable would not be visible anywhere.
+ * Therefore, there is no point in setting it at all.
+ */
+ DEBUG3(VAR,
+ "%s: ignoring '%s = %s' "
+ "due to a command line variable of the same name\n",
+ scope->name, name, val);
return;
+ }
/*
* Only look for a variable in the given scope since anything set
@@ -971,85 +1007,69 @@ Var_SetWithFlags(GNode *scope, const char *name, const char *val,
if (v == NULL) {
if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT)) {
/*
- * This var would normally prevent the same name being
- * added to SCOPE_GLOBAL, so delete it from there if
- * needed. Otherwise -V name may show the wrong value.
+ * This variable would normally prevent the same name
+ * being added to SCOPE_GLOBAL, so delete it from
+ * there if needed. Otherwise -V name may show the
+ * wrong value.
*
* See ExistsInCmdline.
*/
Var_Delete(SCOPE_GLOBAL, name);
}
+ if (strcmp(name, ".SUFFIXES") == 0) {
+ /* special: treat as read-only */
+ DEBUG3(VAR,
+ "%s: ignoring '%s = %s' as it is read-only\n",
+ scope->name, name, val);
+ return;
+ }
v = VarAdd(name, val, scope, flags);
} else {
if (v->readOnly && !(flags & VAR_SET_READONLY)) {
- DEBUG3(VAR, "%s: %s = %s ignored (read-only)\n",
+ DEBUG3(VAR,
+ "%s: ignoring '%s = %s' as it is read-only\n",
scope->name, name, val);
return;
}
- Buf_Empty(&v->val);
+ Buf_Clear(&v->val);
Buf_AddStr(&v->val, val);
- DEBUG3(VAR, "%s: %s = %s\n", scope->name, name, val);
+ DEBUG4(VAR, "%s: %s = %s%s\n",
+ scope->name, name, val, ValueDescription(val));
if (v->exported)
- ExportVar(name, VEM_PLAIN);
+ ExportVar(name, scope, VEM_PLAIN);
}
- /*
- * Any variables given on the command line are automatically exported
- * to the environment (as per POSIX standard), except for internals.
- */
- if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT) &&
- name[0] != '.') {
+ if (scope == SCOPE_CMDLINE) {
v->fromCmd = true;
/*
- * If requested, don't export these in the environment
- * individually. We still put them in MAKEOVERRIDES so
- * that the command-line settings continue to override
- * Makefile settings.
+ * Any variables given on the command line are automatically
+ * exported to the environment (as per POSIX standard), except
+ * for internals.
*/
- if (!opts.varNoExportEnv)
- setenv(name, val, 1);
- /* XXX: What about .MAKE.EXPORTED? */
- /* XXX: Why not just mark the variable for needing export,
- * as in ExportVarPlain? */
+ if (!(flags & VAR_SET_NO_EXPORT)) {
+
+ /*
+ * If requested, don't export these in the
+ * environment individually. We still put
+ * them in .MAKEOVERRIDES so that the
+ * command-line settings continue to override
+ * Makefile settings.
+ */
+ if (!opts.varNoExportEnv && name[0] != '.')
+ setenv(name, val, 1);
- Global_Append(MAKEOVERRIDES, name);
+ if (!(flags & VAR_SET_INTERNAL))
+ Global_Append(".MAKEOVERRIDES", name);
+ }
}
if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0)
save_dollars = ParseBoolean(val, save_dollars);
if (v != NULL)
- VarFreeEnv(v);
-}
-
-/* See Var_Set for documentation. */
-void
-Var_SetExpandWithFlags(GNode *scope, const char *name, const char *val,
- VarSetFlags flags)
-{
- const char *unexpanded_name = name;
- FStr varname = FStr_InitRefer(name);
-
- assert(val != NULL);
-
- if (strchr(varname.str, '$') != NULL) {
- char *expanded;
- (void)Var_Subst(varname.str, scope, VARE_WANTRES, &expanded);
- /* TODO: handle errors */
- varname = FStr_InitOwn(expanded);
- }
-
- if (varname.str[0] == '\0') {
- DEBUG2(VAR,
- "Var_SetExpand: variable name \"%s\" expands "
- "to empty string, with value \"%s\" - ignored\n",
- unexpanded_name, val);
- } else
- Var_SetWithFlags(scope, varname.str, val, flags);
-
- FStr_Done(&varname);
+ VarFreeShortLived(v);
}
void
@@ -1059,20 +1079,27 @@ Var_Set(GNode *scope, const char *name, const char *val)
}
/*
- * Set the variable name to the value val in the given scope.
- *
- * If the variable doesn't yet exist, it is created.
- * Otherwise the new value overwrites and replaces the old value.
- *
- * Input:
- * name name of the variable to set, is expanded once
- * val value to give to the variable
- * scope scope in which to set it
+ * In the scope, expand the variable name once, then create the variable or
+ * replace its value.
*/
void
Var_SetExpand(GNode *scope, const char *name, const char *val)
{
- Var_SetExpandWithFlags(scope, name, val, VAR_SET_NONE);
+ FStr varname = FStr_InitRefer(name);
+
+ assert(val != NULL);
+
+ Var_Expand(&varname, scope, VARE_WANTRES);
+
+ if (varname.str[0] == '\0') {
+ DEBUG4(VAR,
+ "%s: ignoring '%s = %s' "
+ "as the variable name '%s' expands to empty\n",
+ scope->name, varname.str, val, name);
+ } else
+ Var_SetWithFlags(scope, varname.str, val, VAR_SET_NONE);
+
+ FStr_Done(&varname);
}
void
@@ -1082,15 +1109,15 @@ Global_Set(const char *name, const char *value)
}
void
-Global_SetExpand(const char *name, const char *value)
+Global_Delete(const char *name)
{
- Var_SetExpand(SCOPE_GLOBAL, name, value);
+ Var_Delete(SCOPE_GLOBAL, name);
}
void
-Global_Delete(const char *name)
+Global_Set_ReadOnly(const char *name, const char *value)
{
- Var_Delete(SCOPE_GLOBAL, name);
+ Var_SetWithFlags(SCOPE_GLOBAL, name, value, VAR_SET_READONLY);
}
/*
@@ -1109,51 +1136,34 @@ Var_Append(GNode *scope, const char *name, const char *val)
if (v == NULL) {
Var_SetWithFlags(scope, name, val, VAR_SET_NONE);
} else if (v->readOnly) {
- DEBUG1(VAR, "Ignoring append to %s since it is read-only\n",
- name);
+ DEBUG3(VAR, "%s: ignoring '%s += %s' as it is read-only\n",
+ scope->name, name, val);
} else if (scope == SCOPE_CMDLINE || !v->fromCmd) {
Buf_AddByte(&v->val, ' ');
Buf_AddStr(&v->val, val);
DEBUG3(VAR, "%s: %s = %s\n", scope->name, name, v->val.data);
- if (v->fromEnv) {
- /*
- * If the original variable came from the environment,
- * we have to install it in the global scope (we
- * could place it in the environment, but then we
- * should provide a way to export other variables...)
- */
- v->fromEnv = false;
- /*
- * This is the only place where a variable is
- * created whose v->name is not the same as
- * scope->vars->key.
- */
- HashTable_Set(&scope->vars, name, v);
+ if (v->fromEnvironment) {
+ /* See VarAdd. */
+ HashEntry *he =
+ HashTable_CreateEntry(&scope->vars, name, NULL);
+ HashEntry_Set(he, v);
+ FStr_Done(&v->name);
+ v->name = FStr_InitRefer(/* aliased to */ he->key);
+ v->shortLived = false;
+ v->fromEnvironment = false;
}
}
}
/*
- * The variable of the given name has the given value appended to it in the
- * given scope.
+ * In the scope, expand the variable name once. If the variable exists in the
+ * scope, add a space and the value, otherwise set the variable to the value.
*
- * If the variable doesn't exist, it is created. Otherwise the strings are
- * concatenated, with a space in between.
- *
- * Input:
- * 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
- * environment searched.
- * XXX: Knows its calling circumstances in that if called with scope
- * an actual target, it will only search that scope since only
- * a local variable could be being appended to. This is actually
- * a big win and must be tolerated.
+ * Appending to an environment variable only works in the global scope, that
+ * is, for variable assignments in makefiles, but not inside conditions or the
+ * commands of a target.
*/
void
Var_AppendExpand(GNode *scope, const char *name, const char *val)
@@ -1162,22 +1172,14 @@ Var_AppendExpand(GNode *scope, const char *name, const char *val)
assert(val != NULL);
- if (strchr(name, '$') != NULL) {
- char *expanded;
- (void)Var_Subst(name, scope, VARE_WANTRES, &expanded);
- /* TODO: handle errors */
- xname = FStr_InitOwn(expanded);
- if (expanded[0] == '\0') {
- DEBUG2(VAR,
- "Var_AppendExpand: variable name \"%s\" expands "
- "to empty string, with value \"%s\" - ignored\n",
- name, val);
- FStr_Done(&xname);
- return;
- }
- }
-
- Var_Append(scope, xname.str, val);
+ Var_Expand(&xname, scope, VARE_WANTRES);
+ if (xname.str != name && xname.str[0] == '\0')
+ DEBUG4(VAR,
+ "%s: ignoring '%s += %s' "
+ "as the variable name '%s' expands to empty\n",
+ scope->name, xname.str, val, name);
+ else
+ Var_Append(scope, xname.str, val);
FStr_Done(&xname);
}
@@ -1195,7 +1197,7 @@ Var_Exists(GNode *scope, const char *name)
if (v == NULL)
return false;
- VarFreeEnv(v);
+ VarFreeShortLived(v);
return true;
}
@@ -1204,8 +1206,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)
@@ -1213,13 +1215,7 @@ Var_ExistsExpand(GNode *scope, const char *name)
FStr varname = FStr_InitRefer(name);
bool exists;
- if (strchr(varname.str, '$') != NULL) {
- char *expanded;
- (void)Var_Subst(varname.str, scope, VARE_WANTRES, &expanded);
- /* TODO: handle errors */
- varname = FStr_InitOwn(expanded);
- }
-
+ Var_Expand(&varname, scope, VARE_WANTRES);
exists = Var_Exists(scope, varname.str);
FStr_Done(&varname);
return exists;
@@ -1227,11 +1223,11 @@ Var_ExistsExpand(GNode *scope, const char *name)
/*
* Return the unexpanded value of the given variable in the given scope,
- * or the usual scopes.
+ * falling back to the command, global and environment scopes, in this order,
+ * but see the -e option.
*
* Input:
- * name name to find, is not expanded any further
- * scope scope in which to search for it
+ * name the name to find, is not expanded any further
*
* Results:
* The value if the variable exists, NULL if it doesn't.
@@ -1246,16 +1242,31 @@ Var_Value(GNode *scope, const char *name)
if (v == NULL)
return FStr_InitRefer(NULL);
- if (!v->fromEnv)
+ if (!v->shortLived)
return FStr_InitRefer(v->val.data);
- /* Since environment variables are short-lived, free it now. */
- FStr_Done(&v->name);
- value = Buf_DoneData(&v->val);
- free(v);
+ value = v->val.data;
+ v->val.data = NULL;
+ VarFreeShortLived(v);
+
return FStr_InitOwn(value);
}
+/* Set or clear the read-only attribute of the variable if it exists. */
+void
+Var_ReadOnly(const char *name, bool bf)
+{
+ Var *v;
+
+ v = VarFind(name, SCOPE_GLOBAL, false);
+ if (v == NULL) {
+ DEBUG1(VAR, "Var_ReadOnly: %s not found\n", name);
+ return;
+ }
+ v->readOnly = bf;
+ DEBUG2(VAR, "Var_ReadOnly: %s %s\n", name, bf ? "true" : "false");
+}
+
/*
* Return the unexpanded variable value from this node, without trying to look
* up the variable in any other scope.
@@ -1331,7 +1342,7 @@ SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size)
}
static void
-SepBuf_AddBytesBetween(SepBuf *buf, const char *start, const char *end)
+SepBuf_AddRange(SepBuf *buf, const char *start, const char *end)
{
SepBuf_AddBytes(buf, start, (size_t)(end - start));
}
@@ -1345,7 +1356,7 @@ SepBuf_AddStr(SepBuf *buf, const char *str)
static void
SepBuf_AddSubstring(SepBuf *buf, Substring sub)
{
- SepBuf_AddBytesBetween(buf, sub.start, sub.end);
+ SepBuf_AddRange(buf, sub.start, sub.end);
}
static char *
@@ -1356,7 +1367,7 @@ SepBuf_DoneData(SepBuf *buf)
/*
- * This callback for ModifyWords gets a single word from a variable expression
+ * This callback for ModifyWords gets a single word from an expression
* and typically adds a modification of this word to the buffer. It may also
* do nothing or add several words.
*
@@ -1370,10 +1381,6 @@ SepBuf_DoneData(SepBuf *buf)
typedef void (*ModifyWordProc)(Substring word, SepBuf *buf, void *data);
-/*
- * Callback for ModifyWords to implement the :H modifier.
- * Add the dirname of the given word to the buffer.
- */
/*ARGSUSED*/
static void
ModifyWord_Head(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
@@ -1381,10 +1388,6 @@ ModifyWord_Head(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
SepBuf_AddSubstring(buf, Substring_Dirname(word));
}
-/*
- * Callback for ModifyWords to implement the :T modifier.
- * Add the basename of the given word to the buffer.
- */
/*ARGSUSED*/
static void
ModifyWord_Tail(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
@@ -1392,63 +1395,26 @@ ModifyWord_Tail(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
SepBuf_AddSubstring(buf, Substring_Basename(word));
}
-/*
- * Callback for ModifyWords to implement the :E modifier.
- * Add the filename suffix of the given word to the buffer, if it exists.
- */
/*ARGSUSED*/
static void
ModifyWord_Suffix(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
{
- const char *lastDot = Substring_LastIndex(word, '.');
+ const char *lastDot = Substring_FindLast(word, '.');
if (lastDot != NULL)
- SepBuf_AddBytesBetween(buf, lastDot + 1, word.end);
+ SepBuf_AddRange(buf, lastDot + 1, word.end);
}
-/*
- * Callback for ModifyWords to implement the :R modifier.
- * Add the filename without extension of the given word to the buffer.
- */
/*ARGSUSED*/
static void
ModifyWord_Root(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
{
const char *lastDot, *end;
- lastDot = Substring_LastIndex(word, '.');
+ lastDot = Substring_FindLast(word, '.');
end = lastDot != NULL ? lastDot : word.end;
- SepBuf_AddBytesBetween(buf, word.start, end);
-}
-
-/*
- * Callback for ModifyWords to implement the :M modifier.
- * Place the word in the buffer if it matches the given pattern.
- */
-static void
-ModifyWord_Match(Substring word, SepBuf *buf, void *data)
-{
- const char *pattern = data;
-
- assert(word.end[0] == '\0'); /* assume null-terminated word */
- if (Str_Match(word.start, pattern))
- SepBuf_AddSubstring(buf, word);
-}
-
-/*
- * Callback for ModifyWords to implement the :N modifier.
- * Place the word in the buffer if it doesn't match the given pattern.
- */
-static void
-ModifyWord_NoMatch(Substring word, SepBuf *buf, void *data)
-{
- const char *pattern = data;
-
- assert(word.end[0] == '\0'); /* assume null-terminated word */
- if (!Str_Match(word.start, pattern))
- SepBuf_AddSubstring(buf, word);
+ SepBuf_AddRange(buf, word.start, end);
}
-#ifdef SYSVVARSUB
struct ModifyWord_SysVSubstArgs {
GNode *scope;
Substring lhsPrefix;
@@ -1457,55 +1423,37 @@ struct ModifyWord_SysVSubstArgs {
const char *rhs;
};
-/* Callback for ModifyWords to implement the :%.from=%.to modifier. */
static void
ModifyWord_SysVSubst(Substring word, SepBuf *buf, void *data)
{
const struct ModifyWord_SysVSubstArgs *args = data;
FStr rhs;
- char *rhsExp;
const char *percent;
if (Substring_IsEmpty(word))
return;
- if (!Substring_HasPrefix(word, args->lhsPrefix))
- goto no_match;
- if (!Substring_HasSuffix(word, args->lhsSuffix))
- goto no_match;
+ if (!Substring_HasPrefix(word, args->lhsPrefix) ||
+ !Substring_HasSuffix(word, args->lhsSuffix)) {
+ SepBuf_AddSubstring(buf, word);
+ return;
+ }
rhs = FStr_InitRefer(args->rhs);
- if (strchr(rhs.str, '$') != NULL) {
- (void)Var_Subst(args->rhs, args->scope, VARE_WANTRES, &rhsExp);
- /* TODO: handle errors */
- rhs = FStr_InitOwn(rhsExp);
- }
+ Var_Expand(&rhs, args->scope, VARE_WANTRES);
percent = args->lhsPercent ? strchr(rhs.str, '%') : NULL;
if (percent != NULL)
- SepBuf_AddBytesBetween(buf, rhs.str, percent);
+ SepBuf_AddRange(buf, rhs.str, percent);
if (percent != NULL || !args->lhsPercent)
- SepBuf_AddBytesBetween(buf,
+ SepBuf_AddRange(buf,
word.start + Substring_Length(args->lhsPrefix),
word.end - Substring_Length(args->lhsSuffix));
SepBuf_AddStr(buf, percent != NULL ? percent + 1 : rhs.str);
FStr_Done(&rhs);
- return;
-
-no_match:
- SepBuf_AddSubstring(buf, word);
}
-#endif
-
-
-struct ModifyWord_SubstArgs {
- Substring lhs;
- Substring rhs;
- PatternFlags pflags;
- bool matched;
-};
static const char *
Substring_Find(Substring haystack, Substring needle)
@@ -1520,19 +1468,21 @@ Substring_Find(Substring haystack, Substring needle)
return NULL;
}
-/*
- * Callback for ModifyWords to implement the :S,from,to, modifier.
- * Perform a string substitution on the given word.
- */
+struct ModifyWord_SubstArgs {
+ Substring lhs;
+ Substring rhs;
+ PatternFlags pflags;
+ bool matched;
+};
+
static void
ModifyWord_Subst(Substring word, SepBuf *buf, void *data)
{
struct ModifyWord_SubstArgs *args = data;
size_t wordLen, lhsLen;
- const char *wordEnd, *match;
+ const char *match;
wordLen = Substring_Length(word);
- wordEnd = word.end;
if (args->pflags.subOnce && args->matched)
goto nosub;
@@ -1547,7 +1497,7 @@ ModifyWord_Subst(Substring word, SepBuf *buf, void *data)
/* :S,^prefix,replacement, or :S,^whole$,replacement, */
SepBuf_AddSubstring(buf, args->rhs);
- SepBuf_AddBytesBetween(buf, word.start + lhsLen, wordEnd);
+ SepBuf_AddRange(buf, word.start + lhsLen, word.end);
args->matched = true;
return;
}
@@ -1555,11 +1505,11 @@ ModifyWord_Subst(Substring word, SepBuf *buf, void *data)
if (args->pflags.anchorEnd) {
if (wordLen < lhsLen)
goto nosub;
- if (memcmp(wordEnd - lhsLen, args->lhs.start, lhsLen) != 0)
+ if (memcmp(word.end - lhsLen, args->lhs.start, lhsLen) != 0)
goto nosub;
/* :S,suffix$,replacement, */
- SepBuf_AddBytesBetween(buf, word.start, wordEnd - lhsLen);
+ SepBuf_AddRange(buf, word.start, word.end - lhsLen);
SepBuf_AddSubstring(buf, args->rhs);
args->matched = true;
return;
@@ -1570,7 +1520,7 @@ ModifyWord_Subst(Substring word, SepBuf *buf, void *data)
/* unanchored case, may match more than once */
while ((match = Substring_Find(word, args->lhs)) != NULL) {
- SepBuf_AddBytesBetween(buf, word.start, match);
+ SepBuf_AddRange(buf, word.start, match);
SepBuf_AddSubstring(buf, args->rhs);
args->matched = true;
word.start = match + lhsLen;
@@ -1581,10 +1531,10 @@ nosub:
SepBuf_AddSubstring(buf, word);
}
-#ifndef NO_REGEX
+#ifdef HAVE_REGEX_H
/* Print the error caused by a regcomp or regexec call. */
static void
-VarREError(int reerr, const regex_t *pat, const char *str)
+RegexError(int reerr, const regex_t *pat, const char *str)
{
size_t errlen = regerror(reerr, pat, NULL, 0);
char *errbuf = bmake_malloc(errlen);
@@ -1593,109 +1543,104 @@ VarREError(int reerr, const regex_t *pat, const char *str)
free(errbuf);
}
+/* In the modifier ':C', replace a backreference from \0 to \9. */
+static void
+RegexReplaceBackref(char ref, SepBuf *buf, const char *wp,
+ const regmatch_t *m, size_t nsub)
+{
+ unsigned int n = (unsigned)ref - '0';
+
+ 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_AddRange(buf,
+ wp + (size_t)m[n].rm_so,
+ wp + (size_t)m[n].rm_eo);
+ }
+}
+
+/*
+ * The regular expression matches the word; now add the replacement to the
+ * buffer, taking back-references from 'wp'.
+ */
+static void
+RegexReplace(Substring replace, SepBuf *buf, const char *wp,
+ const regmatch_t *m, size_t nsub)
+{
+ const char *rp;
+
+ for (rp = replace.start; rp != replace.end; rp++) {
+ if (*rp == '\\' && rp + 1 != replace.end &&
+ (rp[1] == '&' || rp[1] == '\\'))
+ SepBuf_AddBytes(buf, ++rp, 1);
+ else if (*rp == '\\' && rp + 1 != replace.end &&
+ ch_isdigit(rp[1]))
+ RegexReplaceBackref(*++rp, buf, wp, m, nsub);
+ else if (*rp == '&') {
+ SepBuf_AddRange(buf,
+ wp + (size_t)m[0].rm_so,
+ wp + (size_t)m[0].rm_eo);
+ } else
+ SepBuf_AddBytes(buf, rp, 1);
+ }
+}
+
struct ModifyWord_SubstRegexArgs {
regex_t re;
size_t nsub;
- const char *replace;
+ Substring replace;
PatternFlags pflags;
bool matched;
};
-/*
- * Callback for ModifyWords to implement the :C/from/to/ modifier.
- * Perform a regex substitution on the given word.
- */
static void
ModifyWord_SubstRegex(Substring word, SepBuf *buf, void *data)
{
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)
+ RegexError(xrv, &args->re, "Unexpected regex error");
+no_match:
+ SepBuf_AddRange(buf, wp, word.end);
+ 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 && *wp != '\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
-
struct ModifyWord_LoopArgs {
GNode *scope;
const char *var; /* name of the temporary variable */
@@ -1703,7 +1648,6 @@ struct ModifyWord_LoopArgs {
VarEvalMode emode;
};
-/* Callback for ModifyWords to implement the :@var@...@ modifier of ODE make. */
static void
ModifyWord_Loop(Substring word, SepBuf *buf, void *data)
{
@@ -1717,13 +1661,11 @@ ModifyWord_Loop(Substring word, SepBuf *buf, void *data)
assert(word.end[0] == '\0'); /* assume null-terminated word */
Var_SetWithFlags(args->scope, args->var, word.start,
VAR_SET_NO_EXPORT);
- (void)Var_Subst(args->body, args->scope, args->emode, &s);
+ s = Var_Subst(args->body, args->scope, args->emode);
/* TODO: handle errors */
- assert(word.end[0] == '\0'); /* assume null-terminated word */
- DEBUG4(VAR, "ModifyWord_Loop: "
- "in \"%s\", replace \"%s\" with \"%s\" to \"%s\"\n",
- word.start, args->var, args->body, s);
+ DEBUG2(VAR, "ModifyWord_Loop: expand \"%s\" to \"%s\"\n",
+ args->body, s);
if (s[0] == '\n' || Buf_EndsWith(&buf->buf, '\n'))
buf->needSep = false;
@@ -1740,7 +1682,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,29 +1690,22 @@ 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);
}
- /*
- * Now sanitize the given range. If first or last are negative,
- * convert them to the positive equivalents (-1 gets converted to len,
- * -2 gets converted to (len - 1), etc.).
- */
+ /* Convert -1 to len, -2 to (len - 1), etc. */
len = (int)words.len;
if (first < 0)
first += len + 1;
if (last < 0)
last += len + 1;
- /* We avoid scanning more of the list than we need to. */
if (first > last) {
start = (first > len ? len : first) - 1;
end = last < 1 ? 0 : last - 1;
@@ -1782,20 +1717,16 @@ 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);
}
-/*
- * Callback for ModifyWords to implement the :tA modifier.
- * Replace each word with the result of realpath() if successful.
- */
/*ARGSUSED*/
static void
ModifyWord_Realpath(Substring word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
@@ -1814,7 +1745,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;
@@ -1823,13 +1754,15 @@ Words_JoinFree(Words words)
for (i = 0; i < words.len; i++) {
if (i != 0) {
- /* XXX: Use ch->sep instead of ' ', for consistency. */
+ /*
+ * XXX: Use ch->sep instead of ' ', for consistency.
+ */
Buf_AddByte(&buf, ' ');
}
- Buf_AddStr(&buf, words.words[i]);
+ Buf_AddRange(&buf, words.words[i].start, words.words[i].end);
}
- Words_Free(words);
+ SubstringWords_Free(words);
return Buf_DoneData(&buf);
}
@@ -1840,7 +1773,7 @@ Words_JoinFree(Words words)
* If quoteDollar is set, also quote and double any '$' characters.
*/
static void
-VarQuote(const char *str, bool quoteDollar, LazyBuf *buf)
+QuoteShell(const char *str, bool quoteDollar, LazyBuf *buf)
{
const char *p;
@@ -1853,7 +1786,7 @@ VarQuote(const char *str, bool quoteDollar, LazyBuf *buf)
LazyBuf_AddStr(buf, newline);
continue;
}
- if (ch_isspace(*p) || is_shell_metachar((unsigned char)*p))
+ if (ch_isspace(*p) || ch_is_shell_meta(*p))
LazyBuf_Add(buf, '\\');
LazyBuf_Add(buf, *p);
if (quoteDollar && *p == '$')
@@ -1866,7 +1799,7 @@ VarQuote(const char *str, bool quoteDollar, LazyBuf *buf)
* algorithm. Output is encoded as 8 hex digits, in Little Endian order.
*/
static char *
-VarHash(const char *str)
+Hash(const char *str)
{
static const char hexdigits[16] = "0123456789abcdef";
const unsigned char *ustr = (const unsigned char *)str;
@@ -1926,15 +1859,28 @@ VarHash(const char *str)
}
static char *
-VarStrftime(const char *fmt, bool zulu, time_t tim)
+FormatTime(const char *fmt, time_t t, bool gmt)
{
char buf[BUFSIZ];
- if (tim == 0)
- time(&tim);
+ if (t == 0)
+ time(&t);
if (*fmt == '\0')
fmt = "%c";
- strftime(buf, sizeof buf, fmt, zulu ? gmtime(&tim) : localtime(&tim));
+ if (gmt && strchr(fmt, 's') != NULL) {
+ /* strftime "%s" only works with localtime, not with gmtime. */
+ const char *prev_tz_env = getenv("TZ");
+ char *prev_tz = prev_tz_env != NULL
+ ? bmake_strdup(prev_tz_env) : NULL;
+ setenv("TZ", "UTC", 1);
+ strftime(buf, sizeof buf, fmt, localtime(&t));
+ if (prev_tz != NULL) {
+ setenv("TZ", prev_tz, 1);
+ free(prev_tz);
+ } else
+ unsetenv("TZ");
+ } else
+ strftime(buf, sizeof buf, fmt, (gmt ? gmtime : localtime)(&t));
buf[sizeof buf - 1] = '\0';
return bmake_strdup(buf);
@@ -1956,78 +1902,71 @@ VarStrftime(const char *fmt, bool zulu, time_t tim)
* XXX: As of 2020-11-15, some modifiers such as :S, :C, :P, :L do not
* need to be followed by a ':' or endc; this was an unintended mistake.
*
- * If parsing fails because of a missing delimiter (as in the :S, :C or :@
- * modifiers), return AMR_CLEANUP.
+ * If parsing fails because of a missing delimiter after a modifier part (as
+ * in the :S, :C or :@ modifiers), return AMR_CLEANUP.
*
* If parsing fails because the modifier is unknown, return AMR_UNKNOWN to
- * try the SysV modifier ${VAR:from=to} as fallback. This should only be
+ * try the SysV modifier ':from=to' as fallback. This should only be
* done as long as there have been no side effects from evaluating nested
* variables, to avoid evaluating them more than once. In this case, the
* parsing position may or may not be updated. (XXX: Why not? The original
* parsing position is well-known in ApplyModifiers.)
*
* If parsing fails and the SysV modifier ${VAR:from=to} should not be used
- * as a fallback, either issue an error message using Error or Parse_Error
- * and then return AMR_CLEANUP, or return AMR_BAD for the default error
- * message. Both of these return values will stop processing the variable
- * expression. (XXX: As of 2020-08-23, evaluation of the whole string
- * continues nevertheless after skipping a few bytes, which essentially is
- * undefined behavior. Not in the sense of C, but still the resulting string
- * is garbage.)
+ * as a fallback, issue an error message using Parse_Error (preferred over
+ * Error) and then return AMR_CLEANUP, which stops processing the expression.
+ * (XXX: As of 2020-08-23, evaluation of the string continues nevertheless
+ * after skipping a few bytes, which results in garbage.)
*
* Evaluating the modifier
*
* After parsing, the modifier is evaluated. The side effects from evaluating
- * nested variable expressions in the modifier text often already happen
+ * nested expressions in the modifier text often already happen
* during parsing though. For most modifiers this doesn't matter since their
- * only noticeable effect is that 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
- * expression from ch->expr->value, or the variable name from ch->var->name
- * and stores the result back in expr->value via Expr_SetValueOwn or
+ * Evaluating the modifier usually takes the current value of the
+ * expression from ch->expr->value, or the variable name from ch->var->name,
+ * and stores the result back in ch->expr->value via Expr_SetValueOwn or
* Expr_SetValueRefer.
*
- * If evaluating fails (as of 2020-08-23), an error message is printed using
- * Error. This function has no side-effects, it really just prints the error
- * message. Processing the expression continues as if everything were ok.
- * XXX: This should be fixed by adding proper error handling to Var_Subst,
- * Var_Parse, ApplyModifiers and ModifyWords.
- *
- * Housekeeping
+ * If evaluating fails, the fallback error message "Bad modifier" is printed
+ * using Error. This function has no side effects, it really just prints the
+ * error message, continuing as if nothing had happened. TODO: This should be
+ * fixed by adding proper error handling to Var_Subst, Var_Parse,
+ * ApplyModifiers and ModifyWords.
*
* Some modifiers such as :D and :U turn undefined expressions into defined
- * expressions (see Expr_Define).
- *
- * Some modifiers need to free some memory.
+ * expressions using Expr_Define.
*/
typedef enum ExprDefined {
- /* The variable expression is based on a regular, defined variable. */
+ /* The expression is based on a regular, defined variable. */
DEF_REGULAR,
- /* The variable expression is based on an undefined variable. */
+ /* The expression is based on an undefined variable. */
DEF_UNDEF,
/*
- * The variable expression started as an undefined expression, but one
+ * The expression started as an undefined expression, but one
* of the modifiers (such as ':D' or ':U') has turned the expression
* from undefined to defined.
*/
DEF_DEFINED
} ExprDefined;
-static const char *const ExprDefined_Name[] = {
+static const char ExprDefined_Name[][10] = {
"regular",
"undefined",
"defined"
};
#if __STDC_VERSION__ >= 199901L
-#define const_member const
+#define const_member const
#else
-#define const_member /* no const possible */
+#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,14 +1991,11 @@ 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.
*
- * It may or may not have been intended that 'defined' has scope Expr while
- * 'sep' and 'oneBigWord' have smaller scope.
- *
* See varmod-indirect.mk.
*/
typedef struct ModChain {
@@ -2068,10 +2004,10 @@ typedef struct ModChain {
char const_member startc;
/* '\0' or '}' or ')' */
char const_member endc;
- /* Word separator in expansions (see the :ts modifier). */
+ /* Separator when joining words (see the :ts modifier). */
char sep;
/*
- * True if some modifiers that otherwise split the variable value
+ * Whether some modifiers that otherwise split the variable value
* into words, like :S and :C, treat the variable value as a single
* big word, possibly containing spaces.
*/
@@ -2085,6 +2021,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)
{
@@ -2120,7 +2068,7 @@ ModChain_ShouldEval(const ModChain *ch)
typedef enum ApplyModifierResult {
/* Continue parsing */
AMR_OK,
- /* Not a match, try other modifiers as well. */
+ /* Not a match, try the ':from=to' modifier as well. */
AMR_UNKNOWN,
/* Error out with "Bad modifier" message. */
AMR_BAD,
@@ -2143,120 +2091,119 @@ 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 = Var_Parse(&p, ch->expr->scope,
+ VarEvalMode_WithoutKeepDollar(emode));
+ /* TODO: handle errors */
+ if (VarEvalMode_ShouldEval(emode))
+ LazyBuf_AddStr(part, nested_val.str);
+ else
+ LazyBuf_AddSubstring(part, Substring_Init(*pp, p));
+ FStr_Done(&nested_val);
+ *pp = p;
+}
+
+/*
+ * In a part of a modifier, parse some text that looks like a subexpression.
+ * If the text starts with '$(', any '(' and ')' must be balanced.
+ * If the text starts with '${', any '{' and '}' must be balanced.
+ * If the text starts with '$', that '$' is copied verbatim, it is not parsed
+ * as a short-name expression.
+ */
+static void
+ParseModifierPartBalanced(const char **pp, LazyBuf *part)
+{
+ const char *p = *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_AddSubstring(part, Substring_Init(*pp, p));
+ *pp = p;
+ } else {
+ LazyBuf_Add(part, *p);
+ *pp = p + 1;
+ }
+}
+
/* See ParseModifierPart for the documentation. */
-static VarParseResult
+static bool
ParseModifierPartSubst(
const char **pp,
+ /* If true, parse up to but excluding the next ':' or ch->endc. */
+ bool whole,
char delim,
VarEvalMode emode,
ModChain *ch,
LazyBuf *part,
- /* For the first part of the modifier ':S', set anchorEnd if the last
- * character of the pattern is a $. */
+ /*
+ * For the first part of the ':S' modifier, set anchorEnd if the last
+ * character of the pattern is a $.
+ */
PatternFlags *out_pflags,
- /* For the second part of the :S modifier, allow ampersands to be
- * escaped and replace unescaped ampersands with subst->lhs. */
+ /*
+ * For the second part of the ':S' modifier, allow ampersands to be
+ * escaped and replace unescaped ampersands with subst->lhs.
+ */
struct ModifyWord_SubstArgs *subst
)
{
const char *p;
+ char end1, end2;
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;
-
+ end1 = whole ? ':' : delim;
+ end2 = whole ? ch->endc : delim;
+ while (*p != '\0' && *p != end1 && *p != end2) {
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 (emode == VARE_PARSE_BALANCED)
+ ParseModifierPartBalanced(&p, part);
+ else
+ ParseModifierPartExpr(&p, part, ch, emode);
}
- if (*p != delim) {
- *pp = p;
+ *pp = p;
+ if (*p != end1 && *p != end2) {
Error("Unfinished modifier for \"%s\" ('%c' missing)",
- ch->expr->name, delim);
+ ch->expr->name, end2);
LazyBuf_Done(part);
- return VPR_ERR;
+ return false;
}
-
- *pp = p + 1;
+ if (!whole)
+ (*pp)++;
{
Substring sub = LazyBuf_Get(part);
@@ -2264,7 +2211,7 @@ ParseModifierPartSubst(
(int)Substring_Length(sub), sub.start);
}
- return VPR_OK;
+ return true;
}
/*
@@ -2273,29 +2220,30 @@ ParseModifierPartSubst(
* including the next unescaped delimiter. The delimiter, as well as the
* backslash or the dollar, can be escaped with a backslash.
*
- * Return VPR_OK if parsing succeeded, together with the parsed (and possibly
+ * Return true if parsing succeeded, together with the parsed (and possibly
* expanded) part. In that case, pp points right after the delimiter. The
* delimiter is not included in the part though.
*/
-static VarParseResult
+static bool
ParseModifierPart(
/* The parsing position, updated upon return */
const char **pp,
/* Parsing stops at this delimiter */
char delim,
- /* Mode for evaluating nested variables. */
+ /* Mode for evaluating nested expressions. */
VarEvalMode emode,
ModChain *ch,
LazyBuf *part
)
{
- return ParseModifierPartSubst(pp, delim, emode, ch, part, NULL, NULL);
+ return ParseModifierPartSubst(pp, false, delim, emode, ch, part,
+ NULL, NULL);
}
MAKE_INLINE bool
IsDelimiter(char c, const ModChain *ch)
{
- return c == ':' || c == ch->endc;
+ return c == ':' || c == ch->endc || c == '\0';
}
/* Test whether mod starts with modname, followed by a delimiter. */
@@ -2388,12 +2336,15 @@ 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;
Substring word;
+ if (!ModChain_ShouldEval(ch))
+ return;
+
if (oneBigWord) {
SepBuf_Init(&result, ch->sep);
/* XXX: performance: Substring_InitStr calls strlen */
@@ -2404,8 +2355,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++) {
@@ -2427,28 +2378,25 @@ ApplyModifier_Loop(const char **pp, ModChain *ch)
Expr *expr = ch->expr;
struct ModifyWord_LoopArgs args;
char prev_sep;
- VarParseResult res;
LazyBuf tvarBuf, strBuf;
FStr tvar, str;
args.scope = expr->scope;
(*pp)++; /* Skip the first '@' */
- res = ParseModifierPart(pp, '@', VARE_PARSE_ONLY, ch, &tvarBuf);
- if (res != VPR_OK)
+ if (!ParseModifierPart(pp, '@', VARE_PARSE_ONLY, ch, &tvarBuf))
return AMR_CLEANUP;
tvar = LazyBuf_DoneGet(&tvarBuf);
args.var = tvar.str;
if (strchr(args.var, '$') != NULL) {
Parse_Error(PARSE_FATAL,
- "In the :@ modifier of \"%s\", the variable name \"%s\" "
- "must not contain a dollar.",
- expr->name, args.var);
+ "In the :@ modifier, the variable name \"%s\" "
+ "must not contain a dollar",
+ args.var);
return AMR_CLEANUP;
}
- res = ParseModifierPart(pp, '@', VARE_PARSE_ONLY, ch, &strBuf);
- if (res != VPR_OK)
+ if (!ParseModifierPart(pp, '@', VARE_PARSE_BALANCED, ch, &strBuf))
return AMR_CLEANUP;
str = LazyBuf_DoneGet(&strBuf);
args.body = str.str;
@@ -2470,62 +2418,66 @@ done:
return AMR_OK;
}
-/* :Ddefined or :Uundefined */
-static ApplyModifierResult
-ApplyModifier_Defined(const char **pp, ModChain *ch)
+static void
+ParseModifier_Defined(const char **pp, ModChain *ch, bool shouldEval,
+ LazyBuf *buf)
{
- Expr *expr = ch->expr;
- LazyBuf buf;
const char *p;
- VarEvalMode emode = VARE_PARSE_ONLY;
- if (Expr_ShouldEval(expr))
- if ((**pp == 'D') == (expr->defined == DEF_REGULAR))
- emode = expr->emode;
-
p = *pp + 1;
- LazyBuf_Init(&buf, p);
- while (!IsDelimiter(*p, ch) && *p != '\0') {
+ LazyBuf_Init(buf, p);
+ while (!IsDelimiter(*p, ch)) {
- /* XXX: This code is similar to the one in Var_Parse.
- * See if the code can be merged.
- * See also ApplyModifier_Match and ParseModifierPart. */
+ /*
+ * XXX: This code is similar to the one in Var_Parse. See if
+ * the code can be merged. See also ParseModifier_Match and
+ * ParseModifierPart.
+ */
- /* Escaped delimiter or other special character */
- /* See Buf_AddEscaped in for.c. */
+ /* See Buf_AddEscaped in for.c for the counterpart. */
if (*p == '\\') {
char c = p[1];
- if (IsDelimiter(c, ch) || c == '$' || c == '\\') {
- LazyBuf_Add(&buf, c);
+ if ((IsDelimiter(c, ch) && c != '\0') ||
+ c == '$' || c == '\\') {
+ if (shouldEval)
+ LazyBuf_Add(buf, c);
p += 2;
continue;
}
}
- /* Nested variable expression */
if (*p == '$') {
- FStr nested_val;
-
- (void)Var_Parse(&p, expr->scope, emode, &nested_val);
+ FStr val = Var_Parse(&p, ch->expr->scope,
+ shouldEval ? ch->expr->emode : VARE_PARSE_ONLY);
/* TODO: handle errors */
- if (Expr_ShouldEval(expr))
- LazyBuf_AddStr(&buf, nested_val.str);
- FStr_Done(&nested_val);
+ if (shouldEval)
+ LazyBuf_AddStr(buf, val.str);
+ FStr_Done(&val);
continue;
}
- /* Ordinary text */
- LazyBuf_Add(&buf, *p);
+ if (shouldEval)
+ LazyBuf_Add(buf, *p);
p++;
}
*pp = p;
+}
- Expr_Define(expr);
+/* :Ddefined or :Uundefined */
+static ApplyModifierResult
+ApplyModifier_Defined(const char **pp, ModChain *ch)
+{
+ Expr *expr = ch->expr;
+ LazyBuf buf;
+ bool shouldEval =
+ Expr_ShouldEval(expr) &&
+ (**pp == 'D') == (expr->defined == DEF_REGULAR);
- if (VarEvalMode_ShouldEval(emode))
+ ParseModifier_Defined(pp, ch, shouldEval, &buf);
+
+ Expr_Define(expr);
+ if (shouldEval)
Expr_SetValue(expr, Substring_Str(LazyBuf_Get(&buf)));
- else
- LazyBuf_Done(&buf);
return AMR_OK;
}
@@ -2565,62 +2517,48 @@ TryParseTime(const char **pp, time_t *out_time)
return true;
}
-/* :gmtime */
+/* :gmtime and :localtime */
static ApplyModifierResult
-ApplyModifier_Gmtime(const char **pp, ModChain *ch)
+ApplyModifier_Time(const char **pp, ModChain *ch)
{
- time_t utc;
-
+ Expr *expr;
+ time_t t;
+ const char *args;
const char *mod = *pp;
- if (!ModMatchEq(mod, "gmtime", ch))
- return AMR_UNKNOWN;
-
- if (mod[6] == '=') {
- const char *p = mod + 7;
- if (!TryParseTime(&p, &utc)) {
- Parse_Error(PARSE_FATAL,
- "Invalid time value: %s", mod + 7);
- return AMR_CLEANUP;
- }
- *pp = p;
- } else {
- utc = 0;
- *pp = mod + 6;
- }
+ bool gmt = mod[0] == 'g';
- if (ModChain_ShouldEval(ch))
- Expr_SetValueOwn(ch->expr,
- VarStrftime(ch->expr->value.str, true, utc));
-
- return AMR_OK;
-}
-
-/* :localtime */
-static ApplyModifierResult
-ApplyModifier_Localtime(const char **pp, ModChain *ch)
-{
- time_t utc;
-
- const char *mod = *pp;
- if (!ModMatchEq(mod, "localtime", ch))
+ if (!ModMatchEq(mod, gmt ? "gmtime" : "localtime", ch))
return AMR_UNKNOWN;
-
- if (mod[9] == '=') {
- const char *p = mod + 10;
- if (!TryParseTime(&p, &utc)) {
- Parse_Error(PARSE_FATAL,
- "Invalid time value: %s", mod + 10);
+ args = mod + (gmt ? 6 : 9);
+
+ if (args[0] == '=') {
+ const char *p = args + 1;
+ LazyBuf buf;
+ FStr arg;
+ if (!ParseModifierPartSubst(&p, true, '\0', ch->expr->emode,
+ ch, &buf, NULL, NULL))
return AMR_CLEANUP;
- }
+ arg = LazyBuf_DoneGet(&buf);
+ if (ModChain_ShouldEval(ch)) {
+ const char *arg_p = arg.str;
+ if (!TryParseTime(&arg_p, &t) || *arg_p != '\0') {
+ Parse_Error(PARSE_FATAL,
+ "Invalid time value \"%s\"", arg.str);
+ FStr_Done(&arg);
+ return AMR_CLEANUP;
+ }
+ } else
+ t = 0;
+ FStr_Done(&arg);
*pp = p;
} else {
- utc = 0;
- *pp = mod + 9;
+ t = 0;
+ *pp = args;
}
- 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, FormatTime(Expr_Str(expr), t, gmt));
return AMR_OK;
}
@@ -2634,7 +2572,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, Hash(Expr_Str(ch->expr)));
return AMR_OK;
}
@@ -2649,17 +2587,17 @@ ApplyModifier_Path(const char **pp, ModChain *ch)
(*pp)++;
- if (!ModChain_ShouldEval(ch))
+ if (!Expr_ShouldEval(expr))
return AMR_OK;
Expr_Define(expr);
gn = Targ_FindNode(expr->name);
- if (gn == NULL || gn->type & OP_NOPATH) {
+ if (gn == NULL || gn->type & OP_NOPATH)
path = NULL;
- } else if (gn->path != NULL) {
+ else if (gn->path != NULL)
path = bmake_strdup(gn->path);
- } else {
+ else {
SearchPath *searchPath = Suff_FindPath(gn);
path = Dir_FindFile(expr->name, searchPath);
}
@@ -2675,25 +2613,26 @@ static ApplyModifierResult
ApplyModifier_ShellCommand(const char **pp, ModChain *ch)
{
Expr *expr = ch->expr;
- const char *errfmt;
- VarParseResult res;
LazyBuf cmdBuf;
FStr cmd;
(*pp)++;
- res = ParseModifierPart(pp, '!', expr->emode, ch, &cmdBuf);
- if (res != VPR_OK)
+ if (!ParseModifierPart(pp, '!', expr->emode, ch, &cmdBuf))
return AMR_CLEANUP;
cmd = LazyBuf_DoneGet(&cmdBuf);
-
- errfmt = NULL;
- if (Expr_ShouldEval(expr))
- Expr_SetValueOwn(expr, Cmd_Exec(cmd.str, &errfmt));
- else
+ if (Expr_ShouldEval(expr)) {
+ char *output, *error;
+ output = Cmd_Exec(cmd.str, &error);
+ Expr_SetValueOwn(expr, output);
+ if (error != NULL) {
+ /* XXX: why still return AMR_OK? */
+ Error("%s", error);
+ free(error);
+ }
+ } else
Expr_SetValueRefer(expr, "");
- if (errfmt != NULL)
- Error(errfmt, cmd.str); /* XXX: why still return AMR_OK? */
+
FStr_Done(&cmd);
Expr_Define(expr);
@@ -2733,16 +2672,18 @@ 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);
for (i = 0; i < n; i++) {
if (i != 0) {
- /* XXX: Use ch->sep instead of ' ', for consistency. */
+ /*
+ * XXX: Use ch->sep instead of ' ', for consistency.
+ */
Buf_AddByte(&buf, ' ');
}
Buf_AddInt(&buf, 1 + (int)i);
@@ -2753,9 +2694,8 @@ ApplyModifier_Range(const char **pp, ModChain *ch)
}
/* Parse a ':M' or ':N' modifier. */
-static void
-ParseModifier_Match(const char **pp, const ModChain *ch,
- char **out_pattern)
+static char *
+ParseModifier_Match(const char **pp, const ModChain *ch)
{
const char *mod = *pp;
Expr *expr = ch->expr;
@@ -2774,10 +2714,10 @@ ParseModifier_Match(const char **pp, const ModChain *ch,
* See if the code can be merged.
* See also ApplyModifier_Defined.
*/
- int nest = 0;
+ int depth = 0;
const char *p;
- for (p = mod + 1; *p != '\0' && !(*p == ':' && nest == 0); p++) {
- if (*p == '\\' &&
+ for (p = mod + 1; *p != '\0' && !(*p == ':' && depth == 0); p++) {
+ if (*p == '\\' && p[1] != '\0' &&
(IsDelimiter(p[1], ch) || p[1] == ch->startc)) {
if (!needSubst)
copy = true;
@@ -2787,10 +2727,10 @@ ParseModifier_Match(const char **pp, const ModChain *ch,
if (*p == '$')
needSubst = true;
if (*p == '(' || *p == '{')
- nest++;
+ depth++;
if (*p == ')' || *p == '}') {
- nest--;
- if (nest < 0)
+ depth--;
+ if (depth < 0)
break;
}
}
@@ -2819,35 +2759,134 @@ ParseModifier_Match(const char **pp, const ModChain *ch,
if (needSubst) {
char *old_pattern = pattern;
- (void)Var_Subst(pattern, expr->scope, expr->emode, &pattern);
+ /*
+ * XXX: Contrary to ParseModifierPart, a dollar in a ':M' or
+ * ':N' modifier must be escaped as '$$', not as '\$'.
+ */
+ pattern = Var_Subst(pattern, expr->scope, expr->emode);
/* TODO: handle errors */
free(old_pattern);
}
DEBUG2(VAR, "Pattern for ':%c' is \"%s\"\n", mod[0], pattern);
- *out_pattern = pattern;
+ return pattern;
+}
+
+struct ModifyWord_MatchArgs {
+ const char *pattern;
+ bool neg;
+ bool error_reported;
+};
+
+static void
+ModifyWord_Match(Substring word, SepBuf *buf, void *data)
+{
+ struct ModifyWord_MatchArgs *args = data;
+ StrMatchResult res;
+ assert(word.end[0] == '\0'); /* assume null-terminated word */
+ res = Str_Match(word.start, args->pattern);
+ if (res.error != NULL && !args->error_reported) {
+ args->error_reported = true;
+ Parse_Error(PARSE_WARNING,
+ "%s in pattern '%s' of modifier '%s'",
+ res.error, args->pattern, args->neg ? ":N" : ":M");
+ }
+ if (res.matched != args->neg)
+ SepBuf_AddSubstring(buf, word);
}
/* :Mpattern or :Npattern */
static ApplyModifierResult
ApplyModifier_Match(const char **pp, ModChain *ch)
{
- const char mod = **pp;
+ char mod = **pp;
char *pattern;
- ParseModifier_Match(pp, ch, &pattern);
+ pattern = ParseModifier_Match(pp, ch);
if (ModChain_ShouldEval(ch)) {
- ModifyWordProc modifyWord =
- mod == 'M' ? ModifyWord_Match : ModifyWord_NoMatch;
- ModifyWords(ch, modifyWord, pattern, ch->oneBigWord);
+ struct ModifyWord_MatchArgs args;
+ args.pattern = pattern;
+ args.neg = mod == 'N';
+ args.error_reported = false;
+ ModifyWords(ch, ModifyWord_Match, &args, ch->oneBigWord);
}
free(pattern);
return AMR_OK;
}
+struct ModifyWord_MtimeArgs {
+ bool error;
+ bool use_fallback;
+ ApplyModifierResult rc;
+ time_t fallback;
+};
+
+static void
+ModifyWord_Mtime(Substring word, SepBuf *buf, void *data)
+{
+ struct ModifyWord_MtimeArgs *args = data;
+ struct stat st;
+ char tbuf[21];
+
+ if (Substring_IsEmpty(word))
+ return;
+ assert(word.end[0] == '\0'); /* assume null-terminated word */
+ if (stat(word.start, &st) < 0) {
+ if (args->error) {
+ Parse_Error(PARSE_FATAL,
+ "Cannot determine mtime for '%s': %s",
+ word.start, strerror(errno));
+ args->rc = AMR_CLEANUP;
+ return;
+ }
+ if (args->use_fallback)
+ st.st_mtime = args->fallback;
+ else
+ time(&st.st_mtime);
+ }
+ snprintf(tbuf, sizeof(tbuf), "%u", (unsigned)st.st_mtime);
+ SepBuf_AddStr(buf, tbuf);
+}
+
+/* :mtime */
+static ApplyModifierResult
+ApplyModifier_Mtime(const char **pp, ModChain *ch)
+{
+ const char *p, *mod = *pp;
+ struct ModifyWord_MtimeArgs args;
+
+ if (!ModMatchEq(mod, "mtime", ch))
+ return AMR_UNKNOWN;
+ *pp += 5;
+ p = *pp;
+ args.error = false;
+ args.use_fallback = p[0] == '=';
+ args.rc = AMR_OK;
+ if (args.use_fallback) {
+ p++;
+ if (TryParseTime(&p, &args.fallback)) {
+ } else if (strncmp(p, "error", 5) == 0) {
+ p += 5;
+ args.error = true;
+ } else
+ goto invalid_argument;
+ if (!IsDelimiter(*p, ch))
+ goto invalid_argument;
+ *pp = p;
+ }
+ ModifyWords(ch, ModifyWord_Mtime, &args, ch->oneBigWord);
+ return args.rc;
+
+invalid_argument:
+ Parse_Error(PARSE_FATAL,
+ "Invalid argument '%.*s' for modifier ':mtime'",
+ (int)strcspn(*pp + 1, ":{}()"), *pp + 1);
+ return AMR_CLEANUP;
+}
+
static void
ParsePatternFlags(const char **pp, PatternFlags *pflags, bool *oneBigWord)
{
@@ -2876,7 +2915,6 @@ ApplyModifier_Subst(const char **pp, ModChain *ch)
{
struct ModifyWord_SubstArgs args;
bool oneBigWord;
- VarParseResult res;
LazyBuf lhsBuf, rhsBuf;
char delim = (*pp)[1];
@@ -2896,15 +2934,13 @@ ApplyModifier_Subst(const char **pp, ModChain *ch)
(*pp)++;
}
- res = ParseModifierPartSubst(pp, delim, ch->expr->emode, ch, &lhsBuf,
- &args.pflags, NULL);
- if (res != VPR_OK)
+ if (!ParseModifierPartSubst(pp,
+ false, delim, ch->expr->emode, ch, &lhsBuf, &args.pflags, NULL))
return AMR_CLEANUP;
args.lhs = LazyBuf_Get(&lhsBuf);
- res = ParseModifierPartSubst(pp, delim, ch->expr->emode, ch, &rhsBuf,
- NULL, &args);
- if (res != VPR_OK) {
+ if (!ParseModifierPartSubst(pp,
+ false, delim, ch->expr->emode, ch, &rhsBuf, NULL, &args)) {
LazyBuf_Done(&lhsBuf);
return AMR_CLEANUP;
}
@@ -2920,7 +2956,7 @@ ApplyModifier_Subst(const char **pp, ModChain *ch)
return AMR_OK;
}
-#ifndef NO_REGEX
+#ifdef HAVE_REGEX_H
/* :C,from,to, */
static ApplyModifierResult
@@ -2929,9 +2965,8 @@ ApplyModifier_Regex(const char **pp, ModChain *ch)
struct ModifyWord_SubstRegexArgs args;
bool oneBigWord;
int error;
- VarParseResult res;
LazyBuf reBuf, replaceBuf;
- FStr re, replace;
+ FStr re;
char delim = (*pp)[1];
if (delim == '\0') {
@@ -2942,34 +2977,28 @@ ApplyModifier_Regex(const char **pp, ModChain *ch)
*pp += 2;
- res = ParseModifierPart(pp, delim, ch->expr->emode, ch, &reBuf);
- if (res != VPR_OK)
+ if (!ParseModifierPart(pp, delim, ch->expr->emode, ch, &reBuf))
return AMR_CLEANUP;
re = LazyBuf_DoneGet(&reBuf);
- res = ParseModifierPart(pp, delim, ch->expr->emode, ch, &replaceBuf);
- if (res != VPR_OK) {
+ if (!ParseModifierPart(pp, delim, ch->expr->emode, ch, &replaceBuf)) {
FStr_Done(&re);
return AMR_CLEANUP;
}
- replace = LazyBuf_DoneGet(&replaceBuf);
- args.replace = replace.str;
+ args.replace = LazyBuf_Get(&replaceBuf);
args.pflags = PatternFlags_None();
args.matched = false;
oneBigWord = ch->oneBigWord;
ParsePatternFlags(pp, &args.pflags, &oneBigWord);
- if (!ModChain_ShouldEval(ch)) {
- FStr_Done(&replace);
- FStr_Done(&re);
- return AMR_OK;
- }
+ if (!ModChain_ShouldEval(ch))
+ goto done;
error = regcomp(&args.re, re.str, REG_EXTENDED);
if (error != 0) {
- VarREError(error, &args.re, "Regex compilation error");
- FStr_Done(&replace);
+ RegexError(error, &args.re, "Regex compilation error");
+ LazyBuf_Done(&replaceBuf);
FStr_Done(&re);
return AMR_CLEANUP;
}
@@ -2981,7 +3010,8 @@ ApplyModifier_Regex(const char **pp, ModChain *ch)
ModifyWords(ch, ModifyWord_SubstRegex, &args, oneBigWord);
regfree(&args.re);
- FStr_Done(&replace);
+done:
+ LazyBuf_Done(&replaceBuf);
FStr_Done(&re);
return AMR_OK;
}
@@ -3003,7 +3033,7 @@ ApplyModifier_Quote(const char **pp, ModChain *ch)
if (!ModChain_ShouldEval(ch))
return AMR_OK;
- VarQuote(ch->expr->value.str, quoteDollar, &buf);
+ QuoteShell(Expr_Str(ch->expr), quoteDollar, &buf);
if (buf.data != NULL)
Expr_SetValue(ch->expr, LazyBuf_DoneGet(&buf));
else
@@ -3026,8 +3056,8 @@ ApplyModifier_ToSep(const char **pp, ModChain *ch)
const char *sep = *pp + 2;
/*
- * Even in parse-only mode, proceed as normal since there is
- * neither any observable side effect nor a performance penalty.
+ * Even in parse-only mode, apply the side effects, since the side
+ * effects are neither observable nor is there a performance penalty.
* Checking for wantRes for every single piece of code in here
* would make the code in this function too hard to read.
*/
@@ -3046,7 +3076,7 @@ ApplyModifier_ToSep(const char **pp, ModChain *ch)
goto ok;
}
- /* ":ts<unrecognised><unrecognised>". */
+ /* ":ts<unrecognized><unrecognized>". */
if (sep[0] != '\\') {
(*pp)++; /* just for backwards compatibility */
return AMR_BAD;
@@ -3076,12 +3106,12 @@ ApplyModifier_ToSep(const char **pp, ModChain *ch)
p++;
} else if (!ch_isdigit(sep[1])) {
(*pp)++; /* just for backwards compatibility */
- return AMR_BAD; /* ":ts<backslash><unrecognised>". */
+ return AMR_BAD; /* ":ts<backslash><unrecognized>". */
}
if (!TryParseChar(&p, base, &ch->sep)) {
Parse_Error(PARSE_FATAL,
- "Invalid character number: %s", p);
+ "Invalid character number at \"%s\"", p);
return AMR_CLEANUP;
}
if (!IsDelimiter(*p, ch)) {
@@ -3100,28 +3130,20 @@ ok:
static char *
str_toupper(const char *str)
{
- char *res;
- size_t i, len;
-
- len = strlen(str);
- res = bmake_malloc(len + 1);
- for (i = 0; i < len + 1; i++)
+ size_t i, n = strlen(str) + 1;
+ char *res = bmake_malloc(n);
+ for (i = 0; i < n; i++)
res[i] = ch_toupper(str[i]);
-
return res;
}
static char *
str_tolower(const char *str)
{
- char *res;
- size_t i, len;
-
- len = strlen(str);
- res = bmake_malloc(len + 1);
- for (i = 0; i < len + 1; i++)
+ size_t i, n = strlen(str) + 1;
+ char *res = bmake_malloc(n);
+ for (i = 0; i < n; i++)
res[i] = ch_tolower(str[i]);
-
return res;
}
@@ -3133,7 +3155,7 @@ ApplyModifier_To(const char **pp, ModChain *ch)
const char *mod = *pp;
assert(mod[0] == 't');
- if (IsDelimiter(mod[1], ch) || mod[1] == '\0') {
+ if (IsDelimiter(mod[1], ch)) {
*pp = mod + 1;
return AMR_BAD; /* Found ":t<endc>" or ":t:". */
}
@@ -3141,7 +3163,7 @@ ApplyModifier_To(const char **pp, ModChain *ch)
if (mod[1] == 's')
return ApplyModifier_ToSep(pp, ch);
- if (!IsDelimiter(mod[2], ch)) { /* :t<unrecognized> */
+ if (!IsDelimiter(mod[2], ch)) { /* :t<any><any> */
*pp = mod + 1;
return AMR_BAD;
}
@@ -3154,15 +3176,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;
}
@@ -3172,7 +3194,7 @@ ApplyModifier_To(const char **pp, ModChain *ch)
return AMR_OK;
}
- /* Found ":t<unrecognised>:" or ":t<unrecognised><endc>". */
+ /* Found ":t<unrecognized>:" or ":t<unrecognized><endc>". */
*pp = mod + 1; /* XXX: unnecessary but observable */
return AMR_BAD;
}
@@ -3182,19 +3204,16 @@ static ApplyModifierResult
ApplyModifier_Words(const char **pp, ModChain *ch)
{
Expr *expr = ch->expr;
- const char *estr;
int first, last;
- VarParseResult res;
const char *p;
- LazyBuf estrBuf;
- FStr festr;
+ LazyBuf argBuf;
+ FStr arg;
(*pp)++; /* skip the '[' */
- res = ParseModifierPart(pp, ']', expr->emode, ch, &estrBuf);
- if (res != VPR_OK)
+ if (!ParseModifierPart(pp, ']', expr->emode, ch, &argBuf))
return AMR_CLEANUP;
- festr = LazyBuf_DoneGet(&estrBuf);
- estr = festr.str;
+ arg = LazyBuf_DoneGet(&argBuf);
+ p = arg.str;
if (!IsDelimiter(**pp, ch))
goto bad_modifier; /* Found junk after ']' */
@@ -3202,139 +3221,209 @@ ApplyModifier_Words(const char **pp, ModChain *ch)
if (!ModChain_ShouldEval(ch))
goto ok;
- if (estr[0] == '\0')
- goto bad_modifier; /* Found ":[]". */
+ if (p[0] == '\0')
+ goto bad_modifier; /* Found ":[]". */
- if (estr[0] == '#' && estr[1] == '\0') { /* Found ":[#]" */
- if (ch->oneBigWord) {
+ if (strcmp(p, "#") == 0) { /* Found ":[#]" */
+ if (ch->oneBigWord)
Expr_SetValueRefer(expr, "1");
- } else {
+ else {
Buffer buf;
- 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);
+ Buf_Init(&buf);
Buf_AddInt(&buf, (int)ac);
Expr_SetValueOwn(expr, Buf_DoneData(&buf));
}
goto ok;
}
- if (estr[0] == '*' && estr[1] == '\0') { /* Found ":[*]" */
+ if (strcmp(p, "*") == 0) { /* ":[*]" */
ch->oneBigWord = true;
goto ok;
}
- if (estr[0] == '@' && estr[1] == '\0') { /* Found ":[@]" */
+ if (strcmp(p, "@") == 0) { /* ":[@]" */
ch->oneBigWord = false;
goto ok;
}
- /*
- * We expect estr to contain a single integer for :[N], or two
- * integers separated by ".." for :[start..end].
- */
- p = estr;
+ /* Expect ":[N]" or ":[start..end]" */
if (!TryParseIntBase0(&p, &first))
- goto bad_modifier; /* Found junk instead of a number */
+ goto bad_modifier;
- if (p[0] == '\0') { /* Found only one integer in :[N] */
+ if (p[0] == '\0') /* ":[N]" */
last = first;
- } else if (p[0] == '.' && p[1] == '.' && p[2] != '\0') {
- /* Expecting another integer after ".." */
+ else if (strncmp(p, "..", 2) == 0) {
p += 2;
if (!TryParseIntBase0(&p, &last) || *p != '\0')
- goto bad_modifier; /* Found junk after ".." */
+ goto bad_modifier;
} else
- goto bad_modifier; /* Found junk instead of ".." */
+ goto bad_modifier;
- /*
- * Now first and last are properly filled in, but we still have to
- * check for 0 as a special case.
- */
- if (first == 0 && last == 0) {
- /* ":[0]" or perhaps ":[0..0]" */
+ if (first == 0 && last == 0) { /* ":[0]" or ":[0..0]" */
ch->oneBigWord = true;
goto ok;
}
- /* ":[0..N]" or ":[N..0]" */
- if (first == 0 || last == 0)
+ if (first == 0 || last == 0) /* ":[0..N]" or ":[N..0]" */
goto bad_modifier;
- /* Normal case: select the words described by first and last. */
Expr_SetValueOwn(expr,
- VarSelectWords(expr->value.str, first, last,
- ch->sep, ch->oneBigWord));
+ VarSelectWords(Expr_Str(expr), first, last,
+ ch->sep, ch->oneBigWord));
ok:
- FStr_Done(&festr);
+ FStr_Done(&arg);
return AMR_OK;
bad_modifier:
- FStr_Done(&festr);
+ FStr_Done(&arg);
return AMR_BAD;
}
+#if __STDC__ >= 199901L || defined(HAVE_LONG_LONG_INT)
+# define NUM_TYPE long long
+# define PARSE_NUM_TYPE strtoll
+#else
+# define NUM_TYPE long
+# define PARSE_NUM_TYPE strtol
+#endif
+
+static NUM_TYPE
+num_val(Substring s)
+{
+ NUM_TYPE val;
+ char *ep;
+
+ val = PARSE_NUM_TYPE(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
+SubNumAsc(const void *sa, const void *sb)
+{
+ 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_asc(const void *a, const void *b)
+SubNumDesc(const void *sa, const void *sb)
{
- return strcmp(*(const char *const *)a, *(const char *const *)b);
+ return SubNumAsc(sb, sa);
}
static int
-str_cmp_desc(const void *a, const void *b)
+Substring_Cmp(Substring a, Substring b)
{
- return strcmp(*(const char *const *)b, *(const char *const *)a);
+ for (; a.start < a.end && b.start < b.end; a.start++, b.start++)
+ if (a.start[0] != b.start[0])
+ return (unsigned char)a.start[0]
+ - (unsigned char)b.start[0];
+ return (int)((a.end - a.start) - (b.end - b.start));
+}
+
+static int
+SubStrAsc(const void *sa, const void *sb)
+{
+ return Substring_Cmp(*(const Substring *)sa, *(const Substring *)sb);
+}
+
+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)) {
+ cmp = SubStrAsc;
(*pp)++;
- mode = mod[1] == 'r' ? DESC : SHUFFLE;
+ } else if (IsDelimiter(mod[2], ch)) {
+ 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)) {
+ if ((mod[1] == 'n' && mod[2] == 'r') ||
+ (mod[1] == 'r' && mod[2] == 'n'))
+ cmp = SubNumDesc;
+ else
+ goto bad;
+ *pp += 3;
} else
- return AMR_BAD;
+ 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 */
@@ -3342,53 +3431,53 @@ static ApplyModifierResult
ApplyModifier_IfElse(const char **pp, ModChain *ch)
{
Expr *expr = ch->expr;
- VarParseResult res;
- LazyBuf buf;
- FStr then_expr, else_expr;
+ LazyBuf thenBuf;
+ LazyBuf elseBuf;
- bool value = false;
VarEvalMode then_emode = VARE_PARSE_ONLY;
VarEvalMode else_emode = VARE_PARSE_ONLY;
- int cond_rc = COND_PARSE; /* anything other than COND_INVALID */
+ CondResult cond_rc = CR_TRUE; /* just not CR_ERROR */
if (Expr_ShouldEval(expr)) {
- cond_rc = Cond_EvalCondition(expr->name, &value);
- if (cond_rc != COND_INVALID && value)
+ cond_rc = Cond_EvalCondition(expr->name);
+ if (cond_rc == CR_TRUE)
then_emode = expr->emode;
- if (cond_rc != COND_INVALID && !value)
+ if (cond_rc == CR_FALSE)
else_emode = expr->emode;
}
- (*pp)++; /* skip past the '?' */
- res = ParseModifierPart(pp, ':', then_emode, ch, &buf);
- if (res != VPR_OK)
+ (*pp)++; /* skip past the '?' */
+ if (!ParseModifierPart(pp, ':', then_emode, ch, &thenBuf))
return AMR_CLEANUP;
- then_expr = LazyBuf_DoneGet(&buf);
- res = ParseModifierPart(pp, ch->endc, else_emode, ch, &buf);
- if (res != VPR_OK) {
- FStr_Done(&then_expr);
+ if (!ParseModifierPart(pp, ch->endc, else_emode, ch, &elseBuf)) {
+ LazyBuf_Done(&thenBuf);
return AMR_CLEANUP;
}
- else_expr = LazyBuf_DoneGet(&buf);
(*pp)--; /* Go back to the ch->endc. */
- if (cond_rc == COND_INVALID) {
- Error("Bad conditional expression '%s' in '%s?%s:%s'",
- expr->name, expr->name, then_expr.str, else_expr.str);
+ if (cond_rc == CR_ERROR) {
+ Substring thenExpr = LazyBuf_Get(&thenBuf);
+ Substring elseExpr = LazyBuf_Get(&elseBuf);
+ Error("Bad conditional expression '%s' before '?%.*s:%.*s'",
+ expr->name,
+ (int)Substring_Length(thenExpr), thenExpr.start,
+ (int)Substring_Length(elseExpr), elseExpr.start);
+ LazyBuf_Done(&thenBuf);
+ LazyBuf_Done(&elseBuf);
return AMR_CLEANUP;
}
- if (!ModChain_ShouldEval(ch)) {
- FStr_Done(&then_expr);
- FStr_Done(&else_expr);
- } else if (value) {
- Expr_SetValue(expr, then_expr);
- FStr_Done(&else_expr);
+ if (!Expr_ShouldEval(expr)) {
+ LazyBuf_Done(&thenBuf);
+ LazyBuf_Done(&elseBuf);
+ } else if (cond_rc == CR_TRUE) {
+ Expr_SetValue(expr, LazyBuf_DoneGet(&thenBuf));
+ LazyBuf_Done(&elseBuf);
} else {
- FStr_Done(&then_expr);
- Expr_SetValue(expr, else_expr);
+ LazyBuf_Done(&thenBuf);
+ Expr_SetValue(expr, LazyBuf_DoneGet(&elseBuf));
}
Expr_Define(expr);
return AMR_OK;
@@ -3422,37 +3511,26 @@ ApplyModifier_Assign(const char **pp, ModChain *ch)
Expr *expr = ch->expr;
GNode *scope;
FStr val;
- VarParseResult res;
LazyBuf buf;
const char *mod = *pp;
const char *op = mod + 1;
if (op[0] == '=')
- goto ok;
- if ((op[0] == '!' || op[0] == '+' || op[0] == '?') && op[1] == '=')
- goto ok;
- return AMR_UNKNOWN; /* "::<unrecognised>" */
+ goto found_op;
+ if ((op[0] == '+' || op[0] == '?' || op[0] == '!') && op[1] == '=')
+ goto found_op;
+ return AMR_UNKNOWN; /* "::<unrecognized>" */
-ok:
+found_op:
if (expr->name[0] == '\0') {
*pp = mod + 1;
return AMR_BAD;
}
- switch (op[0]) {
- case '+':
- case '?':
- case '!':
- *pp = mod + 3;
- break;
- default:
- *pp = mod + 2;
- break;
- }
+ *pp = mod + (op[0] != '=' ? 3 : 2);
- res = ParseModifierPart(pp, ch->endc, expr->emode, ch, &buf);
- if (res != VPR_OK)
+ if (!ParseModifierPart(pp, ch->endc, expr->emode, ch, &buf))
return AMR_CLEANUP;
val = LazyBuf_DoneGet(&buf);
@@ -3462,36 +3540,26 @@ ok:
goto done;
scope = expr->scope; /* scope where v belongs */
- if (expr->defined == DEF_REGULAR && expr->scope != SCOPE_GLOBAL) {
- Var *gv = VarFind(expr->name, expr->scope, false);
- if (gv == NULL)
- scope = SCOPE_GLOBAL;
- else
- VarFreeEnv(gv);
- }
+ if (expr->defined == DEF_REGULAR && expr->scope != SCOPE_GLOBAL
+ && VarFind(expr->name, expr->scope, false) == NULL)
+ scope = SCOPE_GLOBAL;
- switch (op[0]) {
- case '+':
+ if (op[0] == '+')
Var_Append(scope, expr->name, val.str);
- break;
- case '!': {
- const char *errfmt;
- char *cmd_output = Cmd_Exec(val.str, &errfmt);
- if (errfmt != NULL)
- Error(errfmt, val.str);
- else
- Var_Set(scope, expr->name, cmd_output);
- free(cmd_output);
- break;
- }
- case '?':
- if (expr->defined == DEF_REGULAR)
- break;
- /* FALLTHROUGH */
- default:
+ else if (op[0] == '!') {
+ char *output, *error;
+ output = Cmd_Exec(val.str, &error);
+ if (error != NULL) {
+ Error("%s", error);
+ free(error);
+ } else
+ Var_Set(scope, expr->name, output);
+ free(output);
+ } else if (op[0] == '?' && expr->defined == DEF_REGULAR) {
+ /* Do nothing. */
+ } else
Var_Set(scope, expr->name, val.str);
- break;
- }
+
Expr_SetValueRefer(expr, "");
done:
@@ -3518,7 +3586,7 @@ ApplyModifier_Remember(const char **pp, ModChain *ch)
/*
* XXX: This ad-hoc call to strcspn deviates from the usual
* behavior defined in ParseModifierPart. This creates an
- * unnecessary, undocumented inconsistency in make.
+ * unnecessary and undocumented inconsistency in make.
*/
const char *arg = mod + 2;
size_t argLen = strcspn(arg, ":)}");
@@ -3528,7 +3596,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(SCOPE_GLOBAL, name.str, Expr_Str(expr));
FStr_Done(&name);
return AMR_OK;
@@ -3546,8 +3614,7 @@ ApplyModifier_WordFunc(const char **pp, ModChain *ch,
return AMR_UNKNOWN;
(*pp)++;
- if (ModChain_ShouldEval(ch))
- ModifyWords(ch, modifyWord, NULL, ch->oneBigWord);
+ ModifyWords(ch, modifyWord, NULL, ch->oneBigWord);
return AMR_OK;
}
@@ -3556,7 +3623,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 +3632,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;
+ size_t di, si;
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,18 +3648,36 @@ 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;
}
-#ifdef SYSVVARSUB
+/* Test whether the modifier has the form '<lhs>=<rhs>'. */
+static bool
+IsSysVModifier(const char *p, char startc, char endc)
+{
+ bool eqFound = false;
+
+ int depth = 1;
+ while (*p != '\0' && depth > 0) {
+ if (*p == '=') /* XXX: should also test depth == 1 */
+ eqFound = true;
+ else if (*p == endc)
+ depth--;
+ else if (*p == startc)
+ depth++;
+ if (depth > 0)
+ p++;
+ }
+ return *p == endc && eqFound;
+}
+
/* :from=to */
static ApplyModifierResult
ApplyModifier_SysV(const char **pp, ModChain *ch)
{
Expr *expr = ch->expr;
- VarParseResult res;
LazyBuf lhsBuf, rhsBuf;
FStr rhs;
struct ModifyWord_SysVSubstArgs args;
@@ -3600,35 +3685,17 @@ ApplyModifier_SysV(const char **pp, ModChain *ch)
const char *lhsSuffix;
const char *mod = *pp;
- bool eqFound = false;
- /*
- * First we make a pass through the string trying to verify it is a
- * SysV-make-style translation. It must be: <lhs>=<rhs>
- */
- int depth = 1;
- const char *p = mod;
- while (*p != '\0' && depth > 0) {
- if (*p == '=') { /* XXX: should also test depth == 1 */
- eqFound = true;
- /* continue looking for ch->endc */
- } else if (*p == ch->endc)
- depth--;
- else if (*p == ch->startc)
- depth++;
- if (depth > 0)
- p++;
- }
- if (*p != ch->endc || !eqFound)
+ if (!IsSysVModifier(mod, ch->startc, ch->endc))
return AMR_UNKNOWN;
- res = ParseModifierPart(pp, '=', expr->emode, ch, &lhsBuf);
- if (res != VPR_OK)
+ if (!ParseModifierPart(pp, '=', expr->emode, ch, &lhsBuf))
return AMR_CLEANUP;
- /* The SysV modifier lasts until the end of the variable expression. */
- res = ParseModifierPart(pp, ch->endc, expr->emode, ch, &rhsBuf);
- if (res != VPR_OK) {
+ /*
+ * The SysV modifier lasts until the end of the expression.
+ */
+ if (!ParseModifierPart(pp, ch->endc, expr->emode, ch, &rhsBuf)) {
LazyBuf_Done(&lhsBuf);
return AMR_CLEANUP;
}
@@ -3637,7 +3704,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);
@@ -3654,11 +3721,10 @@ ApplyModifier_SysV(const char **pp, ModChain *ch)
done:
LazyBuf_Done(&lhsBuf);
+ FStr_Done(&rhs);
return AMR_OK;
}
-#endif
-#ifdef SUNSHCMD
/* :sh */
static ApplyModifierResult
ApplyModifier_SunShell(const char **pp, ModChain *ch)
@@ -3670,16 +3736,29 @@ ApplyModifier_SunShell(const char **pp, ModChain *ch)
*pp = p + 2;
if (Expr_ShouldEval(expr)) {
- const char *errfmt;
- char *output = Cmd_Exec(expr->value.str, &errfmt);
- if (errfmt != NULL)
- Error(errfmt, expr->value.str);
+ char *output, *error;
+ output = Cmd_Exec(Expr_Str(expr), &error);
+ if (error != NULL) {
+ Error("%s", error);
+ free(error);
+ }
Expr_SetValueOwn(expr, output);
}
return AMR_OK;
}
-#endif
+
+/*
+ * In cases where the evaluation mode and the definedness are the "standard"
+ * ones, don't log them, to keep the logs readable.
+ */
+static bool
+ShouldLogInSimpleFormat(const Expr *expr)
+{
+ return (expr->emode == VARE_WANTRES ||
+ expr->emode == VARE_UNDEFERR) &&
+ expr->defined == DEF_REGULAR;
+}
static void
LogBeforeApply(const ModChain *ch, const char *mod)
@@ -3698,18 +3777,17 @@ LogBeforeApply(const ModChain *ch, const char *mod)
return;
}
- if ((expr->emode == VARE_WANTRES || expr->emode == VARE_UNDEFERR) &&
- expr->defined == DEF_REGULAR) {
+ if (ShouldLogInSimpleFormat(expr)) {
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,12 +3795,10 @@ 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) &&
- expr->defined == DEF_REGULAR) {
-
+ if (ShouldLogInSimpleFormat(expr)) {
debug_printf("Result of ${%s:%.*s} is %s%s%s\n",
expr->name, (int)(p - mod), mod,
quot, value == var_Error ? "error" : value, quot);
@@ -3752,27 +3828,29 @@ ApplyModifier(const char **pp, ModChain *ch)
return ApplyModifier_Words(pp, ch);
case '_':
return ApplyModifier_Remember(pp, ch);
-#ifndef NO_REGEX
+#ifdef HAVE_REGEX_H
case 'C':
return ApplyModifier_Regex(pp, ch);
#endif
case 'D':
+ case 'U':
return ApplyModifier_Defined(pp, ch);
case 'E':
return ApplyModifier_WordFunc(pp, ch, ModifyWord_Suffix);
case 'g':
- return ApplyModifier_Gmtime(pp, ch);
+ case 'l':
+ return ApplyModifier_Time(pp, ch);
case 'H':
return ApplyModifier_WordFunc(pp, ch, ModifyWord_Head);
case 'h':
return ApplyModifier_Hash(pp, ch);
case 'L':
return ApplyModifier_Literal(pp, ch);
- case 'l':
- return ApplyModifier_Localtime(pp, ch);
case 'M':
case 'N':
return ApplyModifier_Match(pp, ch);
+ case 'm':
+ return ApplyModifier_Mtime(pp, ch);
case 'O':
return ApplyModifier_Order(pp, ch);
case 'P':
@@ -3786,16 +3864,12 @@ ApplyModifier(const char **pp, ModChain *ch)
return ApplyModifier_Range(pp, ch);
case 'S':
return ApplyModifier_Subst(pp, ch);
-#ifdef SUNSHCMD
case 's':
return ApplyModifier_SunShell(pp, ch);
-#endif
case 'T':
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:
@@ -3815,16 +3889,16 @@ typedef enum ApplyModifiersIndirectResult {
} ApplyModifiersIndirectResult;
/*
- * While expanding a variable expression, expand and apply indirect modifiers,
+ * While expanding an expression, expand and apply indirect modifiers,
* such as in ${VAR:${M_indirect}}.
*
- * All indirect modifiers of a group must come from a single variable
+ * All indirect modifiers of a group must come from a single
* expression. ${VAR:${M1}} is valid but ${VAR:${M1}${M2}} is not.
*
* Multiple groups of indirect modifiers can be chained by separating them
* with colons. ${VAR:${M1}:${M2}} contains 2 indirect modifiers.
*
- * If the variable expression is not followed by ch->endc or ':', fall
+ * If the expression is not followed by ch->endc or ':', fall
* back to trying the SysV modifier, such as in ${VAR:${FROM}=${TO}}.
*/
static ApplyModifiersIndirectResult
@@ -3832,12 +3906,10 @@ ApplyModifiersIndirect(ModChain *ch, const char **pp)
{
Expr *expr = ch->expr;
const char *p = *pp;
- FStr mods;
-
- (void)Var_Parse(&p, expr->scope, expr->emode, &mods);
+ FStr mods = Var_Parse(&p, expr->scope, expr->emode);
/* TODO: handle errors */
- if (mods.str[0] != '\0' && *p != '\0' && !IsDelimiter(*p, ch)) {
+ if (mods.str[0] != '\0' && !IsDelimiter(*p, ch)) {
FStr_Done(&mods);
return AMIR_SYSV;
}
@@ -3845,10 +3917,10 @@ ApplyModifiersIndirect(ModChain *ch, const char **pp)
DEBUG3(VAR, "Indirect modifier \"%s\" from \"%.*s\"\n",
mods.str, (int)(p - *pp), *pp);
- if (mods.str[0] != '\0') {
+ if (ModChain_ShouldEval(ch) && 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 */
@@ -3859,8 +3931,8 @@ ApplyModifiersIndirect(ModChain *ch, const char **pp)
if (*p == ':')
p++;
else if (*p == '\0' && ch->endc != '\0') {
- Error("Unclosed variable expression after indirect "
- "modifier, expecting '%c' for variable \"%s\"",
+ Error("Unclosed expression after indirect modifier, "
+ "expecting '%c' for variable \"%s\"",
ch->endc, expr->name);
*pp = p;
return AMIR_OUT;
@@ -3882,12 +3954,10 @@ ApplySingleModifier(const char **pp, ModChain *ch)
res = ApplyModifier(&p, ch);
-#ifdef SYSVVARSUB
if (res == AMR_UNKNOWN) {
assert(p == mod);
res = ApplyModifier_SysV(&p, ch);
}
-#endif
if (res == AMR_UNKNOWN) {
/*
@@ -3896,7 +3966,7 @@ ApplySingleModifier(const char **pp, ModChain *ch)
* errors and leads to wrong results.
* Parsing should rather stop here.
*/
- for (p++; !IsDelimiter(*p, ch) && *p != '\0'; p++)
+ for (p++; !IsDelimiter(*p, ch); p++)
continue;
Parse_Error(PARSE_FATAL, "Unknown modifier \"%.*s\"",
(int)(p - mod), mod);
@@ -3912,11 +3982,11 @@ ApplySingleModifier(const char **pp, ModChain *ch)
if (*p == '\0' && ch->endc != '\0') {
Error(
- "Unclosed variable expression, expecting '%c' for "
+ "Unclosed expression, expecting '%c' for "
"modifier \"%.*s\" of variable \"%s\" with value \"%s\"",
ch->endc,
(int)(p - mod), mod,
- 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) {
@@ -3933,11 +4003,11 @@ ApplySingleModifier(const char **pp, ModChain *ch)
}
#if __STDC_VERSION__ >= 199901L
-#define ModChain_Literal(expr, startc, endc, sep, oneBigWord) \
+#define ModChain_Init(expr, startc, endc, sep, oneBigWord) \
(ModChain) { expr, startc, endc, sep, oneBigWord }
#else
MAKE_INLINE ModChain
-ModChain_Literal(Expr *expr, char startc, char endc, char sep, bool oneBigWord)
+ModChain_Init(Expr *expr, char startc, char endc, char sep, bool oneBigWord)
{
ModChain ch;
ch.expr = expr;
@@ -3958,19 +4028,19 @@ ApplyModifiers(
char endc /* ')' or '}'; or '\0' for indirect modifiers */
)
{
- ModChain ch = ModChain_Literal(expr, startc, endc, ' ', false);
+ ModChain ch = ModChain_Init(expr, startc, endc, ' ', false);
const char *p;
const char *mod;
assert(startc == '(' || startc == '{' || startc == '\0');
assert(endc == ')' || endc == '}' || endc == '\0');
- assert(expr->value.str != NULL);
+ assert(Expr_Str(expr) != NULL);
p = *pp;
if (*p == '\0' && endc != '\0') {
Error(
- "Unclosed variable expression (expecting '%c') for \"%s\"",
+ "Unclosed expression, expecting '%c' for \"%s\"",
ch.endc, expr->name);
goto cleanup;
}
@@ -3979,17 +4049,17 @@ ApplyModifiers(
ApplyModifierResult res;
if (*p == '$') {
+ /*
+ * TODO: Only evaluate the expression once, no matter
+ * whether it's an indirect modifier or the initial
+ * part of a SysV modifier.
+ */
ApplyModifiersIndirectResult amir =
ApplyModifiersIndirect(&ch, &p);
if (amir == AMIR_CONTINUE)
continue;
if (amir == AMIR_OUT)
break;
- /*
- * It's neither '${VAR}:' nor '${VAR}}'. Try to parse
- * it as a SysV modifier, as that is the only modifier
- * that can start with '$'.
- */
}
mod = p;
@@ -4002,11 +4072,11 @@ 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:
- /* XXX: The modifier end is only guessed. */
+ /* Take a guess at where the modifier ends. */
Error("Bad modifier \":%.*s\" for variable \"%s\"",
(int)strcspn(mod, ":)}"), mod, expr->name);
@@ -4014,20 +4084,20 @@ cleanup:
/*
* TODO: Use p + strlen(p) instead, to stop parsing immediately.
*
- * In the unit tests, this generates a few unterminated strings in the
- * shell commands though. Instead of producing these unfinished
- * strings, commands with evaluation errors should not be run at all.
+ * In the unit tests, this generates a few shell commands with
+ * unbalanced quotes. Instead of producing these incomplete strings,
+ * commands with evaluation errors should not be run at all.
*
* To make that happen, Var_Subst must report the actual errors
- * instead of returning VPR_OK unconditionally.
+ * instead of returning the resulting string unconditionally.
*/
*pp = p;
Expr_SetValueRefer(expr, var_Error);
}
/*
- * Only 4 of the 7 local variables are treated specially as they are the only
- * ones that will be set when dynamic sources are expanded.
+ * Only 4 of the 7 built-in local variables are treated specially as they are
+ * the only ones that will be set when dynamic sources are expanded.
*/
static bool
VarnameIsDynamic(Substring varname)
@@ -4095,7 +4165,7 @@ ParseVarname(const char **pp, char startc, char endc,
LazyBuf *buf)
{
const char *p = *pp;
- int depth = 0; /* Track depth so we can spot parse errors. */
+ int depth = 0;
LazyBuf_Init(buf, p);
@@ -4107,10 +4177,8 @@ ParseVarname(const char **pp, char startc, char endc,
if (*p == endc)
depth--;
- /* A variable inside a variable, expand. */
if (*p == '$') {
- FStr nested_val;
- (void)Var_Parse(&p, scope, emode, &nested_val);
+ FStr nested_val = Var_Parse(&p, scope, emode);
/* TODO: handle errors */
LazyBuf_AddStr(buf, nested_val.str);
FStr_Done(&nested_val);
@@ -4122,26 +4190,26 @@ ParseVarname(const char **pp, char startc, char endc,
*pp = p;
}
-static VarParseResult
-ValidShortVarname(char varname, const char *start)
+static bool
+IsShortVarnameValid(char varname, const char *start)
{
if (varname != '$' && varname != ':' && varname != '}' &&
varname != ')' && varname != '\0')
- return VPR_OK;
+ return true;
if (!opts.strict)
- return VPR_ERR; /* XXX: Missing error message */
+ return false; /* XXX: Missing error message */
- if (varname == '$')
+ if (varname == '$' && save_dollars)
Parse_Error(PARSE_FATAL,
"To escape a dollar, use \\$, not $$, at \"%s\"", start);
else if (varname == '\0')
Parse_Error(PARSE_FATAL, "Dollar followed by nothing");
- else
+ else if (save_dollars)
Parse_Error(PARSE_FATAL,
"Invalid variable name '%c', at \"%s\"", varname, start);
- return VPR_ERR;
+ return false;
}
/*
@@ -4151,17 +4219,15 @@ ValidShortVarname(char varname, const char *start)
static bool
ParseVarnameShort(char varname, const char **pp, GNode *scope,
VarEvalMode emode,
- VarParseResult *out_false_res, const char **out_false_val,
+ const char **out_false_val,
Var **out_true_var)
{
char name[2];
Var *v;
- VarParseResult vpr;
+ const char *val;
- vpr = ValidShortVarname(varname, *pp);
- if (vpr != VPR_OK) {
- (*pp)++;
- *out_false_res = vpr;
+ if (!IsShortVarnameValid(varname, *pp)) {
+ (*pp)++; /* only skip the '$' */
*out_false_val = var_Error;
return false;
}
@@ -4169,41 +4235,25 @@ ParseVarnameShort(char varname, const char **pp, GNode *scope,
name[0] = varname;
name[1] = '\0';
v = VarFind(name, scope, true);
- if (v == NULL) {
- const char *val;
- *pp += 2;
+ if (v != NULL) {
+ /* No need to advance *pp, the calling code handles this. */
+ *out_true_var = v;
+ return true;
+ }
- val = UndefinedShortVarValue(varname, scope);
- if (val == NULL)
- val = emode == VARE_UNDEFERR
- ? var_Error : varUndefined;
+ *pp += 2;
- if (opts.strict && val == var_Error) {
- Parse_Error(PARSE_FATAL,
- "Variable \"%s\" is undefined", name);
- *out_false_res = VPR_ERR;
- *out_false_val = val;
- return false;
- }
+ val = UndefinedShortVarValue(varname, scope);
+ if (val == NULL)
+ val = emode == VARE_UNDEFERR ? var_Error : varUndefined;
- /*
- * XXX: This looks completely wrong.
- *
- * If undefined expressions are not allowed, this should
- * rather be VPR_ERR instead of VPR_UNDEF, together with an
- * error message.
- *
- * If undefined expressions are allowed, this should rather
- * be VPR_UNDEF instead of VPR_OK.
- */
- *out_false_res = emode == VARE_UNDEFERR
- ? VPR_UNDEF : VPR_OK;
- *out_false_val = val;
- return false;
+ if (opts.strict && val == var_Error) {
+ Parse_Error(PARSE_FATAL,
+ "Variable \"%s\" is undefined", name);
}
- *out_true_var = v;
- return true;
+ *out_false_val = val;
+ return false;
}
/* Find variables like @F or <D. */
@@ -4224,7 +4274,8 @@ FindLocalLegacyVar(Substring varname, GNode *scope,
if (strchr("@%?*!<>", varname.start[0]) == NULL)
return NULL;
- v = VarFindSubstring(Substring_Sub(varname, 0, 1), scope, false);
+ v = VarFindSubstring(Substring_Init(varname.start, varname.start + 1),
+ scope, false);
if (v == NULL)
return NULL;
@@ -4232,30 +4283,22 @@ FindLocalLegacyVar(Substring varname, GNode *scope,
return v;
}
-static VarParseResult
+static FStr
EvalUndefined(bool dynamic, const char *start, const char *p,
- Substring varname, VarEvalMode emode, FStr *out_val)
+ Substring varname, VarEvalMode emode)
{
- if (dynamic) {
- *out_val = FStr_InitOwn(bmake_strsedup(start, p));
- return VPR_OK;
- }
+ if (dynamic)
+ return FStr_InitOwn(bmake_strsedup(start, p));
if (emode == VARE_UNDEFERR && opts.strict) {
Parse_Error(PARSE_FATAL,
"Variable \"%.*s\" is undefined",
(int)Substring_Length(varname), varname.start);
- *out_val = FStr_InitRefer(var_Error);
- return VPR_ERR;
+ return FStr_InitRefer(var_Error);
}
- if (emode == VARE_UNDEFERR) {
- *out_val = FStr_InitRefer(var_Error);
- return VPR_UNDEF; /* XXX: Should be VPR_ERR instead. */
- }
-
- *out_val = FStr_InitRefer(varUndefined);
- return VPR_OK;
+ return FStr_InitRefer(
+ emode == VARE_UNDEFERR ? var_Error : varUndefined);
}
/*
@@ -4272,7 +4315,6 @@ ParseVarnameLong(
VarEvalMode emode,
const char **out_false_pp,
- VarParseResult *out_false_res,
FStr *out_false_val,
char *out_true_endc,
@@ -4284,73 +4326,80 @@ ParseVarnameLong(
)
{
LazyBuf varname;
+ Substring name;
Var *v;
bool haveModifier;
bool dynamic = false;
const char *p = *pp;
- const char *const start = p;
+ const char *start = p;
char endc = startc == '(' ? ')' : '}';
p += 2; /* skip "${" or "$(" or "y(" */
ParseVarname(&p, startc, endc, scope, emode, &varname);
+ name = LazyBuf_Get(&varname);
- if (*p == ':') {
+ if (*p == ':')
haveModifier = true;
- } else if (*p == endc) {
+ else if (*p == endc)
haveModifier = false;
- } else {
- Substring name = LazyBuf_Get(&varname);
+ else {
Parse_Error(PARSE_FATAL, "Unclosed variable \"%.*s\"",
(int)Substring_Length(name), name.start);
LazyBuf_Done(&varname);
*out_false_pp = p;
*out_false_val = FStr_InitRefer(var_Error);
- *out_false_res = VPR_ERR;
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. */
+ /*
+ * 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 (v == NULL && Substring_Equals(name, ".SUFFIXES")) {
+ char *suffixes = Suff_NamesStr();
+ v = VarNew(FStr_InitRefer(".SUFFIXES"), suffixes,
+ true, false, true);
+ free(suffixes);
+ } else if (v == NULL)
+ v = FindLocalLegacyVar(name, scope, out_true_extraModifiers);
if (v == NULL) {
/*
* 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);
+ *out_false_val = EvalUndefined(dynamic, start, p,
+ name, emode);
+ LazyBuf_Done(&varname);
return false;
}
/*
- * The variable expression is based on an undefined variable.
+ * The expression is based on an undefined variable.
* Nevertheless it needs a Var, for modifiers that access the
* variable name, such as :L or :?.
*
* Most modifiers leave this expression in the "undefined"
- * state (VES_UNDEF), only a few modifiers like :D, :U, :L,
+ * state (DEF_UNDEF), only a few modifiers like :D, :U, :L,
* :P turn this undefined expression into a defined
- * expression (VES_DEF).
+ * expression (DEF_DEFINED).
*
* In the end, after applying all modifiers, if the expression
* is still undefined, Var_Parse will return an empty string
* instead of the actually computed value.
*/
- v = VarNew(LazyBuf_DoneGet(&varname), "", false, false);
+ v = VarNew(LazyBuf_DoneGet(&varname), "",
+ true, false, false);
*out_true_exprDefined = DEF_UNDEF;
} else
LazyBuf_Done(&varname);
@@ -4363,27 +4412,13 @@ ParseVarnameLong(
return true;
}
-/* Free the environment variable now since we own it. */
-static void
-FreeEnvVar(Var *v, FStr *inout_val)
-{
- char *varValue = Buf_DoneData(&v->val);
- if (inout_val->str == varValue)
- inout_val->freeIt = varValue;
- else
- free(varValue);
-
- FStr_Done(&v->name);
- free(v);
-}
-
#if __STDC_VERSION__ >= 199901L
-#define Expr_Literal(name, value, emode, scope, defined) \
- { name, value, emode, scope, defined }
+#define Expr_Init(name, value, emode, scope, defined) \
+ (Expr) { name, value, emode, scope, defined }
#else
MAKE_INLINE Expr
-Expr_Literal(const char *name, FStr value,
- VarEvalMode emode, GNode *scope, ExprDefined defined)
+Expr_Init(const char *name, FStr value,
+ VarEvalMode emode, GNode *scope, ExprDefined defined)
{
Expr expr;
@@ -4398,11 +4433,10 @@ Expr_Literal(const char *name, FStr value,
/*
* Expressions of the form ${:U...} with a trivial value are often generated
- * by .for loops and are boring, therefore parse and evaluate them in a fast
- * lane without debug logging.
+ * by .for loops and are boring, so evaluate them without debug logging.
*/
static bool
-Var_Parse_FastLane(const char **pp, VarEvalMode emode, FStr *out_value)
+Var_Parse_U(const char **pp, VarEvalMode emode, FStr *out_value)
{
const char *p;
@@ -4417,28 +4451,24 @@ Var_Parse_FastLane(const char **pp, VarEvalMode emode, FStr *out_value)
if (*p != '}')
return false;
- if (emode == VARE_PARSE_ONLY)
- *out_value = FStr_InitRefer("");
- else
- *out_value = FStr_InitOwn(bmake_strsedup(*pp + 4, p));
+ *out_value = emode == VARE_PARSE_ONLY
+ ? FStr_InitRefer("")
+ : FStr_InitOwn(bmake_strsedup(*pp + 4, p));
*pp = p + 1;
return true;
}
/*
- * Given the start of a variable expression (such as $v, $(VAR),
- * ${VAR:Mpattern}), extract the variable name and value, and the modifiers,
- * if any. While doing that, apply the modifiers to the value of the
- * expression, forming its final value. A few of the modifiers such as :!cmd!
- * or ::= have side effects.
+ * Given the start of an expression (such as $v, $(VAR), ${VAR:Mpattern}),
+ * extract the variable name and the modifiers, if any. While parsing, apply
+ * the modifiers to the value of the expression.
*
* Input:
* *pp The string to parse.
- * When parsing a condition in ParseEmptyArg, 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
+ * When called from CondParser_FuncCallEmpty, it can
+ * also point to the "y" of "empty(VARNAME:Modifiers)".
+ * scope The scope for finding variables.
+ * emode Controls the exact details of parsing and evaluation.
*
* Output:
* *pp The position where to continue parsing.
@@ -4447,103 +4477,114 @@ Var_Parse_FastLane(const char **pp, VarEvalMode emode, FStr *out_value)
* point to some random character in the string, to the
* location of the parse error, or at the end of the
* string.
- * *out_val The value of the variable expression, never NULL.
- * *out_val var_Error if there was a parse error.
- * *out_val var_Error if the base variable of the expression was
+ * return The value of the expression, never NULL.
+ * return var_Error if there was a parse error.
+ * return var_Error if the base variable of the expression was
* undefined, emode is VARE_UNDEFERR, and none of
* the modifiers turned the undefined expression into a
* defined expression.
* XXX: It is not guaranteed that an error message has
* been printed.
- * *out_val varUndefined if the base variable of the expression
+ * return varUndefined if the base variable of the expression
* was undefined, emode was not VARE_UNDEFERR,
* and none of the modifiers turned the undefined
* expression into a defined expression.
* XXX: It is not guaranteed that an error message has
* been printed.
*/
-VarParseResult
-Var_Parse(const char **pp, GNode *scope, VarEvalMode emode, FStr *out_val)
+FStr
+Var_Parse(const char **pp, GNode *scope, VarEvalMode emode)
{
- const char *p = *pp;
- const char *const start = p;
- /* true if have modifiers for the variable. */
- bool haveModifier;
- /* Starting character if variable in parens or braces. */
- char startc;
- /* Ending character if variable in parens or braces. */
- char endc;
+ const char *start, *p;
+ bool haveModifier; /* true for ${VAR:...}, false for ${VAR} */
+ char startc; /* the actual '{' or '(' or '\0' */
+ char endc; /* the expected '}' or ')' or '\0' */
/*
- * true if the variable is local and we're expanding it in a
- * non-local scope. This is done to support dynamic sources.
- * The result is just the expression, unaltered.
+ * true if the expression is based on one of the 7 predefined
+ * variables that are local to a target, and the expression is
+ * expanded in a non-local scope. The result is the text of the
+ * expression, unaltered. This is needed to support dynamic sources.
*/
bool dynamic;
const char *extramodifiers;
Var *v;
- Expr expr = Expr_Literal(NULL, FStr_InitRefer(NULL), emode,
+ Expr expr = Expr_Init(NULL, FStr_InitRefer(NULL), emode,
scope, DEF_REGULAR);
+ FStr val;
- if (Var_Parse_FastLane(pp, emode, out_val))
- return VPR_OK;
+ if (Var_Parse_U(pp, emode, &val))
+ return val;
+ p = *pp;
+ start = p;
DEBUG2(VAR, "Var_Parse: %s (%s)\n", start, VarEvalMode_Name[emode]);
- *out_val = FStr_InitRefer(NULL);
+ val = FStr_InitRefer(NULL);
extramodifiers = NULL; /* extra modifiers to apply first */
dynamic = false;
- /*
- * Appease GCC, which thinks that the variable might not be
- * initialized.
- */
- endc = '\0';
+ endc = '\0'; /* Appease GCC. */
startc = p[1];
if (startc != '(' && startc != '{') {
- VarParseResult res;
- if (!ParseVarnameShort(startc, pp, scope, emode, &res,
- &out_val->str, &v))
- return res;
+ if (!ParseVarnameShort(startc, pp, scope, emode, &val.str, &v))
+ return val;
haveModifier = false;
p++;
} else {
- VarParseResult res;
if (!ParseVarnameLong(&p, startc, scope, emode,
- pp, &res, out_val,
+ pp, &val,
&endc, &v, &haveModifier, &extramodifiers,
&dynamic, &expr.defined))
- return res;
+ return val;
}
expr.name = v->name.str;
- if (v->inUse)
+ if (v->inUse && VarEvalMode_ShouldEval(emode)) {
+ if (scope->fname != NULL) {
+ fprintf(stderr, "In a command near ");
+ PrintLocation(stderr, false, scope);
+ }
Fatal("Variable %s is recursive.", v->name.str);
+ }
/*
- * XXX: This assignment creates an alias to the current value of the
+ * FIXME: This assignment creates an alias to the current value of the
* variable. This means that as long as the value of the expression
- * stays the same, the value of the variable must not change.
- * Using the '::=' modifier, it could be possible to do exactly this.
+ * stays the same, the value of the variable must not change, and the
+ * variable must not be deleted. Using the ':@' modifier, it is
+ * possible (since var.c 1.212 from 2017-02-01) to delete the variable
+ * while its value is still being used:
+ *
+ * VAR= value
+ * _:= ${VAR:${:U:@VAR@@}:S,^,prefix,}
+ *
+ * The same effect might be achievable using the '::=' or the ':_'
+ * modifiers.
+ *
* At the bottom of this function, the resulting value is compared to
* the then-current value of the variable. This might also invoke
* undefined behavior.
*/
expr.value = FStr_InitRefer(v->val.data);
+ if (expr.name[0] != '\0')
+ EvalStack_Push(NULL, NULL, expr.name);
+ else
+ EvalStack_Push(NULL, start, NULL);
+
/*
* Before applying any modifiers, expand any nested expressions from
* the variable value.
*/
- if (strchr(expr.value.str, '$') != NULL &&
- VarEvalMode_ShouldEval(emode)) {
+ if (VarEvalMode_ShouldEval(emode) &&
+ strchr(Expr_Str(&expr), '$') != NULL) {
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,
- &expanded);
+ expanded = Var_Subst(Expr_Str(&expr), scope, nested_emode);
v->inUse = false;
/* TODO: handle errors */
Expr_SetValueOwn(&expr, expanded);
@@ -4555,7 +4596,7 @@ Var_Parse(const char **pp, GNode *scope, VarEvalMode emode, FStr *out_val)
}
if (haveModifier) {
- p++; /* Skip initial colon. */
+ p++; /* Skip initial colon. */
ApplyModifiers(&expr, &p, startc, endc);
}
@@ -4564,33 +4605,32 @@ Var_Parse(const char **pp, GNode *scope, VarEvalMode emode, FStr *out_val)
*pp = p;
- if (v->fromEnv) {
- FreeEnvVar(v, &expr.value);
-
- } else if (expr.defined != DEF_REGULAR) {
- if (expr.defined == DEF_UNDEF) {
- if (dynamic) {
- Expr_SetValueOwn(&expr,
- bmake_strsedup(start, p));
- } else {
- /*
- * The expression is still undefined,
- * therefore discard the actual value and
- * return an error marker instead.
- */
- Expr_SetValueRefer(&expr,
- emode == VARE_UNDEFERR
- ? var_Error : varUndefined);
- }
+ if (expr.defined == DEF_UNDEF) {
+ if (dynamic)
+ Expr_SetValueOwn(&expr, bmake_strsedup(start, p));
+ else {
+ /*
+ * The expression is still undefined, therefore
+ * discard the actual value and return an error marker
+ * instead.
+ */
+ Expr_SetValueRefer(&expr,
+ emode == VARE_UNDEFERR
+ ? var_Error : varUndefined);
}
- /* XXX: This is not standard memory management. */
- if (expr.value.str != v->val.data)
- Buf_Done(&v->val);
- FStr_Done(&v->name);
- free(v);
}
- *out_val = expr.value;
- return VPR_OK; /* XXX: Is not correct in all cases */
+
+ if (v->shortLived) {
+ if (expr.value.str == v->val.data) {
+ /* move ownership */
+ expr.value.freeIt = v->val.data;
+ v->val.data = NULL;
+ }
+ VarFreeShortLived(v);
+ }
+
+ EvalStack_Pop();
+ return expr.value;
}
static void
@@ -4609,21 +4649,19 @@ VarSubstExpr(const char **pp, Buffer *buf, GNode *scope,
{
const char *p = *pp;
const char *nested_p = p;
- FStr val;
-
- (void)Var_Parse(&nested_p, scope, emode, &val);
+ FStr val = Var_Parse(&nested_p, scope, emode);
/* TODO: handle errors */
if (val.str == var_Error || val.str == varUndefined) {
if (!VarEvalMode_ShouldKeepUndef(emode)) {
p = nested_p;
- } else if (emode == VARE_UNDEFERR || val.str == var_Error) {
+ } else if (val.str == var_Error) {
/*
- * XXX: This condition is wrong. If val == var_Error,
- * this doesn't necessarily mean there was an undefined
- * variable. It could equally well be a parse error;
- * see unit-tests/varmod-order.exp.
+ * FIXME: The condition 'val.str == var_Error' doesn't
+ * mean there was an undefined variable. It could
+ * equally well be a parse error; see
+ * unit-tests/varmod-order.mk.
*/
/*
@@ -4634,15 +4672,17 @@ VarSubstExpr(const char **pp, Buffer *buf, GNode *scope,
if (!*inout_errorReported) {
Parse_Error(PARSE_FATAL,
"Undefined variable \"%.*s\"",
- (int)(size_t)(nested_p - p), p);
+ (int)(nested_p - p), p);
+ *inout_errorReported = true;
}
p = nested_p;
- *inout_errorReported = true;
} else {
- /* Copy the initial '$' of the undefined expression,
+ /*
+ * Copy the initial '$' of the undefined expression,
* thereby deferring expansion of the expression, but
- * expand nested expressions if already possible.
- * See unit-tests/varparse-undef-partial.mk. */
+ * expand nested expressions if already possible. See
+ * unit-tests/varparse-undef-partial.mk.
+ */
Buf_AddByte(buf, *p);
p++;
}
@@ -4657,8 +4697,8 @@ VarSubstExpr(const char **pp, Buffer *buf, GNode *scope,
}
/*
- * Skip as many characters as possible -- either to the end of the string
- * or to the next dollar sign (variable expression).
+ * Skip as many characters as possible -- either to the end of the string,
+ * or to the next dollar sign, which may start an expression.
*/
static void
VarSubstPlain(const char **pp, Buffer *res)
@@ -4668,30 +4708,30 @@ VarSubstPlain(const char **pp, Buffer *res)
for (p++; *p != '$' && *p != '\0'; p++)
continue;
- Buf_AddBytesBetween(res, start, p);
+ Buf_AddRange(res, start, p);
*pp = p;
}
/*
- * Expand all variable expressions like $V, ${VAR}, $(VAR:Modifiers) in the
+ * Expand all expressions like $V, ${VAR}, $(VAR:Modifiers) in the
* given string.
*
* Input:
- * str The string in which the variable expressions are
- * expanded.
- * scope The scope in which to start searching for
- * variables. The other scopes are searched as well.
+ * str The string in which the expressions are expanded.
+ * scope The scope in which to start searching for variables.
+ * The other scopes are searched as well.
* emode The mode for parsing or evaluating subexpressions.
*/
-VarParseResult
-Var_Subst(const char *str, GNode *scope, VarEvalMode emode, char **out_res)
+char *
+Var_Subst(const char *str, GNode *scope, VarEvalMode emode)
{
const char *p = str;
Buffer res;
- /* Set true if an error has already been reported,
- * to prevent a plethora of messages when recursing */
- /* XXX: Why is the 'static' necessary here? */
+ /*
+ * Set true if an error has already been reported, to prevent a
+ * plethora of messages when recursing
+ */
static bool errorReported;
Buf_Init(&res);
@@ -4706,8 +4746,20 @@ Var_Subst(const char *str, GNode *scope, VarEvalMode emode, char **out_res)
VarSubstPlain(&p, &res);
}
- *out_res = Buf_DoneDataCompact(&res);
- return VPR_OK;
+ return Buf_DoneData(&res);
+}
+
+void
+Var_Expand(FStr *str, GNode *scope, VarEvalMode emode)
+{
+ char *expanded;
+
+ if (strchr(str->str, '$') == NULL)
+ return;
+ expanded = Var_Subst(str->str, scope, emode);
+ /* TODO: handle errors */
+ FStr_Done(str);
+ *str = FStr_InitOwn(expanded);
}
/* Initialize the variables module. */
@@ -4732,6 +4784,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,12 +4808,13 @@ 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];
- Var *var = HashTable_FindValue(&scope->vars, varname);
- debug_printf("%-16s = %s\n", varname, var->val.data);
+ const Var *var = HashTable_FindValue(&scope->vars, varname);
+ debug_printf("%-16s = %s%s\n", varname,
+ var->val.data, ValueDescription(var->val.data));
}
Vector_Done(&vec);