aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon J. Gerraty <sjg@FreeBSD.org>2020-11-07 19:39:21 +0000
committerSimon J. Gerraty <sjg@FreeBSD.org>2020-11-07 19:39:21 +0000
commit302da1a3d35c15cb29d76e0a939f8bcb13f7ad80 (patch)
treec2146dca82d530521c4d2cc46a95c26964311a2c
parent6bbc783f48498b808e19db4441299dc7d85a278b (diff)
downloadsrc-vendor/NetBSD/bmake/20201101.tar.gz
src-vendor/NetBSD/bmake/20201101.zip
Import bmake-20201101vendor/NetBSD/bmake/20201101
Lots of new unit-tests increase code coverage. Lots of refactoring, cleanup and simlpification to reduce code size. Fixes for Bug 223564 and 245807 Updates to dirdeps.mk and meta2deps.py
-rw-r--r--ChangeLog264
-rw-r--r--FILES158
-rw-r--r--Makefile3
-rw-r--r--VERSION2
-rw-r--r--arch.c725
-rw-r--r--bmake.18
-rw-r--r--bmake.cat16
-rwxr-xr-xboot-strap30
-rw-r--r--buf.c120
-rw-r--r--buf.h38
-rw-r--r--compat.c432
-rw-r--r--cond.c938
-rwxr-xr-xconfigure27
-rw-r--r--configure.in13
-rw-r--r--dir.c1201
-rw-r--r--dir.h55
-rwxr-xr-xenum.c23
-rwxr-xr-xenum.h44
-rw-r--r--filemon/filemon.h8
-rw-r--r--filemon/filemon_ktrace.c12
-rw-r--r--for.c544
-rw-r--r--hash.c454
-rw-r--r--hash.h72
-rw-r--r--job.c1630
-rw-r--r--job.h190
-rw-r--r--lst.c522
-rw-r--r--lst.h128
-rw-r--r--main.c1859
-rwxr-xr-xmake-bootstrap.sh.in13
-rw-r--r--make-conf.h31
-rw-r--r--make.18
-rw-r--r--make.c992
-rw-r--r--make.h465
-rw-r--r--make_malloc.c13
-rw-r--r--make_malloc.h16
-rw-r--r--meta.c341
-rw-r--r--meta.h7
-rw-r--r--metachar.c11
-rw-r--r--metachar.h14
-rw-r--r--mk/ChangeLog28
-rw-r--r--mk/dirdeps-cache-update.mk8
-rw-r--r--mk/dirdeps.mk16
-rw-r--r--mk/install-mk4
-rwxr-xr-xmk/meta2deps.py3
-rwxr-xr-xmk/meta2deps.sh21
-rw-r--r--mk/sys.vars.mk16
-rw-r--r--nonints.h176
-rw-r--r--parse.c3754
-rw-r--r--str.c62
-rw-r--r--strlist.c91
-rw-r--r--strlist.h62
-rw-r--r--suff.c1595
-rw-r--r--targ.c501
-rw-r--r--trace.c21
-rw-r--r--trace.h4
-rw-r--r--unit-tests/Makefile153
-rw-r--r--unit-tests/archive.exp15
-rw-r--r--unit-tests/archive.mk45
-rw-r--r--unit-tests/comment.exp9
-rw-r--r--unit-tests/comment.mk85
-rw-r--r--unit-tests/cond-cmp-numeric-eq.exp7
-rwxr-xr-xunit-tests/cond-cmp-numeric-eq.mk33
-rwxr-xr-xunit-tests/cond-cmp-numeric-ge.mk26
-rwxr-xr-xunit-tests/cond-cmp-numeric-gt.mk26
-rwxr-xr-xunit-tests/cond-cmp-numeric-le.mk26
-rwxr-xr-xunit-tests/cond-cmp-numeric-lt.mk26
-rwxr-xr-xunit-tests/cond-cmp-numeric-ne.mk18
-rw-r--r--unit-tests/cond-cmp-numeric.exp12
-rw-r--r--unit-tests/cond-cmp-numeric.mk25
-rw-r--r--unit-tests/cond-cmp-string.exp3
-rw-r--r--unit-tests/cond-cmp-string.mk65
-rwxr-xr-x[-rw-r--r--]unit-tests/cond-cmp-unary.exp (renamed from unit-tests/misc.exp)0
-rwxr-xr-xunit-tests/cond-cmp-unary.mk43
-rw-r--r--unit-tests/cond-func-commands.mk10
-rw-r--r--unit-tests/cond-func-defined.mk16
-rw-r--r--unit-tests/cond-func-empty.mk148
-rw-r--r--unit-tests/cond-func-exists.mk16
-rw-r--r--unit-tests/cond-func-make.exp2
-rw-r--r--unit-tests/cond-func-make.mk26
-rw-r--r--unit-tests/cond-func-target.mk10
-rw-r--r--unit-tests/cond-func.mk32
-rw-r--r--unit-tests/cond-op-and.exp5
-rw-r--r--unit-tests/cond-op-and.mk31
-rw-r--r--unit-tests/cond-op-not.mk8
-rw-r--r--unit-tests/cond-op-or.exp5
-rw-r--r--unit-tests/cond-op-or.mk31
-rw-r--r--unit-tests/cond-op.exp4
-rw-r--r--unit-tests/cond-op.mk39
-rw-r--r--unit-tests/cond-short.mk46
-rw-r--r--unit-tests/cond-token-number.exp9
-rw-r--r--unit-tests/cond-token-number.mk56
-rw-r--r--unit-tests/cond-token-plain.exp28
-rw-r--r--unit-tests/cond-token-plain.mk89
-rw-r--r--unit-tests/cond-token-var.mk10
-rwxr-xr-xunit-tests/cond-undef-lint.exp7
-rwxr-xr-xunit-tests/cond-undef-lint.mk69
-rw-r--r--unit-tests/cond1.exp2
-rw-r--r--unit-tests/cond1.mk2
-rw-r--r--unit-tests/cond2.exp7
-rw-r--r--unit-tests/cond2.mk29
-rwxr-xr-xunit-tests/counter-append.exp2
-rwxr-xr-xunit-tests/counter-append.mk28
-rw-r--r--unit-tests/counter.exp88
-rw-r--r--unit-tests/counter.mk17
-rw-r--r--unit-tests/dep-colon-bug-cross-file.exp4
-rw-r--r--unit-tests/dep-colon-bug-cross-file.mk41
-rw-r--r--unit-tests/dep-colon.exp2
-rw-r--r--unit-tests/dep-colon.mk18
-rw-r--r--unit-tests/dep-double-colon-indep.exp3
-rw-r--r--unit-tests/dep-double-colon-indep.mk32
-rw-r--r--unit-tests/dep-double-colon.mk11
-rw-r--r--unit-tests/dep-percent.exp3
-rw-r--r--unit-tests/dep-percent.mk14
-rwxr-xr-xunit-tests/dep-var.exp4
-rwxr-xr-xunit-tests/dep-var.mk63
-rw-r--r--unit-tests/dep-wildcards.exp9
-rw-r--r--unit-tests/dep-wildcards.mk11
-rw-r--r--unit-tests/depsrc-end.exp4
-rw-r--r--unit-tests/depsrc-end.mk14
-rw-r--r--unit-tests/depsrc-exec.exp1
-rw-r--r--unit-tests/depsrc-exec.mk18
-rw-r--r--unit-tests/depsrc-made.exp2
-rw-r--r--unit-tests/depsrc-made.mk17
-rw-r--r--unit-tests/depsrc-make.exp2
-rw-r--r--unit-tests/depsrc-make.mk18
-rw-r--r--unit-tests/depsrc-notmain.exp1
-rw-r--r--unit-tests/depsrc-notmain.mk11
-rw-r--r--unit-tests/depsrc-optional.exp1
-rw-r--r--unit-tests/depsrc-optional.mk20
-rw-r--r--unit-tests/depsrc-phony.exp1
-rw-r--r--unit-tests/depsrc-phony.mk13
-rw-r--r--unit-tests/depsrc-recursive.exp2
-rw-r--r--unit-tests/depsrc-recursive.mk18
-rw-r--r--unit-tests/depsrc-wait.exp12
-rw-r--r--unit-tests/depsrc-wait.mk23
-rw-r--r--unit-tests/deptgt-delete_on_error.exp50
-rw-r--r--unit-tests/deptgt-delete_on_error.mk45
-rwxr-xr-xunit-tests/deptgt-end-jobs.exp8
-rwxr-xr-xunit-tests/deptgt-end-jobs.mk46
-rw-r--r--unit-tests/deptgt-end.exp11
-rw-r--r--unit-tests/deptgt-end.mk40
-rw-r--r--unit-tests/deptgt-makeflags.exp8
-rw-r--r--unit-tests/deptgt-makeflags.mk49
-rw-r--r--unit-tests/deptgt-silent.exp2
-rw-r--r--unit-tests/deptgt-silent.mk8
-rw-r--r--unit-tests/deptgt-suffixes.exp2
-rw-r--r--unit-tests/deptgt.exp6
-rw-r--r--unit-tests/deptgt.mk24
-rw-r--r--unit-tests/dir.exp65
-rw-r--r--unit-tests/dir.mk65
-rwxr-xr-xunit-tests/directive-dinclude.exp1
-rwxr-xr-xunit-tests/directive-dinclude.mk9
-rw-r--r--unit-tests/directive-else.mk16
-rw-r--r--unit-tests/directive-export-gmake.exp1
-rw-r--r--unit-tests/directive-export-gmake.mk64
-rw-r--r--unit-tests/directive-export-literal.exp1
-rw-r--r--unit-tests/directive-export-literal.mk11
-rw-r--r--unit-tests/directive-export.mk19
-rwxr-xr-xunit-tests/directive-for.exp18
-rwxr-xr-xunit-tests/directive-for.mk53
-rwxr-xr-xunit-tests/directive-hyphen-include.exp1
-rwxr-xr-xunit-tests/directive-hyphen-include.mk9
-rw-r--r--unit-tests/directive-ifndef.exp1
-rw-r--r--unit-tests/directive-ifndef.mk22
-rw-r--r--unit-tests/directive-ifnmake.exp2
-rw-r--r--unit-tests/directive-ifnmake.mk22
-rwxr-xr-xunit-tests/directive-include-fatal.exp4
-rwxr-xr-xunit-tests/directive-include-fatal.mk27
-rwxr-xr-xunit-tests/directive-include.exp5
-rwxr-xr-xunit-tests/directive-include.mk26
-rwxr-xr-xunit-tests/directive-sinclude.exp1
-rwxr-xr-xunit-tests/directive-sinclude.mk9
-rw-r--r--unit-tests/directive-undef.mk4
-rw-r--r--unit-tests/directive-unexport.exp4
-rw-r--r--unit-tests/directive-unexport.mk21
-rw-r--r--unit-tests/directives.mk14
-rw-r--r--unit-tests/doterror.mk2
-rw-r--r--unit-tests/dotwait.mk8
-rw-r--r--unit-tests/envfirst.mk14
-rw-r--r--unit-tests/error.exp2
-rw-r--r--unit-tests/error.mk2
-rw-r--r--unit-tests/escape.mk90
-rw-r--r--unit-tests/export-all.mk18
-rw-r--r--unit-tests/export-env.mk10
-rwxr-xr-xunit-tests/export-variants.mk14
-rw-r--r--unit-tests/export.exp1
-rw-r--r--unit-tests/export.mk25
-rw-r--r--unit-tests/forloop.exp2
-rw-r--r--unit-tests/forloop.mk56
-rw-r--r--unit-tests/forsubst.mk2
-rw-r--r--unit-tests/hanoi-include.exp32
-rw-r--r--unit-tests/hanoi-include.mk41
-rw-r--r--unit-tests/hash.exp9
-rw-r--r--unit-tests/hash.mk18
-rw-r--r--unit-tests/include-main.exp14
-rw-r--r--unit-tests/include-main.mk41
-rw-r--r--unit-tests/include-sub.mk46
-rw-r--r--unit-tests/include-subsub.mk8
-rw-r--r--unit-tests/job-output-long-lines.exp1
-rw-r--r--unit-tests/job-output-long-lines.mk32
-rwxr-xr-xunit-tests/lint.exp2
-rwxr-xr-xunit-tests/lint.mk4
-rwxr-xr-xunit-tests/make-exported.exp1
-rwxr-xr-xunit-tests/make-exported.mk21
-rw-r--r--unit-tests/misc.mk16
-rw-r--r--unit-tests/moderrs.exp95
-rw-r--r--unit-tests/moderrs.mk149
-rw-r--r--unit-tests/modmatch.mk16
-rw-r--r--unit-tests/modmisc.mk28
-rw-r--r--unit-tests/modts.mk19
-rw-r--r--unit-tests/modword.exp2
-rw-r--r--unit-tests/modword.mk36
-rw-r--r--unit-tests/opt-debug-all.exp1
-rw-r--r--unit-tests/opt-debug-all.mk10
-rw-r--r--unit-tests/opt-debug-archive.exp1
-rw-r--r--unit-tests/opt-debug-archive.mk9
-rw-r--r--unit-tests/opt-debug-cond.exp1
-rw-r--r--unit-tests/opt-debug-cond.mk10
-rw-r--r--unit-tests/opt-debug-curdir.exp1
-rw-r--r--unit-tests/opt-debug-curdir.mk8
-rw-r--r--unit-tests/opt-debug-dir.exp1
-rw-r--r--unit-tests/opt-debug-dir.mk9
-rw-r--r--unit-tests/opt-debug-errors.exp34
-rw-r--r--unit-tests/opt-debug-errors.mk42
-rw-r--r--unit-tests/opt-debug-file.exp1
-rw-r--r--unit-tests/opt-debug-file.mk37
-rw-r--r--unit-tests/opt-debug-for.exp22
-rw-r--r--unit-tests/opt-debug-for.mk26
-rw-r--r--[-rwxr-xr-x]unit-tests/opt-debug-graph1.exp (renamed from unit-tests/opt-debug-g1.exp)6
-rw-r--r--[-rwxr-xr-x]unit-tests/opt-debug-graph1.mk (renamed from unit-tests/opt-debug-g1.mk)8
-rw-r--r--unit-tests/opt-debug-graph2.exp1
-rw-r--r--unit-tests/opt-debug-graph2.mk9
-rw-r--r--unit-tests/opt-debug-graph3.exp1
-rw-r--r--unit-tests/opt-debug-graph3.mk9
-rw-r--r--unit-tests/opt-debug-hash.exp1
-rw-r--r--unit-tests/opt-debug-hash.mk10
-rw-r--r--unit-tests/opt-debug-jobs.exp25
-rw-r--r--unit-tests/opt-debug-jobs.mk26
-rw-r--r--unit-tests/opt-debug-lint.exp9
-rw-r--r--unit-tests/opt-debug-lint.mk72
-rw-r--r--unit-tests/opt-debug-loud.exp3
-rw-r--r--unit-tests/opt-debug-loud.mk22
-rw-r--r--unit-tests/opt-debug-making.exp1
-rw-r--r--unit-tests/opt-debug-making.mk9
-rw-r--r--unit-tests/opt-debug-meta.exp1
-rw-r--r--unit-tests/opt-debug-meta.mk9
-rw-r--r--unit-tests/opt-debug-no-rm.exp1
-rw-r--r--unit-tests/opt-debug-no-rm.mk13
-rw-r--r--unit-tests/opt-debug-parse.exp1
-rw-r--r--unit-tests/opt-debug-parse.mk9
-rw-r--r--unit-tests/opt-debug-suff.exp1
-rw-r--r--unit-tests/opt-debug-suff.mk11
-rw-r--r--unit-tests/opt-debug-targets.exp1
-rw-r--r--unit-tests/opt-debug-targets.mk11
-rw-r--r--unit-tests/opt-debug-var.exp1
-rw-r--r--unit-tests/opt-debug-var.mk9
-rw-r--r--unit-tests/opt-debug-varraw.exp1
-rw-r--r--unit-tests/opt-debug-varraw.mk12
-rw-r--r--unit-tests/opt-debug-x-trace.exp1
-rw-r--r--unit-tests/opt-debug-x-trace.mk10
-rw-r--r--unit-tests/opt-debug.exp3
-rw-r--r--unit-tests/opt-debug.mk12
-rw-r--r--unit-tests/opt-ignore.exp8
-rw-r--r--unit-tests/opt-ignore.mk4
-rw-r--r--unit-tests/opt-keep-going.exp2
-rw-r--r--unit-tests/opt-keep-going.mk4
-rw-r--r--unit-tests/parse-var.exp1
-rw-r--r--unit-tests/parse-var.mk13
-rw-r--r--unit-tests/phony-end.mk2
-rw-r--r--unit-tests/posix.mk2
-rw-r--r--unit-tests/posix1.mk18
-rw-r--r--unit-tests/qequals.mk8
-rw-r--r--unit-tests/recursive.mk20
-rwxr-xr-xunit-tests/sh-dots.exp11
-rwxr-xr-xunit-tests/sh-dots.mk21
-rw-r--r--unit-tests/shell-csh.exp9
-rw-r--r--unit-tests/shell-csh.mk40
-rw-r--r--unit-tests/shell-custom.exp8
-rw-r--r--unit-tests/shell-custom.mk14
-rw-r--r--unit-tests/shell-ksh.exp4
-rw-r--r--unit-tests/shell-ksh.mk11
-rw-r--r--unit-tests/shell-sh.exp4
-rw-r--r--unit-tests/shell-sh.mk12
-rw-r--r--unit-tests/suff-add-later.exp15
-rw-r--r--unit-tests/suff-add-later.mk34
-rw-r--r--unit-tests/suff-clear-regular.exp5
-rw-r--r--unit-tests/suff-clear-regular.mk31
-rw-r--r--unit-tests/suff-clear-single.exp3
-rw-r--r--unit-tests/suff-clear-single.mk19
-rw-r--r--unit-tests/suff-lookup.exp58
-rw-r--r--unit-tests/suff-lookup.mk40
-rw-r--r--unit-tests/suff-main.exp2
-rw-r--r--unit-tests/suff-main.mk22
-rw-r--r--unit-tests/suff-rebuild.exp5
-rw-r--r--unit-tests/suff-rebuild.mk33
-rw-r--r--unit-tests/suff-transform-endless.exp4
-rw-r--r--unit-tests/suff-transform-endless.mk36
-rw-r--r--unit-tests/suff-transform-expand.exp5
-rw-r--r--unit-tests/suff-transform-expand.mk25
-rw-r--r--unit-tests/suff-transform-select.exp4
-rw-r--r--unit-tests/suff-transform-select.mk28
-rw-r--r--unit-tests/suffixes.exp35
-rw-r--r--unit-tests/suffixes.mk89
-rw-r--r--unit-tests/sunshcmd.mk8
-rw-r--r--unit-tests/sysv.exp15
-rw-r--r--unit-tests/sysv.mk43
-rw-r--r--unit-tests/ternary.mk1
-rw-r--r--unit-tests/unexport-env.mk6
-rw-r--r--unit-tests/unexport.mk4
-rw-r--r--unit-tests/var-class-local.exp1
-rw-r--r--unit-tests/var-class-local.mk27
-rw-r--r--unit-tests/var-op-append.exp6
-rw-r--r--unit-tests/var-op-append.mk43
-rw-r--r--unit-tests/var-op-assign.mk12
-rw-r--r--unit-tests/var-op-sunsh.exp1
-rw-r--r--unit-tests/var-op-sunsh.mk122
-rw-r--r--unit-tests/var-recursive.exp12
-rw-r--r--unit-tests/var-recursive.mk49
-rw-r--r--unit-tests/varcmd.mk20
-rw-r--r--unit-tests/vardebug.exp78
-rw-r--r--unit-tests/vardebug.mk17
-rw-r--r--unit-tests/varmisc.mk41
-rw-r--r--unit-tests/varmod-assign.exp2
-rw-r--r--unit-tests/varmod-assign.mk38
-rw-r--r--unit-tests/varmod-defined.mk67
-rw-r--r--unit-tests/varmod-edge.mk6
-rw-r--r--unit-tests/varmod-exclam-shell.mk12
-rw-r--r--unit-tests/varmod-gmtime.exp22
-rw-r--r--unit-tests/varmod-gmtime.mk68
-rw-r--r--unit-tests/varmod-hash.mk58
-rw-r--r--unit-tests/varmod-ifelse.exp9
-rw-r--r--unit-tests/varmod-ifelse.mk54
-rw-r--r--unit-tests/varmod-l-name-to-value.mk23
-rw-r--r--unit-tests/varmod-localtime.exp23
-rw-r--r--unit-tests/varmod-localtime.mk97
-rw-r--r--unit-tests/varmod-loop.exp1
-rw-r--r--unit-tests/varmod-loop.mk43
-rwxr-xr-xunit-tests/varmod-match-escape.exp60
-rwxr-xr-xunit-tests/varmod-match-escape.mk60
-rw-r--r--unit-tests/varmod-match.exp15
-rw-r--r--unit-tests/varmod-match.mk55
-rw-r--r--unit-tests/varmod-order-reverse.mk4
-rw-r--r--unit-tests/varmod-order-shuffle.mk10
-rw-r--r--unit-tests/varmod-order.mk4
-rw-r--r--unit-tests/varmod-quote.mk16
-rw-r--r--unit-tests/varmod-range.exp16
-rw-r--r--unit-tests/varmod-range.mk98
-rw-r--r--unit-tests/varmod-subst-regex.mk39
-rw-r--r--unit-tests/varmod-subst.mk36
-rw-r--r--unit-tests/varmod-sysv.exp13
-rw-r--r--unit-tests/varmod-sysv.mk272
-rw-r--r--unit-tests/varmod-to-lower.mk8
-rw-r--r--unit-tests/varmod-to-separator.exp20
-rw-r--r--unit-tests/varmod-to-separator.mk59
-rw-r--r--unit-tests/varmod-to-upper.mk8
-rw-r--r--unit-tests/varmod-undefined.mk32
-rw-r--r--unit-tests/varmod.exp7
-rw-r--r--unit-tests/varmod.mk51
-rw-r--r--unit-tests/varname-dot-curdir.mk43
-rwxr-xr-xunit-tests/varname-dot-includes.mk6
-rwxr-xr-xunit-tests/varname-dot-libs.mk6
-rw-r--r--unit-tests/varname-dot-newline.mk6
-rw-r--r--unit-tests/varname-dot-parsedir.exp4
-rw-r--r--unit-tests/varname-dot-parsedir.mk41
-rw-r--r--unit-tests/varname-dot-parsefile.exp4
-rw-r--r--unit-tests/varname-dot-parsefile.mk36
-rw-r--r--unit-tests/varname-dot-path.mk54
-rwxr-xr-xunit-tests/varname-dot-shell.exp33
-rwxr-xr-xunit-tests/varname-dot-shell.mk19
-rw-r--r--unit-tests/varname-empty.exp2
-rwxr-xr-xunit-tests/varname-empty.mk46
-rw-r--r--unit-tests/varname-make_print_var_on_error-jobs.exp7
-rw-r--r--unit-tests/varname-make_print_var_on_error-jobs.mk21
-rw-r--r--unit-tests/varname-make_print_var_on_error.exp9
-rw-r--r--unit-tests/varname-make_print_var_on_error.mk16
-rwxr-xr-xunit-tests/varname-makefile.exp1
-rwxr-xr-xunit-tests/varname-makefile.mk44
-rw-r--r--unit-tests/varname.mk2
-rw-r--r--unit-tests/varparse-dynamic.mk13
-rw-r--r--unit-tests/varparse-mod.exp1
-rw-r--r--unit-tests/varparse-mod.mk61
-rw-r--r--unit-tests/varparse-undef-partial.exp1
-rw-r--r--unit-tests/varparse-undef-partial.mk64
-rw-r--r--unit-tests/varshell.exp6
-rw-r--r--unit-tests/varshell.mk19
-rw-r--r--util.c29
-rw-r--r--var.c2837
387 files changed, 15726 insertions, 12190 deletions
diff --git a/ChangeLog b/ChangeLog
index 847c2e4c0f90..82995f735b2d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,267 @@
+2020-11-01 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20201101
+ Merge with NetBSD make, pick up
+ o negate NoExecute to GNode_ShouldExecute
+ o job.c: rename JobMatchShell to FindShellByName
+ extract EscapeShellDblQuot from JobPrintCommand
+ extract ParseRunOptions from JobPrintCommand
+ o var.c: extract ApplyModifiersIndirect from ApplyModifiers
+ treat malformed :range, :ts and :[...] as errors
+ add tests for the variable modifiers :[words] and :range
+
+2020-10-31 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20201031
+ Merge with NetBSD make, pick up
+ o format #include directives consistently
+ o do not look up local variables like .TARGET anywhere else
+ o main.c: Main_SetObjdir is first called for curdir which may be
+ readonly
+ reduce the scope where recursive expressions are detected
+ remove redundant :tl from getBoolean
+ clean up mkTempFile
+ o meta.c: simplify memory allocation in meta_create and meta_oodate
+ o parse.c: extract loadedfile_mmap from loadfile
+ o trace.c: document possible undefined behavior with .CURDIR
+ o var.c: make parsing of the :gmtime and :localtime modifiers stricter
+ rename ismeta to is_shell_metachar
+ remove debug logging for the :Q variable modifier
+ rename VarIsDynamic to VarnameIsDynamic
+ use consistent parameter order in varname parsing functions
+ extract ParseVarnameLong from Var_Parse
+ extract ParseVarnameShort from Var_Parse
+ fix type of ParseModifierPart parameter delim
+ extract IsEscapedModifierPart from ParseModifierPart
+ clean up ModifyWords
+ add test for combining the :@ and :? variable modifiers
+
+2020-10-30 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20201030
+ Merge with NetBSD make, pick up
+ o change char * to void * in Var_Value
+ o make iterating over HashTable simpler
+ o rename VAR_CMD to VAR_CMDLINE
+ o cond.c: clean up is_separator
+ fix parse error in string literal in conditional
+ o main.c: do not use objdir that is not writable
+ in lint mode, exit with error status on errors
+ o parse.c: clean up StrContainsWord
+ fix out-of-bounds pointer in ParseTrackInput
+ o var.c: rename Str_SYSVMatch and its parameters
+ remove unsatisfiable conditions in Var_Set_with_flags
+ document where the variable name is expanded
+ fix documentation for VARP_SUB_ONE
+ rename VAR_EXPORTED_YES to VAR_EXPORTED_SOME
+ document VAR_READONLY
+ prevent appending to read-only variables
+ extract MayExport from Var_Export1
+ remove redundant evaluations in VarFind
+ replace VarFindFlags with a simple Boolean
+ rename FIND_CMD to FIND_CMDLINE, to match VAR_CMDLINE
+
+2020-10-28 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20201028
+ Merge with NetBSD make, pick up
+ o rename defIncPath to defSysIncPath
+ o initialize all CmdOpts fields
+ o lst.c: inline Vector_Get
+ o main.c: refactor main extract
+ InitMaxJobs,InitObjdir,InitVarMake,InitRandom,
+ ReadMakefiles,CleanUp,InitVpath,ReadBuiltinRules,
+ InitDefIncPath,CmdOpts_Init,UnlimitFiles
+ o parse.c: merge curFile into includes
+ rename predecessor to order_pred
+ sort ParseSpecial alphabetically
+ remove unused, undocumented .NOEXPORT
+ rename ParseSpecial enum values consistently
+ rename some fields of struct IFile
+
+2020-10-26 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20201026
+ Merge with NetBSD make, pick up
+ o group the command line options and arguments into a struct
+ o rename GNode.cmgn to youngestChild
+ o rename hash functions to identify the type name
+ o negate OP_NOP and rename it to GNode_IsTarget
+ o add GNode_Path to access the path of a GNode
+ o remove macros MIN and MAX
+ o remove unused Lst_Find and Lst_FindFrom
+ o arch.c: and make Arch_FindLib simpler
+ clean up code layout
+ make Arch_ParseArchive simpler
+ o cond.c: inline CondFindStrMatch into FuncMake
+ o dir.c: replace Dir_CopyDir with Dir_CopyDirSearchPath
+ omit trailing space in debug output for expanding file patterns
+ refactor DirMatchFiles
+ document that the SearchPath of Dir_FindFile may be NULL
+ remove UNCONST from Dir_Expand
+ inline DirFindName
+ o for.c: clean up code for handling .for loops
+ o hash.c: print hash in debug log with fixed width
+ clean up hash table functions
+ reduce amount of string hashing
+ o job.c: refactor JobDeleteTarget
+ use proper enum constants for aborting
+ convert result of JobStart from macros to enum
+ convert abort reason macros to enum
+ rework Job_CheckCommands to reduce indentation
+ rename Shell fields
+ add field names in declaration of DEFSHELL_CUSTOM
+ convert JobState and JobFlags to enum types
+ move handling of the "..." command to JobPrintCommands
+ o lst.c: clean up
+ refactor LstNodeNew
+ remove Lst_Open, Lst_Next, Lst_Close
+ remove code for circular lists from Lst_Next
+ o main.c: do not attempt to read .MAKE.DEPENFILE if set to
+ /dev/null or anything starting with "no"
+ convert macros for debug flags into enum
+ o make.c: inline Lst_Copy in Make_ExpandUse
+ o meta.c: inline Lst_Find in meta_oodate
+ make Lst_RemoveIf simpler in meta_oodate
+ o parse.c: convert error level for Parse_Error to an enum
+ o suff.c: properly terminate debug output with newline
+ add more details to DEBUG_SRC log
+ replace Dir_CopyDir with Dir_CopyDirSearchPath
+ don't modify GNode name while rebuilding the suffix graph
+ o var.c: reduce duplicate code in VarFind
+
+2020-10-22 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20201022
+ Merge with NetBSD make, pick up
+ o more refactoring and simplification to reduce code size
+ o var.c: extract CanonicalVarname from VarFind
+ o make.c: extract UpdateImplicitParentsVars from Make_Update
+ o main.c: extract PrintVar from doPrintVars
+ extract HandlePWD from main
+ o lst.c: inline simple Lst getters
+ remove unused Lst_ForEach
+ o job.c: move struct Shell from job.h to job.c
+ o more unit tests
+
+2020-10-19 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * configure.in: remove inappropriate use of AC_INCLUDES_DEFAULT
+
+2020-10-18 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20201018
+ Merge with NetBSD make, pick up
+ o remove USE_IOVEC
+ o rename some Hash_* apis to Hash*
+ o replace execError with execDie
+ o rename Lst_Init to Lst_New
+ o add tags to enum types
+ o rename Stack to Vector
+ o parse.c: more refactoring
+ o unit-tests: make some tests use line buffered stdout
+ o unit-tests/Makefile: in meta mode do not make all tests depend on
+ Makefile, it isn't necessary.
+
+2020-10-10 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * main.c: check for CTL_HW being defined.
+ * unit-tests/Makefile: ensure export tests output are POSIX compliant
+ disable opt-debug-jobs test until it works on ubuntu
+
+ * VERSION (_MAKE_VERSION): 20201010
+ Merge with NetBSD make, pick up
+ o dir.c: remove pathname limit for Dir_FindHereOrAbove
+ o hash.c: replace strcpy with memcpy in Hash_CreateEntry
+ o main.c: extract init_machine and init_machine_arch from main
+ allow to disable debug logging options
+ o parse.c: enable format string truncation warnings
+ extract parsing of sources from ParseDoDependency
+ split ParseDoSrc into smaller functions
+ hide implementation details from Parse_DoVar
+ clean up parsing of variable assignments
+ split Parse_DoVar into manageable pieces
+ don't modify the given line during Parse_DoVar
+ fix out-of-bounds memory access in Parse_DoVar
+ fix parsing of the :sh assignment modifier
+ o var.c: rework memory allocation for the name of variables
+ extract ApplyModifier_Literal into separate function
+ in lint mode, reject modifiers without delimiter
+ do not export variable names starting with '-'
+ o fix double-free bug in -DCLEANUP mode
+ o more cleanup to enable higher warnings level
+ o more unit tests
+
+2020-10-02 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20201002
+ Merge with NetBSD make, pick up
+ o dir.c: use hash table for looking up open directories by name
+ o main.c: clean up option handling
+ o parse.c: add missing const for Parse_AddIncludeDir
+ o var.c: ApplyModifier_To, update pp in each branch
+ o remove redundant function prototypes
+ o more unit tests
+
+2020-10-01 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20201001
+ Merge with NetBSD make, pick up
+ o compat.c: comment about "..."
+
+2020-09-30 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200930
+ Merge with NetBSD make, pick up
+ o job.c: split Job.jobPipe into 2 separate fields
+ replace Lst_Open with direct iteration
+ o lst.c: remove redundant assertions
+ o targ.c: replace Lst_Open with direct iteration
+ o var.c: fix bug in evaluation of indirect variable modifiers
+ extract ApplyModifier_Quote into separate function
+ o make debug logging simpler
+
+2020-09-27 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200927
+ Merge with NetBSD make, pick up
+ o parse.c: ensure parse errors result in 'stopped in' message.
+ o compat.c: make parameter of Compat_RunCommand const
+ o main.c: extract InitVarTarget from main
+ o parse.c: rename ParseFinishLine to FinishDependencyGroup
+ refactor ParseDoDependency
+ o var.c: Var_Subst no longer returns string result
+ rename Var_ParsePP back to Var_Parse
+ in lint mode, improve error handling for undefined variables
+ extract ParseVarname from Var_Parse
+ o rename Lst_ForEach to Lst_ForEachUntil
+ o inline Lst_ForEachUntil in several cases
+ o clean up API for finding and creating GNodes
+ o fix assertion failure in -j mode with .END node
+ o inline and remove LstNode_Prev and LstNode_Next
+ o use fine-grained type names for lists and their nodes
+ o more unit tests
+
+2020-09-11 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20200911
+ Merge with NetBSD make, pick up
+ o cond.c: split EvalComparison into smaller functions
+ reorder parameters of condition parsing functions
+ reduce code size in CondParser_Eval
+ rename CondGetString to CondParser_String
+ add CondLexer_SkipWhitespace
+ group the condition parsing state into a struct
+ in CondGetString, replace repeated Buf_Add with Buf_AddStr
+ o migrate Var_Parse to Var_ParsePP
+ o add wrappers around ctype.h functions
+ o lst.c: use a stack instead of a list for the nested include path
+ o more unit tests
+
+2020-09-04 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * make-bootstrap.sh.in: adjust object list
+
2020-09-02 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20200902
diff --git a/FILES b/FILES
index 33127290a76e..f8687ad3a1d9 100644
--- a/FILES
+++ b/FILES
@@ -65,18 +65,16 @@ sigcompat.c
str.c
stresep.c
strlcpy.c
-strlist.c
-strlist.h
suff.c
targ.c
trace.c
trace.h
unit-tests/Makefile
unit-tests/Makefile.config.in
-unit-tests/archive.exp
-unit-tests/archive.mk
unit-tests/archive-suffix.exp
unit-tests/archive-suffix.mk
+unit-tests/archive.exp
+unit-tests/archive.mk
unit-tests/cmd-interrupt.exp
unit-tests/cmd-interrupt.mk
unit-tests/cmdline.exp
@@ -99,8 +97,8 @@ unit-tests/cond-cmp-numeric.exp
unit-tests/cond-cmp-numeric.mk
unit-tests/cond-cmp-string.exp
unit-tests/cond-cmp-string.mk
-unit-tests/cond-func.exp
-unit-tests/cond-func.mk
+unit-tests/cond-cmp-unary.exp
+unit-tests/cond-cmp-unary.mk
unit-tests/cond-func-commands.exp
unit-tests/cond-func-commands.mk
unit-tests/cond-func-defined.exp
@@ -113,6 +111,8 @@ unit-tests/cond-func-make.exp
unit-tests/cond-func-make.mk
unit-tests/cond-func-target.exp
unit-tests/cond-func-target.mk
+unit-tests/cond-func.exp
+unit-tests/cond-func.mk
unit-tests/cond-late.exp
unit-tests/cond-late.mk
unit-tests/cond-op-and.exp
@@ -135,26 +135,36 @@ unit-tests/cond-token-string.exp
unit-tests/cond-token-string.mk
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/cond2.exp
-unit-tests/cond2.mk
+unit-tests/counter-append.exp
+unit-tests/counter-append.mk
unit-tests/counter.exp
unit-tests/counter.mk
+unit-tests/dep-colon-bug-cross-file.exp
+unit-tests/dep-colon-bug-cross-file.mk
unit-tests/dep-colon.exp
unit-tests/dep-colon.mk
+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-exclam.exp
unit-tests/dep-exclam.mk
unit-tests/dep-none.exp
unit-tests/dep-none.mk
+unit-tests/dep-percent.exp
+unit-tests/dep-percent.mk
unit-tests/dep-var.exp
unit-tests/dep-var.mk
unit-tests/dep-wildcards.exp
unit-tests/dep-wildcards.mk
unit-tests/dep.exp
unit-tests/dep.mk
+unit-tests/depsrc-end.exp
+unit-tests/depsrc-end.mk
unit-tests/depsrc-exec.exp
unit-tests/depsrc-exec.mk
unit-tests/depsrc-ignore.exp
@@ -185,10 +195,10 @@ unit-tests/depsrc-silent.exp
unit-tests/depsrc-silent.mk
unit-tests/depsrc-use.exp
unit-tests/depsrc-use.mk
-unit-tests/depsrc-usebefore.exp
-unit-tests/depsrc-usebefore.mk
unit-tests/depsrc-usebefore-double-colon.exp
unit-tests/depsrc-usebefore-double-colon.mk
+unit-tests/depsrc-usebefore.exp
+unit-tests/depsrc-usebefore.mk
unit-tests/depsrc-wait.exp
unit-tests/depsrc-wait.mk
unit-tests/depsrc.exp
@@ -199,6 +209,8 @@ unit-tests/deptgt-default.exp
unit-tests/deptgt-default.mk
unit-tests/deptgt-delete_on_error.exp
unit-tests/deptgt-delete_on_error.mk
+unit-tests/deptgt-end-jobs.exp
+unit-tests/deptgt-end-jobs.mk
unit-tests/deptgt-end.exp
unit-tests/deptgt-end.mk
unit-tests/deptgt-error.exp
@@ -239,10 +251,12 @@ unit-tests/deptgt-suffixes.exp
unit-tests/deptgt-suffixes.mk
unit-tests/deptgt.exp
unit-tests/deptgt.mk
-unit-tests/dir.exp
-unit-tests/dir.mk
unit-tests/dir-expand-path.exp
unit-tests/dir-expand-path.mk
+unit-tests/dir.exp
+unit-tests/dir.mk
+unit-tests/directive-dinclude.exp
+unit-tests/directive-dinclude.mk
unit-tests/directive-elif.exp
unit-tests/directive-elif.mk
unit-tests/directive-elifdef.exp
@@ -261,14 +275,18 @@ unit-tests/directive-error.exp
unit-tests/directive-error.mk
unit-tests/directive-export-env.exp
unit-tests/directive-export-env.mk
+unit-tests/directive-export-gmake.exp
+unit-tests/directive-export-gmake.mk
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.exp
-unit-tests/directive-for.mk
unit-tests/directive-for-generating-endif.exp
unit-tests/directive-for-generating-endif.mk
+unit-tests/directive-for.exp
+unit-tests/directive-for.mk
+unit-tests/directive-hyphen-include.exp
+unit-tests/directive-hyphen-include.mk
unit-tests/directive-if.exp
unit-tests/directive-if.mk
unit-tests/directive-ifdef.exp
@@ -279,8 +297,14 @@ unit-tests/directive-ifndef.exp
unit-tests/directive-ifndef.mk
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.exp
+unit-tests/directive-include.mk
unit-tests/directive-info.exp
unit-tests/directive-info.mk
+unit-tests/directive-sinclude.exp
+unit-tests/directive-sinclude.mk
unit-tests/directive-undef.exp
unit-tests/directive-undef.mk
unit-tests/directive-unexport-env.exp
@@ -317,20 +341,20 @@ unit-tests/forloop.exp
unit-tests/forloop.mk
unit-tests/forsubst.exp
unit-tests/forsubst.mk
-unit-tests/hash.exp
-unit-tests/hash.mk
+unit-tests/hanoi-include.exp
+unit-tests/hanoi-include.mk
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/job-output-long-lines.exp
+unit-tests/job-output-long-lines.mk
unit-tests/lint.exp
unit-tests/lint.mk
unit-tests/make-exported.exp
unit-tests/make-exported.mk
-unit-tests/misc.exp
-unit-tests/misc.mk
unit-tests/moderrs.exp
unit-tests/moderrs.mk
unit-tests/modmatch.exp
@@ -345,10 +369,56 @@ unit-tests/opt-backwards.exp
unit-tests/opt-backwards.mk
unit-tests/opt-chdir.exp
unit-tests/opt-chdir.mk
+unit-tests/opt-debug-all.exp
+unit-tests/opt-debug-all.mk
+unit-tests/opt-debug-archive.exp
+unit-tests/opt-debug-archive.mk
+unit-tests/opt-debug-cond.exp
+unit-tests/opt-debug-cond.mk
+unit-tests/opt-debug-curdir.exp
+unit-tests/opt-debug-curdir.mk
+unit-tests/opt-debug-dir.exp
+unit-tests/opt-debug-dir.mk
+unit-tests/opt-debug-errors.exp
+unit-tests/opt-debug-errors.mk
+unit-tests/opt-debug-file.exp
+unit-tests/opt-debug-file.mk
+unit-tests/opt-debug-for.exp
+unit-tests/opt-debug-for.mk
+unit-tests/opt-debug-graph1.exp
+unit-tests/opt-debug-graph1.mk
+unit-tests/opt-debug-graph2.exp
+unit-tests/opt-debug-graph2.mk
+unit-tests/opt-debug-graph3.exp
+unit-tests/opt-debug-graph3.mk
+unit-tests/opt-debug-hash.exp
+unit-tests/opt-debug-hash.mk
+unit-tests/opt-debug-jobs.exp
+unit-tests/opt-debug-jobs.mk
+unit-tests/opt-debug-lint.exp
+unit-tests/opt-debug-lint.mk
+unit-tests/opt-debug-loud.exp
+unit-tests/opt-debug-loud.mk
+unit-tests/opt-debug-making.exp
+unit-tests/opt-debug-making.mk
+unit-tests/opt-debug-meta.exp
+unit-tests/opt-debug-meta.mk
+unit-tests/opt-debug-no-rm.exp
+unit-tests/opt-debug-no-rm.mk
+unit-tests/opt-debug-parse.exp
+unit-tests/opt-debug-parse.mk
+unit-tests/opt-debug-suff.exp
+unit-tests/opt-debug-suff.mk
+unit-tests/opt-debug-targets.exp
+unit-tests/opt-debug-targets.mk
+unit-tests/opt-debug-var.exp
+unit-tests/opt-debug-var.mk
+unit-tests/opt-debug-varraw.exp
+unit-tests/opt-debug-varraw.mk
+unit-tests/opt-debug-x-trace.exp
+unit-tests/opt-debug-x-trace.mk
unit-tests/opt-debug.exp
unit-tests/opt-debug.mk
-unit-tests/opt-debug-g1.exp
-unit-tests/opt-debug-g1.mk
unit-tests/opt-define.exp
unit-tests/opt-define.mk
unit-tests/opt-env.exp
@@ -395,6 +465,8 @@ unit-tests/opt.exp
unit-tests/opt.mk
unit-tests/order.exp
unit-tests/order.mk
+unit-tests/parse-var.exp
+unit-tests/parse-var.mk
unit-tests/phony-end.exp
unit-tests/phony-end.mk
unit-tests/posix.exp
@@ -425,12 +497,34 @@ unit-tests/sh-single-line.exp
unit-tests/sh-single-line.mk
unit-tests/sh.exp
unit-tests/sh.mk
-unit-tests/suffixes.exp
-unit-tests/suffixes.mk
+unit-tests/shell-csh.exp
+unit-tests/shell-csh.mk
+unit-tests/shell-custom.exp
+unit-tests/shell-custom.mk
+unit-tests/shell-ksh.exp
+unit-tests/shell-ksh.mk
+unit-tests/shell-sh.exp
+unit-tests/shell-sh.mk
+unit-tests/suff-add-later.exp
+unit-tests/suff-add-later.mk
+unit-tests/suff-clear-regular.exp
+unit-tests/suff-clear-regular.mk
+unit-tests/suff-clear-single.exp
+unit-tests/suff-clear-single.mk
+unit-tests/suff-lookup.exp
+unit-tests/suff-lookup.mk
+unit-tests/suff-main.exp
+unit-tests/suff-main.mk
+unit-tests/suff-rebuild.exp
+unit-tests/suff-rebuild.mk
+unit-tests/suff-transform-endless.exp
+unit-tests/suff-transform-endless.mk
+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/sunshcmd.exp
unit-tests/sunshcmd.mk
-unit-tests/sysv.exp
-unit-tests/sysv.mk
unit-tests/ternary.exp
unit-tests/ternary.mk
unit-tests/unexport-env.exp
@@ -461,8 +555,12 @@ unit-tests/var-op-expand.exp
unit-tests/var-op-expand.mk
unit-tests/var-op-shell.exp
unit-tests/var-op-shell.mk
+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-recursive.exp
+unit-tests/var-recursive.mk
unit-tests/varcmd.exp
unit-tests/varcmd.mk
unit-tests/vardebug.exp
@@ -555,12 +653,12 @@ unit-tests/varname-dot-alltargets.exp
unit-tests/varname-dot-alltargets.mk
unit-tests/varname-dot-curdir.exp
unit-tests/varname-dot-curdir.mk
-unit-tests/varname-dot-includes.exp
-unit-tests/varname-dot-includes.mk
unit-tests/varname-dot-includedfromdir.exp
unit-tests/varname-dot-includedfromdir.mk
unit-tests/varname-dot-includedfromfile.exp
unit-tests/varname-dot-includedfromfile.mk
+unit-tests/varname-dot-includes.exp
+unit-tests/varname-dot-includes.mk
unit-tests/varname-dot-libs.exp
unit-tests/varname-dot-libs.mk
unit-tests/varname-dot-make-dependfile.exp
@@ -623,8 +721,12 @@ unit-tests/varname-empty.exp
unit-tests/varname-empty.mk
unit-tests/varname-make.exp
unit-tests/varname-make.mk
+unit-tests/varname-make_print_var_on_error-jobs.exp
+unit-tests/varname-make_print_var_on_error-jobs.mk
unit-tests/varname-make_print_var_on_error.exp
unit-tests/varname-make_print_var_on_error.mk
+unit-tests/varname-makefile.exp
+unit-tests/varname-makefile.mk
unit-tests/varname-makeflags.exp
unit-tests/varname-makeflags.mk
unit-tests/varname-pwd.exp
@@ -635,6 +737,10 @@ unit-tests/varname.exp
unit-tests/varname.mk
unit-tests/varparse-dynamic.exp
unit-tests/varparse-dynamic.mk
+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
unit-tests/varshell.exp
diff --git a/Makefile b/Makefile
index 7277d9554de4..248af9a8bcdf 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.112 2020/08/28 16:26:17 sjg Exp $
+# $Id: Makefile,v 1.113 2020/10/26 17:55:09 sjg Exp $
PROG= bmake
@@ -20,7 +20,6 @@ SRCS= \
metachar.c \
parse.c \
str.c \
- strlist.c \
suff.c \
targ.c \
trace.c \
diff --git a/VERSION b/VERSION
index b13b442cd4a4..51afe049fb7a 100644
--- a/VERSION
+++ b/VERSION
@@ -1,2 +1,2 @@
# keep this compatible with sh and make
-_MAKE_VERSION=20200902
+_MAKE_VERSION=20201101
diff --git a/arch.c b/arch.c
index fa54908d8817..d0bc901171f6 100644
--- a/arch.c
+++ b/arch.c
@@ -1,4 +1,4 @@
-/* $NetBSD: arch.c,v 1.107 2020/08/30 11:15:05 rillig Exp $ */
+/* $NetBSD: arch.c,v 1.151 2020/10/31 18:41:07 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -68,19 +68,6 @@
* SUCH DAMAGE.
*/
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: arch.c,v 1.107 2020/08/30 11:15:05 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)arch.c 8.2 (Berkeley) 1/2/94";
-#else
-__RCSID("$NetBSD: arch.c,v 1.107 2020/08/30 11:15:05 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
/*-
* arch.c --
* Functions to manipulate libraries, archives and their members.
@@ -92,52 +79,53 @@ __RCSID("$NetBSD: arch.c,v 1.107 2020/08/30 11:15:05 rillig Exp $");
* is referenced.
*
* The interface to this module is:
- * Arch_ParseArchive Given an archive specification, return a list
- * of GNode's, one for each member in the spec.
- * FALSE is returned if the specification is
- * invalid for some reason.
+ * Arch_ParseArchive
+ * Given an archive specification, return a list
+ * of GNode's, one for each member in the spec.
+ * FALSE is returned if the specification is
+ * invalid for some reason.
*
- * Arch_Touch Alter the modification time of the archive
- * member described by the given node to be
- * the current time.
+ * Arch_Touch Alter the modification time of the archive
+ * member described by the given node to be
+ * the current time.
*
- * Arch_TouchLib Update the modification time of the library
- * described by the given node. This is special
- * because it also updates the modification time
- * of the library's table of contents.
+ * Arch_TouchLib Update the modification time of the library
+ * described by the given node. This is special
+ * because it also updates the modification time
+ * of the library's table of contents.
*
- * Arch_MTime Find the modification time of a member of
- * an archive *in the archive*. The time is also
- * placed in the member's GNode. Returns the
- * modification time.
+ * Arch_MTime Find the modification time of a member of
+ * an archive *in the archive*. The time is also
+ * placed in the member's GNode. Returns the
+ * modification time.
*
- * Arch_MemTime Find the modification time of a member of
- * an archive. Called when the member doesn't
- * already exist. Looks in the archive for the
- * modification time. Returns the modification
- * time.
+ * Arch_MemTime Find the modification time of a member of
+ * an archive. Called when the member doesn't
+ * already exist. Looks in the archive for the
+ * modification time. Returns the modification
+ * time.
*
- * Arch_FindLib Search for a library along a path. The
- * library name in the GNode should be in
- * -l<name> format.
+ * Arch_FindLib Search for a library along a path. The
+ * library name in the GNode should be in
+ * -l<name> format.
*
- * Arch_LibOODate Special function to decide if a library node
- * is out-of-date.
+ * Arch_LibOODate Special function to decide if a library node
+ * is out-of-date.
*
- * Arch_Init Initialize this module.
+ * Arch_Init Initialize this module.
*
- * Arch_End Cleanup this module.
+ * Arch_End Clean up this module.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/param.h>
#ifdef HAVE_AR_H
-#include <ar.h>
+#include <ar.h>
#else
struct ar_hdr {
char ar_name[16]; /* name */
@@ -153,17 +141,17 @@ struct ar_hdr {
};
#endif
#if defined(HAVE_RANLIB_H) && !(defined(__ELF__) || defined(NO_RANLIB))
-#include <ranlib.h>
+#include <ranlib.h>
#endif
-#include <stdio.h>
-#include <stdlib.h>
#ifdef HAVE_UTIME_H
-#include <utime.h>
+#include <utime.h>
#endif
-#include "make.h"
-#include "hash.h"
-#include "dir.h"
+#include "make.h"
+#include "dir.h"
+
+/* "@(#)arch.c 8.2 (Berkeley) 1/2/94" */
+MAKE_RCSID("$NetBSD: arch.c,v 1.151 2020/10/31 18:41:07 rillig Exp $");
#ifdef TARGET_MACHINE
#undef MAKE_MACHINE
@@ -174,17 +162,19 @@ struct ar_hdr {
#define MAKE_MACHINE_ARCH TARGET_MACHINE_ARCH
#endif
-static Lst archives; /* Lst of archives we've already examined */
+typedef struct List ArchList;
+typedef struct ListNode ArchListNode;
+
+static ArchList *archives; /* The archives we've already examined */
typedef struct Arch {
- char *name; /* Name of archive */
- Hash_Table members; /* All the members of the archive described
- * by <name, struct ar_hdr *> key/value pairs */
- char *fnametab; /* Extended name table strings */
- size_t fnamesize; /* Size of the string table */
+ char *name; /* Name of archive */
+ HashTable members; /* All the members of the archive described
+ * by <name, struct ar_hdr *> key/value pairs */
+ char *fnametab; /* Extended name table strings */
+ size_t fnamesize; /* Size of the string table */
} Arch;
-static struct ar_hdr *ArchStatMember(const char *, const char *, Boolean);
static FILE *ArchFindMember(const char *, const char *,
struct ar_hdr *, const char *);
#if defined(__svr4__) || defined(__SVR4) || defined(__ELF__)
@@ -219,25 +209,22 @@ static int ArchSVR4Entry(Arch *, char *, size_t, FILE *);
# define SARMAG 8
#endif
-#define AR_MAX_NAME_LEN (sizeof(arh.AR_NAME)-1)
#ifdef CLEANUP
static void
ArchFree(void *ap)
{
- Arch *a = (Arch *)ap;
- Hash_Search search;
- Hash_Entry *entry;
+ Arch *a = ap;
+ HashIter hi;
/* Free memory from hash entries */
- for (entry = Hash_EnumFirst(&a->members, &search);
- entry != NULL;
- entry = Hash_EnumNext(&search))
- free(Hash_GetValue(entry));
+ HashIter_Init(&hi, &a->members);
+ while (HashIter_Next(&hi) != NULL)
+ free(hi.entry->value);
free(a->name);
free(a->fnametab);
- Hash_DeleteTable(&a->members);
+ HashTable_Done(&a->members);
free(a);
}
#endif
@@ -262,46 +249,49 @@ ArchFree(void *ap)
*-----------------------------------------------------------------------
*/
Boolean
-Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt)
+Arch_ParseArchive(char **linePtr, GNodeList *nodeLst, GNode *ctxt)
{
- char *cp; /* Pointer into line */
- GNode *gn; /* New node */
- char *libName; /* Library-part of specification */
- char *memName; /* Member-part of specification */
- char saveChar; /* Ending delimiter of member-name */
- Boolean subLibName; /* TRUE if libName should have/had
- * variable substitution performed on it */
+ char *cp; /* Pointer into line */
+ GNode *gn; /* New node */
+ char *libName; /* Library-part of specification */
+ char *memName; /* Member-part of specification */
+ char saveChar; /* Ending delimiter of member-name */
+ Boolean subLibName; /* TRUE if libName should have/had
+ * variable substitution performed on it */
libName = *linePtr;
subLibName = FALSE;
- for (cp = libName; *cp != '(' && *cp != '\0'; cp++) {
+ for (cp = libName; *cp != '(' && *cp != '\0';) {
if (*cp == '$') {
/*
* Variable spec, so call the Var module to parse the puppy
* so we can safely advance beyond it...
*/
- int length;
- void *result_freeIt;
- const char *result;
+ const char *nested_p = cp;
+ void *result_freeIt;
+ const char *result;
Boolean isError;
- result = Var_Parse(cp, ctxt, VARE_UNDEFERR|VARE_WANTRES,
- &length, &result_freeIt);
+ (void)Var_Parse(&nested_p, ctxt, VARE_UNDEFERR|VARE_WANTRES,
+ &result, &result_freeIt);
+ /* TODO: handle errors */
isError = result == var_Error;
free(result_freeIt);
if (isError)
return FALSE;
subLibName = TRUE;
- cp += length - 1;
- }
+ cp += nested_p - cp;
+ } else
+ cp++;
}
*cp++ = '\0';
if (subLibName) {
- libName = Var_Subst(libName, ctxt, VARE_UNDEFERR|VARE_WANTRES);
+ (void)Var_Subst(libName, ctxt, VARE_UNDEFERR|VARE_WANTRES, &libName);
+ /* TODO: handle errors */
}
@@ -311,25 +301,25 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt)
* place and skip to the end of it (either white-space or
* a close paren).
*/
- Boolean doSubst = FALSE; /* TRUE if need to substitute in memName */
+ Boolean doSubst = FALSE; /* TRUE if need to substitute in memName */
+
+ pp_skip_whitespace(&cp);
- while (*cp != '\0' && *cp != ')' && isspace ((unsigned char)*cp)) {
- cp++;
- }
memName = cp;
- while (*cp != '\0' && *cp != ')' && !isspace ((unsigned char)*cp)) {
+ while (*cp != '\0' && *cp != ')' && !ch_isspace(*cp)) {
if (*cp == '$') {
/*
* Variable spec, so call the Var module to parse the puppy
* so we can safely advance beyond it...
*/
- int length;
- void *freeIt;
+ void *freeIt;
const char *result;
Boolean isError;
+ const char *nested_p = cp;
- result = Var_Parse(cp, ctxt, VARE_UNDEFERR|VARE_WANTRES,
- &length, &freeIt);
+ (void)Var_Parse(&nested_p, ctxt, VARE_UNDEFERR|VARE_WANTRES,
+ &result, &freeIt);
+ /* TODO: handle errors */
isError = result == var_Error;
free(freeIt);
@@ -337,7 +327,7 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt)
return FALSE;
doSubst = TRUE;
- cp += length;
+ cp += nested_p - cp;
} else {
cp++;
}
@@ -376,11 +366,13 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt)
* later.
*/
if (doSubst) {
- char *buf;
- char *sacrifice;
- char *oldMemName = memName;
+ char *buf;
+ char *sacrifice;
+ char *oldMemName = memName;
- memName = Var_Subst(memName, ctxt, VARE_UNDEFERR | VARE_WANTRES);
+ (void)Var_Subst(memName, ctxt, VARE_UNDEFERR|VARE_WANTRES,
+ &memName);
+ /* TODO: handle errors */
/*
* Now form an archive spec and recurse to deal with nested
@@ -395,85 +387,48 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt)
* Just create an ARCHV node for the thing and let
* SuffExpandChildren handle it...
*/
- gn = Targ_FindNode(buf, TARG_CREATE);
+ gn = Targ_GetNode(buf);
+ gn->type |= OP_ARCHV;
+ Lst_Append(nodeLst, gn);
- if (gn == NULL) {
- free(buf);
- return FALSE;
- } else {
- gn->type |= OP_ARCHV;
- Lst_Append(nodeLst, gn);
- }
} else if (!Arch_ParseArchive(&sacrifice, nodeLst, ctxt)) {
- /*
- * Error in nested call -- free buffer and return FALSE
- * ourselves.
- */
+ /* Error in nested call. */
free(buf);
return FALSE;
}
- /*
- * Free buffer and continue with our work.
- */
free(buf);
- } else if (Dir_HasWildcards(memName)) {
- Lst members = Lst_Init();
- Buffer nameBuf;
- Buf_Init(&nameBuf, 0);
+ } else if (Dir_HasWildcards(memName)) {
+ StringList *members = Lst_New();
Dir_Expand(memName, dirSearchPath, members);
+
while (!Lst_IsEmpty(members)) {
char *member = Lst_Dequeue(members);
-
- Buf_Empty(&nameBuf);
- Buf_AddStr(&nameBuf, libName);
- Buf_AddStr(&nameBuf, "(");
- Buf_AddStr(&nameBuf, member);
- Buf_AddStr(&nameBuf, ")");
+ char *fullname = str_concat4(libName, "(", member, ")");
free(member);
- gn = Targ_FindNode(Buf_GetAll(&nameBuf, NULL), TARG_CREATE);
- if (gn == NULL) {
- Buf_Destroy(&nameBuf, TRUE);
- return FALSE;
- } else {
- /*
- * 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(nodeLst, gn);
- }
- }
- Lst_Free(members);
- Buf_Destroy(&nameBuf, TRUE);
- } else {
- Buffer nameBuf;
-
- Buf_Init(&nameBuf, 0);
- Buf_AddStr(&nameBuf, libName);
- Buf_AddStr(&nameBuf, "(");
- Buf_AddStr(&nameBuf, memName);
- Buf_AddStr(&nameBuf, ")");
+ gn = Targ_GetNode(fullname);
+ free(fullname);
- gn = Targ_FindNode(Buf_GetAll(&nameBuf, NULL), TARG_CREATE);
- Buf_Destroy(&nameBuf, TRUE);
- if (gn == NULL) {
- return FALSE;
- } else {
- /*
- * 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(nodeLst, gn);
}
+ Lst_Free(members);
+
+ } else {
+ char *fullname = str_concat4(libName, "(", memName, ")");
+ 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(nodeLst, gn);
}
if (doSubst) {
free(memName);
@@ -489,91 +444,70 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt)
free(libName);
}
- /*
- * We promised the pointer would be set up at the next non-space, so
- * we must advance cp there before setting *linePtr... (note that on
- * entrance to the loop, cp is guaranteed to point at a ')')
- */
- do {
- cp++;
- } while (*cp != '\0' && isspace ((unsigned char)*cp));
-
+ cp++; /* skip the ')' */
+ /* We promised that linePtr would be set up at the next non-space. */
+ pp_skip_whitespace(&cp);
*linePtr = cp;
return TRUE;
}
-/* See if the given archive is the one we are looking for.
- * Called via Lst_Find. */
-static Boolean
-ArchFindArchive(const void *ar, const void *desiredName)
-{
- return strcmp(((const Arch *)ar)->name, desiredName) == 0;
-}
-
-/*-
- *-----------------------------------------------------------------------
- * ArchStatMember --
- * Locate a member of an archive, given the path of the archive and
- * the path of the desired member.
+/* 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. If it is a path, only the last
- * component is used.
+ * member Name of member; only its basename is used.
* hash TRUE if archive should be hashed if not already so.
*
* Results:
- * A pointer to the current struct ar_hdr structure for the member. Note
- * That no position is returned, so this is not useful for touching
- * archive members. This is mostly because we have no assurances that
- * The archive will remain constant after we read all the headers, so
- * there's not much point in remembering the position...
- *-----------------------------------------------------------------------
+ * The ar_hdr for the member.
*/
static struct ar_hdr *
ArchStatMember(const char *archive, const char *member, Boolean hash)
{
- FILE * arch; /* Stream to archive */
- size_t size; /* Size of archive member */
- char magic[SARMAG];
- LstNode ln; /* Lst member containing archive descriptor */
- Arch *ar; /* Archive descriptor */
- Hash_Entry *he; /* Entry containing member's description */
- struct ar_hdr arh; /* archive-member header for reading archive */
- char memName[MAXPATHLEN+1];
- /* Current member name while hashing. */
+#define AR_MAX_NAME_LEN (sizeof(arh.AR_NAME) - 1)
+ FILE *arch; /* Stream to archive */
+ size_t size; /* Size of archive member */
+ char magic[SARMAG];
+ ArchListNode *ln;
+ Arch *ar; /* Archive descriptor */
+ struct ar_hdr arh; /* archive-member header for reading archive */
+ char memName[MAXPATHLEN + 1];
+ /* Current member name while hashing. */
/*
* Because of space constraints and similar things, files are archived
- * using their final path components, not the entire thing, so we need
- * to point 'member' to the final component, if there is one, to make
- * the comparisons easier...
+ * using their basename, not the entire path.
*/
- const char *base = strrchr(member, '/');
- if (base != NULL) {
- member = base + 1;
+ const char *lastSlash = strrchr(member, '/');
+ if (lastSlash != NULL)
+ member = lastSlash + 1;
+
+ for (ln = archives->first; ln != NULL; ln = ln->next) {
+ const Arch *archPtr = ln->datum;
+ if (strcmp(archPtr->name, archive) == 0)
+ break;
}
- ln = Lst_Find(archives, ArchFindArchive, archive);
if (ln != NULL) {
- ar = LstNode_Datum(ln);
+ struct ar_hdr *hdr;
- he = Hash_FindEntry(&ar->members, member);
+ ar = ln->datum;
+ hdr = HashTable_FindValue(&ar->members, member);
+ if (hdr != NULL)
+ return hdr;
- if (he != NULL) {
- return (struct ar_hdr *)Hash_GetValue(he);
- } else {
+ {
/* Try truncated name */
- char copy[AR_MAX_NAME_LEN+1];
+ char copy[AR_MAX_NAME_LEN + 1];
size_t len = strlen(member);
if (len > AR_MAX_NAME_LEN) {
len = AR_MAX_NAME_LEN;
snprintf(copy, sizeof copy, "%s", member);
}
- if ((he = Hash_FindEntry(&ar->members, copy)) != NULL)
- return (struct ar_hdr *)Hash_GetValue(he);
- return NULL;
+ hdr = HashTable_FindValue(&ar->members, copy);
+ return hdr;
}
}
@@ -585,16 +519,14 @@ ArchStatMember(const char *archive, const char *member, Boolean hash)
* no need to allocate extra room for the header we're returning,
* so just declare it static.
*/
- static struct ar_hdr sarh;
-
- arch = ArchFindMember(archive, member, &sarh, "r");
+ static struct ar_hdr sarh;
- if (arch == NULL) {
+ arch = ArchFindMember(archive, member, &sarh, "r");
+ if (arch == NULL)
return NULL;
- } else {
- fclose(arch);
- return &sarh;
- }
+
+ fclose(arch);
+ return &sarh;
}
/*
@@ -602,9 +534,8 @@ ArchStatMember(const char *archive, const char *member, Boolean hash)
* everything that's in it and cache it so we can get at it quickly.
*/
arch = fopen(archive, "r");
- if (arch == NULL) {
+ if (arch == NULL)
return NULL;
- }
/*
* We use the ARMAG string to make sure this is an archive we
@@ -612,19 +543,19 @@ ArchStatMember(const char *archive, const char *member, Boolean hash)
*/
if ((fread(magic, SARMAG, 1, arch) != 1) ||
(strncmp(magic, ARMAG, SARMAG) != 0)) {
- fclose(arch);
- return NULL;
+ fclose(arch);
+ return NULL;
}
ar = bmake_malloc(sizeof(Arch));
ar->name = bmake_strdup(archive);
ar->fnametab = NULL;
ar->fnamesize = 0;
- Hash_InitTable(&ar->members, -1);
+ HashTable_Init(&ar->members);
memName[AR_MAX_NAME_LEN] = '\0';
while (fread((char *)&arh, sizeof(struct ar_hdr), 1, arch) == 1) {
- if (strncmp( arh.AR_FMAG, ARFMAG, sizeof(arh.AR_FMAG)) != 0) {
+ if (strncmp(arh.AR_FMAG, ARFMAG, sizeof(arh.AR_FMAG)) != 0) {
/*
* The header is bogus, so the archive is bad
* and there's no way we can recover...
@@ -639,7 +570,7 @@ ArchStatMember(const char *archive, const char *member, Boolean hash)
* boundary, so we need to extract the size of the file from the
* 'size' field of the header and round it up during the seek.
*/
- arh.AR_SIZE[sizeof(arh.AR_SIZE)-1] = '\0';
+ arh.AR_SIZE[sizeof(arh.AR_SIZE) - 1] = '\0';
size = (size_t)strtol(arh.ar_size, NULL, 10);
memcpy(memName, arh.AR_NAME, sizeof(arh.AR_NAME));
@@ -658,15 +589,14 @@ ArchStatMember(const char *archive, const char *member, Boolean hash)
* svr4 magic mode; handle it
*/
switch (ArchSVR4Entry(ar, memName, size, arch)) {
- case -1: /* Invalid data */
+ case -1: /* Invalid data */
goto badarch;
- case 0: /* List of files entry */
+ case 0: /* List of files entry */
continue;
- default: /* Got the entry */
+ default: /* Got the entry */
break;
}
- }
- else {
+ } else {
if (nameend[0] == '/')
nameend[0] = '\0';
}
@@ -678,26 +608,30 @@ ArchStatMember(const char *archive, const char *member, Boolean hash)
* first <namelen> bytes of the file
*/
if (strncmp(memName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 &&
- isdigit((unsigned char)memName[sizeof(AR_EFMT1) - 1])) {
+ ch_isdigit(memName[sizeof(AR_EFMT1) - 1])) {
- int elen = atoi(&memName[sizeof(AR_EFMT1)-1]);
+ int elen = atoi(&memName[sizeof(AR_EFMT1) - 1]);
if ((unsigned int)elen > MAXPATHLEN)
- goto badarch;
+ goto badarch;
if (fread(memName, (size_t)elen, 1, arch) != 1)
- goto badarch;
+ goto badarch;
memName[elen] = '\0';
if (fseek(arch, -elen, SEEK_CUR) != 0)
- goto badarch;
+ goto badarch;
if (DEBUG(ARCH) || DEBUG(MAKE)) {
- fprintf(debug_file, "ArchStat: Extended format entry for %s\n", memName);
+ debug_printf("ArchStat: Extended format entry for %s\n",
+ memName);
}
}
#endif
- he = Hash_CreateEntry(&ar->members, memName, NULL);
- Hash_SetValue(he, bmake_malloc(sizeof(struct ar_hdr)));
- memcpy(Hash_GetValue(he), &arh, sizeof(struct ar_hdr));
+ {
+ HashEntry *he;
+ he = HashTable_CreateEntry(&ar->members, memName, NULL);
+ HashEntry_Set(he, bmake_malloc(sizeof(struct ar_hdr)));
+ memcpy(HashEntry_Get(he), &arh, sizeof(struct ar_hdr));
+ }
}
if (fseek(arch, ((long)size + 1) & ~1, SEEK_CUR) != 0)
goto badarch;
@@ -711,17 +645,11 @@ ArchStatMember(const char *archive, const char *member, Boolean hash)
* Now that the archive has been read and cached, we can look into
* the hash table to find the desired member's header.
*/
- he = Hash_FindEntry(&ar->members, member);
-
- if (he != NULL) {
- return (struct ar_hdr *)Hash_GetValue(he);
- } else {
- return NULL;
- }
+ return HashTable_FindValue(&ar->members, member);
badarch:
fclose(arch);
- Hash_DeleteTable(&ar->members);
+ HashTable_Done(&ar->members);
free(ar->fnametab);
free(ar);
return NULL;
@@ -757,9 +685,7 @@ ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch)
strncmp(name, ARLONGNAMES2, sizeof(ARLONGNAMES2) - 1) == 0) {
if (ar->fnametab != NULL) {
- if (DEBUG(ARCH)) {
- fprintf(debug_file, "Attempted to redefine an SVR4 name table\n");
- }
+ DEBUG0(ARCH, "Attempted to redefine an SVR4 name table\n");
return -1;
}
@@ -771,29 +697,17 @@ ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch)
ar->fnamesize = size;
if (fread(ar->fnametab, size, 1, arch) != 1) {
- if (DEBUG(ARCH)) {
- fprintf(debug_file, "Reading an SVR4 name table failed\n");
- }
+ DEBUG0(ARCH, "Reading an SVR4 name table failed\n");
return -1;
}
eptr = ar->fnametab + size;
for (entry = 0, ptr = ar->fnametab; ptr < eptr; ptr++)
- switch (*ptr) {
- case '/':
+ if (*ptr == '/') {
entry++;
*ptr = '\0';
- break;
-
- case '\n':
- break;
-
- default:
- break;
}
- if (DEBUG(ARCH)) {
- fprintf(debug_file, "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;
}
@@ -802,22 +716,16 @@ ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch)
entry = (size_t)strtol(&name[1], &eptr, 0);
if ((*eptr != ' ' && *eptr != '\0') || eptr == &name[1]) {
- if (DEBUG(ARCH)) {
- fprintf(debug_file, "Could not parse SVR4 name %s\n", name);
- }
+ DEBUG1(ARCH, "Could not parse SVR4 name %s\n", name);
return 2;
}
if (entry >= ar->fnamesize) {
- if (DEBUG(ARCH)) {
- fprintf(debug_file, "SVR4 entry offset %s is greater than %lu\n",
- name, (unsigned long)ar->fnamesize);
- }
+ DEBUG2(ARCH, "SVR4 entry offset %s is greater than %lu\n",
+ name, (unsigned long)ar->fnamesize);
return 2;
}
- if (DEBUG(ARCH)) {
- fprintf(debug_file, "Replaced %s with %s\n", name, &ar->fnametab[entry]);
- }
+ DEBUG2(ARCH, "Replaced %s with %s\n", name, &ar->fnametab[entry]);
snprintf(name, MAXPATHLEN + 1, "%s", &ar->fnametab[entry]);
return 1;
@@ -848,18 +756,17 @@ ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch)
*/
static FILE *
ArchFindMember(const char *archive, const char *member, struct ar_hdr *arhPtr,
- const char *mode)
+ const char *mode)
{
- FILE * arch; /* Stream to archive */
- int size; /* Size of archive member */
- char magic[SARMAG];
- size_t len, tlen;
- const char * base;
+ FILE *arch; /* Stream to archive */
+ int size; /* Size of archive member */
+ char magic[SARMAG];
+ size_t len, tlen;
+ const char *lastSlash;
arch = fopen(archive, mode);
- if (arch == NULL) {
+ if (arch == NULL)
return NULL;
- }
/*
* We use the ARMAG string to make sure this is an archive we
@@ -867,34 +774,35 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *arhPtr,
*/
if ((fread(magic, SARMAG, 1, arch) != 1) ||
(strncmp(magic, ARMAG, SARMAG) != 0)) {
- fclose(arch);
- return NULL;
+ fclose(arch);
+ return NULL;
}
/*
* Because of space constraints and similar things, files are archived
- * using their final path components, not the entire thing, so we need
- * to point 'member' to the final component, if there is one, to make
- * the comparisons easier...
+ * using their basename, not the entire path.
*/
- base = strrchr(member, '/');
- if (base != NULL) {
- member = base + 1;
- }
+ lastSlash = strrchr(member, '/');
+ if (lastSlash != NULL)
+ member = lastSlash + 1;
+
len = tlen = strlen(member);
if (len > sizeof(arhPtr->AR_NAME)) {
tlen = sizeof(arhPtr->AR_NAME);
}
while (fread((char *)arhPtr, sizeof(struct ar_hdr), 1, arch) == 1) {
- if (strncmp(arhPtr->AR_FMAG, ARFMAG, sizeof(arhPtr->AR_FMAG) ) != 0) {
- /*
- * The header is bogus, so the archive is bad
- * and there's no way we can recover...
- */
- fclose(arch);
- return NULL;
- } else if (strncmp(member, arhPtr->AR_NAME, tlen) == 0) {
+
+ if (strncmp(arhPtr->AR_FMAG, ARFMAG, sizeof(arhPtr->AR_FMAG)) != 0) {
+ /*
+ * The header is bogus, so the archive is bad
+ * and there's no way we can recover...
+ */
+ fclose(arch);
+ return NULL;
+ }
+
+ if (strncmp(member, arhPtr->AR_NAME, tlen) == 0) {
/*
* If the member's name doesn't take up the entire 'name' field,
* we have to be careful of matching prefixes. Names are space-
@@ -902,79 +810,76 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *arhPtr,
* of the matched string is anything but a space, this isn't the
* member we sought.
*/
- if (tlen != sizeof(arhPtr->AR_NAME) && arhPtr->AR_NAME[tlen] != ' '){
+ if (tlen != sizeof arhPtr->AR_NAME && arhPtr->AR_NAME[tlen] != ' ')
goto skip;
- } else {
- /*
- * To make life easier, 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(struct ar_hdr), SEEK_CUR) != 0) {
- fclose(arch);
- return NULL;
- }
- return arch;
+
+ /*
+ * To make life easier, 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(struct ar_hdr), SEEK_CUR) != 0) {
+ fclose(arch);
+ return NULL;
}
- } else
-#ifdef AR_EFMT1
- /*
- * BSD 4.4 extended AR format: #1/<namelen>, with name as the
- * first <namelen> bytes of the file
- */
- if (strncmp(arhPtr->AR_NAME, AR_EFMT1,
- sizeof(AR_EFMT1) - 1) == 0 &&
- isdigit((unsigned char)arhPtr->AR_NAME[sizeof(AR_EFMT1) - 1])) {
+ return arch;
+ }
- int elen = atoi(&arhPtr->AR_NAME[sizeof(AR_EFMT1)-1]);
- char ename[MAXPATHLEN + 1];
+#ifdef AR_EFMT1
+ /*
+ * BSD 4.4 extended AR format: #1/<namelen>, with name as the
+ * first <namelen> bytes of the file
+ */
+ if (strncmp(arhPtr->AR_NAME, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 &&
+ ch_isdigit(arhPtr->AR_NAME[sizeof(AR_EFMT1) - 1]))
+ {
+ int elen = atoi(&arhPtr->AR_NAME[sizeof(AR_EFMT1) - 1]);
+ char ename[MAXPATHLEN + 1];
- if ((unsigned int)elen > MAXPATHLEN) {
- fclose(arch);
- return NULL;
- }
- if (fread(ename, (size_t)elen, 1, arch) != 1) {
- fclose(arch);
- return NULL;
- }
- ename[elen] = '\0';
- if (DEBUG(ARCH) || DEBUG(MAKE)) {
- fprintf(debug_file, "ArchFind: Extended format entry for %s\n", ename);
- }
- if (strncmp(ename, member, len) == 0) {
- /* Found as extended name */
- if (fseek(arch, -(long)sizeof(struct ar_hdr) - elen,
- SEEK_CUR) != 0) {
- fclose(arch);
- return NULL;
- }
- return arch;
- }
- if (fseek(arch, -elen, SEEK_CUR) != 0) {
+ if ((unsigned int)elen > MAXPATHLEN) {
+ fclose(arch);
+ return NULL;
+ }
+ if (fread(ename, (size_t)elen, 1, arch) != 1) {
+ fclose(arch);
+ return NULL;
+ }
+ ename[elen] = '\0';
+ if (DEBUG(ARCH) || DEBUG(MAKE)) {
+ debug_printf("ArchFind: Extended format entry for %s\n", ename);
+ }
+ if (strncmp(ename, member, len) == 0) {
+ /* Found as extended name */
+ if (fseek(arch, -(long)sizeof(struct ar_hdr) - elen,
+ SEEK_CUR) != 0) {
fclose(arch);
return NULL;
}
- goto skip;
- } else
-#endif
- {
-skip:
- /*
- * 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.
- */
- arhPtr->AR_SIZE[sizeof(arhPtr->AR_SIZE)-1] = '\0';
- size = (int)strtol(arhPtr->AR_SIZE, NULL, 10);
- if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0) {
+ return arch;
+ }
+ if (fseek(arch, -elen, SEEK_CUR) != 0) {
fclose(arch);
return NULL;
}
}
+#endif
+
+skip:
+ /*
+ * 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.
+ */
+ arhPtr->ar_size[sizeof(arhPtr->ar_size) - 1] = '\0';
+ size = (int)strtol(arhPtr->ar_size, NULL, 10);
+ if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0) {
+ fclose(arch);
+ return NULL;
+ }
}
/*
@@ -1003,18 +908,13 @@ skip:
void
Arch_Touch(GNode *gn)
{
- FILE * arch; /* Stream open to archive, positioned properly */
- struct ar_hdr arh; /* Current header describing member */
- char *p1, *p2;
+ FILE *arch; /* Stream open to archive, positioned properly */
+ struct ar_hdr arh; /* Current header describing member */
- arch = ArchFindMember(Var_Value(ARCHIVE, gn, &p1),
- Var_Value(MEMBER, gn, &p2),
+ arch = ArchFindMember(GNode_VarArchive(gn), GNode_VarMember(gn),
&arh, "r+");
- bmake_free(p1);
- bmake_free(p2);
-
- snprintf(arh.AR_DATE, sizeof(arh.AR_DATE), "%-12ld", (long) now);
+ snprintf(arh.AR_DATE, sizeof(arh.AR_DATE), "%-12ld", (long)now);
if (arch != NULL) {
(void)fwrite((char *)&arh, sizeof(struct ar_hdr), 1, arch);
@@ -1036,7 +936,7 @@ Arch_TouchLib(GNode *gn)
{
#ifdef RANLIBMAG
FILE * arch; /* Stream open to archive */
- struct ar_hdr arh; /* Header describing table of contents */
+ struct ar_hdr arh; /* Header describing table of contents */
struct utimbuf times; /* Times for utime() call */
arch = ArchFindMember(gn->path, RANLIBMAG, &arh, "r+");
@@ -1063,17 +963,10 @@ Arch_TouchLib(GNode *gn)
time_t
Arch_MTime(GNode *gn)
{
- struct ar_hdr *arhPtr; /* Header of desired member */
- time_t modTime; /* Modification time as an integer */
- char *p1, *p2;
-
- arhPtr = ArchStatMember(Var_Value(ARCHIVE, gn, &p1),
- Var_Value(MEMBER, gn, &p2),
- TRUE);
-
- bmake_free(p1);
- bmake_free(p2);
+ struct ar_hdr *arhPtr; /* Header of desired member */
+ time_t modTime; /* Modification time as an integer */
+ arhPtr = ArchStatMember(GNode_VarArchive(gn), GNode_VarMember(gn), TRUE);
if (arhPtr != NULL) {
modTime = (time_t)strtol(arhPtr->AR_DATE, NULL, 10);
} else {
@@ -1089,12 +982,10 @@ Arch_MTime(GNode *gn)
time_t
Arch_MemMTime(GNode *gn)
{
- LstNode ln;
- GNode *pgn;
+ GNodeListNode *ln;
- Lst_Open(gn->parents);
- while ((ln = Lst_Next(gn->parents)) != NULL) {
- pgn = LstNode_Datum(ln);
+ for (ln = gn->parents->first; ln != NULL; ln = ln->next) {
+ GNode *pgn = ln->datum;
if (pgn->type & OP_ARCHV) {
/*
@@ -1122,8 +1013,6 @@ Arch_MemMTime(GNode *gn)
}
}
- Lst_Close(gn->parents);
-
return gn->mtime;
}
@@ -1140,26 +1029,19 @@ Arch_MemMTime(GNode *gn)
*
* Input:
* gn Node of library to find
- * path Search path
*/
void
-Arch_FindLib(GNode *gn, Lst path)
+Arch_FindLib(GNode *gn, SearchPath *path)
{
- char *libName; /* file name for archive */
- size_t sz = strlen(gn->name) + 6 - 2;
-
- libName = bmake_malloc(sz);
- snprintf(libName, sz, "lib%s.a", &gn->name[2]);
-
+ char *libName = str_concat3("lib", gn->name + 2, ".a");
gn->path = Dir_FindFile(libName, path);
-
free(libName);
#ifdef LIBRARIES
Var_Set(TARGET, gn->name, gn);
#else
Var_Set(TARGET, gn->path == NULL ? gn->name : gn->path, gn);
-#endif /* LIBRARIES */
+#endif
}
/* Decide if a node with the OP_LIB attribute is out-of-date. Called from
@@ -1176,7 +1058,7 @@ Arch_FindLib(GNode *gn, Lst path)
* 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->cmgn->mtime).
+ * (gn->mtime < gn->youngestChild->mtime).
*
* Its modification time is greater than the time at which the make
* began (i.e. it's been modified in the course of the make, probably
@@ -1198,20 +1080,21 @@ Arch_FindLib(GNode *gn, Lst path)
Boolean
Arch_LibOODate(GNode *gn)
{
- Boolean oodate;
+ Boolean oodate;
if (gn->type & OP_PHONY) {
oodate = TRUE;
- } else if (OP_NOP(gn->type) && Lst_IsEmpty(gn->children)) {
+ } else if (!GNode_IsTarget(gn) && Lst_IsEmpty(gn->children)) {
oodate = FALSE;
- } else if ((!Lst_IsEmpty(gn->children) && gn->cmgn == NULL) ||
+ } else if ((!Lst_IsEmpty(gn->children) && gn->youngestChild == NULL) ||
(gn->mtime > now) ||
- (gn->cmgn != NULL && gn->mtime < gn->cmgn->mtime)) {
+ (gn->youngestChild != NULL &&
+ gn->mtime < gn->youngestChild->mtime)) {
oodate = TRUE;
} else {
#ifdef RANLIBMAG
- struct ar_hdr *arhPtr; /* Header for __.SYMDEF */
- int modTimeTOC; /* The table-of-contents's mod time */
+ struct ar_hdr *arhPtr; /* Header for __.SYMDEF */
+ int modTimeTOC; /* The table-of-contents's mod time */
arhPtr = ArchStatMember(gn->path, RANLIBMAG, FALSE);
@@ -1219,15 +1102,15 @@ Arch_LibOODate(GNode *gn)
modTimeTOC = (int)strtol(arhPtr->AR_DATE, NULL, 10);
if (DEBUG(ARCH) || DEBUG(MAKE)) {
- fprintf(debug_file, "%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC));
+ debug_printf("%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC));
}
- oodate = (gn->cmgn == NULL || gn->cmgn->mtime > modTimeTOC);
+ oodate = (gn->youngestChild == NULL || gn->youngestChild->mtime > modTimeTOC);
} else {
/*
* A library w/o a table of contents is out-of-date
*/
if (DEBUG(ARCH) || DEBUG(MAKE)) {
- fprintf(debug_file, "No t.o.c....");
+ debug_printf("No t.o.c....");
}
oodate = TRUE;
}
@@ -1238,14 +1121,14 @@ Arch_LibOODate(GNode *gn)
return oodate;
}
-/* Initialize things for this module. */
+/* Initialize the archives module. */
void
Arch_Init(void)
{
- archives = Lst_Init();
+ archives = Lst_New();
}
-/* Clean up things for this module. */
+/* Clean up the archives module. */
void
Arch_End(void)
{
diff --git a/bmake.1 b/bmake.1
index 2cc452f2d171..d086fc7cdec0 100644
--- a/bmake.1
+++ b/bmake.1
@@ -1,4 +1,4 @@
-.\" $NetBSD: make.1,v 1.289 2020/08/28 17:15:04 rillig Exp $
+.\" $NetBSD: make.1,v 1.290 2020/11/01 20:24:45 rillig 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 August 28, 2020
+.Dd November 1, 2020
.Dt BMAKE 1
.Os
.Sh NAME
@@ -1872,7 +1872,7 @@ has been defined and has commands associated with it.
.Ar Expression
may also be an arithmetic or string comparison.
Variable expansion is
-performed on both sides of the comparison, after which the integral
+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.
@@ -1882,7 +1882,7 @@ variable expansion, either the left or right hand side of a
.Ql Ic ==
or
.Ql Ic "!="
-operator is not an integral value, then
+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
diff --git a/bmake.cat1 b/bmake.cat1
index 564e811737da..5e78b65c13c5 100644
--- a/bmake.cat1
+++ b/bmake.cat1
@@ -1197,11 +1197,11 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
Expression may also be an arithmetic or string comparison. Variable
expansion is performed on both sides of the comparison, after which the
- integral values are compared. A value is interpreted as hexadecimal if
+ numerical 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 an integral value, then string comparison is performed
+ 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.
@@ -1568,4 +1568,4 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
There is no way of escaping a space character in a filename.
-FreeBSD 11.3 August 28, 2020 FreeBSD 11.3
+FreeBSD 11.3 November 1, 2020 FreeBSD 11.3
diff --git a/boot-strap b/boot-strap
index 00c86b6725e4..f3b98f2a7b23 100755
--- a/boot-strap
+++ b/boot-strap
@@ -115,7 +115,7 @@
# Simon J. Gerraty <sjg@crufty.net>
# RCSid:
-# $Id: boot-strap,v 1.51 2020/02/19 16:46:23 sjg Exp $
+# $Id: boot-strap,v 1.53 2020/09/16 02:12:01 sjg Exp $
#
# @(#) Copyright (c) 2001 Simon J. Gerraty
#
@@ -458,17 +458,25 @@ op_all() {
else
op_test
MAKE_VERSION=`sed -n '/^_MAKE_VERSION/ { s,.*= *,,;p; }' $srcdir/Makefile`
- echo You can install by running:
- echo
- echo $0 $cmd_args op=install
- echo
- echo "Use --install-prefix=/something to install somewhere other than $prefix"
- echo "Use --install-destdir=/somewhere to set DESTDIR during install"
- echo "Use --install-host-target to use INSTALL_BIN=$HOST_TARGET/bin"
- echo "Use -DWITH_PROG_VERSION to install as bmake-$MAKE_VERSION"
- echo "Use -DWITHOUT_PROG_LINK to suppress bmake -> bmake-$MAKE_VERSION symlink"
- echo "Use -DWITHOUT_INSTALL_MK to skip installing files to $prefix/share/mk"
+ cat << EOM
+You can install by running:
+
+$0 $cmd_args op=install
+
+Use --install-prefix=/something to install somewhere other than $prefix
+Use --install-destdir=/somewhere to set DESTDIR during install
+Use --install-host-target to use INSTALL_BIN=$HOST_TARGET/bin
+Use -DWITH_PROG_VERSION to install as bmake-$MAKE_VERSION
+Use -DWITHOUT_PROG_LINK to suppress bmake -> bmake-$MAKE_VERSION symlink
+Use -DWITHOUT_INSTALL_MK to skip installing files to $prefix/share/mk
+EOM
fi
+ cat << EOM
+
+Note: bmake.cat1 contains ANSI escape sequences.
+You may need the -r or -R option to more/less to view it correctly.
+
+EOM
}
op_$op
diff --git a/buf.c b/buf.c
index f96d8fbf9792..6b4ec07670e4 100644
--- a/buf.c
+++ b/buf.c
@@ -1,4 +1,4 @@
-/* $NetBSD: buf.c,v 1.37 2020/08/23 08:21:50 rillig Exp $ */
+/* $NetBSD: buf.c,v 1.42 2020/10/24 20:51:49 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -69,115 +69,103 @@
* SUCH DAMAGE.
*/
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: buf.c,v 1.37 2020/08/23 08:21:50 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)buf.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: buf.c,v 1.37 2020/08/23 08:21:50 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/* Functions for automatically-expanded null-terminated buffers. */
+/* Automatically-expanding null-terminated buffers. */
#include <limits.h>
#include "make.h"
-/* Extend the buffer for adding a single byte. */
+/* "@(#)buf.c 8.1 (Berkeley) 6/6/93" */
+MAKE_RCSID("$NetBSD: buf.c,v 1.42 2020/10/24 20:51:49 rillig Exp $");
+
+/* Make space in the buffer for adding a single byte. */
void
-Buf_Expand_1(Buffer *bp)
+Buf_Expand_1(Buffer *buf)
{
- bp->size += MAX(bp->size, 16);
- bp->buffer = bmake_realloc(bp->buffer, bp->size);
+ buf->cap += buf->cap > 16 ? buf->cap : 16;
+ buf->data = bmake_realloc(buf->data, buf->cap);
}
-/* Add the given bytes to the buffer. */
+/* Add the bytes to the buffer. */
void
-Buf_AddBytes(Buffer *bp, const char *bytesPtr, size_t numBytes)
+Buf_AddBytes(Buffer *buf, const char *bytes, size_t bytes_len)
{
- size_t count = bp->count;
- char *ptr;
+ size_t old_len = buf->len;
+ char *end;
- if (__predict_false(count + numBytes >= bp->size)) {
- bp->size += MAX(bp->size, numBytes + 16);
- bp->buffer = bmake_realloc(bp->buffer, bp->size);
+ if (__predict_false(old_len + bytes_len >= buf->cap)) {
+ buf->cap += buf->cap > bytes_len + 16 ? buf->cap : bytes_len + 16;
+ buf->data = bmake_realloc(buf->data, buf->cap);
}
- ptr = bp->buffer + count;
- bp->count = count + numBytes;
- memcpy(ptr, bytesPtr, numBytes);
- ptr[numBytes] = '\0';
+ end = buf->data + old_len;
+ buf->len = old_len + bytes_len;
+ memcpy(end, bytes, bytes_len);
+ end[bytes_len] = '\0';
}
/* Add the bytes between start and end to the buffer. */
void
-Buf_AddBytesBetween(Buffer *bp, const char *start, const char *end)
+Buf_AddBytesBetween(Buffer *buf, const char *start, const char *end)
{
- Buf_AddBytes(bp, start, (size_t)(end - start));
+ Buf_AddBytes(buf, start, (size_t)(end - start));
}
-/* Add the given string to the buffer. */
+/* Add the string to the buffer. */
void
-Buf_AddStr(Buffer *bp, const char *str)
+Buf_AddStr(Buffer *buf, const char *str)
{
- Buf_AddBytes(bp, str, strlen(str));
+ Buf_AddBytes(buf, str, strlen(str));
}
-/* Add the given number to the buffer. */
+/* Add the number to the buffer. */
void
-Buf_AddInt(Buffer *bp, int n)
+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,
- buf_size = max_sign_chars + max_decimal_digits + 1
+ str_size = max_sign_chars + max_decimal_digits + 1
};
- char buf[buf_size];
+ char str[str_size];
- size_t len = (size_t)snprintf(buf, sizeof buf, "%d", n);
- Buf_AddBytes(bp, buf, len);
+ size_t len = (size_t)snprintf(str, sizeof str, "%d", n);
+ Buf_AddBytes(buf, str, len);
}
/* Get the data (usually a string) from the buffer.
* The returned data is valid until the next modifying operation
* on the buffer.
*
- * Returns the pointer to the data and optionally the length of the
- * data in the buffer. */
+ * Returns the data and optionally the length of the data. */
char *
-Buf_GetAll(Buffer *bp, size_t *numBytesPtr)
+Buf_GetAll(Buffer *buf, size_t *out_len)
{
- if (numBytesPtr != NULL)
- *numBytesPtr = bp->count;
- return bp->buffer;
+ if (out_len != NULL)
+ *out_len = buf->len;
+ return buf->data;
}
/* Mark the buffer as empty, so it can be filled with data again. */
void
-Buf_Empty(Buffer *bp)
+Buf_Empty(Buffer *buf)
{
- bp->count = 0;
- bp->buffer[0] = '\0';
+ buf->len = 0;
+ buf->data[0] = '\0';
}
/* Initialize a buffer.
- * If the given initial size is 0, a reasonable default is used. */
+ * If the given initial capacity is 0, a reasonable default is used. */
void
-Buf_Init(Buffer *bp, size_t size)
+Buf_Init(Buffer *buf, size_t cap)
{
- if (size <= 0) {
- size = 256;
- }
- bp->size = size;
- bp->count = 0;
- bp->buffer = bmake_malloc(size);
- bp->buffer[0] = '\0';
+ if (cap <= 0)
+ cap = 256;
+ buf->cap = cap;
+ buf->len = 0;
+ buf->data = bmake_malloc(cap);
+ buf->data[0] = '\0';
}
/* Reset the buffer.
@@ -186,15 +174,15 @@ Buf_Init(Buffer *bp, size_t size)
char *
Buf_Destroy(Buffer *buf, Boolean freeData)
{
- char *data = buf->buffer;
+ char *data = buf->data;
if (freeData) {
free(data);
data = NULL;
}
- buf->size = 0;
- buf->count = 0;
- buf->buffer = NULL;
+ buf->cap = 0;
+ buf->len = 0;
+ buf->data = NULL;
return data;
}
@@ -211,10 +199,10 @@ char *
Buf_DestroyCompact(Buffer *buf)
{
#if BUF_COMPACT_LIMIT > 0
- if (buf->size - buf->count >= BUF_COMPACT_LIMIT) {
+ if (buf->cap - buf->len >= BUF_COMPACT_LIMIT) {
/* We trust realloc to be smart */
- char *data = bmake_realloc(buf->buffer, buf->count + 1);
- data[buf->count] = '\0';
+ char *data = bmake_realloc(buf->data, buf->len + 1);
+ data[buf->len] = '\0'; /* XXX: unnecessary */
Buf_Destroy(buf, FALSE);
return data;
}
diff --git a/buf.h b/buf.h
index 1dc3cdbf58f3..6b50e36e04f4 100644
--- a/buf.h
+++ b/buf.h
@@ -1,4 +1,4 @@
-/* $NetBSD: buf.h,v 1.28 2020/09/01 17:38:26 rillig Exp $ */
+/* $NetBSD: buf.h,v 1.34 2020/09/27 16:59:02 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -81,9 +81,9 @@
/* An automatically growing null-terminated buffer of characters. */
typedef struct Buffer {
- size_t size; /* Allocated size of the buffer, including the null */
- size_t count; /* Number of bytes in buffer, excluding the null */
- char *buffer; /* The buffer itself (always null-terminated) */
+ size_t cap; /* Allocated size of the buffer, including the null */
+ size_t len; /* Number of bytes in buffer, excluding the null */
+ char *data; /* The buffer itself (always null-terminated) */
} Buffer;
/* If we aren't on NetBSD, __predict_false() might not be defined. */
@@ -94,22 +94,28 @@ typedef struct Buffer {
void Buf_Expand_1(Buffer *);
/* Buf_AddByte adds a single byte to a buffer. */
-static inline void MAKE_ATTR_UNUSED
-Buf_AddByte(Buffer *bp, char byte)
+static inline MAKE_ATTR_UNUSED void
+Buf_AddByte(Buffer *buf, char byte)
{
- size_t count = ++bp->count;
- char *ptr;
- if (__predict_false(count >= bp->size))
- Buf_Expand_1(bp);
- ptr = bp->buffer + count;
- ptr[-1] = byte;
- ptr[0] = 0;
+ size_t old_len = buf->len++;
+ char *end;
+ if (__predict_false(old_len + 1 >= buf->cap))
+ Buf_Expand_1(buf);
+ end = buf->data + old_len;
+ end[0] = byte;
+ end[1] = '\0';
}
-static inline size_t MAKE_ATTR_UNUSED
-Buf_Size(const Buffer *bp)
+static inline MAKE_ATTR_UNUSED size_t
+Buf_Len(const Buffer *buf)
{
- return bp->count;
+ return buf->len;
+}
+
+static inline MAKE_ATTR_UNUSED Boolean
+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);
diff --git a/compat.c b/compat.c
index 150696ad6713..1bce02bf5c08 100644
--- a/compat.c
+++ b/compat.c
@@ -1,4 +1,4 @@
-/* $NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $ */
+/* $NetBSD: compat.c,v 1.173 2020/11/01 17:47:26 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -69,19 +69,6 @@
* SUCH DAMAGE.
*/
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)compat.c 8.2 (Berkeley) 3/19/94";
-#else
-__RCSID("$NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
/*-
* compat.c --
* The routines in this file implement the full-compatibility
@@ -91,33 +78,30 @@ __RCSID("$NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $");
* - friendly variable substitution.
*
* Interface:
- * Compat_Run Initialize things for this module and recreate
- * thems as need creatin'
+ * Compat_Run Initialize things for this module and recreate
+ * thems as need creatin'
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "wait.h"
-
-#include <ctype.h>
-#include <errno.h>
-#include <signal.h>
-#include <stdio.h>
-
-#include "make.h"
-#include "hash.h"
-#include "dir.h"
-#include "job.h"
-#include "metachar.h"
-#include "pathnames.h"
-
-
-static GNode *curTarg = NULL;
-static GNode *ENDNode;
-static void CompatInterrupt(int);
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "wait.h"
+
+#include <errno.h>
+#include <signal.h>
+
+#include "make.h"
+#include "dir.h"
+#include "job.h"
+#include "metachar.h"
+#include "pathnames.h"
+
+/* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */
+MAKE_RCSID("$NetBSD: compat.c,v 1.173 2020/11/01 17:47:26 rillig Exp $");
+
+static GNode *curTarg = NULL;
static pid_t compatChild;
static int compatSigno;
@@ -128,15 +112,12 @@ static int compatSigno;
static void
CompatDeleteTarget(GNode *gn)
{
- if ((gn != NULL) && !Targ_Precious (gn)) {
- char *p1;
- const char *file = Var_Value(TARGET, gn, &p1);
+ if (gn != NULL && !Targ_Precious(gn)) {
+ const char *file = GNode_VarTarget(gn);
- if (!noExecute && eunlink(file) != -1) {
+ if (!opts.noExecute && eunlink(file) != -1) {
Error("*** %s removed", file);
}
-
- bmake_free(p1);
}
}
@@ -155,22 +136,24 @@ CompatInterrupt(int signo)
CompatDeleteTarget(curTarg);
- if ((curTarg != NULL) && !Targ_Precious (curTarg)) {
+ if (curTarg != NULL && !Targ_Precious(curTarg)) {
/*
* Run .INTERRUPT only if hit with interrupt signal
*/
if (signo == SIGINT) {
- gn = Targ_FindNode(".INTERRUPT", TARG_NOCREATE);
+ gn = Targ_FindNode(".INTERRUPT");
if (gn != NULL) {
Compat_Make(gn, gn);
}
}
}
+
if (signo == SIGQUIT)
_exit(signo);
+
/*
- * If there is a child running, pass the signal on
- * we will exist after it has exited.
+ * If there is a child running, pass the signal on.
+ * We will exist after it has exited.
*/
compatSigno = signo;
if (compatChild > 0) {
@@ -181,11 +164,8 @@ CompatInterrupt(int signo)
}
}
-/*-
- *-----------------------------------------------------------------------
- * CompatRunCommand --
- * Execute the next command for a target. If the command returns an
- * error, the node's made field is set to ERROR and creation stops.
+/* Execute the next command for a target. If the command returns an error,
+ * the node's made field is set to ERROR and creation stops.
*
* Input:
* cmdp Command to execute
@@ -193,45 +173,38 @@ CompatInterrupt(int signo)
*
* Results:
* 0 if the command succeeded, 1 if an error occurred.
- *
- * Side Effects:
- * The node's 'made' field may be set to ERROR.
- *
- *-----------------------------------------------------------------------
*/
int
-CompatRunCommand(void *cmdp, void *gnp)
+Compat_RunCommand(const char *cmdp, GNode *gn)
{
- char *cmdStart; /* Start of expanded command */
- char *cp, *bp;
- Boolean silent, /* Don't print command */
- doIt; /* Execute even if -n */
- volatile Boolean errCheck; /* Check errors */
- WAIT_T reason; /* Reason for child's death */
- int status; /* Description of child's death */
- pid_t cpid; /* Child actually found */
- pid_t retstat; /* Result of wait */
- LstNode cmdNode; /* Node where current command is located */
- const char ** volatile av; /* Argument vector for thing to exec */
- char ** volatile mav;/* Copy of the argument vector for freeing */
- Boolean useShell; /* TRUE if command should be executed
+ char *cmdStart; /* Start of expanded command */
+ char *bp;
+ Boolean silent; /* Don't print command */
+ Boolean doIt; /* Execute even if -n */
+ volatile Boolean errCheck; /* Check errors */
+ WAIT_T reason; /* Reason for child's death */
+ int status; /* Description of child's death */
+ pid_t cpid; /* Child actually found */
+ pid_t retstat; /* Result of wait */
+ StringListNode *cmdNode; /* Node where current command is located */
+ const char **volatile av; /* Argument vector for thing to exec */
+ char **volatile mav; /* Copy of the argument vector for freeing */
+ Boolean useShell; /* TRUE if command should be executed
* using a shell */
- char * volatile cmd = (char *)cmdp;
- GNode *gn = (GNode *)gnp;
+ const char *volatile cmd = cmdp;
silent = (gn->type & OP_SILENT) != 0;
errCheck = !(gn->type & OP_IGNORE);
doIt = FALSE;
+ /* Luckily the commands don't end up in a string pool, otherwise
+ * this comparison could match too early, in a dependency using "..."
+ * for delayed commands, run in parallel mode, using the same shell
+ * command line more than once; see JobPrintCommand.
+ * TODO: write a unit-test to protect against this potential bug. */
cmdNode = Lst_FindDatum(gn->commands, cmd);
- cmdStart = Var_Subst(cmd, gn, VARE_WANTRES);
-
- /*
- * brk_string will return an argv with a NULL in av[0], thus causing
- * execvp to choke and die horribly. Besides, how can we execute a null
- * command? In any case, we warn the user that the command expanded to
- * nothing (is this the right thing to do?).
- */
+ (void)Var_Subst(cmd, gn, VARE_WANTRES, &cmdStart);
+ /* TODO: handle errors */
if (*cmdStart == '\0') {
free(cmdStart);
@@ -240,17 +213,19 @@ CompatRunCommand(void *cmdp, void *gnp)
cmd = cmdStart;
LstNode_Set(cmdNode, cmdStart);
- if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) {
- assert(ENDNode != NULL);
- Lst_Append(ENDNode->commands, cmdStart);
- return 0;
+ if (gn->type & OP_SAVE_CMDS) {
+ GNode *endNode = Targ_GetEndNode();
+ if (gn != endNode) {
+ Lst_Append(endNode->commands, cmdStart);
+ return 0;
+ }
}
if (strcmp(cmdStart, "...") == 0) {
gn->type |= OP_SAVE_CMDS;
return 0;
}
- while ((*cmd == '@') || (*cmd == '-') || (*cmd == '+')) {
+ while (*cmd == '@' || *cmd == '-' || *cmd == '+') {
switch (*cmd) {
case '@':
silent = !DEBUG(LOUD);
@@ -267,7 +242,7 @@ CompatRunCommand(void *cmdp, void *gnp)
cmd++;
}
- while (isspace((unsigned char)*cmd))
+ while (ch_isspace(*cmd))
cmd++;
/*
@@ -295,14 +270,14 @@ CompatRunCommand(void *cmdp, void *gnp)
* meta characters as documented in make(1).
*/
- useShell = needshell(cmd, FALSE);
+ useShell = needshell(cmd);
#endif
/*
* 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 || NoExecute(gn)) {
+ if (!silent || !GNode_ShouldExecute(gn)) {
printf("%s\n", cmd);
fflush(stdout);
}
@@ -311,11 +286,10 @@ CompatRunCommand(void *cmdp, void *gnp)
* If we're not supposed to execute any commands, this is as far as
* we go...
*/
- if (!doIt && NoExecute(gn)) {
+ if (!doIt && !GNode_ShouldExecute(gn)) {
return 0;
}
- if (DEBUG(JOB))
- fprintf(debug_file, "Execute: '%s'\n", cmd);
+ DEBUG1(JOB, "Execute: '%s'\n", cmd);
if (useShell) {
/*
@@ -374,8 +348,7 @@ CompatRunCommand(void *cmdp, void *gnp)
}
#endif
(void)execvp(av[0], (char *const *)UNCONST(av));
- execError("exec", av[0]);
- _exit(1);
+ execDie("exec", av[0]);
}
free(mav);
@@ -394,85 +367,77 @@ CompatRunCommand(void *cmdp, void *gnp)
/*
* The child is off and running. Now all we can do is wait...
*/
- while (1) {
-
- while ((retstat = wait(&reason)) != cpid) {
- if (retstat > 0)
- JobReapChild(retstat, reason, FALSE); /* not ours? */
- if (retstat == -1 && errno != EINTR) {
- break;
- }
+ while ((retstat = wait(&reason)) != cpid) {
+ if (retstat > 0)
+ JobReapChild(retstat, reason, FALSE); /* not ours? */
+ if (retstat == -1 && errno != EINTR) {
+ break;
}
+ }
- if (retstat > -1) {
- if (WIFSTOPPED(reason)) {
- status = WSTOPSIG(reason); /* stopped */
- } else if (WIFEXITED(reason)) {
- status = WEXITSTATUS(reason); /* exited */
+ if (retstat < 0)
+ Fatal("error in wait: %d: %s", retstat, strerror(errno));
+
+ if (WIFSTOPPED(reason)) {
+ status = WSTOPSIG(reason); /* stopped */
+ } else if (WIFEXITED(reason)) {
+ status = WEXITSTATUS(reason); /* exited */
#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)) {
- fprintf(debug_file, "\n*** Failed target: %s\n*** Failed command: ",
- gn->name);
- for (cp = cmd; *cp; ) {
- if (isspace((unsigned char)*cp)) {
- fprintf(debug_file, " ");
- while (isspace((unsigned char)*cp))
- cp++;
- } else {
- fprintf(debug_file, "%c", *cp);
- cp++;
- }
- }
- fprintf(debug_file, "\n");
+ if (status != 0) {
+ if (DEBUG(ERROR)) {
+ const char *cp;
+ debug_printf("\n*** Failed target: %s\n*** Failed command: ",
+ gn->name);
+ for (cp = cmd; *cp; ) {
+ if (ch_isspace(*cp)) {
+ debug_printf(" ");
+ while (ch_isspace(*cp))
+ cp++;
+ } else {
+ debug_printf("%c", *cp);
+ cp++;
}
- printf("*** Error code %d", status);
}
- } else {
- status = WTERMSIG(reason); /* signaled */
- printf("*** Signal %d", status);
+ debug_printf("\n");
}
+ printf("*** Error code %d", status);
+ }
+ } else {
+ status = WTERMSIG(reason); /* signaled */
+ printf("*** Signal %d", status);
+ }
- if (!WIFEXITED(reason) || (status != 0)) {
- if (errCheck) {
+ if (!WIFEXITED(reason) || status != 0) {
+ if (errCheck) {
#ifdef USE_META
- if (useMeta) {
- meta_job_error(NULL, gn, 0, status);
- }
+ if (useMeta) {
+ meta_job_error(NULL, gn, 0, status);
+ }
#endif
- gn->made = ERROR;
- if (keepgoing) {
- /*
- * Abort the current target, but let others
- * continue.
- */
- printf(" (continuing)\n");
- } else {
- printf("\n");
- }
- if (deleteOnError) {
- CompatDeleteTarget(gn);
- }
- } else {
- /*
- * Continue executing commands for this target.
- * If we return 0, this will happen...
- */
- printf(" (ignored)\n");
- status = 0;
- }
+ gn->made = ERROR;
+ if (opts.keepgoing) {
+ /* Abort the current target, but let others continue. */
+ printf(" (continuing)\n");
+ } else {
+ printf("\n");
}
- break;
+ if (deleteOnError)
+ CompatDeleteTarget(gn);
} else {
- Fatal("error in wait: %d: %s", retstat, strerror(errno));
- /*NOTREACHED*/
+ /*
+ * Continue executing commands for this target.
+ * If we return 0, this will happen...
+ */
+ printf(" (ignored)\n");
+ status = 0;
}
}
+
free(cmdStart);
compatChild = 0;
if (compatSigno) {
@@ -483,32 +448,41 @@ CompatRunCommand(void *cmdp, void *gnp)
return status;
}
-/*-
- *-----------------------------------------------------------------------
- * Compat_Make --
- * Make a target.
- *
- * Input:
- * gnp The node to make
- * pgnp Parent to abort if necessary
- *
- * Results:
- * 0
+static void
+RunCommands(GNode *gn)
+{
+ StringListNode *ln;
+ for (ln = gn->commands->first; ln != NULL; ln = ln->next) {
+ const char *cmd = ln->datum;
+ if (Compat_RunCommand(cmd, gn) != 0)
+ break;
+ }
+}
+
+static void
+MakeNodes(GNodeList *gnodes, GNode *pgn)
+{
+ GNodeListNode *ln;
+ for (ln = gnodes->first; ln != NULL; ln = ln->next) {
+ GNode *cohort = ln->datum;
+ Compat_Make(cohort, pgn);
+ }
+}
+
+/* Make a target.
*
- * Side Effects:
- * If an error is detected and not being ignored, the process exits.
+ * If an error is detected and not being ignored, the process exits.
*
- *-----------------------------------------------------------------------
+ * Input:
+ * gn The node to make
+ * pgn Parent to abort if necessary
*/
-int
-Compat_Make(void *gnp, void *pgnp)
+void
+Compat_Make(GNode *gn, GNode *pgn)
{
- GNode *gn = (GNode *)gnp;
- GNode *pgn = (GNode *)pgnp;
-
if (!shellName) /* we came here from jobs */
Shell_Init();
- if (gn->made == UNMADE && (gn == pgn || (pgn->type & OP_MADE) == 0)) {
+ if (gn->made == UNMADE && (gn == pgn || !(pgn->type & OP_MADE))) {
/*
* First mark ourselves to be made, then apply whatever transformations
* the suffix module thinks are necessary. Once that's done, we can
@@ -519,45 +493,37 @@ Compat_Make(void *gnp, void *pgnp)
*/
gn->flags |= REMAKE;
gn->made = BEINGMADE;
- if ((gn->type & OP_MADE) == 0)
+ if (!(gn->type & OP_MADE))
Suff_FindDeps(gn);
- Lst_ForEach(gn->children, Compat_Make, gn);
- if ((gn->flags & REMAKE) == 0) {
+ MakeNodes(gn->children, gn);
+ if (!(gn->flags & REMAKE)) {
gn->made = ABORTED;
pgn->flags &= ~(unsigned)REMAKE;
goto cohorts;
}
- if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) {
- char *p1;
- Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn);
- bmake_free(p1);
- }
+ if (Lst_FindDatum(gn->implicitParents, pgn) != NULL)
+ Var_Set(IMPSRC, GNode_VarTarget(gn), pgn);
/*
- * All the children were made ok. Now cmgn->mtime contains the
+ * All the children were made ok. Now youngestChild->mtime contains the
* modification time of the newest child, we need to find out if we
* exist and when we were modified last. The criteria for datedness
* are defined by the Make_OODate function.
*/
- if (DEBUG(MAKE)) {
- fprintf(debug_file, "Examining %s...", gn->name);
- }
- if (! Make_OODate(gn)) {
+ DEBUG1(MAKE, "Examining %s...", gn->name);
+ if (!Make_OODate(gn)) {
gn->made = UPTODATE;
- if (DEBUG(MAKE)) {
- fprintf(debug_file, "up-to-date.\n");
- }
+ DEBUG0(MAKE, "up-to-date.\n");
goto cohorts;
- } else if (DEBUG(MAKE)) {
- fprintf(debug_file, "out-of-date.\n");
- }
+ } else
+ DEBUG0(MAKE, "out-of-date.\n");
/*
* If the user is just seeing if something is out-of-date, exit now
* to tell him/her "yes".
*/
- if (queryFlag) {
+ if (opts.queryFlag) {
exit(1);
}
@@ -572,26 +538,24 @@ Compat_Make(void *gnp, void *pgnp)
* Alter our type to tell if errors should be ignored or things
* should not be printed so CompatRunCommand knows what to do.
*/
- if (Targ_Ignore(gn)) {
+ if (Targ_Ignore(gn))
gn->type |= OP_IGNORE;
- }
- if (Targ_Silent(gn)) {
+ if (Targ_Silent(gn))
gn->type |= OP_SILENT;
- }
if (Job_CheckCommands(gn, Fatal)) {
/*
* Our commands are ok, but we still have to worry about the -t
* flag...
*/
- if (!touchFlag || (gn->type & OP_MAKE)) {
+ if (!opts.touchFlag || (gn->type & OP_MAKE)) {
curTarg = gn;
#ifdef USE_META
- if (useMeta && !NoExecute(gn)) {
+ if (useMeta && GNode_ShouldExecute(gn)) {
meta_job_start(NULL, gn);
}
#endif
- Lst_ForEach(gn->commands, CompatRunCommand, gn);
+ RunCommands(gn);
curTarg = NULL;
} else {
Job_Touch(gn, (gn->type & OP_SILENT) != 0);
@@ -600,7 +564,7 @@ Compat_Make(void *gnp, void *pgnp)
gn->made = ERROR;
}
#ifdef USE_META
- if (useMeta && !NoExecute(gn)) {
+ if (useMeta && GNode_ShouldExecute(gn)) {
if (meta_job_finish(NULL) != 0)
gn->made = ERROR;
}
@@ -619,24 +583,19 @@ Compat_Make(void *gnp, void *pgnp)
pgn->flags |= CHILDMADE;
Make_TimeStamp(pgn, gn);
}
- } else if (keepgoing) {
+ } else if (opts.keepgoing) {
pgn->flags &= ~(unsigned)REMAKE;
} else {
PrintOnError(gn, "\nStop.");
exit(1);
}
} else if (gn->made == ERROR) {
- /*
- * Already had an error when making this beastie. Tell the parent
- * to abort.
- */
+ /* Already had an error when making this. Tell the parent to abort. */
pgn->flags &= ~(unsigned)REMAKE;
} else {
if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) {
- char *p1;
- const char *target = Var_Value(TARGET, gn, &p1);
+ const char *target = GNode_VarTarget(gn);
Var_Set(IMPSRC, target != NULL ? target : "", pgn);
- bmake_free(p1);
}
switch(gn->made) {
case BEINGMADE:
@@ -661,8 +620,7 @@ Compat_Make(void *gnp, void *pgnp)
}
cohorts:
- Lst_ForEach(gn->cohorts, Compat_Make, pgnp);
- return 0;
+ MakeNodes(gn->cohorts, pgn);
}
/* Initialize this module and start making.
@@ -671,35 +629,33 @@ cohorts:
* targs The target nodes to re-create
*/
void
-Compat_Run(Lst targs)
+Compat_Run(GNodeList *targs)
{
- GNode *gn = NULL;/* Current root target */
- int errors; /* Number of targets not remade due to errors */
+ GNode *gn = NULL; /* Current root target */
+ int errors; /* Number of targets not remade due to errors */
if (!shellName)
Shell_Init();
- if (bmake_signal(SIGINT, SIG_IGN) != SIG_IGN) {
+ if (bmake_signal(SIGINT, SIG_IGN) != SIG_IGN)
bmake_signal(SIGINT, CompatInterrupt);
- }
- if (bmake_signal(SIGTERM, SIG_IGN) != SIG_IGN) {
+ if (bmake_signal(SIGTERM, SIG_IGN) != SIG_IGN)
bmake_signal(SIGTERM, CompatInterrupt);
- }
- if (bmake_signal(SIGHUP, SIG_IGN) != SIG_IGN) {
+ if (bmake_signal(SIGHUP, SIG_IGN) != SIG_IGN)
bmake_signal(SIGHUP, CompatInterrupt);
- }
- if (bmake_signal(SIGQUIT, SIG_IGN) != SIG_IGN) {
+ if (bmake_signal(SIGQUIT, SIG_IGN) != SIG_IGN)
bmake_signal(SIGQUIT, CompatInterrupt);
- }
- ENDNode = Targ_FindNode(".END", TARG_CREATE);
- ENDNode->type = OP_SPECIAL;
+ /* 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 the user has defined a .BEGIN target, execute the commands attached
* to it.
*/
- if (!queryFlag) {
- gn = Targ_FindNode(".BEGIN", TARG_NOCREATE);
+ if (!opts.queryFlag) {
+ gn = Targ_FindNode(".BEGIN");
if (gn != NULL) {
Compat_Make(gn, gn);
if (gn->made == ERROR) {
@@ -719,11 +675,11 @@ Compat_Run(Lst targs)
* For each entry in the list of targets to create, call Compat_Make on
* it to create the thing. Compat_Make will leave the 'made' field of gn
* in one of several states:
- * UPTODATE gn was already up-to-date
- * MADE gn was recreated successfully
- * ERROR An error occurred while gn was being created
- * ABORTED gn was not remade because one of its inferiors
- * could not be made due to errors.
+ * UPTODATE gn was already up-to-date
+ * MADE gn was recreated successfully
+ * ERROR An error occurred while gn was being created
+ * ABORTED gn was not remade because one of its inferiors
+ * could not be made due to errors.
*/
errors = 0;
while (!Lst_IsEmpty(targs)) {
@@ -734,7 +690,7 @@ Compat_Run(Lst 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);
- errors += 1;
+ errors++;
}
}
@@ -742,7 +698,9 @@ Compat_Run(Lst targs)
* If the user has defined a .END target, run its commands.
*/
if (errors == 0) {
- Compat_Make(ENDNode, ENDNode);
+ GNode *endNode = Targ_GetEndNode();
+ Compat_Make(endNode, endNode);
+ /* XXX: Did you mean endNode->made instead of gn->made? */
if (gn->made == ERROR) {
PrintOnError(gn, "\nStop.");
exit(1);
diff --git a/cond.c b/cond.c
index 7aa072d4eb22..678dded0fc1e 100644
--- a/cond.c
+++ b/cond.c
@@ -1,4 +1,4 @@
-/* $NetBSD: cond.c,v 1.106 2020/08/29 13:38:48 rillig Exp $ */
+/* $NetBSD: cond.c,v 1.173 2020/10/30 20:30:44 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -69,26 +69,22 @@
* SUCH DAMAGE.
*/
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: cond.c,v 1.106 2020/08/29 13:38:48 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)cond.c 8.2 (Berkeley) 1/2/94";
-#else
-__RCSID("$NetBSD: cond.c,v 1.106 2020/08/29 13:38:48 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * cond.c --
- * Functions to handle conditionals in a makefile.
+/* Handling of conditionals in a makefile.
*
* Interface:
- * Cond_Eval Evaluate the conditional in the passed line.
+ * Cond_EvalLine Evaluate the conditional.
+ *
+ * Cond_EvalCondition
+ * Evaluate the conditional, which is either the argument
+ * 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.
*/
#include <errno.h>
@@ -96,6 +92,9 @@ __RCSID("$NetBSD: cond.c,v 1.106 2020/08/29 13:38:48 rillig Exp $");
#include "make.h"
#include "dir.h"
+/* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */
+MAKE_RCSID("$NetBSD: cond.c,v 1.173 2020/10/30 20:30:44 rillig Exp $");
+
/*
* The parsing of conditional expressions is based on this grammar:
* E -> F || E
@@ -119,185 +118,198 @@ __RCSID("$NetBSD: cond.c,v 1.106 2020/08/29 13:38:48 rillig Exp $");
*
* 'symbol' is some other symbol to which the default function is applied.
*
- * Tokens are scanned from the 'condExpr' string. The scanner (CondToken)
- * will return TOK_AND for '&' and '&&', TOK_OR for '|' and '||',
- * TOK_NOT for '!', TOK_LPAREN for '(', TOK_RPAREN for ')' and will evaluate
- * the other terminal symbols, using either the default function or the
- * function given in the terminal, and return the result as either TOK_TRUE
- * or TOK_FALSE.
+ * The tokens are scanned by CondToken, which returns:
+ * TOK_AND for '&' or '&&'
+ * TOK_OR for '|' or '||'
+ * TOK_NOT for '!'
+ * TOK_LPAREN for '('
+ * TOK_RPAREN for ')'
+ * Other terminal symbols are evaluated using either the default function or
+ * the function given in the terminal, they return either TOK_TRUE or
+ * TOK_FALSE.
*
* TOK_FALSE is 0 and TOK_TRUE 1 so we can directly assign C comparisons.
*
- * All Non-Terminal functions (CondE, CondF and CondT) return TOK_ERROR on
- * error.
+ * All non-terminal functions (CondParser_Expr, CondParser_Factor and
+ * CondParser_Term) return either TOK_FALSE, TOK_TRUE, or TOK_ERROR on error.
*/
-typedef enum {
+typedef enum Token {
TOK_FALSE = 0, TOK_TRUE = 1, TOK_AND, TOK_OR, TOK_NOT,
TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR
} Token;
-static Token CondE(Boolean);
-static CondEvalResult do_Cond_EvalExpression(Boolean *);
+typedef struct CondParser {
+ const struct If *if_info; /* Info for current statement */
+ 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. */
+ Boolean printedError;
+} CondParser;
-static const struct If *if_info; /* Info for current statement */
-static const char *condExpr; /* The expression to parse */
-static Token condPushBack = TOK_NONE; /* Single push-back token used in
- * parsing */
+static Token CondParser_Expr(CondParser *par, Boolean);
static unsigned int cond_depth = 0; /* current .if nesting level */
static unsigned int cond_min_depth = 0; /* depth at makefile open */
/*
* Indicate when we should be strict about lhs of comparisons.
- * TRUE when Cond_EvalExpression is called from Cond_Eval (.if etc)
- * FALSE when Cond_EvalExpression is called from var.c:ApplyModifiers
+ * 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 we cannot tell if
* it was a variable reference or not.
*/
static Boolean lhsStrict;
static int
-istoken(const char *str, const char *tok, size_t len)
+is_token(const char *str, const char *tok, size_t len)
{
- return strncmp(str, tok, len) == 0 && !isalpha((unsigned char)str[len]);
+ return strncmp(str, tok, len) == 0 && !ch_isalpha(str[len]);
}
-/* Push back the most recent token read. We only need one level of
- * this, so the thing is just stored in 'condPushback'. */
+/* Push back the most recent token read. We only need one level of this. */
static void
-CondPushBack(Token t)
+CondParser_PushBack(CondParser *par, Token t)
{
- condPushBack = t;
+ assert(par->curr == TOK_NONE);
+ assert(t != TOK_NONE);
+
+ par->curr = t;
}
-/*-
- * Parse the argument of a built-in function.
- *
- * Results:
- * The length of the argument.
- * *argPtr receives the argument as string.
- * *linePtr is updated to point behind the ')' of the function call.
- */
-static int
-CondGetArg(Boolean doEval, const char **linePtr, char **argPtr,
- const char *func)
+static void
+CondParser_SkipWhitespace(CondParser *par)
{
- const char *cp;
- Buffer buf;
+ cpp_skip_whitespace(&par->p);
+}
+
+/* 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. */
+static size_t
+ParseFuncArg(const char **pp, Boolean doEval, const char *func,
+ char **out_arg) {
+ const char *p = *pp;
+ Buffer argBuf;
int paren_depth;
- char ch;
size_t argLen;
- cp = *linePtr;
if (func != NULL)
- /* Skip opening '(' - verified by caller */
- cp++;
+ p++; /* Skip opening '(' - verified by caller */
- if (*cp == '\0') {
+ if (*p == '\0') {
/*
* No arguments whatsoever. Because 'make' and 'defined' aren't really
* "reserved words", we don't print a message. I think this is better
* than hitting the user with a warning message every time s/he uses
* the word 'make' or 'defined' at the beginning of a symbol...
*/
- *argPtr = NULL;
+ *out_arg = NULL;
return 0;
}
- while (*cp == ' ' || *cp == '\t') {
- cp++;
+ while (*p == ' ' || *p == '\t') {
+ p++;
}
- /*
- * Create a buffer for the argument and start it out at 16 characters
- * long. Why 16? Why not?
- */
- Buf_Init(&buf, 16);
+ Buf_Init(&argBuf, 16);
paren_depth = 0;
for (;;) {
- ch = *cp;
+ char ch = *p;
if (ch == 0 || ch == ' ' || ch == '\t')
break;
if ((ch == '&' || ch == '|') && paren_depth == 0)
break;
- if (*cp == '$') {
+ if (*p == '$') {
/*
* Parse the variable spec and install it as part of the argument
* if it's valid. We tell Var_Parse to complain on an undefined
- * variable, so we don't do it too. Nor do we return an error,
+ * variable, so we don't need to do it. Nor do we return an error,
* though perhaps we should...
*/
- int len;
- void *freeIt;
+ void *nestedVal_freeIt;
VarEvalFlags eflags = VARE_UNDEFERR | (doEval ? VARE_WANTRES : 0);
- const char *cp2 = Var_Parse(cp, VAR_CMD, eflags, &len, &freeIt);
- Buf_AddStr(&buf, cp2);
- free(freeIt);
- cp += len;
+ const char *nestedVal;
+ (void)Var_Parse(&p, VAR_CMDLINE, eflags, &nestedVal,
+ &nestedVal_freeIt);
+ /* TODO: handle errors */
+ Buf_AddStr(&argBuf, nestedVal);
+ free(nestedVal_freeIt);
continue;
}
if (ch == '(')
paren_depth++;
else if (ch == ')' && --paren_depth < 0)
break;
- Buf_AddByte(&buf, *cp);
- cp++;
+ Buf_AddByte(&argBuf, *p);
+ p++;
}
- *argPtr = Buf_GetAll(&buf, &argLen);
- Buf_Destroy(&buf, FALSE);
+ *out_arg = Buf_GetAll(&argBuf, &argLen);
+ Buf_Destroy(&argBuf, FALSE);
- while (*cp == ' ' || *cp == '\t') {
- cp++;
+ while (*p == ' ' || *p == '\t') {
+ p++;
}
- if (func != NULL && *cp++ != ')') {
+ if (func != NULL && *p++ != ')') {
Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()",
func);
+ /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
return 0;
}
- *linePtr = cp;
+ *pp = p;
return argLen;
}
/* Test whether the given variable is defined. */
static Boolean
-CondDoDefined(int argLen MAKE_ATTR_UNUSED, const char *arg)
+FuncDefined(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
{
- char *freeIt;
- Boolean result = Var_Value(arg, VAR_CMD, &freeIt) != NULL;
+ void *freeIt;
+ Boolean result = Var_Value(arg, VAR_CMDLINE, &freeIt) != NULL;
bmake_free(freeIt);
return result;
}
-/* Wrapper around Str_Match, to be used by Lst_Find. */
-static Boolean
-CondFindStrMatch(const void *string, const void *pattern)
-{
- return Str_Match(string, pattern);
-}
-
/* See if the given target is being made. */
static Boolean
-CondDoMake(int argLen MAKE_ATTR_UNUSED, const char *arg)
+FuncMake(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
{
- return Lst_Find(create, CondFindStrMatch, arg) != NULL;
+ StringListNode *ln;
+
+ for (ln = opts.create->first; ln != NULL; ln = ln->next)
+ if (Str_Match(ln->datum, arg))
+ return TRUE;
+ return FALSE;
}
/* See if the given file exists. */
static Boolean
-CondDoExists(int argLen MAKE_ATTR_UNUSED, const char *arg)
+FuncExists(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
{
Boolean result;
char *path;
path = Dir_FindFile(arg, dirSearchPath);
- if (DEBUG(COND)) {
- fprintf(debug_file, "exists(%s) result is \"%s\"\n",
- arg, path ? path : "");
- }
+ DEBUG2(COND, "exists(%s) result is \"%s\"\n", arg, path ? path : "");
if (path != NULL) {
result = TRUE;
free(path);
@@ -309,23 +321,19 @@ CondDoExists(int argLen MAKE_ATTR_UNUSED, const char *arg)
/* See if the given node exists and is an actual target. */
static Boolean
-CondDoTarget(int argLen MAKE_ATTR_UNUSED, const char *arg)
+FuncTarget(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
{
- GNode *gn;
-
- gn = Targ_FindNode(arg, TARG_NOCREATE);
- return gn != NULL && !OP_NOP(gn->type);
+ GNode *gn = Targ_FindNode(arg);
+ return gn != NULL && GNode_IsTarget(gn);
}
/* See if the given node exists and is an actual target with commands
* associated with it. */
static Boolean
-CondDoCommands(int argLen MAKE_ATTR_UNUSED, const char *arg)
+FuncCommands(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
{
- GNode *gn;
-
- gn = Targ_FindNode(arg, TARG_NOCREATE);
- return gn != NULL && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands);
+ GNode *gn = Targ_FindNode(arg);
+ return gn != NULL && GNode_IsTarget(gn) && !Lst_IsEmpty(gn->commands);
}
/*-
@@ -338,7 +346,7 @@ CondDoCommands(int argLen MAKE_ATTR_UNUSED, const char *arg)
* Returns TRUE if the conversion succeeded.
*/
static Boolean
-CondCvtArg(const char *str, double *value)
+TryParseNumber(const char *str, double *value)
{
char *eptr, ech;
unsigned long l_val;
@@ -346,15 +354,15 @@ CondCvtArg(const char *str, double *value)
errno = 0;
if (!*str) {
- *value = (double)0;
+ *value = 0.0;
return TRUE;
}
l_val = strtoul(str, &eptr, str[1] == 'x' ? 16 : 10);
ech = *eptr;
- if (ech == 0 && errno != ERANGE) {
+ if (ech == '\0' && errno != ERANGE) {
d_val = str[0] == '-' ? -(double)-l_val : (double)l_val;
} else {
- if (ech != 0 && ech != '.' && ech != 'e' && ech != 'E')
+ if (ech != '\0' && ech != '.' && ech != 'e' && ech != 'E')
return FALSE;
d_val = strtod(str, &eptr);
if (*eptr)
@@ -365,51 +373,59 @@ CondCvtArg(const char *str, double *value)
return TRUE;
}
+static Boolean
+is_separator(char ch)
+{
+ return ch == '\0' || ch_isspace(ch) || strchr("!=><)", ch) != NULL;
+}
+
/*-
- * Get a string from a variable reference or an optionally quoted
- * string. This is called for the lhs and rhs of string compares.
+ * Parse a string from a variable reference or an optionally quoted
+ * string. This is called for the lhs and rhs of string comparisons.
*
* Results:
* Returns the string, absent any quotes, or NULL on error.
* Sets quoted if the string was quoted.
* Sets freeIt if needed.
- *
- * Side Effects:
- * Moves condExpr past the end of this token.
*/
-/* coverity:[+alloc : arg-*2] */
+/* coverity:[+alloc : arg-*4] */
static const char *
-CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS)
+CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
+ Boolean *quoted, void **freeIt)
{
Buffer buf;
- const char *cp;
const char *str;
- int len;
+ Boolean atStart;
+ const char *nested_p;
Boolean qt;
const char *start;
VarEvalFlags eflags;
+ VarParseResult parseResult;
Buf_Init(&buf, 0);
str = NULL;
*freeIt = NULL;
- *quoted = qt = *condExpr == '"' ? 1 : 0;
+ *quoted = qt = par->p[0] == '"' ? 1 : 0;
+ start = par->p;
if (qt)
- condExpr++;
- for (start = condExpr; *condExpr && str == NULL; condExpr++) {
- switch (*condExpr) {
+ par->p++;
+ while (par->p[0] && str == NULL) {
+ switch (par->p[0]) {
case '\\':
- if (condExpr[1] != '\0') {
- condExpr++;
- Buf_AddByte(&buf, *condExpr);
+ par->p++;
+ if (par->p[0] != '\0') {
+ Buf_AddByte(&buf, par->p[0]);
+ par->p++;
}
- break;
+ continue;
case '"':
if (qt) {
- condExpr++; /* we don't want the quotes */
+ par->p++; /* we don't want the quotes */
goto got_str;
- } else
- Buf_AddByte(&buf, *condExpr); /* likely? */
- break;
+ }
+ Buf_AddByte(&buf, par->p[0]); /* likely? */
+ par->p++;
+ continue;
case ')':
case '!':
case '=':
@@ -419,15 +435,21 @@ CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS)
case '\t':
if (!qt)
goto got_str;
- else
- Buf_AddByte(&buf, *condExpr);
- break;
+ Buf_AddByte(&buf, par->p[0]);
+ par->p++;
+ continue;
case '$':
- /* if we are in quotes, then an undefined variable is ok */
+ /* if we are in quotes, an undefined variable is ok */
eflags = ((!qt && doEval) ? VARE_UNDEFERR : 0) |
(doEval ? VARE_WANTRES : 0);
- str = Var_Parse(condExpr, VAR_CMD, eflags, &len, freeIt);
+ nested_p = par->p;
+ atStart = nested_p == start;
+ parseResult = Var_Parse(&nested_p, VAR_CMDLINE, eflags, &str,
+ freeIt);
+ /* TODO: handle errors */
if (str == var_Error) {
+ if (parseResult & VPR_ANY_MSG)
+ par->printedError = TRUE;
if (*freeIt) {
free(*freeIt);
*freeIt = NULL;
@@ -439,34 +461,26 @@ CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS)
str = NULL;
goto cleanup;
}
- condExpr += len;
+ par->p = nested_p;
+
/*
- * If the '$' was first char (no quotes), and we are
- * followed by space, the operator or end of expression,
- * we are done.
+ * 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 ((condExpr == start + len) &&
- (*condExpr == '\0' ||
- isspace((unsigned char)*condExpr) ||
- strchr("!=><)", *condExpr))) {
+ if (atStart && is_separator(par->p[0]))
goto cleanup;
- }
- /*
- * Nope, we better copy str to buf
- */
- for (cp = str; *cp; cp++) {
- Buf_AddByte(&buf, *cp);
- }
+
+ Buf_AddStr(&buf, str);
if (*freeIt) {
free(*freeIt);
*freeIt = NULL;
}
str = NULL; /* not finished yet */
- condExpr--; /* don't skip over next char */
- break;
+ continue;
default:
- if (strictLHS && !qt && *start != '$' &&
- !isdigit((unsigned char)*start)) {
+ if (strictLHS && !qt && *start != '$' && !ch_isdigit(*start)) {
/* lhs must be quoted, a variable reference or number */
if (*freeIt) {
free(*freeIt);
@@ -475,8 +489,9 @@ CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS)
str = NULL;
goto cleanup;
}
- Buf_AddByte(&buf, *condExpr);
- break;
+ Buf_AddByte(&buf, par->p[0]);
+ par->p++;
+ continue;
}
}
got_str:
@@ -487,41 +502,116 @@ cleanup:
return str;
}
-/* The different forms of #if's. */
+/* The different forms of .if directives. */
static const struct If {
const char *form; /* Form of if */
size_t formlen; /* Length of form */
Boolean doNot; /* TRUE if default function should be negated */
- Boolean (*defProc)(int, const char *); /* Default function to apply */
+ Boolean (*defProc)(size_t, const char *); /* Default function to apply */
} ifs[] = {
- { "def", 3, FALSE, CondDoDefined },
- { "ndef", 4, TRUE, CondDoDefined },
- { "make", 4, FALSE, CondDoMake },
- { "nmake", 5, TRUE, CondDoMake },
- { "", 0, FALSE, CondDoDefined },
+ { "def", 3, FALSE, FuncDefined },
+ { "ndef", 4, TRUE, FuncDefined },
+ { "make", 4, FALSE, FuncMake },
+ { "nmake", 5, TRUE, FuncMake },
+ { "", 0, FALSE, FuncDefined },
{ NULL, 0, FALSE, NULL }
};
-/*-
- * Return the next token from the input.
+/* Evaluate a "comparison without operator", such as in ".if ${VAR}" or
+ * ".if 0". */
+static Token
+EvalNotEmpty(CondParser *par, const char *lhs, Boolean lhsQuoted)
+{
+ double left;
+
+ /* For .ifxxx "..." check for non-empty string. */
+ if (lhsQuoted)
+ return lhs[0] != '\0';
+
+ /* For .ifxxx <number> compare against zero */
+ if (TryParseNumber(lhs, &left))
+ return left != 0.0;
+
+ /* For .if ${...} check for non-empty string (defProc is ifdef). */
+ if (par->if_info->form[0] == '\0')
+ return lhs[0] != 0;
+
+ /* Otherwise action default test ... */
+ return par->if_info->defProc(strlen(lhs), lhs) == !par->if_info->doNot;
+}
+
+/* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
+static Token
+EvalCompareNum(double lhs, const char *op, double rhs)
+{
+ DEBUG3(COND, "lhs = %f, rhs = %f, op = %.2s\n", lhs, rhs, op);
+
+ switch (op[0]) {
+ case '!':
+ if (op[1] != '=') {
+ Parse_Error(PARSE_WARNING, "Unknown operator");
+ /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
+ return TOK_ERROR;
+ }
+ return lhs != rhs;
+ case '=':
+ if (op[1] != '=') {
+ Parse_Error(PARSE_WARNING, "Unknown operator");
+ /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
+ return TOK_ERROR;
+ }
+ return lhs == rhs;
+ case '<':
+ return op[1] == '=' ? lhs <= rhs : lhs < rhs;
+ case '>':
+ return op[1] == '=' ? lhs >= rhs : lhs > rhs;
+ }
+ return TOK_ERROR;
+}
+
+static Token
+EvalCompareStr(const char *lhs, const char *op, const char *rhs)
+{
+ if (!((op[0] == '!' || op[0] == '=') && op[1] == '=')) {
+ Parse_Error(PARSE_WARNING,
+ "String comparison operator must be either == or !=");
+ /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
+ return TOK_ERROR;
+ }
+
+ DEBUG3(COND, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n", lhs, rhs, op);
+ return (*op == '=') == (strcmp(lhs, rhs) == 0);
+}
+
+/* Evaluate a comparison, such as "${VAR} == 12345". */
+static Token
+EvalCompare(const char *lhs, Boolean lhsQuoted, const char *op,
+ const char *rhs, Boolean rhsQuoted)
+{
+ double left, right;
+
+ if (!rhsQuoted && !lhsQuoted)
+ if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right))
+ return EvalCompareNum(left, op, right);
+
+ return EvalCompareStr(lhs, op, rhs);
+}
+
+/* Parse a comparison condition such as:
*
- * Side Effects:
- * condPushback will be set back to TOK_NONE if it is used.
+ * 0
+ * ${VAR:Mpattern}
+ * ${VAR} == value
+ * ${VAR:U0} < 12345
*/
static Token
-compare_expression(Boolean doEval)
+CondParser_Comparison(CondParser *par, Boolean doEval)
{
- Token t;
- const char *lhs;
- const char *rhs;
- const char *op;
- void *lhsFree;
- void *rhsFree;
- Boolean lhsQuoted;
- Boolean rhsQuoted;
- double left, right;
+ Token t = TOK_ERROR;
+ const char *lhs, *op, *rhs;
+ void *lhsFree, *rhsFree;
+ Boolean lhsQuoted, rhsQuoted;
- t = TOK_ERROR;
rhs = NULL;
lhsFree = rhsFree = NULL;
lhsQuoted = rhsQuoted = FALSE;
@@ -530,69 +620,44 @@ compare_expression(Boolean doEval)
* Parse the variable spec and skip over it, saving its
* value in lhs.
*/
- lhs = CondGetString(doEval, &lhsQuoted, &lhsFree, lhsStrict);
+ lhs = CondParser_String(par, doEval, lhsStrict, &lhsQuoted, &lhsFree);
if (!lhs)
goto done;
- /*
- * Skip whitespace to get to the operator
- */
- while (isspace((unsigned char)*condExpr))
- condExpr++;
+ CondParser_SkipWhitespace(par);
/*
* Make sure the operator is a valid one. If it isn't a
* known relational operator, pretend we got a
* != 0 comparison.
*/
- op = condExpr;
- switch (*condExpr) {
+ op = par->p;
+ switch (par->p[0]) {
case '!':
case '=':
case '<':
case '>':
- if (condExpr[1] == '=') {
- condExpr += 2;
+ if (par->p[1] == '=') {
+ par->p += 2;
} else {
- condExpr += 1;
+ par->p++;
}
break;
default:
- if (!doEval) {
- t = TOK_FALSE;
- goto done;
- }
- /* For .ifxxx "..." check for non-empty string. */
- if (lhsQuoted) {
- t = lhs[0] != 0;
- goto done;
- }
- /* For .ifxxx <number> compare against zero */
- if (CondCvtArg(lhs, &left)) {
- t = left != 0.0;
- goto done;
- }
- /* For .if ${...} check for non-empty string (defProc is ifdef). */
- if (if_info->form[0] == 0) {
- t = lhs[0] != 0;
- goto done;
- }
- /* Otherwise action default test ... */
- t = if_info->defProc(strlen(lhs), lhs) != if_info->doNot;
+ t = doEval ? EvalNotEmpty(par, lhs, lhsQuoted) : TOK_FALSE;
goto done;
}
- while (isspace((unsigned char)*condExpr))
- condExpr++;
+ CondParser_SkipWhitespace(par);
- if (*condExpr == '\0') {
- Parse_Error(PARSE_WARNING,
- "Missing right-hand-side of operator");
+ if (par->p[0] == '\0') {
+ Parse_Error(PARSE_WARNING, "Missing right-hand-side of operator");
+ /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
goto done;
}
- rhs = CondGetString(doEval, &rhsQuoted, &rhsFree, FALSE);
- if (!rhs)
+ rhs = CondParser_String(par, doEval, FALSE, &rhsQuoted, &rhsFree);
+ if (rhs == NULL)
goto done;
if (!doEval) {
@@ -600,73 +665,7 @@ compare_expression(Boolean doEval)
goto done;
}
- if (rhsQuoted || lhsQuoted) {
- do_string_compare:
- if (((*op != '!') && (*op != '=')) || (op[1] != '=')) {
- Parse_Error(PARSE_WARNING,
- "String comparison operator should be either == or !=");
- goto done;
- }
-
- if (DEBUG(COND)) {
- fprintf(debug_file, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
- lhs, rhs, op);
- }
- /*
- * Null-terminate rhs and perform the comparison.
- * t is set to the result.
- */
- if (*op == '=') {
- t = strcmp(lhs, rhs) == 0;
- } else {
- t = strcmp(lhs, rhs) != 0;
- }
- } else {
- /*
- * rhs is either a float or an integer. Convert both the
- * lhs and the rhs to a double and compare the two.
- */
-
- if (!CondCvtArg(lhs, &left) || !CondCvtArg(rhs, &right))
- goto do_string_compare;
-
- if (DEBUG(COND)) {
- fprintf(debug_file, "left = %f, right = %f, op = %.2s\n", left,
- right, op);
- }
- switch (op[0]) {
- case '!':
- if (op[1] != '=') {
- Parse_Error(PARSE_WARNING,
- "Unknown operator");
- goto done;
- }
- t = (left != right);
- break;
- case '=':
- if (op[1] != '=') {
- Parse_Error(PARSE_WARNING,
- "Unknown operator");
- goto done;
- }
- t = (left == right);
- break;
- case '<':
- if (op[1] == '=') {
- t = (left <= right);
- } else {
- t = (left < right);
- }
- break;
- case '>':
- if (op[1] == '=') {
- t = (left >= right);
- } else {
- t = (left > right);
- }
- break;
- }
- }
+ t = EvalCompare(lhs, lhsQuoted, op, rhs, rhsQuoted);
done:
free(lhsFree);
@@ -674,104 +673,96 @@ done:
return t;
}
-static int
-get_mpt_arg(Boolean doEval, const char **linePtr, char **argPtr,
- const char *func MAKE_ATTR_UNUSED)
+static size_t
+ParseEmptyArg(const char **linePtr, Boolean doEval,
+ const char *func MAKE_ATTR_UNUSED, char **argPtr)
{
- /*
- * Use Var_Parse to parse the spec in parens and return
- * TOK_TRUE if the resulting string is empty.
- */
- int length;
void *val_freeIt;
const char *val;
- const char *cp = *linePtr;
+ size_t magic_res;
/* We do all the work here and return the result as the length */
*argPtr = NULL;
- val = Var_Parse(cp - 1, VAR_CMD, doEval ? VARE_WANTRES : 0, &length,
- &val_freeIt);
- /*
- * Advance *linePtr to beyond the closing ). Note that
- * we subtract one because 'length' is calculated from 'cp - 1'.
- */
- *linePtr = cp - 1 + length;
+ (*linePtr)--; /* Make (*linePtr)[1] point to the '('. */
+ (void)Var_Parse(linePtr, VAR_CMDLINE, doEval ? VARE_WANTRES : 0,
+ &val, &val_freeIt);
+ /* TODO: handle errors */
+ /* If successful, *linePtr points beyond the closing ')' now. */
if (val == var_Error) {
free(val_freeIt);
- return -1;
+ return (size_t)-1;
}
/* A variable is empty when it just contains spaces... 4/15/92, christos */
- while (isspace((unsigned char)val[0]))
- val++;
+ cpp_skip_whitespace(&val);
/*
* For consistency with the other functions we can't generate the
* true/false here.
*/
- length = *val ? 2 : 1;
+ magic_res = *val != '\0' ? 2 : 1;
free(val_freeIt);
- return length;
+ return magic_res;
}
static Boolean
-CondDoEmpty(int arglen, const char *arg MAKE_ATTR_UNUSED)
+FuncEmpty(size_t arglen, const char *arg MAKE_ATTR_UNUSED)
{
+ /* Magic values ahead, see ParseEmptyArg. */
return arglen == 1;
}
static Token
-compare_function(Boolean doEval)
+CondParser_Func(CondParser *par, Boolean doEval)
{
static const struct fn_def {
const char *fn_name;
size_t fn_name_len;
- int (*fn_getarg)(Boolean, const char **, char **, const char *);
- Boolean (*fn_proc)(int, const char *);
+ size_t (*fn_parse)(const char **, Boolean, const char *, char **);
+ Boolean (*fn_eval)(size_t, const char *);
} fn_defs[] = {
- { "defined", 7, CondGetArg, CondDoDefined },
- { "make", 4, CondGetArg, CondDoMake },
- { "exists", 6, CondGetArg, CondDoExists },
- { "empty", 5, get_mpt_arg, CondDoEmpty },
- { "target", 6, CondGetArg, CondDoTarget },
- { "commands", 8, CondGetArg, CondDoCommands },
+ { "defined", 7, ParseFuncArg, FuncDefined },
+ { "make", 4, ParseFuncArg, FuncMake },
+ { "exists", 6, ParseFuncArg, FuncExists },
+ { "empty", 5, ParseEmptyArg, FuncEmpty },
+ { "target", 6, ParseFuncArg, FuncTarget },
+ { "commands", 8, ParseFuncArg, FuncCommands },
{ NULL, 0, NULL, NULL },
};
const struct fn_def *fn_def;
Token t;
char *arg = NULL;
- int arglen;
- const char *cp = condExpr;
+ size_t arglen;
+ const char *cp = par->p;
const char *cp1;
for (fn_def = fn_defs; fn_def->fn_name != NULL; fn_def++) {
- if (!istoken(cp, fn_def->fn_name, fn_def->fn_name_len))
+ if (!is_token(cp, fn_def->fn_name, fn_def->fn_name_len))
continue;
cp += fn_def->fn_name_len;
/* There can only be whitespace before the '(' */
- while (isspace((unsigned char)*cp))
- cp++;
+ cpp_skip_whitespace(&cp);
if (*cp != '(')
break;
- arglen = fn_def->fn_getarg(doEval, &cp, &arg, fn_def->fn_name);
- if (arglen <= 0) {
- condExpr = cp;
- return arglen < 0 ? TOK_ERROR : TOK_FALSE;
+ arglen = fn_def->fn_parse(&cp, doEval, fn_def->fn_name, &arg);
+ if (arglen == 0 || arglen == (size_t)-1) {
+ par->p = cp;
+ return arglen == 0 ? TOK_FALSE : TOK_ERROR;
}
/* Evaluate the argument using the required function. */
- t = !doEval || fn_def->fn_proc(arglen, arg);
+ t = !doEval || fn_def->fn_eval(arglen, arg);
free(arg);
- condExpr = cp;
+ par->p = cp;
return t;
}
/* Push anything numeric through the compare expression */
- cp = condExpr;
- if (isdigit((unsigned char)cp[0]) || strchr("+-", cp[0]))
- return compare_expression(doEval);
+ cp = par->p;
+ if (ch_isdigit(cp[0]) || strchr("+-", cp[0]))
+ return CondParser_Comparison(par, doEval);
/*
* Most likely we have a naked token to apply the default function to.
@@ -781,65 +772,66 @@ compare_function(Boolean doEval)
* would be invalid if we did "defined(a)" - so instead treat as an
* expression.
*/
- arglen = CondGetArg(doEval, &cp, &arg, NULL);
- for (cp1 = cp; isspace((unsigned char)*cp1); cp1++)
- continue;
+ arglen = ParseFuncArg(&cp, doEval, NULL, &arg);
+ cp1 = cp;
+ cpp_skip_whitespace(&cp1);
if (*cp1 == '=' || *cp1 == '!')
- return compare_expression(doEval);
- condExpr = cp;
+ return CondParser_Comparison(par, doEval);
+ par->p = cp;
/*
* Evaluate the argument using the default function.
- * This path always treats .if as .ifdef. To get here the character
+ * 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.
*/
- t = !doEval || if_info->defProc(arglen, arg) != if_info->doNot;
+ t = !doEval || par->if_info->defProc(arglen, arg) == !par->if_info->doNot;
free(arg);
return t;
}
+/* Return the next token or comparison result from the parser. */
static Token
-CondToken(Boolean doEval)
+CondParser_Token(CondParser *par, Boolean doEval)
{
Token t;
- t = condPushBack;
+ t = par->curr;
if (t != TOK_NONE) {
- condPushBack = TOK_NONE;
+ par->curr = TOK_NONE;
return t;
}
- while (*condExpr == ' ' || *condExpr == '\t') {
- condExpr++;
+ while (par->p[0] == ' ' || par->p[0] == '\t') {
+ par->p++;
}
- switch (*condExpr) {
+ switch (par->p[0]) {
case '(':
- condExpr++;
+ par->p++;
return TOK_LPAREN;
case ')':
- condExpr++;
+ par->p++;
return TOK_RPAREN;
case '|':
- if (condExpr[1] == '|') {
- condExpr++;
+ par->p++;
+ if (par->p[0] == '|') {
+ par->p++;
}
- condExpr++;
return TOK_OR;
case '&':
- if (condExpr[1] == '&') {
- condExpr++;
+ par->p++;
+ if (par->p[0] == '&') {
+ par->p++;
}
- condExpr++;
return TOK_AND;
case '!':
- condExpr++;
+ par->p++;
return TOK_NOT;
case '#':
@@ -849,36 +841,28 @@ CondToken(Boolean doEval)
case '"':
case '$':
- return compare_expression(doEval);
+ return CondParser_Comparison(par, doEval);
default:
- return compare_function(doEval);
+ return CondParser_Func(par, doEval);
}
}
-/*-
- *-----------------------------------------------------------------------
- * CondT --
- * Parse a single term in the expression. This consists of a terminal
- * symbol or TOK_NOT and a terminal symbol (not including the binary
- * operators):
- * T -> defined(variable) | make(target) | exists(file) | symbol
- * T -> ! T | ( E )
+/* Parse a single term in the expression. This consists of a terminal symbol
+ * or TOK_NOT and a term (not including the binary operators):
+ *
+ * T -> defined(variable) | make(target) | exists(file) | symbol
+ * T -> ! T | ( E )
*
* Results:
* TOK_TRUE, TOK_FALSE or TOK_ERROR.
- *
- * Side Effects:
- * Tokens are consumed.
- *
- *-----------------------------------------------------------------------
*/
static Token
-CondT(Boolean doEval)
+CondParser_Term(CondParser *par, Boolean doEval)
{
Token t;
- t = CondToken(doEval);
+ t = CondParser_Token(par, doEval);
if (t == TOK_EOF) {
/*
@@ -890,14 +874,14 @@ CondT(Boolean doEval)
/*
* T -> ( E )
*/
- t = CondE(doEval);
+ t = CondParser_Expr(par, doEval);
if (t != TOK_ERROR) {
- if (CondToken(doEval) != TOK_RPAREN) {
+ if (CondParser_Token(par, doEval) != TOK_RPAREN) {
t = TOK_ERROR;
}
}
} else if (t == TOK_NOT) {
- t = CondT(doEval);
+ t = CondParser_Term(par, doEval);
if (t == TOK_TRUE) {
t = TOK_FALSE;
} else if (t == TOK_FALSE) {
@@ -907,74 +891,61 @@ CondT(Boolean doEval)
return t;
}
-/*-
- *-----------------------------------------------------------------------
- * CondF --
- * Parse a conjunctive factor (nice name, wot?)
- * F -> T && F | T
+/* Parse a conjunctive factor (nice name, wot?)
+ *
+ * F -> T && F | T
*
* Results:
* TOK_TRUE, TOK_FALSE or TOK_ERROR
- *
- * Side Effects:
- * Tokens are consumed.
- *
- *-----------------------------------------------------------------------
*/
static Token
-CondF(Boolean doEval)
+CondParser_Factor(CondParser *par, Boolean doEval)
{
Token l, o;
- l = CondT(doEval);
+ l = CondParser_Term(par, doEval);
if (l != TOK_ERROR) {
- o = CondToken(doEval);
+ o = CondParser_Token(par, doEval);
if (o == TOK_AND) {
/*
* F -> T && F
*
- * If T is TOK_FALSE, the whole thing will be TOK_FALSE, but we have to
- * parse the r.h.s. anyway (to throw it away).
- * If T is TOK_TRUE, the result is the r.h.s., be it an TOK_ERROR or no.
+ * If T is TOK_FALSE, the whole thing will be TOK_FALSE, but we
+ * have to parse the r.h.s. anyway (to throw it away).
+ * If T is TOK_TRUE, the result is the r.h.s., be it a TOK_ERROR
+ * or not.
*/
if (l == TOK_TRUE) {
- l = CondF(doEval);
+ l = CondParser_Factor(par, doEval);
} else {
- (void)CondF(FALSE);
+ (void)CondParser_Factor(par, FALSE);
}
} else {
/*
* F -> T
*/
- CondPushBack(o);
+ CondParser_PushBack(par, o);
}
}
return l;
}
-/*-
- *-----------------------------------------------------------------------
- * CondE --
- * Main expression production.
- * E -> F || E | F
+/* Main expression production.
+ *
+ * E -> F || E | F
*
* Results:
* TOK_TRUE, TOK_FALSE or TOK_ERROR.
- *
- * Side Effects:
- * Tokens are, of course, consumed.
- *
- *-----------------------------------------------------------------------
*/
static Token
-CondE(Boolean doEval)
+CondParser_Expr(CondParser *par, Boolean doEval)
{
Token l, o;
- l = CondF(doEval);
+ l = CondParser_Factor(par, doEval);
if (l != TOK_ERROR) {
- o = CondToken(doEval);
+ o = CondParser_Token(par, doEval);
if (o == TOK_OR) {
/*
@@ -986,76 +957,60 @@ CondE(Boolean doEval)
* again if l is TOK_TRUE, we parse the r.h.s. to throw it away.
*/
if (l == TOK_FALSE) {
- l = CondE(doEval);
+ l = CondParser_Expr(par, doEval);
} else {
- (void)CondE(FALSE);
+ (void)CondParser_Expr(par, FALSE);
}
} else {
/*
* E -> F
*/
- CondPushBack(o);
+ CondParser_PushBack(par, o);
}
}
return l;
}
static CondEvalResult
-do_Cond_EvalExpression(Boolean *value)
+CondParser_Eval(CondParser *par, Boolean *value)
{
+ Token res;
- switch (CondE(TRUE)) {
- case TOK_TRUE:
- if (CondToken(TRUE) == TOK_EOF) {
- *value = TRUE;
- return COND_PARSE;
- }
- break;
- case TOK_FALSE:
- if (CondToken(TRUE) == TOK_EOF) {
- *value = FALSE;
- return COND_PARSE;
- }
- break;
- default:
- case TOK_ERROR:
- break;
- }
+ DEBUG1(COND, "CondParser_Eval: %s\n", par->p);
+
+ res = CondParser_Expr(par, TRUE);
+ if (res != TOK_FALSE && res != TOK_TRUE)
+ return COND_INVALID;
+
+ if (CondParser_Token(par, TRUE /* XXX: Why TRUE? */) != TOK_EOF)
+ return COND_INVALID;
- return COND_INVALID;
+ *value = res == TOK_TRUE;
+ return COND_PARSE;
}
-/*-
- *-----------------------------------------------------------------------
- * Cond_EvalExpression --
- * Evaluate an expression in the passed line. The expression
- * consists of &&, ||, !, make(target), defined(variable)
- * and parenthetical groupings thereof.
+/* Evaluate the condition, including any side effects from the variable
+ * 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.
+ * COND_INVALID if not a valid conditional.
*
* (*value) is set to the boolean value of the condition
- *
- * Side Effects:
- * Any effects from evaluating the variables.
- *-----------------------------------------------------------------------
*/
-CondEvalResult
-Cond_EvalExpression(const struct If *info, char *line, Boolean *value,
- int eprint, Boolean strictLHS)
+static CondEvalResult
+CondEvalExpression(const struct If *info, const char *cond, Boolean *value,
+ Boolean eprint, Boolean strictLHS)
{
static const struct If *dflt_info;
- const struct If *sv_if_info = if_info;
- const char *sv_condExpr = condExpr;
- Token sv_condPushBack = condPushBack;
+ CondParser par;
int rval;
lhsStrict = strictLHS;
- while (*line == ' ' || *line == '\t')
- line++;
+ while (*cond == ' ' || *cond == '\t')
+ cond++;
if (info == NULL && (info = dflt_info) == NULL) {
/* Scan for the entry for .if - it can't be first */
@@ -1066,49 +1021,48 @@ Cond_EvalExpression(const struct If *info, char *line, Boolean *value,
}
assert(info != NULL);
- if_info = info;
- condExpr = line;
- condPushBack = TOK_NONE;
+ par.if_info = info;
+ par.p = cond;
+ par.curr = TOK_NONE;
+ par.printedError = FALSE;
- rval = do_Cond_EvalExpression(value);
+ rval = CondParser_Eval(&par, value);
- if (rval == COND_INVALID && eprint)
- Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", line);
-
- if_info = sv_if_info;
- condExpr = sv_condExpr;
- condPushBack = sv_condPushBack;
+ if (rval == COND_INVALID && eprint && !par.printedError)
+ Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond);
return rval;
}
+CondEvalResult
+Cond_EvalCondition(const char *cond, Boolean *out_value)
+{
+ return CondEvalExpression(NULL, cond, out_value, FALSE, FALSE);
+}
-/*-
- *-----------------------------------------------------------------------
- * Cond_Eval --
- * Evaluate the conditional in the passed line. The line
- * looks like this:
- * .<cond-type> <expr>
- * where <cond-type> is any of if, ifmake, ifnmake, ifdef,
- * ifndef, elif, elifmake, elifnmake, elifdef, elifndef
- * and <expr> consists of &&, ||, !, make(target), defined(variable)
- * and parenthetical groupings thereof.
- *
- * Input:
- * line Line to parse
- *
- * Results:
- * COND_PARSE if should parse lines after the conditional
- * COND_SKIP if should skip lines after the conditional
- * COND_INVALID if not a valid conditional.
+/* Evaluate the conditional in the passed line. The line looks like this:
+ * .<cond-type> <expr>
+ * In this line, <cond-type> is any of if, ifmake, ifnmake, ifdef, ifndef,
+ * elif, elifmake, elifnmake, elifdef, elifndef.
+ * In this line, <expr> consists of &&, ||, !, function(arg), comparisons
+ * and parenthetical groupings thereof.
*
* Note that the states IF_ACTIVE and ELSE_ACTIVE are only different in order
* to detect spurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF),
* otherwise .else could be treated as '.elif 1'.
- *-----------------------------------------------------------------------
+ *
+ * Results:
+ * COND_PARSE to continue parsing the lines after the conditional
+ * (when .if or .else returns TRUE)
+ * COND_SKIP to skip the lines after the conditional
+ * (when .if or .elif returns FALSE, or when a previous
+ * branch has already been taken)
+ * COND_INVALID 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
-Cond_Eval(char *line)
+Cond_EvalLine(const char *line)
{
enum { MAXIF = 128 }; /* maximum depth of .if'ing */
enum { MAXIF_BUMP = 32 }; /* how much to grow by */
@@ -1125,10 +1079,8 @@ Cond_Eval(char *line)
const struct If *ifp;
Boolean isElif;
Boolean value;
- int level; /* Level at which to report errors. */
enum if_states state;
- level = PARSE_FATAL;
if (!cond_state) {
cond_state = bmake_malloc(max_if_depth * sizeof(*cond_state));
cond_state[0] = IF_ACTIVE;
@@ -1140,11 +1092,11 @@ Cond_Eval(char *line)
/* Find what type of if we're dealing with. */
if (line[0] == 'e') {
if (line[1] != 'l') {
- if (!istoken(line + 1, "ndif", 4))
+ if (!is_token(line + 1, "ndif", 4))
return COND_INVALID;
/* End of conditional section */
if (cond_depth == cond_min_depth) {
- Parse_Error(level, "if-less endif");
+ Parse_Error(PARSE_FATAL, "if-less endif");
return COND_PARSE;
}
/* Return state for previous conditional */
@@ -1155,10 +1107,10 @@ Cond_Eval(char *line)
/* Quite likely this is 'else' or 'elif' */
line += 2;
- if (istoken(line, "se", 2)) {
+ if (is_token(line, "se", 2)) {
/* It is else... */
if (cond_depth == cond_min_depth) {
- Parse_Error(level, "if-less else");
+ Parse_Error(PARSE_FATAL, "if-less else");
return COND_PARSE;
}
@@ -1197,7 +1149,7 @@ Cond_Eval(char *line)
for (ifp = ifs;; ifp++) {
if (ifp->form == NULL)
return COND_INVALID;
- if (istoken(ifp->form, line, ifp->formlen)) {
+ if (is_token(ifp->form, line, ifp->formlen)) {
line += ifp->formlen;
break;
}
@@ -1207,7 +1159,7 @@ Cond_Eval(char *line)
if (isElif) {
if (cond_depth == cond_min_depth) {
- Parse_Error(level, "if-less elif");
+ Parse_Error(PARSE_FATAL, "if-less elif");
return COND_PARSE;
}
state = cond_state[cond_depth];
@@ -1242,8 +1194,8 @@ Cond_Eval(char *line)
}
}
- /* And evaluate the conditional expresssion */
- if (Cond_EvalExpression(ifp, line, &value, 1, TRUE) == COND_INVALID) {
+ /* And evaluate the conditional expression */
+ if (CondEvalExpression(ifp, line, &value, TRUE, TRUE) == COND_INVALID) {
/* Syntax error in conditional, error message already output. */
/* Skip everything to matching .endif */
cond_state[cond_depth] = SKIP_TO_ELSE;
@@ -1261,10 +1213,10 @@ Cond_Eval(char *line)
void
Cond_restore_depth(unsigned int saved_depth)
{
- int open_conds = cond_depth - cond_min_depth;
+ unsigned int open_conds = cond_depth - cond_min_depth;
if (open_conds != 0 || saved_depth > cond_depth) {
- Parse_Error(PARSE_FATAL, "%d open conditional%s", open_conds,
+ Parse_Error(PARSE_FATAL, "%u open conditional%s", open_conds,
open_conds == 1 ? "" : "s");
cond_depth = cond_min_depth;
}
@@ -1275,7 +1227,7 @@ Cond_restore_depth(unsigned int saved_depth)
unsigned int
Cond_save_depth(void)
{
- int depth = cond_min_depth;
+ unsigned int depth = cond_min_depth;
cond_min_depth = cond_depth;
return depth;
diff --git a/configure b/configure
index 7c338bc141c8..814157f4e75f 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for bmake 20200710.
+# Generated by GNU Autoconf 2.69 for bmake 20201018.
#
# Report bugs to <sjg@NetBSD.org>.
#
@@ -580,8 +580,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='bmake'
PACKAGE_TARNAME='bmake'
-PACKAGE_VERSION='20200710'
-PACKAGE_STRING='bmake 20200710'
+PACKAGE_VERSION='20201018'
+PACKAGE_STRING='bmake 20201018'
PACKAGE_BUGREPORT='sjg@NetBSD.org'
PACKAGE_URL=''
@@ -1254,7 +1254,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 20200710 to adapt to many kinds of systems.
+\`configure' configures bmake 20201018 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1315,7 +1315,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of bmake 20200710:";;
+ short | recursive ) echo "Configuration of bmake 20201018:";;
esac
cat <<\_ACEOF
@@ -1323,16 +1323,16 @@ Optional Features:
--disable-option-checking ignore unrecognized --enable/--with options
--disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
- --disable-pwd-override disable \$PWD overriding getcwd()
+ --disable-pwd-override disable $PWD overriding getcwd()
--disable-check-make-chdir disable make trying to guess
- when it should automatically cd \${.CURDIR}
+ when it should automatically cd ${.CURDIR}
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
- --without-makefile dissable use of generated makefile
- --without-meta dissable use of meta-mode
+ --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-machine=MACHINE explicitly set MACHINE
--with-force-machine=MACHINE set FORCE_MACHINE
@@ -1421,7 +1421,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-bmake configure 20200710
+bmake configure 20201018
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2001,7 +2001,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by bmake $as_me 20200710, which was
+It was created by bmake $as_me 20201018, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -4543,7 +4543,6 @@ if test $bmake_path_max -gt 1024; then
fi
echo "Using: BMAKE_PATH_MAX=$bmake_path_max" >&6
-$ac_includes_default
{ $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 :
@@ -6665,7 +6664,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by bmake $as_me 20200710, which was
+This file was extended by bmake $as_me 20201018, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -6727,7 +6726,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-bmake config.status 20200710
+bmake config.status 20201018
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/configure.in b/configure.in
index 5b45329ea3d1..384c403e544a 100644
--- a/configure.in
+++ b/configure.in
@@ -1,11 +1,11 @@
dnl
dnl RCSid:
-dnl $Id: configure.in,v 1.66 2020/07/10 16:34:38 sjg Exp $
+dnl $Id: configure.in,v 1.67 2020/10/19 19:47:50 sjg Exp $
dnl
dnl Process this file with autoconf to produce a configure script
dnl
AC_PREREQ(2.50)
-AC_INIT([bmake], [20200710], [sjg@NetBSD.org])
+AC_INIT([bmake], [20201018], [sjg@NetBSD.org])
AC_CONFIG_HEADERS(config.h)
dnl make srcdir absolute
@@ -38,7 +38,7 @@ CYGWIN*|MINGW*) use_makefile=no;;
*) use_makefile=yes;;
esac
AC_ARG_WITH(makefile,
-[ --without-makefile dissable use of generated makefile],
+[ --without-makefile disable use of generated makefile],
[case "${withval}" in
yes|no) use_makefile=${withval};;
*) AC_MSG_ERROR(bad value ${withval} given for makefile) ;;
@@ -46,7 +46,7 @@ esac])
dnl
use_meta=yes
AC_ARG_WITH(meta,
-[ --without-meta dissable use of meta-mode],
+[ --without-meta disable use of meta-mode],
[case "${withval}" in
yes|no) use_meta=${withval};;
*) AC_MSG_ERROR(bad value ${withval} given for meta) ;;
@@ -128,7 +128,6 @@ dnl AC_C_CROSS
dnl
dnl Checks for header files.
-AC_INCLUDES_DEFAULT
AC_HEADER_SYS_WAIT
AC_HEADER_DIRENT
dnl Keep this list sorted
@@ -352,7 +351,7 @@ dnl
dnl And this can be handy to do with out.
dnl
AC_ARG_ENABLE(pwd-override,
-[ --disable-pwd-override disable \$PWD overriding getcwd()],
+[ --disable-pwd-override disable $PWD overriding getcwd()],
[case "${enableval}" in
yes) ;;
no) CPPFLAGS="$CPPFLAGS -DNO_PWD_OVERRIDE" ;;
@@ -363,7 +362,7 @@ dnl Just for grins
dnl
AC_ARG_ENABLE(check-make-chdir,
[ --disable-check-make-chdir disable make trying to guess
- when it should automatically cd \${.CURDIR}],
+ when it should automatically cd ${.CURDIR}],
[case "${enableval}" in
yes) ;;
no) CPPFLAGS="$CPPFLAGS -DNO_CHECK_MAKE_CHDIR" ;;
diff --git a/dir.c b/dir.c
index 4a561deca6fc..d1778d127b29 100644
--- a/dir.c
+++ b/dir.c
@@ -1,4 +1,4 @@
-/* $NetBSD: dir.c,v 1.135 2020/09/02 04:32:13 rillig Exp $ */
+/* $NetBSD: dir.c,v 1.193 2020/10/31 17:39:20 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -69,70 +69,58 @@
* SUCH DAMAGE.
*/
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: dir.c,v 1.135 2020/09/02 04:32:13 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)dir.c 8.2 (Berkeley) 1/2/94";
-#else
-__RCSID("$NetBSD: dir.c,v 1.135 2020/09/02 04:32:13 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
-/*-
- * dir.c --
- * Directory searching using wildcards and/or normal names...
- * Used both for source wildcarding in the Makefile and for finding
- * implicit sources.
+/* Directory searching using wildcards and/or normal names.
+ * Used both for source wildcarding in the makefile and for finding
+ * implicit sources.
*
* The interface for this module is:
- * Dir_Init Initialize the module.
+ * Dir_Init Initialize the module.
*
- * Dir_InitCur Set the cur Path.
+ * Dir_InitCur Set the cur CachedDir.
*
- * Dir_InitDot Set the dot Path.
+ * Dir_InitDot Set the dot CachedDir.
*
- * Dir_End Cleanup the module.
+ * Dir_End Clean up the module.
*
- * Dir_SetPATH Set ${.PATH} to reflect state of dirSearchPath.
+ * Dir_SetPATH Set ${.PATH} to reflect state of dirSearchPath.
*
- * Dir_HasWildcards Returns TRUE if the name given it needs to
- * be wildcard-expanded.
+ * Dir_HasWildcards
+ * Returns TRUE if the name given it needs to
+ * be wildcard-expanded.
*
- * Dir_Expand Given a pattern and a path, return a Lst of names
- * which match the pattern on the search path.
+ * Dir_Expand Given a pattern and a path, return a Lst of names
+ * which match the pattern on 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.
+ * Dir_FindFile Searches for a file on a given search path.
+ * If it exists, the entire path is returned.
+ * Otherwise NULL is returned.
*
- * 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 ("/").
+ * 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 ("/").
*
- * Dir_MTime Return the modification time of a node. The file
- * is searched for along the default search path.
- * The path and mtime fields of the node are filled
- * in.
+ * Dir_MTime Return the modification time of a node. The file
+ * is searched for along the default search path.
+ * The path and mtime fields of the node are filled in.
*
- * Dir_AddDir Add a directory to a search path.
+ * Dir_AddDir Add a directory to a search path.
*
- * Dir_MakeFlags Given a search path and a command flag, create
- * a string with each of the directories in the path
- * preceded by the command flag and all of them
- * separated by a space.
+ * Dir_MakeFlags Given a search path and a command flag, create
+ * a string with each of the directories in the path
+ * 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.
- * Dir_ClearPath Resets a search path to the empty list.
+ * 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.
+ *
+ * Dir_ClearPath Resets a search path to the empty list.
*
* For debugging:
- * Dir_PrintDirectories Print stats about the directory cache.
+ * Dir_PrintDirectories
+ * Print stats about the directory cache.
*/
#include <sys/types.h>
@@ -140,99 +128,155 @@ __RCSID("$NetBSD: dir.c,v 1.135 2020/09/02 04:32:13 rillig Exp $");
#include <dirent.h>
#include <errno.h>
-#include <stdio.h>
#include "make.h"
#include "dir.h"
#include "job.h"
+/* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */
+MAKE_RCSID("$NetBSD: dir.c,v 1.193 2020/10/31 17:39:20 rillig Exp $");
+
+#define DIR_DEBUG0(text) DEBUG0(DIR, text)
+#define DIR_DEBUG1(fmt, arg1) DEBUG1(DIR, fmt, arg1)
+#define DIR_DEBUG2(fmt, arg1, arg2) DEBUG2(DIR, fmt, arg1, arg2)
+
+/* A search path is a list of CachedDir structures. A CachedDir has in it the
+ * name of the directory and the names of all the files in the directory.
+ * This is used to cut down on the number of system calls necessary to find
+ * implicit dependents and their like. Since these searches are made before
+ * any actions are taken, we need not worry about the directory changing due
+ * to creation commands. If this hampers the style of some makefiles, they
+ * must be changed.
+ *
+ * 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:
+ *
+ * 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
+ * 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 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.
+ *
+ * 3) record the mtime of the directory in the CachedDir structure and
+ * verify the directory hasn't changed since the contents were cached.
+ * This will catch the creation or deletion of files, but not the
+ * updating of files. However, since it is the creation and deletion
+ * that is the problem, this could be a good thing to do. Unfortunately,
+ * if the directory (say ".") were fairly large and changed fairly
+ * frequently, the constant reloading could seriously degrade
+ * performance. It might be good in such cases to keep track of the
+ * 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 pcc-based compilers refused to allow you 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.
+ *
+ * One more note: the location of a target's file is only performed on the
+ * downward traversal of the graph and then only for terminal nodes in the
+ * graph. This could be construed as wrong in some cases, but prevents
+ * inadvertent modification of files when the "installed" directory for a
+ * file is provided in the search path.
+ *
+ * Another data structure maintained by this module is an mtime cache used
+ * when the searching of cached directories fails to find a file. In the past,
+ * Dir_FindFile would simply perform an access() call in such a case to
+ * determine if the file could be found using just the name given. When this
+ * hit, however, all that was gained was the knowledge that the file existed.
+ * Given that an access() is essentially a stat() without the copyout() call,
+ * and that the same filesystem overhead would have to be incurred in
+ * Dir_MTime, it made sense to replace the access() with a stat() and record
+ * the mtime in a cache for when Dir_MTime was actually called.
+ */
-#define DIR_DEBUG0(fmt) \
- if (!DEBUG(DIR)) (void) 0; else fprintf(debug_file, fmt)
+typedef List CachedDirList;
+typedef ListNode CachedDirListNode;
-#define DIR_DEBUG1(fmt, arg1) \
- if (!DEBUG(DIR)) (void) 0; else fprintf(debug_file, fmt, arg1)
+typedef ListNode SearchPathNode;
-#define DIR_DEBUG2(fmt, arg1, arg2) \
- if (!DEBUG(DIR)) (void) 0; else fprintf(debug_file, fmt, arg1, arg2)
+SearchPath *dirSearchPath; /* main search path */
+/* A list of cached directories, with fast lookup by directory name. */
+typedef struct OpenDirs {
+ CachedDirList *list;
+ HashTable /* of CachedDirListNode */ table;
+} OpenDirs;
-/*
- * A search path consists of a Lst of Path structures. A Path structure
- * has in it the name of the directory and a hash table of all the files
- * in the directory. This is used to cut down on the number of system
- * calls necessary to find implicit dependents and their like. Since
- * these searches are made before any actions are taken, we need not
- * worry about the directory changing due to creation commands. If this
- * hampers the style of some makefiles, they must be changed.
- *
- * A list of all previously-read directories is kept in the
- * openDirectories Lst. This list 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 hashing 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:
- *
- * 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 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
- * 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.
- * 3) record the mtime of the directory in the Path structure and
- * verify the directory hasn't changed since the contents were
- * hashed. This will catch the creation or deletion of files,
- * but not the updating of files. However, since it is the
- * creation and deletion that is the problem, this could be
- * a good thing to do. Unfortunately, if the directory (say ".")
- * were fairly large and changed fairly frequently, the constant
- * rehashing could seriously degrade performance. It might be
- * good in such cases to keep track of the number of rehashes
- * 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 pcc-based compilers refused
- * to allow you 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.
- *
- * One more note: the location of a target's file is only performed
- * on the downward traversal of the graph and then only for terminal
- * nodes in the graph. This could be construed as wrong in some cases,
- * but prevents inadvertent modification of files when the "installed"
- * directory for a file is provided in the search path.
- *
- * Another data structure maintained by this module is an mtime
- * cache used when the searching of cached directories fails to find
- * a file. In the past, Dir_FindFile would simply perform an access()
- * call in such a case to determine if the file could be found using
- * just the name given. When this hit, however, all that was gained
- * was the knowledge that the file existed. Given that an access() is
- * essentially a stat() without the copyout() call, and that the same
- * filesystem overhead would have to be incurred in Dir_MTime, it made
- * sense to replace the access() with a stat() and record the mtime
- * in a cache for when Dir_MTime was actually called.
- */
+static void
+OpenDirs_Init(OpenDirs *odirs)
+{
+ odirs->list = Lst_New();
+ HashTable_Init(&odirs->table);
+}
+
+#ifdef CLEANUP
+static void
+OpenDirs_Done(OpenDirs *odirs)
+{
+ CachedDirListNode *ln = odirs->list->first;
+ while (ln != NULL) {
+ CachedDirListNode *next = ln->next;
+ CachedDir *dir = ln->datum;
+ Dir_Destroy(dir); /* removes the dir from odirs->list */
+ ln = next;
+ }
+ Lst_Free(odirs->list);
+ HashTable_Done(&odirs->table);
+}
+#endif
+
+static CachedDir *
+OpenDirs_Find(OpenDirs *odirs, const char *name)
+{
+ CachedDirListNode *ln = HashTable_FindValue(&odirs->table, name);
+ return ln != NULL ? ln->datum : NULL;
+}
+
+static void
+OpenDirs_Add(OpenDirs *odirs, CachedDir *cdir)
+{
+ HashEntry *he = HashTable_FindEntry(&odirs->table, cdir->name);
+ if (he != NULL)
+ return;
+ he = HashTable_CreateEntry(&odirs->table, cdir->name, NULL);
+ Lst_Append(odirs->list, cdir);
+ HashEntry_Set(he, odirs->list->last);
+}
-Lst dirSearchPath; /* main search path */
+static void
+OpenDirs_Remove(OpenDirs *odirs, const char *name)
+{
+ HashEntry *he = HashTable_FindEntry(&odirs->table, name);
+ CachedDirListNode *ln;
+ if (he == NULL)
+ return;
+ ln = HashEntry_Get(he);
+ HashTable_DeleteEntry(&odirs->table, he);
+ Lst_Remove(odirs->list, ln);
+}
-static Lst openDirectories; /* the list of all open directories */
+static OpenDirs openDirs; /* the list of all open directories */
/*
- * Variables for gathering statistics on the efficiency of the hashing
+ * Variables for gathering statistics on the efficiency of the cashing
* mechanism.
*/
static int hits; /* Found in directory cache */
@@ -240,9 +284,9 @@ static int misses; /* Sad, but not evil misses */
static int nearmisses; /* Found under search path */
static int bigmisses; /* Sought by itself */
-static Path *dot; /* contents of current directory */
-static Path *cur; /* contents of current directory, if not dot */
-static Path *dotLast; /* a fake path entry indicating we need to
+static CachedDir *dot; /* contents of current directory */
+static CachedDir *cur; /* contents of current directory, if not dot */
+static CachedDir *dotLast; /* a fake path entry indicating we need to
* look for . last */
/* Results of doing a last-resort stat in Dir_FindFile -- if we have to go to
@@ -252,19 +296,9 @@ static Path *dotLast; /* a fake path entry indicating we need to
* 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... */
-static Hash_Table mtimes;
-
-static Hash_Table lmtimes; /* same as mtimes but for lstat */
-
-static void DirExpandCurly(const char *, const char *, Lst, Lst);
-static void DirExpandInt(const char *, Lst, Lst);
-static int DirPrintWord(void *, void *);
-static int DirPrintDir(void *, void *);
-static char *DirLookup(Path *, const char *, const char *, Boolean);
-static char *DirLookupSubdir(Path *, const char *);
-static char *DirFindDot(Boolean, const char *, const char *);
-static char *DirLookupAbs(Path *, const char *, const char *);
+static HashTable mtimes;
+static HashTable lmtimes; /* same as mtimes but for lstat */
/*
* We use stat(2) a lot, cache the results.
@@ -277,17 +311,17 @@ struct cache_st {
};
/* minimize changes below */
-typedef enum {
+typedef enum CachedStatsFlags {
CST_LSTAT = 0x01, /* call lstat(2) instead of stat(2) */
CST_UPDATE = 0x02 /* ignore existing cached entry */
} CachedStatsFlags;
/* Returns 0 and the result of stat(2) or lstat(2) in *mst, or -1 on error. */
static int
-cached_stats(Hash_Table *htp, const char *pathname, struct make_stat *mst,
+cached_stats(HashTable *htp, const char *pathname, struct make_stat *mst,
CachedStatsFlags flags)
{
- Hash_Entry *entry;
+ HashEntry *entry;
struct stat sys_st;
struct cache_st *cst;
int rc;
@@ -295,10 +329,10 @@ cached_stats(Hash_Table *htp, const char *pathname, struct make_stat *mst,
if (!pathname || !pathname[0])
return -1;
- entry = Hash_FindEntry(htp, pathname);
+ entry = HashTable_FindEntry(htp, pathname);
if (entry && !(flags & CST_UPDATE)) {
- cst = Hash_GetValue(entry);
+ cst = HashEntry_Get(entry);
mst->mst_mode = cst->mode;
mst->mst_mtime = (flags & CST_LSTAT) ? cst->lmtime : cst->mtime;
@@ -322,12 +356,12 @@ cached_stats(Hash_Table *htp, const char *pathname, struct make_stat *mst,
mst->mst_mtime = sys_st.st_mtime;
if (entry == NULL)
- entry = Hash_CreateEntry(htp, pathname, NULL);
- if (Hash_GetValue(entry) == NULL) {
- Hash_SetValue(entry, bmake_malloc(sizeof(*cst)));
- memset(Hash_GetValue(entry), 0, sizeof(*cst));
+ entry = HashTable_CreateEntry(htp, pathname, NULL);
+ if (HashEntry_Get(entry) == NULL) {
+ HashEntry_Set(entry, bmake_malloc(sizeof(*cst)));
+ memset(HashEntry_Get(entry), 0, sizeof(*cst));
}
- cst = Hash_GetValue(entry);
+ cst = HashEntry_Get(entry);
if (flags & CST_LSTAT) {
cst->lmtime = sys_st.st_mtime;
} else {
@@ -352,14 +386,14 @@ cached_lstat(const char *pathname, struct make_stat *st)
return cached_stats(&lmtimes, pathname, st, CST_LSTAT);
}
-/* Initialize things for this module. */
+/* Initialize the directories module. */
void
Dir_Init(void)
{
- dirSearchPath = Lst_Init();
- openDirectories = Lst_Init();
- Hash_InitTable(&mtimes, 0);
- Hash_InitTable(&lmtimes, 0);
+ dirSearchPath = Lst_New();
+ OpenDirs_Init(&openDirs);
+ HashTable_Init(&mtimes);
+ HashTable_Init(&lmtimes);
}
void
@@ -367,11 +401,11 @@ Dir_InitDir(const char *cdname)
{
Dir_InitCur(cdname);
- dotLast = bmake_malloc(sizeof(Path));
+ dotLast = bmake_malloc(sizeof(CachedDir));
dotLast->refCount = 1;
dotLast->hits = 0;
dotLast->name = bmake_strdup(".DOTLAST");
- Hash_InitTable(&dotLast->files, -1);
+ HashTable_Init(&dotLast->files);
}
/*
@@ -380,23 +414,23 @@ Dir_InitDir(const char *cdname)
void
Dir_InitCur(const char *cdname)
{
- Path *p;
+ CachedDir *dir;
if (cdname != NULL) {
/*
* Our build directory is not the same as our source directory.
* Keep this one around too.
*/
- if ((p = Dir_AddDir(NULL, cdname))) {
- p->refCount += 1;
- if (cur && cur != p) {
+ if ((dir = Dir_AddDir(NULL, cdname))) {
+ dir->refCount++;
+ if (cur && cur != dir) {
/*
- * We've been here before, cleanup.
+ * We've been here before, clean up.
*/
- cur->refCount -= 1;
+ cur->refCount--;
Dir_Destroy(cur);
}
- cur = p;
+ cur = dir;
}
}
}
@@ -407,11 +441,8 @@ void
Dir_InitDot(void)
{
if (dot != NULL) {
- LstNode ln;
-
- /* Remove old entry from openDirectories, but do not destroy. */
- ln = Lst_FindDatum(openDirectories, dot);
- Lst_Remove(openDirectories, ln);
+ /* Remove old entry from openDirs, but do not destroy. */
+ OpenDirs_Remove(&openDirs, dot->name);
}
dot = Dir_AddDir(NULL, ".");
@@ -425,28 +456,27 @@ Dir_InitDot(void)
* We always need to have dot around, so we increment its reference count
* to make sure it's not destroyed.
*/
- dot->refCount += 1;
+ dot->refCount++;
Dir_SetPATH(); /* initialize */
}
-/* Clean up things for this module. */
+/* Clean up the directories module. */
void
Dir_End(void)
{
#ifdef CLEANUP
if (cur) {
- cur->refCount -= 1;
+ cur->refCount--;
Dir_Destroy(cur);
}
- dot->refCount -= 1;
- dotLast->refCount -= 1;
+ dot->refCount--;
+ dotLast->refCount--;
Dir_Destroy(dotLast);
Dir_Destroy(dot);
Dir_ClearPath(dirSearchPath);
Lst_Free(dirSearchPath);
- Dir_ClearPath(openDirectories);
- Lst_Free(openDirectories);
- Hash_DeleteTable(&mtimes);
+ OpenDirs_Done(&openDirs);
+ HashTable_Done(&mtimes);
#endif
}
@@ -458,16 +488,14 @@ Dir_End(void)
void
Dir_SetPATH(void)
{
- LstNode ln; /* a list element */
- Path *p;
+ CachedDirListNode *ln;
Boolean hasLastDot = FALSE; /* true if we should search dot last */
Var_Delete(".PATH", VAR_GLOBAL);
- Lst_Open(dirSearchPath);
- if ((ln = Lst_First(dirSearchPath)) != NULL) {
- p = LstNode_Datum(ln);
- if (p == dotLast) {
+ if ((ln = dirSearchPath->first) != NULL) {
+ CachedDir *dir = ln->datum;
+ if (dir == dotLast) {
hasLastDot = TRUE;
Var_Append(".PATH", dotLast->name, VAR_GLOBAL);
}
@@ -480,13 +508,13 @@ Dir_SetPATH(void)
Var_Append(".PATH", cur->name, VAR_GLOBAL);
}
- while ((ln = Lst_Next(dirSearchPath)) != NULL) {
- p = LstNode_Datum(ln);
- if (p == dotLast)
+ for (ln = dirSearchPath->first; ln != NULL; ln = ln->next) {
+ CachedDir *dir = ln->datum;
+ if (dir == dotLast)
continue;
- if (p == dot && hasLastDot)
+ if (dir == dot && hasLastDot)
continue;
- Var_Append(".PATH", p->name, VAR_GLOBAL);
+ Var_Append(".PATH", dir->name, VAR_GLOBAL);
}
if (hasLastDot) {
@@ -495,40 +523,26 @@ Dir_SetPATH(void)
if (cur)
Var_Append(".PATH", cur->name, VAR_GLOBAL);
}
- Lst_Close(dirSearchPath);
-}
-
-/* See if the Path structure describes the same directory as the
- * given one by comparing their names. Called from Dir_AddDir via
- * Lst_Find when searching the list of open directories. */
-static Boolean
-DirFindName(const void *p, const void *desiredName)
-{
- return strcmp(((const Path *)p)->name, desiredName) == 0;
}
-/* See if the given name has any wildcard characters in it. Be careful not to
- * expand unmatching brackets or braces.
+/* See if the given name has any wildcard characters in it and all braces and
+ * brackets are properly balanced.
*
* 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!
*
- * Input:
- * name name to check
- *
- * Results:
- * returns TRUE if the word should be expanded, FALSE otherwise
+ * Return TRUE if the word should be expanded, FALSE otherwise.
*/
Boolean
Dir_HasWildcards(const char *name)
{
- const char *cp;
+ const char *p;
Boolean wild = FALSE;
int braces = 0, brackets = 0;
- for (cp = name; *cp; cp++) {
- switch (*cp) {
+ for (p = name; *p != '\0'; p++) {
+ switch (*p) {
case '{':
braces++;
wild = TRUE;
@@ -554,51 +568,49 @@ Dir_HasWildcards(const char *name)
return wild && brackets == 0 && braces == 0;
}
-/*-
- *-----------------------------------------------------------------------
- * DirMatchFiles --
- * Given a pattern and a Path structure, see if any files
- * match the pattern and add their names to the 'expansions' list if
- * any do. This is incomplete -- it doesn't take care of patterns like
- * src / *src / *.c properly (just *.c on any of the directories), but it
- * will do for now.
+/* See if any files match the pattern and add their names to the 'expansions'
+ * list 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,
+ * use the ':sh' variable modifier such as in ${:!echo src/lib*c/file*.c!}.
*
* Input:
* pattern Pattern to look for
- * p Directory to search
+ * dir Directory to search
* expansion Place to store the results
- *
- * Side Effects:
- * File names are added to the expansions lst. The directory will be
- * fully hashed when this is done.
- *-----------------------------------------------------------------------
*/
static void
-DirMatchFiles(const char *pattern, Path *p, Lst expansions)
+DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions)
{
- Hash_Search search; /* Index into the directory's table */
- Hash_Entry *entry; /* Current entry in the table */
- Boolean isDot; /* TRUE if the directory being searched is . */
+ const char *dirName = dir->name;
+ Boolean isDot = dirName[0] == '.' && dirName[1] == '\0';
+ HashIter hi;
+
+ HashIter_Init(&hi, &dir->files);
+ while (HashIter_Next(&hi) != NULL) {
+ const char *base = hi.entry->key;
- isDot = (*p->name == '.' && p->name[1] == '\0');
+ if (!Str_Match(base, pattern))
+ continue;
- for (entry = Hash_EnumFirst(&p->files, &search);
- entry != NULL;
- entry = Hash_EnumNext(&search))
- {
/*
- * See if the file matches the given pattern. Note we follow the UNIX
- * convention that dot files will only be found if the pattern
- * begins with a dot (note also that as a side effect of the hashing
- * scheme, .* won't match . or .. since they aren't hashed).
+ * Follow the UNIX convention that dot files are only found if the
+ * pattern begins with a dot. The pattern '.*' does not match '.' or
+ * '..' since these are not included in the directory cache.
+ *
+ * This means that the pattern '[a-z.]*' does not find '.file', which
+ * is consistent with bash, NetBSD sh and csh.
*/
- if (Str_Match(entry->name, pattern) &&
- ((entry->name[0] != '.') ||
- (pattern[0] == '.')))
+ if (base[0] == '.' && pattern[0] != '.')
+ continue;
+
{
- Lst_Append(expansions,
- (isDot ? bmake_strdup(entry->name) :
- str_concat3(p->name, "/", entry->name)));
+ char *fullName = isDot
+ ? bmake_strdup(base)
+ : str_concat3(dirName, "/", base);
+ Lst_Append(expansions, fullName);
}
}
}
@@ -667,30 +679,24 @@ concat3(const char *a, size_t a_len, const char *b, size_t b_len,
return s;
}
-/*-
- *-----------------------------------------------------------------------
- * DirExpandCurly --
- * Expand curly braces like the C shell. Does this recursively.
- * Note the special case: if after the piece of the curly brace is
- * done there are no wildcard characters in the result, the result is
- * placed on the list WITHOUT CHECKING FOR ITS EXISTENCE.
+/* Expand curly braces like the C shell. Brace expansion by itself is purely
+ * textual, the expansions are not looked up in the file system. But if an
+ * expanded word contains wildcard characters, it is expanded further,
+ * matching only the actually existing files.
+ *
+ * Example: "{a{b,c}}" expands to "ab" and "ac".
+ * Example: "{a}" expands to "a".
+ * Example: "{a,*.c}" expands to "a" and all "*.c" files that exist.
*
* Input:
* word Entire word to expand
* brace First curly brace in it
* path Search path to use
* expansions Place to store the expansions
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The given list is filled with the expansions...
- *
- *-----------------------------------------------------------------------
*/
static void
-DirExpandCurly(const char *word, const char *brace, Lst path, Lst expansions)
+DirExpandCurly(const char *word, const char *brace, SearchPath *path,
+ StringList *expansions)
{
const char *prefix, *middle, *piece, *middle_end, *suffix;
size_t prefix_len, suffix_len;
@@ -731,72 +737,40 @@ DirExpandCurly(const char *word, const char *brace, Lst path, Lst expansions)
}
-/*-
- *-----------------------------------------------------------------------
- * DirExpandInt --
- * Internal expand routine. Passes through the directories in the
- * path one by one, calling DirMatchFiles for each. NOTE: This still
- * doesn't handle patterns in directories...
- *
- * Input:
- * word Word to expand
- * path Path on which to look
- * expansions Place to store the result
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Things are added to the expansions list.
- *
- *-----------------------------------------------------------------------
- */
+/* Expand the word in each of the directories from the path. */
static void
-DirExpandInt(const char *word, Lst path, Lst expansions)
+DirExpandPath(const char *word, SearchPath *path, StringList *expansions)
{
- LstNode ln; /* Current node */
-
- Lst_Open(path);
- while ((ln = Lst_Next(path)) != NULL) {
- Path *p = LstNode_Datum(ln);
- DirMatchFiles(word, p, expansions);
+ SearchPathNode *ln;
+ for (ln = path->first; ln != NULL; ln = ln->next) {
+ CachedDir *dir = ln->datum;
+ DirMatchFiles(word, dir, expansions);
}
- Lst_Close(path);
}
-/* Print a word in the list of expansions.
- * Callback for Dir_Expand when DEBUG(DIR), via Lst_ForEach. */
-static int
-DirPrintWord(void *word, void *dummy MAKE_ATTR_UNUSED)
+static void
+PrintExpansions(StringList *expansions)
{
- fprintf(debug_file, "%s ", (char *)word);
-
- return 0;
+ const char *sep = "";
+ StringListNode *ln;
+ for (ln = expansions->first; ln != NULL; ln = ln->next) {
+ const char *word = ln->datum;
+ debug_printf("%s%s", sep, word);
+ sep = " ";
+ }
+ debug_printf("\n");
}
-/*-
- *-----------------------------------------------------------------------
- * Dir_Expand --
- * Expand the given word into a list of words by globbing it looking
- * in the directories on the given search path.
+/* Expand the given word into a list of words by globbing it, looking in the
+ * directories on the given search path.
*
* Input:
* word the word to expand
- * path the list of directories in which to find the
- * resulting files
+ * path the directories in which to find the files
* expansions the list on which to place the results
- *
- * Results:
- * A list of words consisting of the files which exist along the search
- * path matching the given pattern.
- *
- * Side Effects:
- * Directories may be opened. Who knows?
- * Undefined behavior if the word is really in read-only memory.
- *-----------------------------------------------------------------------
*/
void
-Dir_Expand(const char *word, Lst path, Lst expansions)
+Dir_Expand(const char *word, SearchPath *path, StringList *expansions)
{
const char *cp;
@@ -816,17 +790,12 @@ Dir_Expand(const char *word, Lst path, Lst expansions)
* in the string.
*/
for (cp = word; *cp; cp++) {
- if (*cp == '?' || *cp == '[' || *cp == '*' || *cp == '{') {
+ if (*cp == '?' || *cp == '[' || *cp == '*') {
break;
}
}
- if (*cp == '{') {
- /*
- * This one will be fun.
- */
- DirExpandCurly(word, cp, path, expansions);
- return;
- } else if (*cp != '\0') {
+
+ if (*cp != '\0') {
/*
* Back up to the start of the component
*/
@@ -834,16 +803,13 @@ Dir_Expand(const char *word, Lst path, Lst expansions)
cp--;
}
if (cp != word) {
- char sc;
- char *dirpath;
+ char *prefix = bmake_strsedup(word, cp + 1);
/*
* If the glob isn't in the first component, try and find
* all the components up to the one with a wildcard.
*/
- sc = cp[1];
- ((char *)UNCONST(cp))[1] = '\0';
- dirpath = Dir_FindFile(word, path);
- ((char *)UNCONST(cp))[1] = sc;
+ char *dirpath = Dir_FindFile(prefix, path);
+ free(prefix);
/*
* dirpath is null if can't find the leading component
* XXX: Dir_FindFile won't find internal components.
@@ -855,22 +821,22 @@ Dir_Expand(const char *word, Lst path, Lst expansions)
char *dp = &dirpath[strlen(dirpath) - 1];
if (*dp == '/')
*dp = '\0';
- path = Lst_Init();
+ path = Lst_New();
(void)Dir_AddDir(path, dirpath);
- DirExpandInt(cp + 1, path, expansions);
+ DirExpandPath(cp + 1, path, expansions);
Lst_Free(path);
}
} else {
/*
* Start the search from the local directory
*/
- DirExpandInt(word, path, expansions);
+ DirExpandPath(word, path, expansions);
}
} else {
/*
* Return the file -- this should never happen.
*/
- DirExpandInt(word, path, expansions);
+ DirExpandPath(word, path, expansions);
}
} else {
/*
@@ -881,108 +847,63 @@ Dir_Expand(const char *word, Lst path, Lst expansions)
/*
* Then the files in every other directory on the path.
*/
- DirExpandInt(word, path, expansions);
+ DirExpandPath(word, path, expansions);
}
}
- if (DEBUG(DIR)) {
- Lst_ForEach(expansions, DirPrintWord, NULL);
- fprintf(debug_file, "\n");
- }
+ if (DEBUG(DIR))
+ PrintExpansions(expansions);
}
-/*-
- *-----------------------------------------------------------------------
- * DirLookup --
- * Find if the file with the given name exists in the given path.
- *
- * Results:
- * The path to the file or NULL. This path is guaranteed to be in a
- * different part of memory than name and so may be safely free'd.
- *
- * Side Effects:
- * None.
- *-----------------------------------------------------------------------
- */
+/* Find if the file with the given name exists in the given path.
+ * Return the freshly allocated path to the file, or NULL. */
static char *
-DirLookup(Path *p, const char *name MAKE_ATTR_UNUSED, const char *cp,
- Boolean hasSlash MAKE_ATTR_UNUSED)
+DirLookup(CachedDir *dir, const char *base)
{
char *file; /* the current filename to check */
- DIR_DEBUG1(" %s ...\n", p->name);
+ DIR_DEBUG1(" %s ...\n", dir->name);
- if (Hash_FindEntry(&p->files, cp) == NULL)
+ if (HashTable_FindEntry(&dir->files, base) == NULL)
return NULL;
- file = str_concat3(p->name, "/", cp);
+ file = str_concat3(dir->name, "/", base);
DIR_DEBUG1(" returning %s\n", file);
- p->hits += 1;
- hits += 1;
+ dir->hits++;
+ hits++;
return file;
}
-/*-
- *-----------------------------------------------------------------------
- * DirLookupSubdir --
- * Find if the file with the given name exists in the given path.
- *
- * Results:
- * The path to the file or NULL. This path is guaranteed to be in a
- * different part of memory than name and so may be safely free'd.
- *
- * Side Effects:
- * If the file is found, it is added in the modification times hash
- * table.
- *-----------------------------------------------------------------------
- */
+/* Find if the file with the given name exists in the given directory.
+ * Return the freshly allocated path to the file, or NULL. */
static char *
-DirLookupSubdir(Path *p, const char *name)
+DirLookupSubdir(CachedDir *dir, const char *name)
{
struct make_stat mst;
- char *file; /* the current filename to check */
-
- if (p != dot) {
- file = str_concat3(p->name, "/", name);
- } else {
- /*
- * Checking in dot -- DON'T put a leading ./ on the thing.
- */
- file = bmake_strdup(name);
- }
+ char *file = dir == dot ? bmake_strdup(name)
+ : str_concat3(dir->name, "/", name);
DIR_DEBUG1("checking %s ...\n", file);
if (cached_stat(file, &mst) == 0) {
- nearmisses += 1;
+ nearmisses++;
return file;
}
free(file);
return NULL;
}
-/*-
- *-----------------------------------------------------------------------
- * DirLookupAbs --
- * Find if the file with the given name exists in the given path.
- *
- * Results:
- * The path to the file, the empty string or NULL. If the file is
- * the empty string, the search should be terminated.
- * This path is guaranteed to be in a different part of memory
- * than name and so may be safely free'd.
- *
- * Side Effects:
- * None.
- *-----------------------------------------------------------------------
+/* 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.
*/
static char *
-DirLookupAbs(Path *p, const char *name, const char *cp)
+DirLookupAbs(CachedDir *dir, const char *name, const char *cp)
{
- char *p1; /* pointer into p->name */
- const char *p2; /* pointer into name */
+ const char *dnp; /* pointer into dir->name */
+ const char *np; /* pointer into name */
- DIR_DEBUG1(" %s ...\n", p->name);
+ DIR_DEBUG1(" %s ...\n", dir->name);
/*
* If the file has a leading path component and that component
@@ -990,88 +911,68 @@ DirLookupAbs(Path *p, const char *name, const char *cp)
* directory, we can attempt another cache lookup. And if we don't
* have a hit, we can safely assume the file does not exist at all.
*/
- for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++) {
+ for (dnp = dir->name, np = name; *dnp != '\0' && *dnp == *np; dnp++, np++)
continue;
- }
- if (*p1 != '\0' || p2 != cp - 1) {
+ if (*dnp != '\0' || np != cp - 1)
return NULL;
- }
- if (Hash_FindEntry(&p->files, cp) == NULL) {
+ if (HashTable_FindEntry(&dir->files, cp) == NULL) {
DIR_DEBUG0(" must be here but isn't -- returning\n");
- /* Return empty string: terminates search */
- return bmake_strdup("");
+ return bmake_strdup(""); /* to terminate the search */
}
- p->hits += 1;
- hits += 1;
+ dir->hits++;
+ hits++;
DIR_DEBUG1(" returning %s\n", name);
return bmake_strdup(name);
}
-/*-
- *-----------------------------------------------------------------------
- * DirFindDot --
- * Find the file given on "." or curdir
- *
- * Results:
- * The path to the file or NULL. This path is guaranteed to be in a
- * different part of memory than name and so may be safely free'd.
- *
- * Side Effects:
- * Hit counts change
- *-----------------------------------------------------------------------
- */
+/* Find the file given on "." or curdir.
+ * Return the freshly allocated path to the file, or NULL. */
static char *
-DirFindDot(Boolean hasSlash MAKE_ATTR_UNUSED, const char *name, const char *cp)
+DirFindDot(const char *name, const char *base)
{
- if (Hash_FindEntry(&dot->files, cp) != NULL) {
+ if (HashTable_FindEntry(&dot->files, base) != NULL) {
DIR_DEBUG0(" in '.'\n");
- hits += 1;
- dot->hits += 1;
+ hits++;
+ dot->hits++;
return bmake_strdup(name);
}
- if (cur && Hash_FindEntry(&cur->files, cp) != NULL) {
+
+ if (cur != NULL && HashTable_FindEntry(&cur->files, base) != NULL) {
DIR_DEBUG1(" in ${.CURDIR} = %s\n", cur->name);
- hits += 1;
- cur->hits += 1;
- return str_concat3(cur->name, "/", cp);
+ hits++;
+ cur->hits++;
+ return str_concat3(cur->name, "/", base);
}
return NULL;
}
-/*-
- *-----------------------------------------------------------------------
- * Dir_FindFile --
- * Find the file with the given name along the given search path.
+/* 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 Lst of directories to search
+ * path the directories to search, or NULL
*
* Results:
- * The path to the file or NULL. This path is guaranteed to be in a
- * different part of memory than name and so may be safely free'd.
- *
- * Side Effects:
- * If the file is found in a directory which 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.
- *-----------------------------------------------------------------------
+ * The freshly allocated path to the file, or NULL.
*/
char *
-Dir_FindFile(const char *name, Lst path)
+Dir_FindFile(const char *name, SearchPath *path)
{
- LstNode ln; /* a list element */
+ SearchPathNode *ln;
char *file; /* the current filename to check */
- Path *p; /* current path member */
- const char *cp; /* Terminal name of file */
- Boolean hasLastDot = FALSE; /* true we should search dot last */
+ const char *base; /* Terminal name of file */
+ Boolean hasLastDot = FALSE; /* true if we should search dot last */
Boolean hasSlash; /* true if 'name' contains a / */
struct make_stat mst; /* Buffer for stat, if necessary */
const char *trailing_dot = ".";
@@ -1080,27 +981,26 @@ Dir_FindFile(const char *name, Lst path)
* Find the final component of the name and note whether it has a
* slash in it (the name, I mean)
*/
- cp = strrchr(name, '/');
- if (cp) {
+ base = strrchr(name, '/');
+ if (base) {
hasSlash = TRUE;
- cp += 1;
+ base++;
} else {
hasSlash = FALSE;
- cp = name;
+ base = name;
}
DIR_DEBUG1("Searching for %s ...", name);
if (path == NULL) {
DIR_DEBUG0("couldn't open path, file not found\n");
- misses += 1;
+ misses++;
return NULL;
}
- Lst_Open(path);
- if ((ln = Lst_First(path)) != NULL) {
- p = LstNode_Datum(ln);
- if (p == dotLast) {
+ if ((ln = path->first) != NULL) {
+ CachedDir *dir = ln->datum;
+ if (dir == dotLast) {
hasLastDot = TRUE;
DIR_DEBUG0("[dot last]...");
}
@@ -1112,7 +1012,7 @@ Dir_FindFile(const char *name, Lst path)
* directory component is exactly `./', consult the cached contents
* of each of the directories on the search path.
*/
- if (!hasSlash || (cp - name == 2 && *name == '.')) {
+ if (!hasSlash || (base - name == 2 && *name == '.')) {
/*
* We look through all the directories on the path seeking one which
* contains the final component of the given name. If such a beast
@@ -1127,27 +1027,20 @@ Dir_FindFile(const char *name, Lst path)
* This is so there are no conflicts between what the user
* specifies (fish.c) and what pmake finds (./fish.c).
*/
- if (!hasLastDot && (file = DirFindDot(hasSlash, name, cp)) != NULL) {
- Lst_Close(path);
+ if (!hasLastDot && (file = DirFindDot(name, base)) != NULL)
return file;
- }
- while ((ln = Lst_Next(path)) != NULL) {
- p = LstNode_Datum(ln);
- if (p == dotLast)
+ for (; ln != NULL; ln = ln->next) {
+ CachedDir *dir = ln->datum;
+ if (dir == dotLast)
continue;
- if ((file = DirLookup(p, name, cp, hasSlash)) != NULL) {
- Lst_Close(path);
+ if ((file = DirLookup(dir, base)) != NULL)
return file;
- }
}
- if (hasLastDot && (file = DirFindDot(hasSlash, name, cp)) != NULL) {
- Lst_Close(path);
+ if (hasLastDot && (file = DirFindDot(name, base)) != NULL)
return file;
- }
}
- Lst_Close(path);
/*
* We didn't find the file on any directory in the search path.
@@ -1165,13 +1058,13 @@ Dir_FindFile(const char *name, Lst path)
*/
if (!hasSlash) {
DIR_DEBUG0(" failed.\n");
- misses += 1;
+ misses++;
return NULL;
}
- if (*cp == '\0') {
+ if (*base == '\0') {
/* we were given a trailing "/" */
- cp = trailing_dot;
+ base = trailing_dot;
}
if (name[0] != '/') {
@@ -1189,22 +1082,18 @@ Dir_FindFile(const char *name, Lst path)
return file;
}
- Lst_Open(path);
- while ((ln = Lst_Next(path)) != NULL) {
- p = LstNode_Datum(ln);
- if (p == dotLast)
+ for (ln = path->first; ln != NULL; ln = ln->next) {
+ CachedDir *dir = ln->datum;
+ if (dir == dotLast)
continue;
- if (p == dot) {
+ if (dir == dot) {
if (checkedDot)
continue;
checkedDot = TRUE;
}
- if ((file = DirLookupSubdir(p, name)) != NULL) {
- Lst_Close(path);
+ if ((file = DirLookupSubdir(dir, name)) != NULL)
return file;
- }
}
- Lst_Close(path);
if (hasLastDot) {
if (dot && !checkedDot) {
@@ -1239,7 +1128,7 @@ Dir_FindFile(const char *name, Lst path)
DIR_DEBUG0(" Trying exact path matches...\n");
if (!hasLastDot && cur &&
- ((file = DirLookupAbs(cur, name, cp)) != NULL)) {
+ ((file = DirLookupAbs(cur, name, base)) != NULL)) {
if (file[0] == '\0') {
free(file);
return NULL;
@@ -1247,13 +1136,11 @@ Dir_FindFile(const char *name, Lst path)
return file;
}
- Lst_Open(path);
- while ((ln = Lst_Next(path)) != NULL) {
- p = LstNode_Datum(ln);
- if (p == dotLast)
+ for (ln = path->first; ln != NULL; ln = ln->next) {
+ CachedDir *dir = ln->datum;
+ if (dir == dotLast)
continue;
- if ((file = DirLookupAbs(p, name, cp)) != NULL) {
- Lst_Close(path);
+ if ((file = DirLookupAbs(dir, name, base)) != NULL) {
if (file[0] == '\0') {
free(file);
return NULL;
@@ -1261,10 +1148,9 @@ Dir_FindFile(const char *name, Lst path)
return file;
}
}
- Lst_Close(path);
if (hasLastDot && cur &&
- ((file = DirLookupAbs(cur, name, cp)) != NULL)) {
+ ((file = DirLookupAbs(cur, name, base)) != NULL)) {
if (file[0] == '\0') {
free(file);
return NULL;
@@ -1291,23 +1177,23 @@ Dir_FindFile(const char *name, Lst path)
* b/c we added it here. This is not good...
*/
#ifdef notdef
- if (cp == traling_dot) {
- cp = strrchr(name, '/');
- cp += 1;
+ if (base == trailing_dot) {
+ base = strrchr(name, '/');
+ base++;
}
- cp[-1] = '\0';
+ base[-1] = '\0';
(void)Dir_AddDir(path, name);
- cp[-1] = '/';
+ base[-1] = '/';
- bigmisses += 1;
+ bigmisses++;
ln = Lst_Last(path);
if (ln == NULL) {
return NULL;
} else {
- p = LstNode_Datum(ln);
+ dir = LstNode_Datum(ln);
}
- if (Hash_FindEntry(&p->files, cp) != NULL) {
+ if (Hash_FindEntry(&dir->files, base) != NULL) {
return bmake_strdup(name);
} else {
return NULL;
@@ -1315,7 +1201,7 @@ Dir_FindFile(const char *name, Lst path)
#else /* !notdef */
DIR_DEBUG1(" Looking for \"%s\" ...\n", name);
- bigmisses += 1;
+ bigmisses++;
if (cached_stat(name, &mst) == 0) {
return bmake_strdup(name);
}
@@ -1326,43 +1212,32 @@ Dir_FindFile(const char *name, Lst path)
}
-/*-
- *-----------------------------------------------------------------------
- * Dir_FindHereOrAbove --
- * search for a path starting at a given directory and then working
- * our way up towards the root.
+/* 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 path we are looking for
- * result the result of a successful search is placed here
- * result_len the length of the result buffer
- * (typically MAXPATHLEN + 1)
+ * search_path the relative path we are looking for
*
* Results:
- * 0 on failure, 1 on success [in which case the found path is put
- * in the result buffer].
- *
- * Side Effects:
- *-----------------------------------------------------------------------
+ * The found path, or NULL.
*/
-Boolean
-Dir_FindHereOrAbove(const char *here, const char *search_path,
- char *result, int result_len)
+char *
+Dir_FindHereOrAbove(const char *here, const char *search_path)
{
struct make_stat mst;
- char dirbase[MAXPATHLEN + 1], *dirbase_end;
- char try[MAXPATHLEN + 1], *try_end;
+ char *dirbase, *dirbase_end;
+ char *try, *try_end;
/* copy out our starting point */
- snprintf(dirbase, sizeof(dirbase), "%s", here);
+ dirbase = bmake_strdup(here);
dirbase_end = dirbase + strlen(dirbase);
/* loop until we determine a result */
- while (TRUE) {
+ for (;;) {
/* try and stat(2) it ... */
- snprintf(try, sizeof(try), "%s/%s", dirbase, search_path);
+ try = str_concat3(dirbase, "/", search_path);
if (cached_stat(try, &mst) != -1) {
/*
* success! if we found a file, chop off
@@ -1376,9 +1251,10 @@ Dir_FindHereOrAbove(const char *here, const char *search_path,
*try_end = '\0'; /* chop! */
}
- snprintf(result, result_len, "%s", try);
- return TRUE;
+ free(dirbase);
+ return try;
}
+ free(try);
/*
* nope, we didn't find it. if we used up dirbase we've
@@ -1393,10 +1269,10 @@ Dir_FindHereOrAbove(const char *here, const char *search_path,
while (dirbase_end > dirbase && *dirbase_end != '/')
dirbase_end--;
*dirbase_end = '\0'; /* chop! */
+ }
- } /* while (TRUE) */
-
- return FALSE;
+ free(dirbase);
+ return NULL;
}
/*-
@@ -1417,7 +1293,7 @@ Dir_FindHereOrAbove(const char *here, const char *search_path,
* found one for it, the full name is placed in the path slot.
*-----------------------------------------------------------------------
*/
-int
+time_t
Dir_MTime(GNode *gn, Boolean recheck)
{
char *fullName; /* the full pathname of name */
@@ -1482,72 +1358,63 @@ Dir_MTime(GNode *gn, Boolean recheck)
}
}
- if (fullName && gn->path == NULL) {
+ if (fullName != NULL && gn->path == NULL)
gn->path = fullName;
- }
gn->mtime = mst.mst_mtime;
return gn->mtime;
}
-/*-
- *-----------------------------------------------------------------------
- * Dir_AddDir --
- * Add the given name to the end of the given path. The order of
- * the arguments is backwards so ParseDoDependency can do a
- * Lst_ForEach of its list of paths...
- *
- * Input:
- * path the path to which the directory should be
- * added
- * XXX: Why would this ever be NULL, and what does
- * that mean?
- * name the name of the directory to add
+/* Read the list of filenames in the directory and store the result
+ * in openDirectories.
*
- * Results:
- * none
+ * If a path is given, append the directory to that path.
*
- * Side Effects:
- * A structure is added to the list and the directory is
- * read and hashed.
- *-----------------------------------------------------------------------
+ * Input:
+ * path The path to which the directory should be
+ * added, or NULL to only add the directory to
+ * openDirectories
+ * name The name of the directory to add.
+ * The name is not normalized in any way.
*/
-Path *
-Dir_AddDir(Lst path, const char *name)
+CachedDir *
+Dir_AddDir(SearchPath *path, const char *name)
{
- LstNode ln = NULL; /* node in case Path structure is found */
- Path *p = NULL; /* pointer to new Path structure */
- DIR *d; /* for reading directory */
- struct dirent *dp; /* entry in directory */
+ CachedDir *dir = NULL; /* the added directory */
+ DIR *d;
+ struct dirent *dp;
if (path != NULL && strcmp(name, ".DOTLAST") == 0) {
- ln = Lst_Find(path, DirFindName, name);
- if (ln != NULL)
- return LstNode_Datum(ln);
+ SearchPathNode *ln;
+
+ for (ln = path->first; ln != NULL; ln = ln->next) {
+ CachedDir *pathDir = ln->datum;
+ if (strcmp(pathDir->name, name) == 0)
+ return pathDir;
+ }
dotLast->refCount++;
Lst_Prepend(path, dotLast);
}
if (path != NULL)
- ln = Lst_Find(openDirectories, DirFindName, name);
- if (ln != NULL) {
- p = LstNode_Datum(ln);
- if (Lst_FindDatum(path, p) == NULL) {
- p->refCount += 1;
- Lst_Append(path, p);
+ dir = OpenDirs_Find(&openDirs, name);
+ if (dir != NULL) {
+ if (Lst_FindDatum(path, dir) == NULL) {
+ dir->refCount++;
+ Lst_Append(path, dir);
}
- return p;
+ return dir;
}
DIR_DEBUG1("Caching %s ...", name);
if ((d = opendir(name)) != NULL) {
- p = bmake_malloc(sizeof(Path));
- p->name = bmake_strdup(name);
- p->hits = 0;
- p->refCount = 1;
- Hash_InitTable(&p->files, -1);
+ dir = bmake_malloc(sizeof(CachedDir));
+ dir->name = bmake_strdup(name);
+ dir->hits = 0;
+ dir->refCount = 1;
+ HashTable_Init(&dir->files);
while ((dp = readdir(d)) != NULL) {
#if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */
@@ -1560,33 +1427,30 @@ Dir_AddDir(Lst path, const char *name)
continue;
}
#endif /* sun && d_ino */
- (void)Hash_CreateEntry(&p->files, dp->d_name, NULL);
+ (void)HashTable_CreateEntry(&dir->files, dp->d_name, NULL);
}
(void)closedir(d);
- Lst_Append(openDirectories, p);
+ OpenDirs_Add(&openDirs, dir);
if (path != NULL)
- Lst_Append(path, p);
+ Lst_Append(path, dir);
}
DIR_DEBUG0("done\n");
- return p;
+ return dir;
}
-/*-
- *-----------------------------------------------------------------------
- * Dir_CopyDir --
- * Callback function for duplicating a search path via Lst_Copy.
- * Ups the reference count for the directory.
- *
- * Results:
- * Returns the Path it was given.
- *-----------------------------------------------------------------------
- */
-void *
-Dir_CopyDir(void *p)
+/* Return a copy of dirSearchPath, incrementing the reference counts for
+ * the contained directories. */
+SearchPath *
+Dir_CopyDirSearchPath(void)
{
- ((Path *)p)->refCount += 1;
-
- return p;
+ SearchPath *path = Lst_New();
+ SearchPathNode *ln;
+ for (ln = dirSearchPath->first; ln != NULL; ln = ln->next) {
+ CachedDir *dir = ln->datum;
+ dir->refCount++;
+ Lst_Append(path, dir);
+ }
+ return path;
}
/*-
@@ -1611,119 +1475,70 @@ Dir_CopyDir(void *p)
*-----------------------------------------------------------------------
*/
char *
-Dir_MakeFlags(const char *flag, Lst path)
+Dir_MakeFlags(const char *flag, SearchPath *path)
{
Buffer buf;
- LstNode ln; /* the node of the current directory */
+ SearchPathNode *ln;
Buf_Init(&buf, 0);
if (path != NULL) {
- Lst_Open(path);
- while ((ln = Lst_Next(path)) != NULL) {
- Path *p = LstNode_Datum(ln);
+ for (ln = path->first; ln != NULL; ln = ln->next) {
+ CachedDir *dir = ln->datum;
Buf_AddStr(&buf, " ");
Buf_AddStr(&buf, flag);
- Buf_AddStr(&buf, p->name);
+ Buf_AddStr(&buf, dir->name);
}
- Lst_Close(path);
}
return Buf_Destroy(&buf, FALSE);
}
-/*-
- *-----------------------------------------------------------------------
- * Dir_Destroy --
- * Nuke a directory descriptor, if possible. Callback procedure
- * for the suffixes module when destroying a search path.
+/* Nuke a directory descriptor, if possible. Callback procedure for the
+ * suffixes module when destroying a search path.
*
* Input:
- * pp The directory descriptor to nuke
- *
- * Results:
- * None.
- *
- * Side Effects:
- * If no other path references this directory (refCount == 0),
- * the Path and all its data are freed.
- *
- *-----------------------------------------------------------------------
+ * dirp The directory descriptor to nuke
*/
void
-Dir_Destroy(void *pp)
+Dir_Destroy(void *dirp)
{
- Path *p = (Path *)pp;
- p->refCount -= 1;
-
- if (p->refCount == 0) {
- LstNode ln;
+ CachedDir *dir = dirp;
+ dir->refCount--;
- ln = Lst_FindDatum(openDirectories, p);
- Lst_Remove(openDirectories, ln);
+ if (dir->refCount == 0) {
+ OpenDirs_Remove(&openDirs, dir->name);
- Hash_DeleteTable(&p->files);
- free(p->name);
- free(p);
+ HashTable_Done(&dir->files);
+ free(dir->name);
+ free(dir);
}
}
-/*-
- *-----------------------------------------------------------------------
- * Dir_ClearPath --
- * Clear out all elements of the given search path. This is different
- * from destroying the list, notice.
- *
- * Input:
- * path Path to clear
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The path is set to the empty list.
- *
- *-----------------------------------------------------------------------
- */
+/* Clear out all elements from the given search path.
+ * The path is set to the empty list but is not destroyed. */
void
-Dir_ClearPath(Lst path)
+Dir_ClearPath(SearchPath *path)
{
while (!Lst_IsEmpty(path)) {
- Path *p = Lst_Dequeue(path);
- Dir_Destroy(p);
+ CachedDir *dir = Lst_Dequeue(path);
+ Dir_Destroy(dir);
}
}
-/*-
- *-----------------------------------------------------------------------
- * Dir_Concat --
- * Concatenate two paths, adding the second to the end of the first.
- * Makes sure to avoid duplicates.
- *
- * Input:
- * path1 Dest
- * path2 Source
- *
- * Results:
- * None
- *
- * Side Effects:
- * Reference counts for added dirs are upped.
- *
- *-----------------------------------------------------------------------
- */
+/* Concatenate two paths, adding the second to the end of the first,
+ * skipping duplicates. */
void
-Dir_Concat(Lst path1, Lst path2)
+Dir_Concat(SearchPath *dst, SearchPath *src)
{
- LstNode ln;
- Path *p;
-
- for (ln = Lst_First(path2); ln != NULL; ln = LstNode_Next(ln)) {
- p = LstNode_Datum(ln);
- if (Lst_FindDatum(path1, p) == NULL) {
- p->refCount += 1;
- Lst_Append(path1, p);
+ SearchPathNode *ln;
+
+ for (ln = src->first; ln != NULL; ln = ln->next) {
+ CachedDir *dir = ln->datum;
+ if (Lst_FindDatum(dst, dir) == NULL) {
+ dir->refCount++;
+ Lst_Append(dst, dir);
}
}
}
@@ -1738,33 +1553,27 @@ percentage(int num, int den)
void
Dir_PrintDirectories(void)
{
- LstNode ln;
-
- fprintf(debug_file, "#*** Directory Cache:\n");
- fprintf(debug_file,
- "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n",
- hits, misses, nearmisses, bigmisses,
- percentage(hits, hits + bigmisses + nearmisses));
- fprintf(debug_file, "# %-20s referenced\thits\n", "directory");
-
- Lst_Open(openDirectories);
- while ((ln = Lst_Next(openDirectories)) != NULL) {
- Path *p = LstNode_Datum(ln);
- fprintf(debug_file, "# %-20s %10d\t%4d\n", p->name, p->refCount,
- p->hits);
+ CachedDirListNode *ln;
+
+ debug_printf("#*** Directory Cache:\n");
+ debug_printf("# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n",
+ hits, misses, nearmisses, bigmisses,
+ percentage(hits, hits + bigmisses + nearmisses));
+ debug_printf("# %-20s referenced\thits\n", "directory");
+
+ for (ln = openDirs.list->first; ln != NULL; ln = ln->next) {
+ CachedDir *dir = ln->datum;
+ debug_printf("# %-20s %10d\t%4d\n", dir->name, dir->refCount,
+ dir->hits);
}
- Lst_Close(openDirectories);
-}
-
-static int
-DirPrintDir(void *p, void *dummy MAKE_ATTR_UNUSED)
-{
- fprintf(debug_file, "%s ", ((Path *)p)->name);
- return 0;
}
void
-Dir_PrintPath(Lst path)
+Dir_PrintPath(SearchPath *path)
{
- Lst_ForEach(path, DirPrintDir, NULL);
+ SearchPathNode *node;
+ for (node = path->first; node != NULL; node = node->next) {
+ const CachedDir *dir = node->datum;
+ debug_printf("%s ", dir->name);
+ }
}
diff --git a/dir.h b/dir.h
index 10da0eb8ba78..886b26e18b47 100644
--- a/dir.h
+++ b/dir.h
@@ -1,4 +1,4 @@
-/* $NetBSD: dir.h,v 1.23 2020/09/02 04:08:54 rillig Exp $ */
+/* $NetBSD: dir.h,v 1.32 2020/10/25 10:00:20 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -75,20 +75,21 @@
#ifndef MAKE_DIR_H
#define MAKE_DIR_H
-/* A cache of a directory, remembering all the files that exist in that
- * directory. */
-typedef struct {
- char *name; /* Name of directory */
- int refCount; /* Number of paths with this directory */
- int hits; /* the number of times a file in this
+/* A cache for the filenames in a directory. */
+typedef struct CachedDir {
+ char *name; /* Name of directory, either absolute or
+ * relative to the current directory.
+ * The name is not normalized in any way,
+ * that is, "." and "./." are different.
+ *
+ * Not sure what happens when .CURDIR is
+ * assigned a new value; see Parse_DoVar. */
+ int refCount; /* Number of SearchPaths with this directory */
+ int hits; /* The number of times a file in this
* directory has been found */
- Hash_Table files; /* Hash set of files in directory */
-} Path;
-
-struct make_stat {
- time_t mst_mtime;
- mode_t mst_mode;
-};
+ HashTable files; /* Hash set of files in directory;
+ * all values are NULL. */
+} CachedDir;
void Dir_Init(void);
void Dir_InitDir(const char *);
@@ -97,18 +98,24 @@ void Dir_InitDot(void);
void Dir_End(void);
void Dir_SetPATH(void);
Boolean Dir_HasWildcards(const char *);
-void Dir_Expand(const char *, Lst, Lst);
-char *Dir_FindFile(const char *, Lst);
-Boolean Dir_FindHereOrAbove(const char *, const char *, char *, int);
-int Dir_MTime(GNode *, Boolean);
-Path *Dir_AddDir(Lst, const char *);
-char *Dir_MakeFlags(const char *, Lst);
-void Dir_ClearPath(Lst);
-void Dir_Concat(Lst, Lst);
+void Dir_Expand(const char *, SearchPath *, StringList *);
+char *Dir_FindFile(const char *, SearchPath *);
+char *Dir_FindHereOrAbove(const char *, const char *);
+time_t Dir_MTime(GNode *, Boolean);
+CachedDir *Dir_AddDir(SearchPath *, const char *);
+char *Dir_MakeFlags(const char *, SearchPath *);
+void Dir_ClearPath(SearchPath *);
+void Dir_Concat(SearchPath *, SearchPath *);
void Dir_PrintDirectories(void);
-void Dir_PrintPath(Lst);
+void Dir_PrintPath(SearchPath *);
void Dir_Destroy(void *);
-void *Dir_CopyDir(void *);
+SearchPath *Dir_CopyDirSearchPath(void);
+
+/* Stripped-down variant of struct stat. */
+struct make_stat {
+ time_t mst_mtime;
+ mode_t mst_mode;
+};
int cached_lstat(const char *, struct make_stat *);
int cached_stat(const char *, struct make_stat *);
diff --git a/enum.c b/enum.c
index 9dec4f3a5f6a..cce986905411 100755
--- a/enum.c
+++ b/enum.c
@@ -1,4 +1,4 @@
-/* $NetBSD: enum.c,v 1.6 2020/09/01 20:34:51 rillig Exp $ */
+/* $NetBSD: enum.c,v 1.12 2020/10/05 19:27:47 rillig Exp $ */
/*
Copyright (c) 2020 Roland Illig <rillig@NetBSD.org>
@@ -27,20 +27,9 @@
POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: enum.c,v 1.6 2020/09/01 20:34:51 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-__RCSID("$NetBSD: enum.c,v 1.6 2020/09/01 20:34:51 rillig Exp $");
-#endif
-#endif
+#include "make.h"
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "enum.h"
+MAKE_RCSID("$NetBSD: enum.c,v 1.12 2020/10/05 19:27:47 rillig Exp $");
/* Convert a bitset into a string representation, showing the names of the
* individual bits.
@@ -59,7 +48,7 @@ Enum_FlagsToString(char *buf, size_t buf_size,
size_t name_len;
if ((value & spec->es_value) != spec->es_value)
- continue;
+ continue;
value &= ~spec->es_value;
assert(buf_size >= sep_len + 1);
@@ -93,8 +82,8 @@ const char *
Enum_ValueToString(int value, const EnumToStringSpec *spec)
{
for (; spec->es_name[0] != '\0'; spec++) {
- if (value == spec->es_value)
- return spec->es_name;
+ if (value == spec->es_value)
+ return spec->es_name;
}
abort(/* unknown enum value */);
}
diff --git a/enum.h b/enum.h
index d6a94f11e7cd..ba7c7a826e97 100755
--- a/enum.h
+++ b/enum.h
@@ -1,4 +1,4 @@
-/* $NetBSD: enum.h,v 1.9 2020/09/01 20:34:51 rillig Exp $ */
+/* $NetBSD: enum.h,v 1.12 2020/09/25 15:54:50 rillig Exp $ */
/*
Copyright (c) 2020 Roland Illig <rillig@NetBSD.org>
@@ -34,7 +34,7 @@
#include <stddef.h>
-typedef struct {
+typedef struct EnumToStringSpec {
int es_value;
const char *es_name;
} EnumToStringSpec;
@@ -108,15 +108,37 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
enum { typnam ## _ ## ToStringSize = sizeof joined }
/* Declare the necessary data structures for calling Enum_FlagsToString
+ * for an enum with 2 flags. */
+#define ENUM_FLAGS_RTTI_2(typnam, v1, v2) \
+ ENUM__FLAGS_RTTI(typnam, \
+ ENUM__SPECS_2( \
+ ENUM__SPEC_1(v1), \
+ ENUM__SPEC_1(v2)), \
+ ENUM__JOIN_2( \
+ ENUM__JOIN_STR_1(v1), \
+ ENUM__JOIN_STR_1(v2)))
+
+/* 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__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_2(v1, v2), \
- ENUM__JOIN_STR_1(v3)))
+ 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 8 flags. */
@@ -156,8 +178,8 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
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, \
+ 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), \
@@ -179,8 +201,8 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
v17, v18, v19, v20, v21, v22, v23, v24, \
v25, v26, v27, v28, v29, v30, v31, v32) \
ENUM__FLAGS_RTTI(typnam, \
- ENUM__SPECS_2( \
- ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \
+ ENUM__SPECS_2( \
+ ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \
v09, v10, v11, v12, v13, v14, v15, v16), \
ENUM__SPEC_16(v17, v18, v19, v20, v21, v22, v23, v24, \
v25, v26, v27, v28, v29, v30, v31, v32)), \
@@ -188,6 +210,6 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
ENUM__JOIN_STR_16(v01, v02, v03, v04, v05, v06, v07, v08, \
v09, v10, v11, v12, v13, v14, v15, v16), \
ENUM__JOIN_STR_16(v17, v18, v19, v20, v21, v22, v23, v24, \
- v25, v26, v27, v28, v29, v30, v31, v32)))
+ v25, v26, v27, v28, v29, v30, v31, v32)))
#endif
diff --git a/filemon/filemon.h b/filemon/filemon.h
index 5a1231d935ec..fcf37a3ab54b 100644
--- a/filemon/filemon.h
+++ b/filemon/filemon.h
@@ -1,4 +1,4 @@
-/* $NetBSD: filemon.h,v 1.2 2020/01/22 22:10:36 sjg Exp $ */
+/* $NetBSD: filemon.h,v 1.3 2020/10/18 11:49:47 rillig Exp $ */
/*-
* Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -29,8 +29,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef FILEMON_H_
-#define FILEMON_H_
+#ifndef MAKE_FILEMON_H
+#define MAKE_FILEMON_H
#include <sys/types.h>
@@ -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 /* FILEMON_H_ */
+#endif /* MAKE_FILEMON_H */
diff --git a/filemon/filemon_ktrace.c b/filemon/filemon_ktrace.c
index 0a532d68f4f2..4d2a5450c093 100644
--- a/filemon/filemon_ktrace.c
+++ b/filemon/filemon_ktrace.c
@@ -1,4 +1,4 @@
-/* $NetBSD: filemon_ktrace.c,v 1.2 2020/01/19 20:22:57 riastradh Exp $ */
+/* $NetBSD: filemon_ktrace.c,v 1.3 2020/10/18 11:54:43 rillig Exp $ */
/*-
* Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -478,7 +478,7 @@ filemon_dispatch(struct filemon *F)
*/
/* XXX What to do if syscall code doesn't match? */
if (S->i == S->npath && S->syscode == ret->ktr_code)
- (*S->show)(F, S, ret);
+ S->show(F, S, ret);
/* Free the state now that it is no longer active. */
for (i = 0; i < S->i; i++)
@@ -771,7 +771,7 @@ filemon_sys_exit(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call)
{
const register_t *args = (const void *)&call[1];
- int status = args[0];
+ int status = (int)args[0];
if (F->out) {
fprintf(F->out, "X %jd %d\n", (intmax_t)key->pid, status);
@@ -806,7 +806,7 @@ filemon_sys_open(struct filemon *F, const struct filemon_key *key,
if (call->ktr_argsize < 2)
return NULL;
- flags = args[1];
+ flags = (int)args[1];
if ((flags & O_RDWR) == O_RDWR)
return syscall_enter(F, key, call, 1, &show_open_readwrite);
@@ -827,8 +827,8 @@ filemon_sys_openat(struct filemon *F, const struct filemon_key *key,
if (call->ktr_argsize < 3)
return NULL;
- fd = args[0];
- flags = args[2];
+ fd = (int)args[0];
+ flags = (int)args[2];
if (fd == AT_CWD) {
if ((flags & O_RDWR) == O_RDWR)
diff --git a/for.c b/for.c
index 2e9963f2a7f6..e46bdf460702 100644
--- a/for.c
+++ b/for.c
@@ -1,4 +1,4 @@
-/* $NetBSD: for.c,v 1.67 2020/08/30 19:56:02 rillig Exp $ */
+/* $NetBSD: for.c,v 1.112 2020/10/31 18:41:07 rillig Exp $ */
/*
* Copyright (c) 1992, The Regents of the University of California.
@@ -29,88 +29,136 @@
* SUCH DAMAGE.
*/
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: for.c,v 1.67 2020/08/30 19:56:02 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: for.c,v 1.67 2020/08/30 19:56:02 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
/*-
- * for.c --
- * Functions to handle loops in a makefile.
+ * Handling of .for/.endfor loops in a makefile.
*
- * Interface:
- * For_Eval Evaluate the loop in the passed line.
- * For_Run Run accumulated loop
+ * For loops are of the form:
*
- */
-
-#include "make.h"
-#include "strlist.h"
-
-#define FOR_SUB_ESCAPE_CHAR 1
-#define FOR_SUB_ESCAPE_BRACE 2
-#define FOR_SUB_ESCAPE_PAREN 4
-
-/*
- * For statements are of the form:
- *
- * .for <variable> in <varlist>
+ * .for <varname...> in <value...>
* ...
* .endfor
*
- * The trick is to look for the matching end inside for for loop
- * To do that, we count the current nesting level of the for loops.
- * and the .endfor statements, accumulating all the statements between
- * the initial .for loop and the matching .endfor;
- * then we evaluate the for loop for each variable in the varlist.
+ * When a .for line is parsed, all following lines are accumulated into a
+ * buffer, up to but excluding the corresponding .endfor line. To find the
+ * corresponding .endfor, the number of nested .for and .endfor directives
+ * are counted.
+ *
+ * During parsing, any nested .for loops are just passed through; they get
+ * handled recursively in For_Eval when the enclosing .for loop is evaluated
+ * in For_Run.
*
- * Note that any nested fors are just passed through; they get handled
- * recursively in For_Eval when we're expanding the enclosing for in
- * For_Run.
+ * When the .for loop has been parsed completely, the variable expressions
+ * for the iteration variables are replaced with expressions of the form
+ * ${:Uvalue}, and then this modified body is "included" as a special file.
+ *
+ * Interface:
+ * For_Eval Evaluate the loop in the passed line.
+ *
+ * For_Run Run accumulated loop
*/
+#include "make.h"
+
+/* "@(#)for.c 8.1 (Berkeley) 6/6/93" */
+MAKE_RCSID("$NetBSD: for.c,v 1.112 2020/10/31 18:41:07 rillig Exp $");
+
+/* The .for loop substitutes the items as ${:U<value>...}, which means
+ * that characters that break this syntax must be backslash-escaped. */
+typedef enum ForEscapes {
+ FOR_SUB_ESCAPE_CHAR = 0x0001,
+ FOR_SUB_ESCAPE_BRACE = 0x0002,
+ FOR_SUB_ESCAPE_PAREN = 0x0004
+} ForEscapes;
+
static int forLevel = 0; /* Nesting level */
+/* One of the variables to the left of the "in" in a .for loop. */
+typedef struct ForVar {
+ char *name;
+ size_t len;
+} ForVar;
+
/*
* State of a for loop.
*/
-typedef struct {
- Buffer buf; /* Body of loop */
- strlist_t vars; /* Iteration variables */
- strlist_t items; /* Substitution items */
- char *parse_buf;
- int short_var;
- int sub_next;
+typedef struct For {
+ 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). */
+ Boolean short_var;
+ unsigned int sub_next; /* Where to continue iterating */
} For;
static For *accumFor; /* Loop being accumulated */
+static void
+ForAddVar(For *f, const char *name, size_t len)
+{
+ ForVar *var = Vector_Push(&f->vars);
+ var->name = bmake_strldup(name, len);
+ var->len = len;
+}
static void
-For_Free(For *arg)
+For_Free(For *f)
{
- Buf_Destroy(&arg->buf, TRUE);
- strlist_clean(&arg->vars);
- strlist_clean(&arg->items);
- free(arg->parse_buf);
+ Buf_Destroy(&f->body, TRUE);
- free(arg);
+ while (f->vars.len > 0) {
+ ForVar *var = Vector_Pop(&f->vars);
+ free(var->name);
+ }
+ Vector_Done(&f->vars);
+
+ Words_Free(f->items);
+ Buf_Destroy(&f->curBody, TRUE);
+
+ free(f);
}
-/*-
- *-----------------------------------------------------------------------
- * For_Eval --
- * Evaluate the for loop in the passed line. The line
- * looks like this:
- * .for <variable> in <varlist>
+static ForEscapes
+GetEscapes(const char *word)
+{
+ const char *p;
+ ForEscapes escapes = 0;
+
+ for (p = word; *p != '\0'; p++) {
+ switch (*p) {
+ case ':':
+ case '$':
+ case '\\':
+ escapes |= FOR_SUB_ESCAPE_CHAR;
+ break;
+ case ')':
+ escapes |= FOR_SUB_ESCAPE_PAREN;
+ break;
+ case '}':
+ escapes |= FOR_SUB_ESCAPE_BRACE;
+ break;
+ }
+ }
+ return escapes;
+}
+
+static Boolean
+IsFor(const char *p)
+{
+ return p[0] == 'f' && p[1] == 'o' && p[2] == 'r' && ch_isspace(p[3]);
+}
+
+static Boolean
+IsEndfor(const char *p)
+{
+ return p[0] == 'e' && strncmp(p, "endfor", 6) == 0 &&
+ (p[6] == '\0' || ch_isspace(p[6]));
+}
+
+/* Evaluate the for loop in the passed line. The line looks like this:
+ * .for <varname...> in <value...>
*
* Input:
* line Line to parse
@@ -119,178 +167,130 @@ For_Free(For *arg)
* 0: Not a .for statement, parse the line
* 1: We found a for loop
* -1: A .for statement with a bad syntax error, discard.
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
*/
int
-For_Eval(char *line)
+For_Eval(const char *line)
{
- For *new_for;
- char *ptr = line, *sub;
- size_t len;
- int escapes;
- unsigned char ch;
- Words words;
+ For *f;
+ const char *p;
- /* Skip the '.' and any following whitespace */
- for (ptr++; *ptr && isspace((unsigned char)*ptr); ptr++)
- continue;
+ p = line + 1; /* skip the '.' */
+ cpp_skip_whitespace(&p);
- /*
- * If we are not in a for loop quickly determine if the statement is
- * a for.
- */
- if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' ||
- !isspace((unsigned char)ptr[3])) {
- if (ptr[0] == 'e' && strncmp(ptr + 1, "ndfor", 5) == 0) {
+ if (!IsFor(p)) {
+ if (IsEndfor(p)) {
Parse_Error(PARSE_FATAL, "for-less endfor");
return -1;
}
return 0;
}
- ptr += 3;
+ p += 3;
/*
* we found a for loop, and now we are going to parse it.
*/
- new_for = bmake_malloc(sizeof *new_for);
- memset(new_for, 0, sizeof *new_for);
+ f = bmake_malloc(sizeof *f);
+ Buf_Init(&f->body, 0);
+ Vector_Init(&f->vars, sizeof(ForVar));
+ f->items.words = NULL;
+ f->items.freeIt = NULL;
+ Buf_Init(&f->curBody, 0);
+ f->short_var = FALSE;
+ f->sub_next = 0;
/* Grab the variables. Terminate on "in". */
- for (;; ptr += len) {
- while (*ptr && isspace((unsigned char)*ptr))
- ptr++;
- if (*ptr == '\0') {
+ for (;;) {
+ size_t len;
+
+ cpp_skip_whitespace(&p);
+ if (*p == '\0') {
Parse_Error(PARSE_FATAL, "missing `in' in for");
- For_Free(new_for);
+ For_Free(f);
return -1;
}
- for (len = 1; ptr[len] && !isspace((unsigned char)ptr[len]); len++)
+
+ /* XXX: This allows arbitrary variable names; see directive-for.mk. */
+ for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++)
continue;
- if (len == 2 && ptr[0] == 'i' && ptr[1] == 'n') {
- ptr += 2;
+
+ if (len == 2 && p[0] == 'i' && p[1] == 'n') {
+ p += 2;
break;
}
if (len == 1)
- new_for->short_var = 1;
- strlist_add_str(&new_for->vars, bmake_strldup(ptr, len), len);
+ f->short_var = TRUE;
+
+ ForAddVar(f, p, len);
+ p += len;
}
- if (strlist_num(&new_for->vars) == 0) {
+ if (f->vars.len == 0) {
Parse_Error(PARSE_FATAL, "no iteration variables in for");
- For_Free(new_for);
+ For_Free(f);
return -1;
}
- while (*ptr && isspace((unsigned char)*ptr))
- ptr++;
-
- /*
- * Make a list with the remaining words
- * The values are substituted as ${:U<value>...} so we must \ escape
- * characters that break that syntax.
- * Variables are fully expanded - so it is safe for escape $.
- * We can't do the escapes here - because we don't know whether
- * we are substuting into ${...} or $(...).
- */
- sub = Var_Subst(ptr, VAR_GLOBAL, VARE_WANTRES);
-
- /*
- * Split into words allowing for quoted strings.
- */
- words = Str_Words(sub, FALSE);
-
- free(sub);
+ cpp_skip_whitespace(&p);
{
- size_t n;
-
- for (n = 0; n < words.len; n++) {
- ptr = words.words[n];
- if (!*ptr)
- continue;
- escapes = 0;
- while ((ch = *ptr++)) {
- switch (ch) {
- case ':':
- case '$':
- case '\\':
- escapes |= FOR_SUB_ESCAPE_CHAR;
- break;
- case ')':
- escapes |= FOR_SUB_ESCAPE_PAREN;
- break;
- case /*{*/ '}':
- escapes |= FOR_SUB_ESCAPE_BRACE;
- break;
- }
- }
- /*
- * We have to dup words[n] to maintain the semantics of
- * strlist.
- */
- strlist_add_str(&new_for->items, bmake_strdup(words.words[n]),
- escapes);
- }
+ char *items;
+ (void)Var_Subst(p, VAR_GLOBAL, VARE_WANTRES, &items);
+ /* TODO: handle errors */
+ f->items = Str_Words(items, FALSE);
+ free(items);
+
+ if (f->items.len == 1 && f->items.words[0][0] == '\0')
+ f->items.len = 0; /* .for var in ${:U} */
+ }
- Words_Free(words);
+ {
+ size_t nitems, nvars;
- if ((len = strlist_num(&new_for->items)) > 0 &&
- len % (n = strlist_num(&new_for->vars))) {
+ if ((nitems = f->items.len) > 0 && nitems % (nvars = f->vars.len)) {
Parse_Error(PARSE_FATAL,
"Wrong number of words (%zu) in .for substitution list"
- " with %zu vars", len, n);
+ " with %zu variables", nitems, nvars);
/*
* Return 'success' so that the body of the .for loop is
* accumulated.
* Remove all items so that the loop doesn't iterate.
*/
- strlist_clean(&new_for->items);
+ f->items.len = 0;
}
}
- Buf_Init(&new_for->buf, 0);
- accumFor = new_for;
+ accumFor = f;
forLevel = 1;
return 1;
}
/*
* Add another line to a .for loop.
- * Returns 0 when the matching .endfor is reached.
+ * Returns FALSE when the matching .endfor is reached.
*/
-
-int
-For_Accum(char *line)
+Boolean
+For_Accum(const char *line)
{
- char *ptr = line;
+ const char *ptr = line;
if (*ptr == '.') {
+ ptr++;
+ cpp_skip_whitespace(&ptr);
- for (ptr++; *ptr && isspace((unsigned char)*ptr); ptr++)
- continue;
-
- if (strncmp(ptr, "endfor", 6) == 0 &&
- (isspace((unsigned char)ptr[6]) || !ptr[6])) {
- if (DEBUG(FOR))
- (void)fprintf(debug_file, "For: end for %d\n", forLevel);
+ if (IsEndfor(ptr)) {
+ DEBUG1(FOR, "For: end for %d\n", forLevel);
if (--forLevel <= 0)
- return 0;
- } else if (strncmp(ptr, "for", 3) == 0 &&
- isspace((unsigned char)ptr[3])) {
+ return FALSE;
+ } else if (IsFor(ptr)) {
forLevel++;
- if (DEBUG(FOR))
- (void)fprintf(debug_file, "For: new loop %d\n", forLevel);
+ DEBUG1(FOR, "For: new loop %d\n", forLevel);
}
}
- Buf_AddStr(&accumFor->buf, line);
- Buf_AddByte(&accumFor->buf, '\n');
- return 1;
+ Buf_AddStr(&accumFor->body, line);
+ Buf_AddByte(&accumFor->body, '\n');
+ return TRUE;
}
@@ -326,23 +326,25 @@ for_var_len(const char *var)
return 0;
}
+/* While expanding the body of a .for loop, write the item in the ${:U...}
+ * expression, escaping characters as needed. See ApplyModifier_Defined. */
static void
-for_substitute(Buffer *cmds, strlist_t *items, unsigned int item_no, char ech)
+Buf_AddEscaped(Buffer *cmds, const char *item, char ech)
{
+ ForEscapes escapes = GetEscapes(item);
char ch;
- const char *item = strlist_str(items, item_no);
-
/* If there were no escapes, or the only escape is the other variable
* terminator, then just substitute the full string */
- if (!(strlist_info(items, item_no) &
- (ech == ')' ? ~FOR_SUB_ESCAPE_BRACE : ~FOR_SUB_ESCAPE_PAREN))) {
+ if (!(escapes & (ech == ')' ? ~(unsigned)FOR_SUB_ESCAPE_BRACE
+ : ~(unsigned)FOR_SUB_ESCAPE_PAREN))) {
Buf_AddStr(cmds, item);
return;
}
- /* Escape ':', '$', '\\' and 'ech' - removed by :U processing */
- while ((ch = *item++) != 0) {
+ /* Escape ':', '$', '\\' and 'ech' - these will be removed later by
+ * :U processing, see ApplyModifier_Defined. */
+ while ((ch = *item++) != '\0') {
if (ch == '$') {
size_t len = for_var_len(item);
if (len != 0) {
@@ -357,111 +359,141 @@ for_substitute(Buffer *cmds, strlist_t *items, unsigned int item_no, char ech)
}
}
+/* While expanding the body of a .for loop, replace expressions like
+ * ${i}, ${i:...}, $(i) or $(i:...) with their ${:U...} expansion. */
+static void
+SubstVarLong(For *f, const char **pp, const char **inout_mark, char ech)
+{
+ size_t i;
+ const char *p = *pp;
+
+ for (i = 0; i < f->vars.len; i++) {
+ ForVar *forVar = Vector_Get(&f->vars, i);
+ char *var = forVar->name;
+ size_t vlen = forVar->len;
+
+ /* XXX: undefined behavior for p if vlen is longer than p? */
+ if (memcmp(p, var, vlen) != 0)
+ continue;
+ /* XXX: why test for backslash here? */
+ if (p[vlen] != ':' && p[vlen] != ech && p[vlen] != '\\')
+ continue;
+
+ /* Found a variable match. Replace with :U<value> */
+ Buf_AddBytesBetween(&f->curBody, *inout_mark, p);
+ Buf_AddStr(&f->curBody, ":U");
+ Buf_AddEscaped(&f->curBody, f->items.words[f->sub_next + i], ech);
+
+ p += vlen;
+ *inout_mark = p;
+ break;
+ }
+
+ *pp = p;
+}
+
+/* While expanding the body of a .for loop, replace single-character
+ * variable expressions like $i with their ${:U...} expansion. */
+static void
+SubstVarShort(For *f, char const ch, const char **pp, const char **inout_mark)
+{
+ const char *p = *pp;
+ size_t i;
+
+ /* Probably a single character name, ignore $$ and stupid ones. */
+ if (!f->short_var || strchr("}):$", ch) != NULL) {
+ p++;
+ *pp = p;
+ return;
+ }
+
+ for (i = 0; i < f->vars.len; i++) {
+ ForVar *var = Vector_Get(&f->vars, i);
+ const char *varname = var->name;
+ if (varname[0] != ch || varname[1] != '\0')
+ continue;
+
+ /* Found a variable match. Replace with ${:U<value>} */
+ Buf_AddBytesBetween(&f->curBody, *inout_mark, p);
+ Buf_AddStr(&f->curBody, "{:U");
+ Buf_AddEscaped(&f->curBody, f->items.words[f->sub_next + i], '}');
+ Buf_AddByte(&f->curBody, '}');
+
+ *inout_mark = ++p;
+ break;
+ }
+
+ *pp = p;
+}
+
+/*
+ * Scan the for loop body and replace references to the loop variables
+ * with variable references that expand to the required text.
+ *
+ * Using variable expansions ensures that the .for loop can't generate
+ * syntax, and that the later parsing will still see a variable.
+ * We assume that the null variable will never be defined.
+ *
+ * The detection of substitutions of the loop control variable is naive.
+ * Many of the modifiers use \ to escape $ (not $) so it is possible
+ * to contrive a makefile where an unwanted substitution happens.
+ */
static char *
-For_Iterate(void *v_arg, size_t *ret_len)
+ForIterate(void *v_arg, size_t *out_len)
{
- For *arg = v_arg;
- int i;
- char *var;
- char *cp;
- char *cmd_cp;
- char *body_end;
- char ch;
- Buffer cmds;
- size_t cmd_len;
+ For *f = v_arg;
+ const char *p;
+ const char *mark; /* where the last replacement left off */
+ const char *body_end;
+ char *cmds_str;
- if (arg->sub_next + strlist_num(&arg->vars) > strlist_num(&arg->items)) {
+ if (f->sub_next + f->vars.len > f->items.len) {
/* No more iterations */
- For_Free(arg);
+ For_Free(f);
return NULL;
}
- free(arg->parse_buf);
- arg->parse_buf = NULL;
+ Buf_Empty(&f->curBody);
- /*
- * Scan the for loop body and replace references to the loop variables
- * with variable references that expand to the required text.
- * Using variable expansions ensures that the .for loop can't generate
- * syntax, and that the later parsing will still see a variable.
- * We assume that the null variable will never be defined.
- *
- * The detection of substitions of the loop control variable is naive.
- * Many of the modifiers use \ to escape $ (not $) so it is possible
- * to contrive a makefile where an unwanted substitution happens.
- */
-
- cmd_cp = Buf_GetAll(&arg->buf, &cmd_len);
- body_end = cmd_cp + cmd_len;
- Buf_Init(&cmds, cmd_len + 256);
- for (cp = cmd_cp; (cp = strchr(cp, '$')) != NULL;) {
- char ech;
- ch = *++cp;
+ mark = Buf_GetAll(&f->body, NULL);
+ body_end = mark + Buf_Len(&f->body);
+ for (p = mark; (p = strchr(p, '$')) != NULL;) {
+ char ch, ech;
+ ch = *++p;
if ((ch == '(' && (ech = ')', 1)) || (ch == '{' && (ech = '}', 1))) {
- cp++;
+ p++;
/* Check variable name against the .for loop variables */
- STRLIST_FOREACH(var, &arg->vars, i) {
- size_t vlen = strlist_info(&arg->vars, i);
- if (memcmp(cp, var, vlen) != 0)
- continue;
- if (cp[vlen] != ':' && cp[vlen] != ech && cp[vlen] != '\\')
- continue;
- /* Found a variable match. Replace with :U<value> */
- Buf_AddBytesBetween(&cmds, cmd_cp, cp);
- Buf_AddStr(&cmds, ":U");
- cp += vlen;
- cmd_cp = cp;
- for_substitute(&cmds, &arg->items, arg->sub_next + i, ech);
- break;
- }
+ SubstVarLong(f, &p, &mark, ech);
continue;
}
- if (ch == 0)
+ if (ch == '\0')
break;
- /* Probably a single character name, ignore $$ and stupid ones. {*/
- if (!arg->short_var || strchr("}):$", ch) != NULL) {
- cp++;
- continue;
- }
- STRLIST_FOREACH(var, &arg->vars, i) {
- if (var[0] != ch || var[1] != 0)
- continue;
- /* Found a variable match. Replace with ${:U<value>} */
- Buf_AddBytesBetween(&cmds, cmd_cp, cp);
- Buf_AddStr(&cmds, "{:U");
- cmd_cp = ++cp;
- for_substitute(&cmds, &arg->items, arg->sub_next + i, /*{*/ '}');
- Buf_AddByte(&cmds, '}');
- break;
- }
+
+ SubstVarShort(f, ch, &p, &mark);
}
- Buf_AddBytesBetween(&cmds, cmd_cp, body_end);
+ Buf_AddBytesBetween(&f->curBody, mark, body_end);
- cp = Buf_Destroy(&cmds, FALSE);
- if (DEBUG(FOR))
- (void)fprintf(debug_file, "For: loop body:\n%s", cp);
+ *out_len = Buf_Len(&f->curBody);
+ cmds_str = Buf_GetAll(&f->curBody, NULL);
+ DEBUG1(FOR, "For: loop body:\n%s", cmds_str);
- arg->sub_next += strlist_num(&arg->vars);
+ f->sub_next += f->vars.len;
- arg->parse_buf = cp;
- *ret_len = strlen(cp);
- return cp;
+ return cmds_str;
}
/* Run the for loop, imitating the actions of an include file. */
void
For_Run(int lineno)
{
- For *arg;
-
- arg = accumFor;
+ For *f = accumFor;
accumFor = NULL;
- if (strlist_num(&arg->items) == 0) {
+ if (f->items.len == 0) {
/* Nothing to expand - possibly due to an earlier syntax error. */
- For_Free(arg);
+ For_Free(f);
return;
}
- Parse_SetInput(NULL, lineno, -1, For_Iterate, arg);
+ Parse_SetInput(NULL, lineno, -1, ForIterate, f);
}
diff --git a/hash.c b/hash.c
index 290bed6b3f5a..2a75fb32b50e 100644
--- a/hash.c
+++ b/hash.c
@@ -1,4 +1,4 @@
-/* $NetBSD: hash.c,v 1.29 2020/09/01 21:11:31 rillig Exp $ */
+/* $NetBSD: hash.c,v 1.55 2020/10/25 19:28:44 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -69,336 +69,242 @@
* SUCH DAMAGE.
*/
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: hash.c,v 1.29 2020/09/01 21:11:31 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)hash.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: hash.c,v 1.29 2020/09/01 21:11:31 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
+/* Hash tables with string keys. */
-/* hash.c --
- *
- * This module contains routines to manipulate a hash table.
- * See hash.h for a definition of the structure of the hash
- * table. Hash tables grow automatically as the amount of
- * information increases.
- */
#include "make.h"
-/*
- * Forward references to local procedures that are used before they're
- * defined:
- */
-
-static void RebuildTable(Hash_Table *);
+/* "@(#)hash.c 8.1 (Berkeley) 6/6/93" */
+MAKE_RCSID("$NetBSD: hash.c,v 1.55 2020/10/25 19:28:44 rillig Exp $");
/*
- * The following defines the ratio of # entries to # buckets
- * at which we rebuild the table to make it larger.
+ * The ratio of # entries to # buckets at which we rebuild the table to
+ * make it larger.
*/
-
#define rebuildLimit 3
-/* The hash function(s) */
+/* This hash function matches Gosling's emacs and java.lang.String. */
+static unsigned int
+hash(const char *key, size_t *out_keylen)
+{
+ unsigned int h = 0;
+ const char *p = key;
+ while (*p != '\0')
+ h = (h << 5) - h + (unsigned char)*p++;
+ if (out_keylen != NULL)
+ *out_keylen = (size_t)(p - key);
+ return h;
+}
+
+unsigned int
+Hash_Hash(const char *key)
+{
+ return hash(key, NULL);
+}
-#ifndef HASH
-/* The default: this one matches Gosling's emacs */
-#define HASH(h, key, p) do { \
- for (h = 0, p = key; *p;) \
- h = (h << 5) - h + *p++; \
- } while (0)
+static HashEntry *
+HashTable_Find(HashTable *t, unsigned int h, const char *key)
+{
+ HashEntry *e;
+ unsigned int chainlen = 0;
+#ifdef DEBUG_HASH_LOOKUP
+ DEBUG4(HASH, "%s: %p h=%08x key=%s\n", __func__, t, h, key);
#endif
-/* Sets up the hash table.
- *
- * Input:
- * t Structure to to hold the table.
- * numBuckets How many buckets to create for starters. This
- * number is rounded up to a power of two. If
- * <= 0, a reasonable default is chosen. The
- * table will grow in size later as needed.
- */
+ for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
+ chainlen++;
+ if (e->key_hash == h && strcmp(e->key, key) == 0)
+ break;
+ }
+
+ if (chainlen > t->maxchain)
+ t->maxchain = chainlen;
+
+ return e;
+}
+
+/* Set up the hash table. */
void
-Hash_InitTable(Hash_Table *t, int numBuckets)
+HashTable_Init(HashTable *t)
{
- int i;
- struct Hash_Entry **hp;
-
- /*
- * Round up the size to a power of two.
- */
- if (numBuckets <= 0)
- i = 16;
- else {
- for (i = 2; i < numBuckets; i <<= 1)
- continue;
- }
+ unsigned int n = 16, i;
+ HashEntry **buckets = bmake_malloc(sizeof(*buckets) * n);
+ for (i = 0; i < n; i++)
+ buckets[i] = NULL;
+
+ t->buckets = buckets;
+ t->bucketsSize = n;
t->numEntries = 0;
+ t->bucketsMask = n - 1;
t->maxchain = 0;
- t->bucketsSize = i;
- t->bucketsMask = i - 1;
- t->buckets = hp = bmake_malloc(sizeof(*hp) * i);
- while (--i >= 0)
- *hp++ = NULL;
}
-/* Removes everything from the hash table and frees up the memory space it
- * occupied (except for the space in the Hash_Table structure). */
+/* Remove everything from the hash table and frees up the memory. */
void
-Hash_DeleteTable(Hash_Table *t)
+HashTable_Done(HashTable *t)
{
- struct Hash_Entry **hp, *h, *nexth = NULL;
- int i;
-
- for (hp = t->buckets, i = t->bucketsSize; --i >= 0;) {
- for (h = *hp++; h != NULL; h = nexth) {
- nexth = h->next;
- free(h);
+ HashEntry **buckets = t->buckets;
+ size_t i, n = t->bucketsSize;
+
+ for (i = 0; i < n; i++) {
+ HashEntry *he = buckets[i];
+ while (he != NULL) {
+ HashEntry *next = he->next;
+ free(he);
+ he = next;
}
}
free(t->buckets);
- /*
- * Set up the hash table to cause memory faults on any future access
- * attempts until re-initialization.
- */
+#ifdef CLEANUP
t->buckets = NULL;
+#endif
}
-/* Searches the hash table for an entry corresponding to the key.
- *
- * Input:
- * t Hash table to search.
- * key A hash key.
- *
- * Results:
- * Returns a pointer to the entry for key, or NULL if the table contains
- * no entry for the key.
- */
-Hash_Entry *
-Hash_FindEntry(Hash_Table *t, const char *key)
+/* Find the entry corresponding to the key, or return NULL. */
+HashEntry *
+HashTable_FindEntry(HashTable *t, const char *key)
{
- Hash_Entry *e;
- unsigned h;
- const char *p;
- int chainlen;
+ unsigned int h = hash(key, NULL);
+ return HashTable_Find(t, h, key);
+}
- if (t == NULL || t->buckets == NULL) {
- return NULL;
- }
- HASH(h, key, p);
- p = key;
- chainlen = 0;
-#ifdef DEBUG_HASH_LOOKUP
- if (DEBUG(HASH))
- fprintf(debug_file, "%s: %p h=%x key=%s\n", __func__,
- t, h, key);
-#endif
- for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
- chainlen++;
- if (e->namehash == h && strcmp(e->name, p) == 0)
- break;
- }
- if (chainlen > t->maxchain)
- t->maxchain = chainlen;
- return e;
+/* Find the value corresponding to the key, or return NULL. */
+void *
+HashTable_FindValue(HashTable *t, const char *key)
+{
+ HashEntry *he = HashTable_FindEntry(t, key);
+ return he != NULL ? he->value : NULL;
}
-/* Searches the hash table for an entry corresponding to the key.
- * If no entry is found, then one is created.
- *
- * Input:
- * t Hash table to search.
- * key A hash key.
- * newPtr Filled with TRUE if new entry created,
- * FALSE otherwise.
- */
-Hash_Entry *
-Hash_CreateEntry(Hash_Table *t, const char *key, Boolean *newPtr)
+/* Find the value corresponding to the key and the precomputed hash,
+ * or return NULL. */
+void *
+HashTable_FindValueHash(HashTable *t, const char *key, unsigned int h)
{
- Hash_Entry *e;
- unsigned h;
- const char *p;
- int keylen;
- int chainlen;
- struct Hash_Entry **hp;
-
- /*
- * Hash the key. As a side effect, save the length (strlen) of the
- * key in case we need to create the entry.
- */
- HASH(h, key, p);
- keylen = p - key;
- p = key;
- chainlen = 0;
-#ifdef DEBUG_HASH_LOOKUP
- if (DEBUG(HASH))
- fprintf(debug_file, "%s: %p h=%x key=%s\n", __func__,
- t, h, key);
-#endif
- for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
- chainlen++;
- if (e->namehash == h && strcmp(e->name, p) == 0) {
- if (newPtr != NULL)
- *newPtr = FALSE;
- break;
+ HashEntry *he = HashTable_Find(t, h, key);
+ 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. */
+static void
+HashTable_Enlarge(HashTable *t)
+{
+ unsigned int oldSize = t->bucketsSize;
+ HashEntry **oldBuckets = t->buckets;
+ unsigned int newSize = 2 * oldSize;
+ unsigned int newMask = newSize - 1;
+ HashEntry **newBuckets = bmake_malloc(sizeof(*newBuckets) * newSize);
+ size_t i;
+
+ for (i = 0; i < newSize; i++)
+ newBuckets[i] = NULL;
+
+ for (i = 0; i < oldSize; i++) {
+ 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;
}
}
- if (chainlen > t->maxchain)
- t->maxchain = chainlen;
- if (e)
- return e;
-
- /*
- * The desired entry isn't there. Before allocating a new entry,
- * expand the table if necessary (and this changes the resulting
- * bucket chain).
- */
+
+ free(oldBuckets);
+
+ t->bucketsSize = newSize;
+ t->bucketsMask = newMask;
+ t->buckets = newBuckets;
+ DEBUG5(HASH, "%s: %p size=%d entries=%d maxchain=%d\n",
+ __func__, t, t->bucketsSize, t->numEntries, t->maxchain);
+ t->maxchain = 0;
+}
+
+/* Find or create an entry corresponding to the key.
+ * Return in out_isNew whether a new entry has been created. */
+HashEntry *
+HashTable_CreateEntry(HashTable *t, const char *key, Boolean *out_isNew)
+{
+ size_t keylen;
+ unsigned int h = hash(key, &keylen);
+ HashEntry *he = HashTable_Find(t, h, key);
+
+ if (he != NULL) {
+ if (out_isNew != NULL)
+ *out_isNew = FALSE;
+ return he;
+ }
+
if (t->numEntries >= rebuildLimit * t->bucketsSize)
- RebuildTable(t);
- e = bmake_malloc(sizeof(*e) + keylen);
- hp = &t->buckets[h & t->bucketsMask];
- e->next = *hp;
- *hp = e;
- Hash_SetValue(e, NULL);
- e->namehash = h;
- (void)strcpy(e->name, p);
+ HashTable_Enlarge(t);
+
+ he = bmake_malloc(sizeof(*he) + keylen);
+ he->value = NULL;
+ he->key_hash = h;
+ memcpy(he->key, key, keylen + 1);
+
+ he->next = t->buckets[h & t->bucketsMask];
+ t->buckets[h & t->bucketsMask] = he;
t->numEntries++;
- if (newPtr != NULL)
- *newPtr = TRUE;
- return e;
+ if (out_isNew != NULL)
+ *out_isNew = TRUE;
+ return he;
}
-/* Delete the given hash table entry and free memory associated with it. */
+/* Delete the entry from the table and free the associated memory. */
void
-Hash_DeleteEntry(Hash_Table *t, Hash_Entry *e)
+HashTable_DeleteEntry(HashTable *t, HashEntry *he)
{
- Hash_Entry **hp, *p;
-
- if (e == NULL)
- return;
- for (hp = &t->buckets[e->namehash & t->bucketsMask];
- (p = *hp) != NULL; hp = &p->next) {
- if (p == e) {
- *hp = p->next;
+ HashEntry **ref = &t->buckets[he->key_hash & t->bucketsMask];
+ HashEntry *p;
+
+ for (; (p = *ref) != NULL; ref = &p->next) {
+ if (p == he) {
+ *ref = p->next;
free(p);
t->numEntries--;
return;
}
}
- (void)write(2, "bad call to Hash_DeleteEntry\n", 29);
abort();
}
-/* Sets things up for enumerating all entries in the hash table.
- *
- * Input:
- * t Table to be searched.
- * searchPtr Area in which to keep state about search.
- *
- * Results:
- * The return value is the address of the first entry in
- * the hash table, or NULL if the table is empty.
- */
-Hash_Entry *
-Hash_EnumFirst(Hash_Table *t, Hash_Search *searchPtr)
+/* Set things up for iterating over all entries in the hash table. */
+void
+HashIter_Init(HashIter *hi, HashTable *t)
{
- searchPtr->table = t;
- searchPtr->nextBucket = 0;
- searchPtr->entry = NULL;
- return Hash_EnumNext(searchPtr);
+ hi->table = t;
+ hi->nextBucket = 0;
+ hi->entry = NULL;
}
-/* Returns the next entry in the hash table, or NULL if the end of the table
- * is reached.
- *
- * Input:
- * searchPtr Area used to keep state about search.
- */
-Hash_Entry *
-Hash_EnumNext(Hash_Search *searchPtr)
+/* Return the next entry in the hash table, or NULL if the end of the table
+ * is reached. */
+HashEntry *
+HashIter_Next(HashIter *hi)
{
- Hash_Entry *e;
- Hash_Table *t = searchPtr->table;
-
- /*
- * The entry field points to the most recently returned
- * entry, or is NULL if we are starting up. If not NULL, we have
- * to start at the next one in the chain.
- */
- e = searchPtr->entry;
- if (e != NULL)
- e = e->next;
- /*
- * If the chain ran out, or if we are starting up, we need to
- * find the next nonempty chain.
- */
- while (e == NULL) {
- if (searchPtr->nextBucket >= t->bucketsSize)
- return NULL;
- e = t->buckets[searchPtr->nextBucket++];
- }
- searchPtr->entry = e;
- return e;
-}
+ HashTable *t = hi->table;
+ HashEntry *he = hi->entry;
+ HashEntry **buckets = t->buckets;
+ unsigned int bucketsSize = t->bucketsSize;
-/* Makes a new hash table that is larger than the old one. The entire hash
- * table is moved, so any bucket numbers from the old table become invalid. */
-static void
-RebuildTable(Hash_Table *t)
-{
- Hash_Entry *e, *next = NULL, **hp, **xp;
- int i, mask;
- Hash_Entry **oldhp;
- int oldsize;
-
- oldhp = t->buckets;
- oldsize = i = t->bucketsSize;
- i <<= 1;
- t->bucketsSize = i;
- t->bucketsMask = mask = i - 1;
- t->buckets = hp = bmake_malloc(sizeof(*hp) * i);
- while (--i >= 0)
- *hp++ = NULL;
- for (hp = oldhp, i = oldsize; --i >= 0;) {
- for (e = *hp++; e != NULL; e = next) {
- next = e->next;
- xp = &t->buckets[e->namehash & mask];
- e->next = *xp;
- *xp = e;
- }
- }
- free(oldhp);
- if (DEBUG(HASH))
- fprintf(debug_file, "%s: %p size=%d entries=%d maxchain=%d\n",
- __func__, t, t->bucketsSize, t->numEntries, t->maxchain);
- t->maxchain = 0;
-}
+ if (he != NULL)
+ he = he->next; /* skip the most recently returned entry */
-void
-Hash_ForEach(Hash_Table *t, void (*action)(void *, void *), void *data)
-{
- Hash_Search search;
- Hash_Entry *e;
-
- for (e = Hash_EnumFirst(t, &search);
- e != NULL;
- e = Hash_EnumNext(&search))
- action(Hash_GetValue(e), data);
+ while (he == NULL) { /* find the next nonempty chain */
+ if (hi->nextBucket >= bucketsSize)
+ return NULL;
+ he = buckets[hi->nextBucket++];
+ }
+ hi->entry = he;
+ return he;
}
void
-Hash_DebugStats(Hash_Table *t, const char *name)
+HashTable_DebugStats(HashTable *t, const char *name)
{
- if (DEBUG(HASH))
- fprintf(debug_file, "Hash_Table %s: size=%d numEntries=%d maxchain=%d\n",
- name, t->bucketsSize, t->numEntries, t->maxchain);
+ DEBUG4(HASH, "HashTable %s: size=%u numEntries=%u maxchain=%u\n",
+ name, t->bucketsSize, t->numEntries, t->maxchain);
}
diff --git a/hash.h b/hash.h
index d98a8144ed85..a05a9f4f4732 100644
--- a/hash.h
+++ b/hash.h
@@ -1,4 +1,4 @@
-/* $NetBSD: hash.h,v 1.21 2020/09/01 21:11:31 rillig Exp $ */
+/* $NetBSD: hash.h,v 1.31 2020/10/25 19:19:07 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -78,54 +78,54 @@
#define MAKE_HASH_H
/* A single key-value entry in the hash table. */
-typedef struct Hash_Entry {
- struct Hash_Entry *next; /* Used to link together all the entries
+typedef struct HashEntry {
+ struct HashEntry *next; /* Used to link together all the entries
* associated with the same bucket. */
- void *value;
- unsigned namehash; /* hash value of key */
- char name[1]; /* key string, variable length */
-} Hash_Entry;
+ void *value;
+ unsigned int key_hash; /* hash value of the key */
+ char key[1]; /* key string, variable length */
+} HashEntry;
/* The hash table containing the entries. */
-typedef struct Hash_Table {
- Hash_Entry **buckets; /* Pointers to Hash_Entry, one
+typedef struct HashTable {
+ HashEntry **buckets; /* Pointers to HashEntry, one
* for each bucket in the table. */
- int bucketsSize;
- int numEntries; /* Number of entries in the table. */
- int bucketsMask; /* Used to select the bucket for a hash. */
- int maxchain; /* max length of chain detected */
-} Hash_Table;
+ unsigned int bucketsSize;
+ unsigned int numEntries; /* Number of entries in the table. */
+ unsigned int bucketsMask; /* Used to select the bucket for a hash. */
+ unsigned int maxchain; /* max length of chain detected */
+} HashTable;
-/*
- * The following structure is used by the searching routines
- * to record where we are in the search.
- */
-typedef struct Hash_Search {
- Hash_Table *table; /* Table being searched. */
- int nextBucket; /* Next bucket to check (after current). */
- Hash_Entry *entry; /* Next entry to check in current bucket. */
-} Hash_Search;
+/* State of an iteration over all entries in a table. */
+typedef struct HashIter {
+ HashTable *table; /* Table being searched. */
+ unsigned int nextBucket; /* Next bucket to check (after current). */
+ HashEntry *entry; /* Next entry to check in current bucket. */
+} HashIter;
-static inline void * MAKE_ATTR_UNUSED
-Hash_GetValue(Hash_Entry *h)
+static inline MAKE_ATTR_UNUSED void *
+HashEntry_Get(HashEntry *h)
{
return h->value;
}
-static inline void MAKE_ATTR_UNUSED
-Hash_SetValue(Hash_Entry *h, void *datum)
+static inline MAKE_ATTR_UNUSED void
+HashEntry_Set(HashEntry *h, void *datum)
{
h->value = datum;
}
-void Hash_InitTable(Hash_Table *, int);
-void Hash_DeleteTable(Hash_Table *);
-Hash_Entry *Hash_FindEntry(Hash_Table *, const char *);
-Hash_Entry *Hash_CreateEntry(Hash_Table *, const char *, Boolean *);
-void Hash_DeleteEntry(Hash_Table *, Hash_Entry *);
-Hash_Entry *Hash_EnumFirst(Hash_Table *, Hash_Search *);
-Hash_Entry *Hash_EnumNext(Hash_Search *);
-void Hash_ForEach(Hash_Table *, void (*)(void *, void *), void *);
-void Hash_DebugStats(Hash_Table *, const char *);
+void HashTable_Init(HashTable *);
+void HashTable_Done(HashTable *);
+HashEntry *HashTable_FindEntry(HashTable *, const char *);
+void *HashTable_FindValue(HashTable *, const char *);
+unsigned int Hash_Hash(const char *);
+void *HashTable_FindValueHash(HashTable *, const char *, unsigned int);
+HashEntry *HashTable_CreateEntry(HashTable *, const char *, Boolean *);
+void HashTable_DeleteEntry(HashTable *, HashEntry *);
+void HashTable_DebugStats(HashTable *, const char *);
+
+void HashIter_Init(HashIter *, HashTable *);
+HashEntry *HashIter_Next(HashIter *);
#endif /* MAKE_HASH_H */
diff --git a/job.c b/job.c
index 08fbc147f901..b3f68209dd59 100644
--- a/job.c
+++ b/job.c
@@ -1,4 +1,4 @@
-/* $NetBSD: job.c,v 1.227 2020/08/30 19:56:02 rillig Exp $ */
+/* $NetBSD: job.c,v 1.302 2020/11/01 18:45:49 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -69,68 +69,58 @@
* SUCH DAMAGE.
*/
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: job.c,v 1.227 2020/08/30 19:56:02 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)job.c 8.2 (Berkeley) 3/19/94";
-#else
-__RCSID("$NetBSD: job.c,v 1.227 2020/08/30 19:56:02 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
/*-
* job.c --
* handle the creation etc. of our child processes.
*
* Interface:
- * Job_Make Start the creation of the given target.
- *
- * Job_CatchChildren Check for and handle the termination of any
- * children. This must be called reasonably
- * frequently to keep the whole make going at
- * a decent clip, since job table entries aren't
- * removed until their process is caught this way.
- *
- * Job_CatchOutput Print any output our children have produced.
- * Should also be called fairly frequently to
- * keep the user informed of what's going on.
- * If no output is waiting, it will block for
- * a time given by the SEL_* constants, below,
- * or until output is ready.
- *
- * Job_Init Called to initialize this module. in addition,
- * any commands attached to the .BEGIN target
- * are executed before this function returns.
- * Hence, the makefile must have been parsed
- * before this function is called.
- *
- * Job_End Cleanup any memory used.
- *
- * Job_ParseShell Given the line following a .SHELL target, parse
- * the line as a shell specification. Returns
- * FALSE if the spec was incorrect.
- *
- * Job_Finish Perform any final processing which needs doing.
- * This includes the execution of any commands
- * which have been/were attached to the .END
- * target. It should only be called when the
- * job table is empty.
- *
- * Job_AbortAll Abort all currently running jobs. It doesn't
- * handle output or do anything for the jobs,
- * just kills them. It should only be called in
- * an emergency, as it were.
- *
- * Job_CheckCommands Verify that the commands for a target are
- * ok. Provide them if necessary and possible.
- *
- * Job_Touch Update a target without really updating it.
- *
- * Job_Wait Wait for all currently-running jobs to finish.
+ * Job_Init Called to initialize this module. In addition,
+ * any commands attached to the .BEGIN target
+ * are executed before this function returns.
+ * Hence, the makefiles must have been parsed
+ * before this function is called.
+ *
+ * Job_End Clean up any memory used.
+ *
+ * Job_Make Start the creation of the given target.
+ *
+ * Job_CatchChildren
+ * Check for and handle the termination of any
+ * children. This must be called reasonably
+ * frequently to keep the whole make going at
+ * a decent clip, since job table entries aren't
+ * removed until their process is caught this way.
+ *
+ * Job_CatchOutput
+ * Print any output our children have produced.
+ * Should also be called fairly frequently to
+ * keep the user informed of what's going on.
+ * If no output is waiting, it will block for
+ * a time given by the SEL_* constants, below,
+ * or until output is ready.
+ *
+ * Job_ParseShell Given the line following a .SHELL target, parse
+ * the line as a shell specification. Returns
+ * FALSE if the spec was incorrect.
+ *
+ * Job_Finish Perform any final processing which needs doing.
+ * This includes the execution of any commands
+ * which have been/were attached to the .END
+ * target. It should only be called when the
+ * job table is empty.
+ *
+ * Job_AbortAll Abort all currently running jobs. It doesn't
+ * handle output or do anything for the jobs,
+ * just kills them. It should only be called in
+ * an emergency.
+ *
+ * Job_CheckCommands
+ * Verify that the commands for a target are
+ * ok. Provide them if necessary and possible.
+ *
+ * Job_Touch Update a target without really updating it.
+ *
+ * Job_Wait Wait for all currently-running jobs to finish.
*/
#ifdef HAVE_CONFIG_H
@@ -164,61 +154,118 @@ __RCSID("$NetBSD: job.c,v 1.227 2020/08/30 19:56:02 rillig Exp $");
#include "job.h"
#include "pathnames.h"
#include "trace.h"
-# define STATIC static
+
+/* "@(#)job.c 8.2 (Berkeley) 3/19/94" */
+MAKE_RCSID("$NetBSD: job.c,v 1.302 2020/11/01 18:45:49 rillig Exp $");
+
+/* A shell defines how the commands are run. All commands for a target are
+ * written into a single file, which is then given to the shell to execute
+ * the commands from it. The commands are written to the file using a few
+ * templates for echo control and error control.
+ *
+ * The name of the shell is the basename for the predefined shells, such as
+ * "sh", "csh", "bash". For custom shells, it is the full pathname, and its
+ * basename is used to select the type of shell; the longest match wins.
+ * So /usr/pkg/bin/bash has type sh, /usr/local/bin/tcsh has type csh.
+ *
+ * The echoing of command lines is controlled using hasEchoCtl, echoOff,
+ * echoOn, noPrint and noPrintLen. When echoOff is executed by the shell, it
+ * still outputs something, but this something is not interesting, therefore
+ * it is filtered out using noPrint and noPrintLen.
+ *
+ * The error checking for individual commands is controlled using hasErrCtl,
+ * errOnOrEcho, errOffOrExecIgnore and errExit.
+ *
+ * If a shell doesn't have error control, errOnOrEcho becomes a printf template
+ * for echoing the command, should echoing be on; errOffOrExecIgnore becomes
+ * another printf template for executing the command while ignoring the return
+ * status. Finally errExit is a printf template for running the command and
+ * causing the shell to exit on error. If any of these strings are empty when
+ * hasErrCtl is FALSE, the command will be executed anyway as is, and if it
+ * causes an error, so be it. Any templates setup to echo the command will
+ * escape any '$ ` \ "' characters in the command string to avoid common
+ * problems with echo "%s\n" as a template.
+ *
+ * The command-line flags "echo" and "exit" also control the behavior. The
+ * "echo" flag causes the shell to start echoing commands right away. The
+ * "exit" flag causes the shell to exit when an error is detected in one of
+ * the commands.
+ */
+typedef struct Shell {
+
+ /* The name of the shell. For Bourne and C shells, this is used only to
+ * find the shell description when used as the single source of a .SHELL
+ * target. For user-defined shells, this is the full path of the shell. */
+ const char *name;
+
+ Boolean hasEchoCtl; /* True if both echoOff and echoOn defined */
+ const char *echoOff; /* command to turn off echo */
+ const char *echoOn; /* command to turn it back on again */
+ const char *noPrint; /* text to skip when printing output from
+ * shell. This is usually the same as echoOff */
+ size_t noPrintLen; /* length of noPrint command */
+
+ Boolean hasErrCtl; /* set if can control error checking for
+ * individual commands */
+ /* XXX: split into errOn and echoCmd */
+ const char *errOnOrEcho; /* template to turn on error checking */
+ /* XXX: split into errOff and execIgnore */
+ const char *errOffOrExecIgnore; /* template to turn off error checking */
+ const char *errExit; /* template to use for testing exit code */
+
+ /* string literal that results in a newline character when it appears
+ * outside of any 'quote' or "quote" characters */
+ const char *newline;
+ char commentChar; /* character used by shell for comment lines */
+
+ /*
+ * command-line flags
+ */
+ const char *echo; /* echo commands */
+ const char *exit; /* exit on error */
+} Shell;
/*
* error handling variables
*/
-static int errors = 0; /* number of errors reported */
-static int aborting = 0; /* why is the make aborting? */
-#define ABORT_ERROR 1 /* Because of an error */
-#define ABORT_INTERRUPT 2 /* Because it was interrupted */
-#define ABORT_WAIT 3 /* Waiting for jobs to finish */
-#define JOB_TOKENS "+EI+" /* Token to requeue for each abort state */
+static int errors = 0; /* number of errors reported */
+typedef enum AbortReason { /* why is the make aborting? */
+ ABORT_NONE,
+ ABORT_ERROR, /* Because of an error */
+ ABORT_INTERRUPT, /* Because it was interrupted */
+ ABORT_WAIT /* Waiting for jobs to finish */
+} AbortReason;
+static AbortReason 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.
*/
int jobTokensRunning = 0;
-/*
- * XXX: Avoid SunOS bug... FILENO() is fp->_file, and file
- * is a char! So when we go above 127 we turn negative!
- */
-#define FILENO(a) ((unsigned) fileno(a))
-
-/*
- * post-make command processing. The node postCommands is really just the
- * .END target but we keep it around to avoid having to search for it
- * all the time.
- */
-static GNode *postCommands = NULL;
- /* node containing commands to execute when
- * everything else is done */
-static int numCommands; /* The number of commands actually printed
- * for a target. Should this number be
- * 0, no shell will be executed. */
+/* The number of commands actually printed to the shell commands file for
+ * the current job. Should this number be 0, no shell will be executed. */
+static int numCommands;
-/*
- * Return values from JobStart.
- */
-#define JOB_RUNNING 0 /* Job is running */
-#define JOB_ERROR 1 /* Error in starting the job */
-#define JOB_FINISHED 2 /* The job is already finished */
+typedef enum JobStartResult {
+ JOB_RUNNING, /* Job is running */
+ JOB_ERROR, /* Error in starting the job */
+ JOB_FINISHED /* The job is already finished */
+} JobStartResult;
/*
* Descriptions for various shells.
*
* The build environment may set DEFSHELL_INDEX to one of
* DEFSHELL_INDEX_SH, DEFSHELL_INDEX_KSH, or DEFSHELL_INDEX_CSH, to
- * select one of the prefedined shells as the default shell.
+ * select one of the predefined shells as the default shell.
*
* Alternatively, the build environment may set DEFSHELL_CUSTOM to the
* name or the full path of a sh-compatible shell, which will be used as
* the default shell.
*
* ".SHELL" lines in Makefiles can choose the default shell from the
- # set defined here, or add additional shells.
+ * set defined here, or add additional shells.
*/
#ifdef DEFSHELL_CUSTOM
@@ -246,11 +293,20 @@ static Shell shells[] = {
* sh-compatible shells.
*/
{
- DEFSHELL_CUSTOM,
- FALSE, "", "", "", 0,
- FALSE, "echo \"%s\"\n", "%s\n", "{ %s \n} || exit $?\n", "'\n'", '#',
- "",
- "",
+ DEFSHELL_CUSTOM, /* .name */
+ FALSE, /* .hasEchoCtl */
+ "", /* .echoOff */
+ "", /* .echoOn */
+ "", /* .noPrint */
+ 0, /* .noPrintLen */
+ FALSE, /* .hasErrCtl */
+ "echo \"%s\"\n", /* .errOnOrEcho */
+ "%s\n", /* .errOffOrExecIgnore */
+ "{ %s \n} || exit $?\n", /* .errExit */
+ "'\n'", /* .newline */
+ '#', /* .commentChar */
+ "", /* .echo */
+ "", /* .exit */
},
#endif /* DEFSHELL_CUSTOM */
/*
@@ -258,25 +314,43 @@ static Shell shells[] = {
* sun UNIX anyway, one can even control error checking.
*/
{
- "sh",
- FALSE, "", "", "", 0,
- FALSE, "echo \"%s\"\n", "%s\n", "{ %s \n} || exit $?\n", "'\n'", '#',
+ "sh", /* .name */
+ FALSE, /* .hasEchoCtl */
+ "", /* .echoOff */
+ "", /* .echoOn */
+ "", /* .noPrint */
+ 0, /* .noPrintLen */
+ FALSE, /* .hasErrCtl */
+ "echo \"%s\"\n", /* .errOnOrEcho */
+ "%s\n", /* .errOffOrExecIgnore */
+ "{ %s \n} || exit $?\n", /* .errExit */
+ "'\n'", /* .newline */
+ '#', /* .commentChar*/
#if defined(MAKE_NATIVE) && defined(__NetBSD__)
- "q",
+ "q", /* .echo */
#else
- "",
+ "", /* .echo */
#endif
- "",
+ "", /* .exit */
},
/*
* KSH description.
*/
{
- "ksh",
- TRUE, "set +v", "set -v", "set +v", 6,
- FALSE, "echo \"%s\"\n", "%s\n", "{ %s \n} || exit $?\n", "'\n'", '#',
- "v",
- "",
+ "ksh", /* .name */
+ TRUE, /* .hasEchoCtl */
+ "set +v", /* .echoOff */
+ "set -v", /* .echoOn */
+ "set +v", /* .noPrint */
+ 6, /* .noPrintLen */
+ FALSE, /* .hasErrCtl */
+ "echo \"%s\"\n", /* .errOnOrEcho */
+ "%s\n", /* .errOffOrExecIgnore */
+ "{ %s \n} || exit $?\n", /* .errExit */
+ "'\n'", /* .newline */
+ '#', /* .commentChar */
+ "v", /* .echo */
+ "", /* .exit */
},
/*
* CSH description. The csh can do echo control by playing
@@ -284,36 +358,36 @@ static Shell shells[] = {
* however, it is unable to do error control nicely.
*/
{
- "csh",
- TRUE, "unset verbose", "set verbose", "unset verbose", 10,
- FALSE, "echo \"%s\"\n", "csh -c \"%s || exit 0\"\n", "", "'\\\n'", '#',
- "v", "e",
-},
- /*
- * UNKNOWN.
- */
-{
- NULL,
- FALSE, NULL, NULL, NULL, 0,
- FALSE, NULL, NULL, NULL, NULL, 0,
- NULL, NULL,
+ "csh", /* .name */
+ TRUE, /* .hasEchoCtl */
+ "unset verbose", /* .echoOff */
+ "set verbose", /* .echoOn */
+ "unset verbose", /* .noPrint */
+ 13, /* .noPrintLen */
+ FALSE, /* .hasErrCtl */
+ "echo \"%s\"\n", /* .errOnOrEcho */
+ /* XXX: Mismatch between errOn and execIgnore */
+ "csh -c \"%s || exit 0\"\n", /* .errOffOrExecIgnore */
+ "", /* .errExit */
+ "'\\\n'", /* .newline */
+ '#', /* .commentChar */
+ "v", /* .echo */
+ "e", /* .exit */
}
};
-static Shell *commandShell = &shells[DEFSHELL_INDEX]; /* this is the shell to
- * which we pass all
- * commands in the Makefile.
- * It is set by the
- * Job_ParseShell function */
-const char *shellPath = NULL, /* full pathname of
- * executable image */
- *shellName = NULL; /* last component of shell */
+
+/* This is the shell to which we pass all commands in the Makefile.
+ * It is set by the Job_ParseShell function. */
+static Shell *commandShell = &shells[DEFSHELL_INDEX];
+const char *shellPath = NULL; /* full pathname of executable image */
+const char *shellName = NULL; /* last component of shellPath */
char *shellErrFlag = NULL;
static char *shellArgv = NULL; /* Custom shell args */
-STATIC Job *job_table; /* The structures that describe them */
-STATIC Job *job_table_end; /* job_table + maxJobs */
-static int wantToken; /* we want a token */
+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 int lurking_children = 0;
static int make_suspended = 0; /* non-zero if we've seen a SIGTSTP (etc) */
@@ -323,12 +397,12 @@ static int make_suspended = 0; /* non-zero if we've seen a SIGTSTP (etc) */
*/
static struct pollfd *fds = NULL;
static Job **jobfds = NULL;
-static int nfds = 0;
+static nfds_t nfds = 0;
static void watchfd(Job *);
static void clearfd(Job *);
static int readyfd(Job *);
-STATIC GNode *lastNode; /* The node for which output was most recently
+static GNode *lastNode; /* The node for which output was most recently
* produced. */
static char *targPrefix = NULL; /* What we print at the start of TARG_FMT */
static Job tokenWaitJob; /* token wait pseudo-job */
@@ -337,32 +411,18 @@ static Job childExitJob; /* child exit pseudo-job */
#define CHILD_EXIT "."
#define DO_JOB_RESUME "R"
-static const int npseudojobs = 2; /* number of pseudo-jobs */
+enum { npseudojobs = 2 }; /* number of pseudo-jobs */
#define TARG_FMT "%s %s ---\n" /* Default format */
#define MESSAGE(fp, gn) \
- if (maxJobs != 1 && targPrefix && *targPrefix) \
+ if (opts.maxJobs != 1 && targPrefix && *targPrefix) \
(void)fprintf(fp, TARG_FMT, targPrefix, gn->name)
static sigset_t caught_signals; /* Set of signals we handle */
-static void JobChildSig(int);
-static void JobContinueSig(int);
-static Job *JobFindPid(int, int, Boolean);
-static int JobPrintCommand(void *, void *);
-static int JobSaveCommand(void *, void *);
-static void JobClose(Job *);
-static void JobExec(Job *, char **);
-static void JobMakeArgv(Job *, char **);
-static int JobStart(GNode *, int);
-static char *JobOutput(Job *, char *, char *, int);
static void JobDoOutput(Job *, Boolean);
-static Shell *JobMatchShell(const char *);
static void JobInterrupt(int, int) MAKE_ATTR_DEAD;
static void JobRestartJobs(void);
-static void JobTokenAdd(void);
-static void JobSigLock(sigset_t *);
-static void JobSigUnlock(sigset_t *);
static void JobSigReset(void);
static unsigned
@@ -380,9 +440,9 @@ job_table_dump(const char *where)
{
Job *job;
- fprintf(debug_file, "job table @ %s\n", where);
+ debug_printf("job table @ %s\n", where);
for (job = job_table; job < job_table_end; job++) {
- fprintf(debug_file, "job %d, status %d, flags %d, pid %d\n",
+ debug_printf("job %d, status %d, flags %d, pid %d\n",
(int)(job - job_table), job->job_state, job->flags, job->pid);
}
}
@@ -394,12 +454,20 @@ job_table_dump(const char *where)
static void
JobDeleteTarget(GNode *gn)
{
- if ((gn->type & (OP_JOIN|OP_PHONY)) == 0 && !Targ_Precious(gn)) {
- char *file = (gn->path == NULL ? gn->name : gn->path);
- if (!noExecute && eunlink(file) != -1) {
- Error("*** %s removed", file);
- }
- }
+ const char *file;
+
+ if (gn->type & OP_JOIN)
+ return;
+ if (gn->type & OP_PHONY)
+ return;
+ if (Targ_Precious(gn))
+ return;
+ if (opts.noExecute)
+ return;
+
+ file = GNode_Path(gn);
+ if (eunlink(file) != -1)
+ Error("*** %s removed", file);
}
/*
@@ -425,23 +493,27 @@ static void
JobCreatePipe(Job *job, int minfd)
{
int i, fd, flags;
+ int pipe_fds[2];
- if (pipe(job->jobPipe) == -1)
+ if (pipe(pipe_fds) == -1)
Punt("Cannot create pipe: %s", strerror(errno));
for (i = 0; i < 2; i++) {
- /* Avoid using low numbered fds */
- fd = fcntl(job->jobPipe[i], F_DUPFD, minfd);
- if (fd != -1) {
- close(job->jobPipe[i]);
- job->jobPipe[i] = fd;
- }
+ /* Avoid using low numbered fds */
+ fd = fcntl(pipe_fds[i], F_DUPFD, minfd);
+ if (fd != -1) {
+ close(pipe_fds[i]);
+ pipe_fds[i] = fd;
+ }
}
+ job->inPipe = pipe_fds[0];
+ job->outPipe = pipe_fds[1];
+
/* Set close-on-exec flag for both */
- if (fcntl(job->jobPipe[0], F_SETFD, FD_CLOEXEC) == -1)
+ if (fcntl(job->inPipe, F_SETFD, FD_CLOEXEC) == -1)
Punt("Cannot set close-on-exec: %s", strerror(errno));
- if (fcntl(job->jobPipe[1], F_SETFD, FD_CLOEXEC) == -1)
+ if (fcntl(job->outPipe, F_SETFD, FD_CLOEXEC) == -1)
Punt("Cannot set close-on-exec: %s", strerror(errno));
/*
@@ -450,65 +522,34 @@ JobCreatePipe(Job *job, int minfd)
* race for the token when a new one becomes available, so the read
* from the pipe should not block.
*/
- flags = fcntl(job->jobPipe[0], F_GETFL, 0);
+ flags = fcntl(job->inPipe, F_GETFL, 0);
if (flags == -1)
Punt("Cannot get flags: %s", strerror(errno));
flags |= O_NONBLOCK;
- if (fcntl(job->jobPipe[0], F_SETFL, flags) == -1)
+ if (fcntl(job->inPipe, F_SETFL, flags) == -1)
Punt("Cannot set flags: %s", strerror(errno));
}
-/*-
- *-----------------------------------------------------------------------
- * JobCondPassSig --
- * Pass a signal to a job
- *
- * Input:
- * signop Signal to send it
- *
- * Side Effects:
- * None, except the job may bite it.
- *
- *-----------------------------------------------------------------------
- */
+/* Pass the signal to each running job. */
static void
JobCondPassSig(int signo)
{
Job *job;
- if (DEBUG(JOB)) {
- (void)fprintf(debug_file, "JobCondPassSig(%d) called.\n", signo);
- }
+ DEBUG1(JOB, "JobCondPassSig(%d) called.\n", signo);
for (job = job_table; job < job_table_end; job++) {
if (job->job_state != JOB_ST_RUNNING)
continue;
- if (DEBUG(JOB)) {
- (void)fprintf(debug_file,
- "JobCondPassSig passing signal %d to child %d.\n",
- signo, job->pid);
- }
+ DEBUG2(JOB, "JobCondPassSig passing signal %d to child %d.\n",
+ signo, job->pid);
KILLPG(job->pid, signo);
}
}
-/*-
- *-----------------------------------------------------------------------
- * JobChldSig --
- * SIGCHLD handler.
- *
- * Input:
- * signo The signal number we've received
+/* SIGCHLD handler.
*
- * Results:
- * None.
- *
- * Side Effects:
- * Sends a token on the child exit pipe to wake us up from
- * select()/poll().
- *
- *-----------------------------------------------------------------------
- */
+ * Sends a token on the child exit pipe to wake us up from select()/poll(). */
static void
JobChildSig(int signo MAKE_ATTR_UNUSED)
{
@@ -517,27 +558,12 @@ JobChildSig(int signo MAKE_ATTR_UNUSED)
}
-/*-
- *-----------------------------------------------------------------------
- * JobContinueSig --
- * Resume all stopped jobs.
- *
- * Input:
- * signo The signal number we've received
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Jobs start running again.
- *
- *-----------------------------------------------------------------------
- */
+/* Resume all stopped jobs. */
static void
JobContinueSig(int signo MAKE_ATTR_UNUSED)
{
/*
- * Defer sending to SIGCONT to our stopped children until we return
+ * Defer sending SIGCONT to our stopped children until we return
* from the signal handler.
*/
while (write(childExitJob.outPipe, DO_JOB_RESUME, 1) == -1 &&
@@ -545,22 +571,8 @@ JobContinueSig(int signo MAKE_ATTR_UNUSED)
continue;
}
-/*-
- *-----------------------------------------------------------------------
- * JobPassSig --
- * Pass a signal on to all jobs, then resend to ourselves.
- *
- * Input:
- * signo The signal number we've received
- *
- * Results:
- * None.
- *
- * Side Effects:
- * We die by the same signal.
- *
- *-----------------------------------------------------------------------
- */
+/* Pass a signal on to all jobs, then resend to ourselves.
+ * We die by the same signal. */
MAKE_ATTR_DEAD static void
JobPassSig_int(int signo)
{
@@ -568,6 +580,8 @@ JobPassSig_int(int signo)
JobInterrupt(TRUE, signo);
}
+/* Pass a signal on to all jobs, then resend to ourselves.
+ * We die by the same signal. */
MAKE_ATTR_DEAD static void
JobPassSig_term(int signo)
{
@@ -602,10 +616,8 @@ JobPassSig_suspend(int signo)
act.sa_flags = 0;
(void)sigaction(signo, &act, NULL);
- if (DEBUG(JOB)) {
- (void)fprintf(debug_file,
- "JobPassSig passing signal %d to self.\n", signo);
- }
+ if (DEBUG(JOB))
+ debug_printf("JobPassSig passing signal %d to self.\n", signo);
(void)kill(getpid(), signo);
@@ -622,7 +634,7 @@ JobPassSig_suspend(int signo)
* events will have happened by then - and that the waitpid() will
* collect the child 'suspended' events.
* For correct sequencing we just need to ensure we process the
- * waitpid() before passign on the SIGCONT.
+ * waitpid() before passing on the SIGCONT.
*
* In any case nothing else is needed here.
*/
@@ -633,26 +645,8 @@ JobPassSig_suspend(int signo)
(void)sigprocmask(SIG_SETMASK, &omask, NULL);
}
-/*-
- *-----------------------------------------------------------------------
- * JobFindPid --
- * Compare the pid of the job with the given pid and return 0 if they
- * are equal. This function is called from Job_CatchChildren
- * to find the job descriptor of the finished job.
- *
- * Input:
- * job job to examine
- * pid process id desired
- *
- * Results:
- * Job with matching pid
- *
- * Side Effects:
- * None
- *-----------------------------------------------------------------------
- */
static Job *
-JobFindPid(int pid, int status, Boolean isJobs)
+JobFindPid(int pid, JobState status, Boolean isJobs)
{
Job *job;
@@ -665,6 +659,52 @@ JobFindPid(int pid, int status, Boolean isJobs)
return NULL;
}
+/* Parse leading '@', '-' and '+', which control the exact execution mode. */
+static void
+ParseRunOptions(
+ char **pp,
+ Boolean *out_shutUp, Boolean *out_errOff, Boolean *out_runAlways)
+{
+ char *p = *pp;
+ *out_shutUp = FALSE;
+ *out_errOff = FALSE;
+ *out_runAlways = FALSE;
+
+ for (;;) {
+ if (*p == '@')
+ *out_shutUp = !DEBUG(LOUD);
+ else if (*p == '-')
+ *out_errOff = TRUE;
+ else if (*p == '+')
+ *out_runAlways = TRUE;
+ else
+ break;
+ p++;
+ }
+
+ pp_skip_whitespace(&p);
+
+ *pp = p;
+}
+
+/* Escape a string for a double-quoted string literal in sh, csh and ksh. */
+static char *
+EscapeShellDblQuot(const char *cmd)
+{
+ size_t i, j;
+
+ /* Worst that could happen is every char needs escaping. */
+ char *esc = bmake_malloc(strlen(cmd) * 2 + 1);
+ for (i = 0, j = 0; cmd[i] != '\0'; i++, j++) {
+ if (cmd[i] == '$' || cmd[i] == '`' || cmd[i] == '\\' || cmd[i] == '"')
+ esc[j++] = '\\';
+ esc[j] = cmd[i];
+ }
+ esc[j] = '\0';
+
+ return esc;
+}
+
/*-
*-----------------------------------------------------------------------
* JobPrintCommand --
@@ -679,93 +719,58 @@ JobFindPid(int pid, int status, Boolean isJobs)
* made and return non-zero to signal that the end of the commands
* was reached. These commands are later attached to the postCommands
* node and executed by Job_End when all things are done.
- * This function is called from JobStart via Lst_ForEach.
- *
- * Input:
- * cmdp command string to print
- * jobp job for which to print it
- *
- * Results:
- * Always 0, unless the command was "..."
*
* Side Effects:
* If the command begins with a '-' and the shell has no error control,
* the JOB_IGNERR flag is set in the job descriptor.
- * If the command is "..." and we're not ignoring such things,
- * tailCmds is set to the successor node of the cmd.
* numCommands is incremented if the command is actually printed.
*-----------------------------------------------------------------------
*/
-static int
-JobPrintCommand(void *cmdp, void *jobp)
+static void
+JobPrintCommand(Job *job, char *cmd)
{
- Boolean noSpecials; /* true if we shouldn't worry about
- * inserting special commands into
- * the input stream. */
- Boolean shutUp = FALSE; /* true if we put a no echo command
- * into the command file */
- Boolean errOff = FALSE; /* true if we turned error checking
- * off before printing the command
- * and need to turn it back on */
- const char *cmdTemplate; /* Template to use when printing the
- * command */
- char *cmdStart; /* Start of expanded command */
- char *escCmd = NULL; /* Command with quotes/backticks escaped */
- char *cmd = (char *)cmdp;
- Job *job = (Job *)jobp;
-
- noSpecials = NoExecute(job->node);
-
- if (strcmp(cmd, "...") == 0) {
- job->node->type |= OP_SAVE_CMDS;
- if ((job->flags & JOB_IGNDOTS) == 0) {
- LstNode dotsNode = Lst_FindDatum(job->node->commands, cmd);
- job->tailCmds = dotsNode != NULL ? LstNode_Next(dotsNode) : NULL;
- return 1;
- }
- return 0;
- }
+ const char *const cmdp = cmd;
+ Boolean noSpecials; /* true if we shouldn't worry about
+ * inserting special commands into
+ * the input stream. */
+ Boolean shutUp; /* true if we put a no echo command
+ * into the command file */
+ Boolean errOff; /* true if we turned error checking
+ * off before printing the command
+ * and need to turn it back on */
+ Boolean runAlways;
+ const char *cmdTemplate; /* Template to use when printing the
+ * command */
+ char *cmdStart; /* Start of expanded command */
+ char *escCmd = NULL; /* Command with quotes/backticks escaped */
+
+ noSpecials = !GNode_ShouldExecute(job->node);
#define DBPRINTF(fmt, arg) if (DEBUG(JOB)) { \
- (void)fprintf(debug_file, fmt, arg); \
+ debug_printf(fmt, arg); \
} \
(void)fprintf(job->cmdFILE, fmt, arg); \
(void)fflush(job->cmdFILE);
- numCommands += 1;
+ numCommands++;
- cmdStart = cmd = Var_Subst(cmd, job->node, VARE_WANTRES);
+ Var_Subst(cmd, job->node, VARE_WANTRES, &cmd);
+ /* TODO: handle errors */
+ cmdStart = cmd;
cmdTemplate = "%s\n";
- /*
- * Check for leading @' and -'s to control echoing and error checking.
- */
- while (*cmd == '@' || *cmd == '-' || (*cmd == '+')) {
- switch (*cmd) {
- case '@':
- shutUp = DEBUG(LOUD) ? FALSE : TRUE;
- break;
- case '-':
- errOff = TRUE;
- break;
- case '+':
- if (noSpecials) {
- /*
- * We're not actually executing anything...
- * but this one needs to be - use compat mode just for it.
- */
- CompatRunCommand(cmdp, job->node);
- free(cmdStart);
- return 0;
- }
- break;
- }
- cmd++;
- }
+ ParseRunOptions(&cmd, &shutUp, &errOff, &runAlways);
- while (isspace((unsigned char) *cmd))
- cmd++;
+ if (runAlways && noSpecials) {
+ /*
+ * We're not actually executing anything...
+ * but this one needs to be - use compat mode just for it.
+ */
+ Compat_RunCommand(cmdp, job->node);
+ free(cmdStart);
+ return;
+ }
/*
* If the shell doesn't have error control the alternate echo'ing will
@@ -773,19 +778,8 @@ JobPrintCommand(void *cmdp, void *jobp)
* and this will need the characters '$ ` \ "' escaped
*/
- if (!commandShell->hasErrCtl) {
- int i, j;
-
- /* Worst that could happen is every char needs escaping. */
- escCmd = bmake_malloc((strlen(cmd) * 2) + 1);
- for (i = 0, j = 0; cmd[i] != '\0'; i++, j++) {
- if (cmd[i] == '$' || cmd[i] == '`' || cmd[i] == '\\' ||
- cmd[i] == '"')
- escCmd[j++] = '\\';
- escCmd[j] = cmd[i];
- }
- escCmd[j] = '\0';
- }
+ if (!commandShell->hasErrCtl)
+ escCmd = EscapeShellDblQuot(cmd);
if (shutUp) {
if (!(job->flags & JOB_SILENT) && !noSpecials &&
@@ -811,19 +805,19 @@ JobPrintCommand(void *cmdp, void *jobp)
if (!(job->flags & JOB_SILENT) && !shutUp &&
commandShell->hasEchoCtl) {
DBPRINTF("%s\n", commandShell->echoOff);
- DBPRINTF("%s\n", commandShell->ignErr);
+ DBPRINTF("%s\n", commandShell->errOffOrExecIgnore);
DBPRINTF("%s\n", commandShell->echoOn);
} else {
- DBPRINTF("%s\n", commandShell->ignErr);
+ DBPRINTF("%s\n", commandShell->errOffOrExecIgnore);
}
- } else if (commandShell->ignErr &&
- (*commandShell->ignErr != '\0'))
+ } else if (commandShell->errOffOrExecIgnore &&
+ commandShell->errOffOrExecIgnore[0] != '\0')
{
/*
* The shell has no error control, so we need to be
* weird to get it to ignore any errors from the command.
* If echoing is turned on, we turn it off and use the
- * errCheck template to echo the command. Leave echoing
+ * errOnOrEcho template to echo the command. Leave echoing
* off so the user doesn't see the weirdness we go through
* to ignore errors. Set cmdTemplate to use the weirdness
* instead of the simple "%s\n" template.
@@ -833,18 +827,18 @@ JobPrintCommand(void *cmdp, void *jobp)
if (commandShell->hasEchoCtl) {
DBPRINTF("%s\n", commandShell->echoOff);
}
- DBPRINTF(commandShell->errCheck, escCmd);
+ DBPRINTF(commandShell->errOnOrEcho, escCmd);
shutUp = TRUE;
} else {
if (!shutUp) {
- DBPRINTF(commandShell->errCheck, escCmd);
+ DBPRINTF(commandShell->errOnOrEcho, escCmd);
}
}
- cmdTemplate = commandShell->ignErr;
+ cmdTemplate = commandShell->errOffOrExecIgnore;
/*
* The error ignoration (hee hee) is already taken care
- * of by the ignErr template, so pretend error checking
- * is still on.
+ * of by the errOffOrExecIgnore template, so pretend error
+ * checking is still on.
*/
errOff = FALSE;
} else {
@@ -857,25 +851,25 @@ JobPrintCommand(void *cmdp, void *jobp)
/*
* If errors are being checked and the shell doesn't have error control
- * but does supply an errOut template, then setup commands to run
+ * but does supply an errExit template, then setup commands to run
* through it.
*/
- if (!commandShell->hasErrCtl && commandShell->errOut &&
- (*commandShell->errOut != '\0')) {
+ if (!commandShell->hasErrCtl && commandShell->errExit &&
+ commandShell->errExit[0] != '\0') {
if (!(job->flags & JOB_SILENT) && !shutUp) {
if (commandShell->hasEchoCtl) {
DBPRINTF("%s\n", commandShell->echoOff);
}
- DBPRINTF(commandShell->errCheck, escCmd);
+ DBPRINTF(commandShell->errOnOrEcho, escCmd);
shutUp = TRUE;
}
/* If it's a comment line or blank, treat as an ignored error */
if ((escCmd[0] == commandShell->commentChar) ||
(escCmd[0] == 0))
- cmdTemplate = commandShell->ignErr;
+ cmdTemplate = commandShell->errOffOrExecIgnore;
else
- cmdTemplate = commandShell->errOut;
+ cmdTemplate = commandShell->errExit;
errOff = FALSE;
}
}
@@ -899,50 +893,56 @@ JobPrintCommand(void *cmdp, void *jobp)
DBPRINTF("%s\n", commandShell->echoOff);
shutUp = TRUE;
}
- DBPRINTF("%s\n", commandShell->errCheck);
+ DBPRINTF("%s\n", commandShell->errOnOrEcho);
}
if (shutUp && commandShell->hasEchoCtl) {
DBPRINTF("%s\n", commandShell->echoOn);
}
- return 0;
}
-/*-
- *-----------------------------------------------------------------------
- * JobSaveCommand --
- * Save a command to be executed when everything else is done.
- * Callback function for JobFinish...
- *
- * Results:
- * Always returns 0
+/* Print all commands to the shell file that is later executed.
*
- * Side Effects:
- * The command is tacked onto the end of postCommands' commands list.
- *
- *-----------------------------------------------------------------------
- */
-static int
-JobSaveCommand(void *cmd, void *gn)
+ * The special command "..." stops printing and saves the remaining commands
+ * to be executed later. */
+static void
+JobPrintCommands(Job *job)
{
- cmd = Var_Subst((char *)cmd, (GNode *)gn, VARE_WANTRES);
- Lst_Append(postCommands->commands, cmd);
- return 0;
+ StringListNode *ln;
+
+ for (ln = job->node->commands->first; ln != NULL; ln = ln->next) {
+ const char *cmd = ln->datum;
+
+ if (strcmp(cmd, "...") == 0) {
+ job->node->type |= OP_SAVE_CMDS;
+ if ((job->flags & JOB_IGNDOTS) == 0) {
+ job->tailCmds = ln->next;
+ break;
+ }
+ } else
+ JobPrintCommand(job, ln->datum);
+ }
}
+/* Save the delayed commands, to be executed when everything else is done. */
+static void
+JobSaveCommands(Job *job)
+{
+ StringListNode *node;
+
+ for (node = job->tailCmds; node != NULL; node = node->next) {
+ const char *cmd = node->datum;
+ char *expanded_cmd;
+ /* XXX: This Var_Subst is only intended to expand the dynamic
+ * variables such as .TARGET, .IMPSRC. It is not intended to
+ * expand the other variables as well; see deptgt-end.mk. */
+ (void)Var_Subst(cmd, job->node, VARE_WANTRES, &expanded_cmd);
+ /* TODO: handle errors */
+ Lst_Append(Targ_GetEndNode()->commands, expanded_cmd);
+ }
+}
-/*-
- *-----------------------------------------------------------------------
- * JobClose --
- * Called to close both input and output pipes when a job is finished.
- *
- * Results:
- * Nada
- *
- * Side Effects:
- * The file descriptors associated with the job are closed.
- *
- *-----------------------------------------------------------------------
- */
+
+/* Called to close both input and output pipes when a job is finished. */
static void
JobClose(Job *job)
{
@@ -969,9 +969,6 @@ JobClose(Job *job)
* job job to finish
* status sub-why job went away
*
- * Results:
- * None
- *
* Side Effects:
* Final commands for the job are placed on postCommands.
*
@@ -981,16 +978,13 @@ JobClose(Job *job)
* to ABORT_ERROR so no more jobs will be started.
*-----------------------------------------------------------------------
*/
-/*ARGSUSED*/
static void
JobFinish (Job *job, WAIT_T status)
{
- Boolean done, return_job_token;
+ Boolean done, return_job_token;
- if (DEBUG(JOB)) {
- fprintf(debug_file, "Jobfinish: %d [%s], status %d\n",
- job->pid, job->node->name, status);
- }
+ DEBUG3(JOB, "JobFinish: %d [%s], status %d\n",
+ job->pid, job->node->name, status);
if ((WIFEXITED(status) &&
(((WEXITSTATUS(status) != 0) && !(job->flags & JOB_IGNERR)))) ||
@@ -1035,10 +1029,8 @@ JobFinish (Job *job, WAIT_T status)
if (done) {
if (WIFEXITED(status)) {
- if (DEBUG(JOB)) {
- (void)fprintf(debug_file, "Process %d [%s] exited.\n",
- job->pid, job->node->name);
- }
+ DEBUG2(JOB, "Process %d [%s] exited.\n",
+ job->pid, job->node->name);
if (WEXITSTATUS(status) != 0) {
if (job->node != lastNode) {
MESSAGE(stdout, job->node);
@@ -1109,28 +1101,23 @@ JobFinish (Job *job, WAIT_T status)
/*
* As long as we aren't aborting and the job didn't return a non-zero
* status that we shouldn't ignore, we call Make_Update to update
- * the parents. In addition, any saved commands for the node are placed
- * on the .END target.
+ * the parents.
*/
- if (job->tailCmds != NULL) {
- Lst_ForEachFrom(job->node->commands, job->tailCmds,
- JobSaveCommand,
- job->node);
- }
+ JobSaveCommands(job);
job->node->made = MADE;
if (!(job->flags & JOB_SPECIAL))
return_job_token = TRUE;
Make_Update(job->node);
job->job_state = JOB_ST_FREE;
} else if (WAIT_STATUS(status)) {
- errors += 1;
+ errors++;
job->job_state = JOB_ST_FREE;
}
/*
* Set aborting if any error.
*/
- if (errors && !keepgoing && (aborting != ABORT_INTERRUPT)) {
+ if (errors && !opts.keepgoing && (aborting != ABORT_INTERRUPT)) {
/*
* If we found any errors in this batch of children and the -k flag
* wasn't given, we set the aborting flag so no more jobs get
@@ -1150,28 +1137,14 @@ JobFinish (Job *job, WAIT_T status)
}
}
-/*-
- *-----------------------------------------------------------------------
- * Job_Touch --
- * Touch the given target. Called by JobStart when the -t flag was
- * given
+/* Touch the given target. Called by JobStart when the -t flag was given.
*
- * Input:
- * gn the node of the file to touch
- * silent TRUE if should not print message
- *
- * Results:
- * None
- *
- * Side Effects:
- * The data modification of the file is changed. In addition, if the
- * file did not exist, it is created.
- *-----------------------------------------------------------------------
- */
+ * The modification date of the file is changed.
+ * If the file did not exist, it is created. */
void
Job_Touch(GNode *gn, Boolean silent)
{
- int streamID; /* ID of stream opened to do the touch */
+ int streamID; /* ID of stream opened to do the touch */
struct utimbuf times; /* Times for utime() call */
if (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC|OP_OPTIONAL|
@@ -1183,12 +1156,12 @@ Job_Touch(GNode *gn, Boolean silent)
return;
}
- if (!silent || NoExecute(gn)) {
+ if (!silent || !GNode_ShouldExecute(gn)) {
(void)fprintf(stdout, "touch %s\n", gn->name);
(void)fflush(stdout);
}
- if (NoExecute(gn)) {
+ if (!GNode_ShouldExecute(gn)) {
return;
}
@@ -1197,7 +1170,7 @@ Job_Touch(GNode *gn, Boolean silent)
} else if (gn->type & OP_LIB) {
Arch_TouchLib(gn);
} else {
- char *file = gn->path ? gn->path : gn->name;
+ const char *file = GNode_Path(gn);
times.actime = times.modtime = now;
if (utime(file, &times) < 0){
@@ -1226,10 +1199,10 @@ Job_Touch(GNode *gn, Boolean silent)
}
}
-/*-
- *-----------------------------------------------------------------------
- * Job_CheckCommands --
- * Make sure the given node has all the commands it needs.
+/* Make sure the given node has all the commands it needs.
+ *
+ * The node will have commands from the .DEFAULT rule added to it if it
+ * needs them.
*
* Input:
* gn The target whose commands need verifying
@@ -1237,106 +1210,96 @@ Job_Touch(GNode *gn, Boolean silent)
*
* Results:
* TRUE if the commands list is/was ok.
- *
- * Side Effects:
- * The node will have commands from the .DEFAULT rule added to it
- * if it needs them.
- *-----------------------------------------------------------------------
*/
Boolean
Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
{
- if (OP_NOP(gn->type) && Lst_IsEmpty(gn->commands) &&
- ((gn->type & OP_LIB) == 0 || Lst_IsEmpty(gn->children))) {
+ if (GNode_IsTarget(gn))
+ return TRUE;
+ if (!Lst_IsEmpty(gn->commands))
+ return TRUE;
+ if ((gn->type & OP_LIB) && !Lst_IsEmpty(gn->children))
+ return TRUE;
+
+ /*
+ * No commands. Look for .DEFAULT rule from which we might infer
+ * commands
+ */
+ if ((DEFAULT != NULL) && !Lst_IsEmpty(DEFAULT->commands) &&
+ (gn->type & OP_SPECIAL) == 0) {
/*
- * No commands. Look for .DEFAULT rule from which we might infer
- * commands
+ * Make only looks for a .DEFAULT if the node was never the
+ * target of an operator, so that's what we do too. If
+ * a .DEFAULT was given, we substitute its commands for gn's
+ * commands and set the IMPSRC variable to be the target's name
+ * The DEFAULT node acts like a transformation rule, in that
+ * gn also inherits any attributes or sources attached to
+ * .DEFAULT itself.
*/
- if ((DEFAULT != NULL) && !Lst_IsEmpty(DEFAULT->commands) &&
- (gn->type & OP_SPECIAL) == 0) {
- char *p1;
- /*
- * Make only looks for a .DEFAULT if the node was never the
- * target of an operator, so that's what we do too. If
- * a .DEFAULT was given, we substitute its commands for gn's
- * commands and set the IMPSRC variable to be the target's name
- * The DEFAULT node acts like a transformation rule, in that
- * gn also inherits any attributes or sources attached to
- * .DEFAULT itself.
- */
- Make_HandleUse(DEFAULT, gn);
- Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), gn);
- bmake_free(p1);
- } else if (Dir_MTime(gn, 0) == 0 && (gn->type & OP_SPECIAL) == 0) {
- /*
- * The node wasn't the target of an operator we have no .DEFAULT
- * rule to go on and the target doesn't already exist. There's
- * nothing more we can do for this branch. If the -k flag wasn't
- * given, we stop in our tracks, otherwise we just don't update
- * this node's parents so they never get examined.
- */
- static const char msg[] = ": don't know how to make";
-
- if (gn->flags & FROM_DEPEND) {
- if (!Job_RunTarget(".STALE", gn->fname))
- fprintf(stdout, "%s: %s, %d: ignoring stale %s for %s\n",
- progname, gn->fname, gn->lineno, makeDependfile,
- gn->name);
- return TRUE;
- }
+ Make_HandleUse(DEFAULT, gn);
+ Var_Set(IMPSRC, GNode_VarTarget(gn), gn);
+ return TRUE;
+ }
- if (gn->type & OP_OPTIONAL) {
- (void)fprintf(stdout, "%s%s %s (ignored)\n", progname,
- msg, gn->name);
- (void)fflush(stdout);
- } else if (keepgoing) {
- (void)fprintf(stdout, "%s%s %s (continuing)\n", progname,
- msg, gn->name);
- (void)fflush(stdout);
- return FALSE;
- } else {
- (*abortProc)("%s%s %s. Stop", progname, msg, gn->name);
- return FALSE;
- }
- }
+ if (Dir_MTime(gn, 0) != 0 || (gn->type & OP_SPECIAL))
+ return TRUE;
+
+ /*
+ * The node wasn't the target of an operator. We have no .DEFAULT
+ * rule to go on and the target doesn't already exist. There's
+ * nothing more we can do for this branch. If the -k flag wasn't
+ * given, we stop in our tracks, otherwise we just don't update
+ * this node's parents so they never get examined.
+ */
+
+ if (gn->flags & FROM_DEPEND) {
+ if (!Job_RunTarget(".STALE", gn->fname))
+ fprintf(stdout, "%s: %s, %d: ignoring stale %s for %s\n",
+ progname, gn->fname, gn->lineno, makeDependfile,
+ gn->name);
+ return TRUE;
}
- return TRUE;
+
+ if (gn->type & OP_OPTIONAL) {
+ (void)fprintf(stdout, "%s: don't know how to make %s (%s)\n",
+ progname, gn->name, "ignored");
+ (void)fflush(stdout);
+ return TRUE;
+ }
+
+ if (opts.keepgoing) {
+ (void)fprintf(stdout, "%s: don't know how to make %s (%s)\n",
+ progname, gn->name, "continuing");
+ (void)fflush(stdout);
+ return FALSE;
+ }
+
+ abortProc("%s: don't know how to make %s. Stop", progname, gn->name);
+ return FALSE;
}
-/*-
- *-----------------------------------------------------------------------
- * JobExec --
- * Execute the shell for the given job. Called from JobStart
+/* Execute the shell for the given job.
*
- * Input:
- * job Job to execute
- *
- * Results:
- * None.
- *
- * Side Effects:
- * A shell is executed, outputs is altered and the Job structure added
- * to the job table.
- *
- *-----------------------------------------------------------------------
+ * A shell is executed, its output is altered and the Job structure added
+ * to the job table.
*/
static void
JobExec(Job *job, char **argv)
{
- int cpid; /* ID of new child */
+ int cpid; /* ID of new child */
sigset_t mask;
job->flags &= ~JOB_TRACED;
if (DEBUG(JOB)) {
- int i;
+ int i;
- (void)fprintf(debug_file, "Running %s %sly\n", job->node->name, "local");
- (void)fprintf(debug_file, "\tCommand: ");
+ debug_printf("Running %s %sly\n", job->node->name, "local");
+ debug_printf("\tCommand: ");
for (i = 0; argv[i] != NULL; i++) {
- (void)fprintf(debug_file, "%s ", argv[i]);
+ debug_printf("%s ", argv[i]);
}
- (void)fprintf(debug_file, "\n");
+ debug_printf("\n");
}
/*
@@ -1384,55 +1347,40 @@ JobExec(Job *job, char **argv)
* reset it to the beginning (again). Since the stream was marked
* close-on-exec, we must clear that bit in the new input.
*/
- if (dup2(FILENO(job->cmdFILE), 0) == -1) {
- execError("dup2", "job->cmdFILE");
- _exit(1);
- }
- if (fcntl(0, F_SETFD, 0) == -1) {
- execError("fcntl clear close-on-exec", "stdin");
- _exit(1);
- }
- if (lseek(0, (off_t)0, SEEK_SET) == -1) {
- execError("lseek to 0", "stdin");
- _exit(1);
- }
+ if (dup2(fileno(job->cmdFILE), 0) == -1)
+ execDie("dup2", "job->cmdFILE");
+ if (fcntl(0, F_SETFD, 0) == -1)
+ execDie("fcntl clear close-on-exec", "stdin");
+ if (lseek(0, (off_t)0, SEEK_SET) == -1)
+ execDie("lseek to 0", "stdin");
if (job->node->type & (OP_MAKE | OP_SUBMAKE)) {
/*
* Pass job token pipe to submakes.
*/
- if (fcntl(tokenWaitJob.inPipe, F_SETFD, 0) == -1) {
- execError("clear close-on-exec", "tokenWaitJob.inPipe");
- _exit(1);
- }
- if (fcntl(tokenWaitJob.outPipe, F_SETFD, 0) == -1) {
- execError("clear close-on-exec", "tokenWaitJob.outPipe");
- _exit(1);
- }
+ if (fcntl(tokenWaitJob.inPipe, F_SETFD, 0) == -1)
+ execDie("clear close-on-exec", "tokenWaitJob.inPipe");
+ if (fcntl(tokenWaitJob.outPipe, F_SETFD, 0) == -1)
+ execDie("clear close-on-exec", "tokenWaitJob.outPipe");
}
/*
* Set up the child's output to be routed through the pipe
* we've created for it.
*/
- if (dup2(job->outPipe, 1) == -1) {
- execError("dup2", "job->outPipe");
- _exit(1);
- }
+ if (dup2(job->outPipe, 1) == -1)
+ execDie("dup2", "job->outPipe");
+
/*
* The output channels are marked close on exec. This bit was
* duplicated by the dup2(on some systems), so we have to clear
* it before routing the shell's error output to the same place as
* its standard output.
*/
- if (fcntl(1, F_SETFD, 0) == -1) {
- execError("clear close-on-exec", "stdout");
- _exit(1);
- }
- if (dup2(1, 2) == -1) {
- execError("dup2", "1, 2");
- _exit(1);
- }
+ if (fcntl(1, F_SETFD, 0) == -1)
+ execDie("clear close-on-exec", "stdout");
+ if (dup2(1, 2) == -1)
+ execDie("dup2", "1, 2");
/*
* We want to switch the child into a different process family so
@@ -1453,8 +1401,7 @@ JobExec(Job *job, char **argv)
Var_ExportVars();
(void)execv(shellPath, argv);
- execError("exec", shellPath);
- _exit(1);
+ execDie("exec", shellPath);
}
/* Parent, continuing after the child exec */
@@ -1485,36 +1432,25 @@ JobExec(Job *job, char **argv)
* Now the job is actually running, add it to the table.
*/
if (DEBUG(JOB)) {
- fprintf(debug_file, "JobExec(%s): pid %d added to jobs table\n",
- job->node->name, job->pid);
+ debug_printf("JobExec(%s): pid %d added to jobs table\n",
+ job->node->name, job->pid);
job_table_dump("job started");
}
JobSigUnlock(&mask);
}
-/*-
- *-----------------------------------------------------------------------
- * JobMakeArgv --
- * Create the argv needed to execute the shell for a given job.
- *
- *
- * Results:
- *
- * Side Effects:
- *
- *-----------------------------------------------------------------------
- */
+/* Create the argv needed to execute the shell for a given job. */
static void
JobMakeArgv(Job *job, char **argv)
{
- int argc;
- static char args[10]; /* For merged arguments */
+ int argc;
+ static char args[10]; /* For merged arguments */
argv[0] = UNCONST(shellName);
argc = 1;
- if ((commandShell->exit && (*commandShell->exit != '-')) ||
- (commandShell->echo && (*commandShell->echo != '-')))
+ if ((commandShell->exit && commandShell->exit[0] != '-') ||
+ (commandShell->echo && commandShell->echo[0] != '-'))
{
/*
* At least one of the flags doesn't have a minus before it, so
@@ -1571,14 +1507,14 @@ JobMakeArgv(Job *job, char **argv)
* Also the return value is ignored by everyone.
*-----------------------------------------------------------------------
*/
-static int
+static JobStartResult
JobStart(GNode *gn, int flags)
{
- Job *job; /* new job descriptor */
- char *argv[10]; /* Argument vector to shell */
- Boolean cmdsOK; /* true if the nodes commands were all right */
- Boolean noExec; /* Set true if we decide not to run the job */
- int tfd; /* File descriptor to the temp file */
+ Job *job; /* new job descriptor */
+ char *argv[10]; /* Argument vector to shell */
+ Boolean cmdsOK; /* true if the nodes commands were all right */
+ Boolean noExec; /* Set true if we decide not to run the job */
+ int tfd; /* File descriptor to the temp file */
for (job = job_table; job < job_table_end; job++) {
if (job->job_state == JOB_ST_FREE)
@@ -1622,8 +1558,8 @@ JobStart(GNode *gn, int flags)
* need to reopen it to feed it to the shell. If the -n flag *was* given,
* we just set the file to be stdout. Cute, huh?
*/
- if (((gn->type & OP_MAKE) && !(noRecursiveExecute)) ||
- (!noExecute && !touchFlag)) {
+ if (((gn->type & OP_MAKE) && !opts.noRecursiveExecute) ||
+ (!opts.noExecute && !opts.touchFlag)) {
/*
* tfile is the name of a file into which all shell commands are
* put. It is removed before the child shell is executed, unless
@@ -1650,7 +1586,7 @@ JobStart(GNode *gn, int flags)
if (job->cmdFILE == NULL) {
Punt("Could not fdopen %s", tfile);
}
- (void)fcntl(FILENO(job->cmdFILE), F_SETFD, FD_CLOEXEC);
+ (void)fcntl(fileno(job->cmdFILE), F_SETFD, FD_CLOEXEC);
/*
* Send the commands to the command file, flush all its buffers then
* rewind and remove the thing.
@@ -1669,7 +1605,7 @@ JobStart(GNode *gn, int flags)
* We can do all the commands at once. hooray for sanity
*/
numCommands = 0;
- Lst_ForEach(gn->commands, JobPrintCommand, job);
+ JobPrintCommands(job);
/*
* If we didn't print out any commands to the shell script,
@@ -1680,7 +1616,7 @@ JobStart(GNode *gn, int flags)
}
free(tfile);
- } else if (NoExecute(gn)) {
+ } else if (!GNode_ShouldExecute(gn)) {
/*
* Not executing anything -- just print all the commands to stdout
* in one fell swoop. This will still set up job->tailCmds correctly.
@@ -1695,9 +1631,8 @@ JobStart(GNode *gn, int flags)
* not -- just let the user know they're bad and keep going. It
* doesn't do any harm in this case and may do some good.
*/
- if (cmdsOK) {
- Lst_ForEach(gn->commands, JobPrintCommand, job);
- }
+ if (cmdsOK)
+ JobPrintCommands(job);
/*
* Don't execute the shell, thank you.
*/
@@ -1736,12 +1671,8 @@ JobStart(GNode *gn, int flags)
* We only want to work our way up the graph if we aren't here because
* the commands for the job were no good.
*/
- if (cmdsOK && aborting == 0) {
- if (job->tailCmds != NULL) {
- Lst_ForEachFrom(job->node->commands, job->tailCmds,
- JobSaveCommand,
- job->node);
- }
+ if (cmdsOK && aborting == ABORT_NONE) {
+ JobSaveCommands(job);
job->node->made = MADE;
Make_Update(job->node);
}
@@ -1763,19 +1694,14 @@ JobStart(GNode *gn, int flags)
}
static char *
-JobOutput(Job *job, char *cp, char *endp, int msg)
+JobOutput(Job *job, char *cp, char *endp)
{
char *ecp;
- if (commandShell->noPrint) {
- ecp = Str_FindSubstring(cp, commandShell->noPrint);
- while (ecp != NULL) {
+ if (commandShell->noPrint && commandShell->noPrint[0] != '\0') {
+ while ((ecp = strstr(cp, commandShell->noPrint)) != NULL) {
if (cp != ecp) {
*ecp = '\0';
- if (!beSilent && msg && job->node != lastNode) {
- MESSAGE(stdout, job->node);
- lastNode = job->node;
- }
/*
* The only way there wouldn't be a newline after
* this line is if it were the last in the buffer.
@@ -1785,7 +1711,7 @@ JobOutput(Job *job, char *cp, char *endp, int msg)
(void)fprintf(stdout, "%s", cp);
(void)fflush(stdout);
}
- cp = ecp + commandShell->noPLen;
+ cp = ecp + commandShell->noPrintLen;
if (cp != endp) {
/*
* Still more to print, look again after skipping
@@ -1796,7 +1722,6 @@ JobOutput(Job *job, char *cp, char *endp, int msg)
while (*cp == ' ' || *cp == '\t' || *cp == '\n') {
cp++;
}
- ecp = Str_FindSubstring(cp, commandShell->noPrint);
} else {
return cp;
}
@@ -1816,7 +1741,7 @@ JobOutput(Job *job, char *cp, char *endp, int msg)
* this makes up a line, we print it tagged by the job's identifier,
* as necessary.
* If output has been collected in a temporary file, we open the
- * file and read it line by line, transfering it to our own
+ * file and read it line by line, transferring it to our own
* output channel until the file is empty. At which point we
* remove the temporary file.
* In both cases, however, we keep our figurative eye out for the
@@ -1830,22 +1755,19 @@ JobOutput(Job *job, char *cp, char *endp, int msg)
* finish TRUE if this is the last time we'll be called
* for this job
*
- * Results:
- * None
- *
* Side Effects:
* curPos may be shifted as may the contents of outBuf.
*-----------------------------------------------------------------------
*/
-STATIC void
+static void
JobDoOutput(Job *job, Boolean finish)
{
- Boolean gotNL = FALSE; /* true if got a newline */
- Boolean fbuf; /* true if our buffer filled up */
- int nr; /* number of bytes read */
- int i; /* auxiliary index into outBuf */
- int max; /* limit for i (end of current data) */
- int nRead; /* (Temporary) number of bytes read */
+ Boolean gotNL = FALSE; /* true if got a newline */
+ Boolean fbuf; /* true if our buffer filled up */
+ size_t nr; /* number of bytes read */
+ size_t i; /* auxiliary index into outBuf */
+ size_t max; /* limit for i (end of current data) */
+ ssize_t nRead; /* (Temporary) number of bytes read */
/*
* Read as many bytes as will fit in the buffer.
@@ -1864,7 +1786,7 @@ end_loop:
}
nr = 0;
} else {
- nr = nRead;
+ nr = (size_t)nRead;
}
/*
@@ -1887,7 +1809,7 @@ end_loop:
* TRUE.
*/
max = job->curPos + nr;
- for (i = job->curPos + nr - 1; i >= job->curPos; i--) {
+ for (i = job->curPos + nr - 1; i >= job->curPos && i != (size_t)-1; i--) {
if (job->outBuf[i] == '\n') {
gotNL = TRUE;
break;
@@ -1925,7 +1847,7 @@ end_loop:
if (i >= job->curPos) {
char *cp;
- cp = JobOutput(job, job->outBuf, &job->outBuf[i], FALSE);
+ cp = JobOutput(job, job->outBuf, &job->outBuf[i]);
/*
* There's still more in that thar buffer. This time, though,
@@ -1933,7 +1855,7 @@ end_loop:
* our own free will.
*/
if (*cp != '\0') {
- if (!beSilent && job->node != lastNode) {
+ if (!opts.beSilent && job->node != lastNode) {
MESSAGE(stdout, job->node);
lastNode = job->node;
}
@@ -1975,13 +1897,16 @@ end_loop:
static void
JobRun(GNode *targ)
{
-#ifdef notyet
+#if 0
/*
- * Unfortunately it is too complicated to run .BEGIN, .END,
- * and .INTERRUPT job in the parallel job module. This has
- * the nice side effect that it avoids a lot of other problems.
+ * Unfortunately it is too complicated to run .BEGIN, .END, and
+ * .INTERRUPT job in the parallel job module. As of 2020-09-25,
+ * unit-tests/deptgt-end-jobs.mk hangs in an endless loop.
+ *
+ * Running these jobs in compat mode also guarantees that these
+ * jobs do not overlap with other unrelated jobs.
*/
- Lst lst = Lst_Init();
+ List *lst = Lst_New();
Lst_Append(lst, targ);
(void)Make_Run(lst);
Lst_Destroy(lst, NULL);
@@ -1998,33 +1923,20 @@ JobRun(GNode *targ)
#endif
}
-/*-
- *-----------------------------------------------------------------------
- * Job_CatchChildren --
- * Handle the exit of a child. Called from Make_Make.
- *
- * Input:
- * block TRUE if should block on the wait
+/* Handle the exit of a child. Called from Make_Make.
*
- * Results:
- * none.
- *
- * Side Effects:
- * The job descriptor is removed from the list of children.
+ * The job descriptor is removed from the list of children.
*
* Notes:
* We do waits, blocking or not, according to the wisdom of our
* caller, until there are no more children to report. For each
* job, call JobFinish to finish things off.
- *
- *-----------------------------------------------------------------------
*/
-
void
Job_CatchChildren(void)
{
- int pid; /* pid of dead child */
- WAIT_T status; /* Exit/termination status */
+ int pid; /* pid of dead child */
+ WAIT_T status; /* Exit/termination status */
/*
* Don't even bother if we know there's no one around.
@@ -2033,10 +1945,8 @@ Job_CatchChildren(void)
return;
while ((pid = waitpid((pid_t) -1, &status, WNOHANG | WUNTRACED)) > 0) {
- if (DEBUG(JOB)) {
- (void)fprintf(debug_file, "Process %d exited/stopped status %x.\n", pid,
- WAIT_STATUS(status));
- }
+ DEBUG2(JOB, "Process %d exited/stopped status %x.\n", pid,
+ WAIT_STATUS(status));
JobReapChild(pid, status, TRUE);
}
}
@@ -2048,7 +1958,7 @@ Job_CatchChildren(void)
void
JobReapChild(pid_t pid, WAIT_T status, Boolean isJobs)
{
- Job *job; /* job descriptor for dead child */
+ Job *job; /* job descriptor for dead child */
/*
* Don't even bother if we know there's no one around.
@@ -2065,10 +1975,7 @@ JobReapChild(pid_t pid, WAIT_T status, Boolean isJobs)
return; /* not ours */
}
if (WIFSTOPPED(status)) {
- if (DEBUG(JOB)) {
- (void)fprintf(debug_file, "Process %d (%s) stopped.\n",
- job->pid, job->node->name);
- }
+ DEBUG2(JOB, "Process %d (%s) stopped.\n", job->pid, job->node->name);
if (!make_suspended) {
switch (WSTOPSIG(status)) {
case SIGTSTP:
@@ -2093,28 +2000,16 @@ JobReapChild(pid_t pid, WAIT_T status, Boolean isJobs)
JobFinish(job, status);
}
-/*-
- *-----------------------------------------------------------------------
- * Job_CatchOutput --
- * Catch the output from our children, if we're using
- * pipes do so. Otherwise just block time until we get a
- * signal(most likely a SIGCHLD) since there's no point in
- * just spinning when there's nothing to do and the reaping
- * of a child can wait for a while.
- *
- * Results:
- * None
- *
- * Side Effects:
- * Output is read from pipes if we're piping.
- * -----------------------------------------------------------------------
- */
+/* Catch the output from our children, if we're using pipes do so. Otherwise
+ * just block time until we get a signal(most likely a SIGCHLD) since there's
+ * no point in just spinning when there's nothing to do and the reaping of a
+ * child can wait for a while. */
void
Job_CatchOutput(void)
{
int nready;
Job *job;
- int i;
+ unsigned int i;
(void)fflush(stdout);
@@ -2148,9 +2043,9 @@ Job_CatchOutput(void)
Job_CatchChildren();
if (nready == 0)
- return;
+ return;
- for (i = npseudojobs*nfds_per_job(); i < nfds; i++) {
+ for (i = npseudojobs * nfds_per_job(); i < nfds; i++) {
if (!fds[i].revents)
continue;
job = jobfds[i];
@@ -2169,24 +2064,12 @@ Job_CatchOutput(void)
}
#endif
if (--nready == 0)
- return;
+ return;
}
}
-/*-
- *-----------------------------------------------------------------------
- * Job_Make --
- * Start the creation of a target. Basically a front-end for
- * JobStart used by the Make module.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Another job is started.
- *
- *-----------------------------------------------------------------------
- */
+/* Start the creation of a target. Basically a front-end for JobStart used by
+ * the Make module. */
void
Job_Make(GNode *gn)
{
@@ -2211,21 +2094,21 @@ Shell_Init(void)
#endif
shellPath = str_concat3(_PATH_DEFSHELLDIR, "/", shellName);
}
- Var_Set_with_flags(".SHELL", shellPath, VAR_CMD, VAR_SET_READONLY);
+ Var_Set_with_flags(".SHELL", shellPath, VAR_CMDLINE, VAR_SET_READONLY);
if (commandShell->exit == NULL) {
commandShell->exit = "";
}
if (commandShell->echo == NULL) {
commandShell->echo = "";
}
- if (commandShell->hasErrCtl && *commandShell->exit) {
+ if (commandShell->hasErrCtl && commandShell->exit[0] != '\0') {
if (shellErrFlag &&
strcmp(commandShell->exit, &shellErrFlag[1]) != 0) {
free(shellErrFlag);
shellErrFlag = NULL;
}
if (!shellErrFlag) {
- int n = strlen(commandShell->exit) + 2;
+ size_t n = strlen(commandShell->exit) + 2;
shellErrFlag = bmake_malloc(n);
if (shellErrFlag) {
@@ -2238,10 +2121,8 @@ Shell_Init(void)
}
}
-/*-
- * Returns the string literal that is used in the current command shell
- * to produce a newline character.
- */
+/* Return the string literal that is used in the current command shell
+ * to produce a newline character. */
const char *
Shell_GetNewline(void)
{
@@ -2257,8 +2138,9 @@ Job_SetPrefix(void)
Var_Set(MAKE_JOB_PREFIX, "---", VAR_GLOBAL);
}
- targPrefix = Var_Subst("${" MAKE_JOB_PREFIX "}",
- VAR_GLOBAL, VARE_WANTRES);
+ (void)Var_Subst("${" MAKE_JOB_PREFIX "}",
+ VAR_GLOBAL, VARE_WANTRES, &targPrefix);
+ /* TODO: handle errors */
}
/* Initialize the process module. */
@@ -2267,15 +2149,15 @@ Job_Init(void)
{
Job_SetPrefix();
/* Allocate space for all the job info */
- job_table = bmake_malloc(maxJobs * sizeof *job_table);
- memset(job_table, 0, maxJobs * sizeof *job_table);
- job_table_end = job_table + maxJobs;
+ job_table = bmake_malloc((size_t)opts.maxJobs * sizeof *job_table);
+ memset(job_table, 0, (size_t)opts.maxJobs * sizeof *job_table);
+ job_table_end = job_table + opts.maxJobs;
wantToken = 0;
- aborting = 0;
- errors = 0;
+ aborting = ABORT_NONE;
+ errors = 0;
- lastNode = NULL;
+ lastNode = NULL;
/*
* There is a non-zero chance that we already have children.
@@ -2300,9 +2182,9 @@ Job_Init(void)
/* Preallocate enough for the maximum number of jobs. */
fds = bmake_malloc(sizeof(*fds) *
- (npseudojobs + maxJobs) * nfds_per_job());
+ (npseudojobs + (size_t)opts.maxJobs) * nfds_per_job());
jobfds = bmake_malloc(sizeof(*jobfds) *
- (npseudojobs + maxJobs) * nfds_per_job());
+ (npseudojobs + (size_t)opts.maxJobs) * nfds_per_job());
/* These are permanent entries and take slots 0 and 1 */
watchfd(&tokenWaitJob);
@@ -2318,7 +2200,7 @@ Job_Init(void)
#define ADDSIG(s,h) \
if (bmake_signal(s, SIG_IGN) != SIG_IGN) { \
sigaddset(&caught_signals, s); \
- (void)bmake_signal(s, h); \
+ (void)bmake_signal(s, h); \
}
/*
@@ -2344,7 +2226,9 @@ Job_Init(void)
#undef ADDSIG
(void)Job_RunTarget(".BEGIN", NULL);
- postCommands = Targ_FindNode(".END", TARG_CREATE);
+ /* Create the .END node now, even though no code in the unit tests
+ * depends on it. See also Targ_GetEndNode in Compat_Run. */
+ (void)Targ_GetEndNode();
}
static void JobSigReset(void)
@@ -2369,11 +2253,12 @@ static void JobSigReset(void)
/* Find a shell in 'shells' given its name, or return NULL. */
static Shell *
-JobMatchShell(const char *name)
+FindShellByName(const char *name)
{
- Shell *sh;
+ Shell *sh = shells;
+ const Shell *shellsEnd = sh + sizeof shells / sizeof shells[0];
- for (sh = shells; sh->name != NULL; sh++) {
+ for (sh = shells; sh < shellsEnd; sh++) {
if (strcmp(name, sh->name) == 0)
return sh;
}
@@ -2406,24 +2291,24 @@ JobMatchShell(const char *name)
* provides the functionality it does in C. Each word consists of
* keyword and value separated by an equal sign. There should be no
* unnecessary spaces in the word. The keywords are as follows:
- * name Name of shell.
- * path Location of shell.
- * quiet Command to turn off echoing.
- * echo Command to turn echoing on
- * filter Result of turning off echoing that shouldn't be
- * printed.
- * echoFlag Flag to turn echoing on at the start
- * errFlag Flag to turn error checking on at the start
- * hasErrCtl True if shell has error checking control
- * newline String literal to represent a newline char
- * check Command to turn on error checking if hasErrCtl
- * is TRUE or template of command to echo a command
- * for which error checking is off if hasErrCtl is
- * FALSE.
- * ignore Command to turn off error checking if hasErrCtl
- * is TRUE or template of command to execute a
- * command so as to ignore any errors it returns if
- * hasErrCtl is FALSE.
+ * name Name of shell.
+ * path Location of shell.
+ * quiet Command to turn off echoing.
+ * echo Command to turn echoing on
+ * filter Result of turning off echoing that shouldn't be
+ * printed.
+ * echoFlag Flag to turn echoing on at the start
+ * errFlag Flag to turn error checking on at the start
+ * hasErrCtl True if shell has error checking control
+ * newline String literal to represent a newline char
+ * check Command to turn on error checking if hasErrCtl
+ * is TRUE or template of command to echo a command
+ * for which error checking is off if hasErrCtl is
+ * FALSE.
+ * ignore Command to turn off error checking if hasErrCtl
+ * is TRUE or template of command to execute a
+ * command so as to ignore any errors it returns if
+ * hasErrCtl is FALSE.
*
*-----------------------------------------------------------------------
*/
@@ -2439,9 +2324,7 @@ Job_ParseShell(char *line)
Boolean fullSpec = FALSE;
Shell *sh;
- while (isspace((unsigned char)*line)) {
- line++;
- }
+ pp_skip_whitespace(&line);
free(shellArgv);
@@ -2461,50 +2344,50 @@ Job_ParseShell(char *line)
shellArgv = path;
for (path = NULL, argv = words; argc != 0; argc--, argv++) {
- if (strncmp(*argv, "path=", 5) == 0) {
- path = &argv[0][5];
- } else if (strncmp(*argv, "name=", 5) == 0) {
- newShell.name = &argv[0][5];
+ char *arg = *argv;
+ if (strncmp(arg, "path=", 5) == 0) {
+ path = arg + 5;
+ } else if (strncmp(arg, "name=", 5) == 0) {
+ newShell.name = arg + 5;
+ } else {
+ if (strncmp(arg, "quiet=", 6) == 0) {
+ newShell.echoOff = arg + 6;
+ } else if (strncmp(arg, "echo=", 5) == 0) {
+ newShell.echoOn = arg + 5;
+ } else if (strncmp(arg, "filter=", 7) == 0) {
+ newShell.noPrint = arg + 7;
+ newShell.noPrintLen = strlen(newShell.noPrint);
+ } else if (strncmp(arg, "echoFlag=", 9) == 0) {
+ newShell.echo = arg + 9;
+ } else if (strncmp(arg, "errFlag=", 8) == 0) {
+ newShell.exit = arg + 8;
+ } else if (strncmp(arg, "hasErrCtl=", 10) == 0) {
+ char c = arg[10];
+ newShell.hasErrCtl = c == 'Y' || c == 'y' ||
+ c == 'T' || c == 't';
+ } else if (strncmp(arg, "newline=", 8) == 0) {
+ newShell.newline = arg + 8;
+ } else if (strncmp(arg, "check=", 6) == 0) {
+ newShell.errOnOrEcho = arg + 6;
+ } else if (strncmp(arg, "ignore=", 7) == 0) {
+ newShell.errOffOrExecIgnore = arg + 7;
+ } else if (strncmp(arg, "errout=", 7) == 0) {
+ newShell.errExit = arg + 7;
+ } else if (strncmp(arg, "comment=", 8) == 0) {
+ newShell.commentChar = arg[8];
} else {
- if (strncmp(*argv, "quiet=", 6) == 0) {
- newShell.echoOff = &argv[0][6];
- } else if (strncmp(*argv, "echo=", 5) == 0) {
- newShell.echoOn = &argv[0][5];
- } else if (strncmp(*argv, "filter=", 7) == 0) {
- newShell.noPrint = &argv[0][7];
- newShell.noPLen = strlen(newShell.noPrint);
- } else if (strncmp(*argv, "echoFlag=", 9) == 0) {
- newShell.echo = &argv[0][9];
- } else if (strncmp(*argv, "errFlag=", 8) == 0) {
- newShell.exit = &argv[0][8];
- } else if (strncmp(*argv, "hasErrCtl=", 10) == 0) {
- char c = argv[0][10];
- newShell.hasErrCtl = !((c != 'Y') && (c != 'y') &&
- (c != 'T') && (c != 't'));
- } else if (strncmp(*argv, "newline=", 8) == 0) {
- newShell.newline = &argv[0][8];
- } else if (strncmp(*argv, "check=", 6) == 0) {
- newShell.errCheck = &argv[0][6];
- } else if (strncmp(*argv, "ignore=", 7) == 0) {
- newShell.ignErr = &argv[0][7];
- } else if (strncmp(*argv, "errout=", 7) == 0) {
- newShell.errOut = &argv[0][7];
- } else if (strncmp(*argv, "comment=", 8) == 0) {
- newShell.commentChar = argv[0][8];
- } else {
- Parse_Error(PARSE_FATAL, "Unknown keyword \"%s\"",
- *argv);
- free(words);
- return FALSE;
- }
- fullSpec = TRUE;
+ Parse_Error(PARSE_FATAL, "Unknown keyword \"%s\"", arg);
+ free(words);
+ return FALSE;
}
+ fullSpec = TRUE;
+ }
}
if (path == NULL) {
/*
* If no path was given, the user wants one of the pre-defined shells,
- * yes? So we find the one s/he wants with the help of JobMatchShell
+ * yes? So we find the one s/he wants with the help of FindShellByName
* and set things up the right way. shellPath will be set up by
* Shell_Init.
*/
@@ -2513,7 +2396,7 @@ Job_ParseShell(char *line)
free(words);
return FALSE;
} else {
- if ((sh = JobMatchShell(newShell.name)) == NULL) {
+ if ((sh = FindShellByName(newShell.name)) == NULL) {
Parse_Error(PARSE_WARNING, "%s: No matching shell",
newShell.name);
free(words);
@@ -2541,7 +2424,7 @@ Job_ParseShell(char *line)
if (path == NULL) {
path = UNCONST(shellPath);
} else {
- path += 1;
+ path++;
}
if (newShell.name != NULL) {
shellName = newShell.name;
@@ -2549,7 +2432,7 @@ Job_ParseShell(char *line)
shellName = path;
}
if (!fullSpec) {
- if ((sh = JobMatchShell(shellName)) == NULL) {
+ if ((sh = FindShellByName(shellName)) == NULL) {
Parse_Error(PARSE_WARNING, "%s: No matching shell",
shellName);
free(words);
@@ -2569,11 +2452,11 @@ Job_ParseShell(char *line)
}
if (!commandShell->hasErrCtl) {
- if (commandShell->errCheck == NULL) {
- commandShell->errCheck = "";
+ if (commandShell->errOnOrEcho == NULL) {
+ commandShell->errOnOrEcho = "";
}
- if (commandShell->ignErr == NULL) {
- commandShell->ignErr = "%s\n";
+ if (commandShell->errOffOrExecIgnore == NULL) {
+ commandShell->errOffOrExecIgnore = "%s\n";
}
}
@@ -2585,23 +2468,15 @@ Job_ParseShell(char *line)
return TRUE;
}
-/*-
- *-----------------------------------------------------------------------
- * JobInterrupt --
- * Handle the receipt of an interrupt.
+/* Handle the receipt of an interrupt.
+ *
+ * All children are killed. Another job will be started if the .INTERRUPT
+ * target is defined.
*
* Input:
* runINTERRUPT Non-zero if commands for the .INTERRUPT target
* should be executed
* signo signal received
- *
- * Results:
- * None
- *
- * Side Effects:
- * All children are killed. Another job will be started if the
- * .INTERRUPT target was given.
- *-----------------------------------------------------------------------
*/
static void
JobInterrupt(int runINTERRUPT, int signo)
@@ -2623,21 +2498,18 @@ JobInterrupt(int runINTERRUPT, int signo)
JobDeleteTarget(gn);
if (job->pid) {
- if (DEBUG(JOB)) {
- (void)fprintf(debug_file,
- "JobInterrupt passing signal %d to child %d.\n",
- signo, job->pid);
- }
+ DEBUG2(JOB, "JobInterrupt passing signal %d to child %d.\n",
+ signo, job->pid);
KILLPG(job->pid, signo);
}
}
JobSigUnlock(&mask);
- if (runINTERRUPT && !touchFlag) {
- interrupt = Targ_FindNode(".INTERRUPT", TARG_NOCREATE);
+ if (runINTERRUPT && !opts.touchFlag) {
+ interrupt = Targ_FindNode(".INTERRUPT");
if (interrupt != NULL) {
- ignoreErrors = FALSE;
+ opts.ignoreErrors = FALSE;
JobRun(interrupt);
}
}
@@ -2645,46 +2517,24 @@ JobInterrupt(int runINTERRUPT, int signo)
exit(signo);
}
-/*
- *-----------------------------------------------------------------------
- * Job_Finish --
- * Do final processing such as the running of the commands
- * attached to the .END target.
- *
- * Results:
- * Number of errors reported.
+/* Do the final processing, i.e. run the commands attached to the .END target.
*
- * Side Effects:
- * None.
- *-----------------------------------------------------------------------
- */
+ * Return the number of errors reported. */
int
Job_Finish(void)
{
- if (postCommands != NULL &&
- (!Lst_IsEmpty(postCommands->commands) ||
- !Lst_IsEmpty(postCommands->children))) {
+ GNode *endNode = Targ_GetEndNode();
+ if (!Lst_IsEmpty(endNode->commands) || !Lst_IsEmpty(endNode->children)) {
if (errors) {
Error("Errors reported so .END ignored");
} else {
- JobRun(postCommands);
+ JobRun(endNode);
}
}
return errors;
}
-/*-
- *-----------------------------------------------------------------------
- * Job_End --
- * Cleanup any memory used by the jobs module
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Memory is freed
- *-----------------------------------------------------------------------
- */
+/* Clean up any memory used by the jobs module. */
void
Job_End(void)
{
@@ -2693,20 +2543,8 @@ Job_End(void)
#endif
}
-/*-
- *-----------------------------------------------------------------------
- * Job_Wait --
- * Waits for all running jobs to finish and returns. Sets 'aborting'
- * to ABORT_WAIT to prevent other jobs from starting.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Currently running jobs finish.
- *
- *-----------------------------------------------------------------------
- */
+/* Waits for all running jobs to finish and returns.
+ * Sets 'aborting' to ABORT_WAIT to prevent other jobs from starting. */
void
Job_Wait(void)
{
@@ -2714,23 +2552,14 @@ Job_Wait(void)
while (jobTokensRunning != 0) {
Job_CatchOutput();
}
- aborting = 0;
+ aborting = ABORT_NONE;
}
-/*-
- *-----------------------------------------------------------------------
- * Job_AbortAll --
- * Abort all currently running jobs without handling output or anything.
- * This function is to be called only in the event of a major
- * error. Most definitely NOT to be called from JobInterrupt.
- *
- * Results:
- * None
+/* Abort all currently running jobs without handling output or anything.
+ * This function is to be called only in the event of a major error.
+ * Most definitely NOT to be called from JobInterrupt.
*
- * Side Effects:
- * All children are killed, not just the firstborn
- *-----------------------------------------------------------------------
- */
+ * All children are killed, not just the firstborn. */
void
Job_AbortAll(void)
{
@@ -2759,20 +2588,8 @@ Job_AbortAll(void)
continue;
}
-/*-
- *-----------------------------------------------------------------------
- * JobRestartJobs --
- * Tries to restart stopped jobs if there are slots available.
- * Called in process context in response to a SIGCONT.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Resumes jobs.
- *
- *-----------------------------------------------------------------------
- */
+/* Tries to restart stopped jobs if there are slots available.
+ * Called in process context in response to a SIGCONT. */
static void
JobRestartJobs(void)
{
@@ -2781,17 +2598,14 @@ JobRestartJobs(void)
for (job = job_table; job < job_table_end; job++) {
if (job->job_state == JOB_ST_RUNNING &&
(make_suspended || job->job_suspended)) {
- if (DEBUG(JOB)) {
- (void)fprintf(debug_file, "Restarting stopped job pid %d.\n",
- job->pid);
- }
+ DEBUG1(JOB, "Restarting stopped job pid %d.\n", job->pid);
if (job->job_suspended) {
(void)printf("*** [%s] Continued\n", job->node->name);
(void)fflush(stdout);
}
job->job_suspended = 0;
if (KILLPG(job->pid, SIGCONT) != 0 && DEBUG(JOB)) {
- fprintf(debug_file, "Failed to send SIGCONT to %d\n", job->pid);
+ debug_printf("Failed to send SIGCONT to %d\n", job->pid);
}
}
if (job->job_state == JOB_ST_FINISHED)
@@ -2825,10 +2639,10 @@ watchfd(Job *job)
static void
clearfd(Job *job)
{
- int i;
+ size_t i;
if (job->inPollfd == NULL)
Punt("Unwatching unwatched job");
- i = job->inPollfd - fds;
+ i = (size_t)(job->inPollfd - fds);
nfds--;
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
if (useMeta) {
@@ -2867,18 +2681,8 @@ readyfd(Job *job)
return (job->inPollfd->revents & POLLIN) != 0;
}
-/*-
- *-----------------------------------------------------------------------
- * JobTokenAdd --
- * Put a token into the job pipe so that some make process can start
- * another job.
- *
- * Side Effects:
- * Allows more build jobs to be spawned somewhere.
- *
- *-----------------------------------------------------------------------
- */
-
+/* Put a token (back) into the job pipe.
+ * This allows a make process to start a build job. */
static void
JobTokenAdd(void)
{
@@ -2888,21 +2692,13 @@ JobTokenAdd(void)
while (tok != '+' && read(tokenWaitJob.inPipe, &tok1, 1) == 1)
continue;
- if (DEBUG(JOB))
- fprintf(debug_file, "(%d) aborting %d, deposit token %c\n",
- getpid(), aborting, JOB_TOKENS[aborting]);
+ DEBUG3(JOB, "(%d) aborting %d, deposit token %c\n",
+ getpid(), aborting, tok);
while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN)
continue;
}
-/*-
- *-----------------------------------------------------------------------
- * Job_ServerStartTokenAdd --
- * Prep the job token pipe in the root make process.
- *
- *-----------------------------------------------------------------------
- */
-
+/* Prep the job token pipe in the root make process. */
void
Job_ServerStart(int max_tokens, int jp_0, int jp_1)
{
@@ -2938,14 +2734,7 @@ Job_ServerStart(int max_tokens, int jp_0, int jp_1)
JobTokenAdd();
}
-/*-
- *-----------------------------------------------------------------------
- * Job_TokenReturn --
- * Return a withdrawn token to the pool.
- *
- *-----------------------------------------------------------------------
- */
-
+/* Return a withdrawn token to the pool. */
void
Job_TokenReturn(void)
{
@@ -2956,35 +2745,24 @@ Job_TokenReturn(void)
JobTokenAdd();
}
-/*-
- *-----------------------------------------------------------------------
- * Job_TokenWithdraw --
- * Attempt to withdraw a token from the pool.
+/* Attempt to withdraw a token from the pool.
*
- * Results:
- * Returns TRUE if a token was withdrawn, and FALSE if the pool
- * is currently empty.
- *
- * Side Effects:
- * If pool is empty, set wantToken so that we wake up
- * when a token is released.
+ * If pool is empty, set wantToken so that we wake up when a token is
+ * released.
*
- *-----------------------------------------------------------------------
- */
-
-
+ * Returns TRUE if a token was withdrawn, and FALSE if the pool is currently
+ * empty. */
Boolean
Job_TokenWithdraw(void)
{
char tok, tok1;
- int count;
+ ssize_t count;
wantToken = 0;
- if (DEBUG(JOB))
- fprintf(debug_file, "Job_TokenWithdraw(%d): aborting %d, running %d\n",
- getpid(), aborting, jobTokensRunning);
+ DEBUG3(JOB, "Job_TokenWithdraw(%d): aborting %d, running %d\n",
+ getpid(), aborting, jobTokensRunning);
- if (aborting || (jobTokensRunning >= maxJobs))
+ if (aborting != ABORT_NONE || (jobTokensRunning >= opts.maxJobs))
return FALSE;
count = read(tokenWaitJob.inPipe, &tok, 1);
@@ -2994,15 +2772,13 @@ Job_TokenWithdraw(void)
if (errno != EAGAIN) {
Fatal("job pipe read: %s", strerror(errno));
}
- if (DEBUG(JOB))
- fprintf(debug_file, "(%d) blocked for token\n", getpid());
+ DEBUG1(JOB, "(%d) blocked for token\n", getpid());
return FALSE;
}
if (count == 1 && tok != '+') {
/* make being abvorted - remove any other job tokens */
- if (DEBUG(JOB))
- fprintf(debug_file, "(%d) aborted by token %c\n", getpid(), tok);
+ DEBUG2(JOB, "(%d) aborted by token %c\n", getpid(), tok);
while (read(tokenWaitJob.inPipe, &tok1, 1) == 1)
continue;
/* And put the stopper back */
@@ -3019,29 +2795,17 @@ Job_TokenWithdraw(void)
continue;
jobTokensRunning++;
- if (DEBUG(JOB))
- fprintf(debug_file, "(%d) withdrew token\n", getpid());
+ DEBUG1(JOB, "(%d) withdrew token\n", getpid());
return TRUE;
}
-/*-
- *-----------------------------------------------------------------------
- * Job_RunTarget --
- * Run the named target if found. If a filename is specified, then
- * set that to the sources.
- *
- * Results:
- * None
- *
- * Side Effects:
- * exits if the target fails.
+/* Run the named target if found. If a filename is specified, then set that
+ * to the sources.
*
- *-----------------------------------------------------------------------
- */
+ * Exits if the target fails. */
Boolean
Job_RunTarget(const char *target, const char *fname) {
- GNode *gn = Targ_FindNode(target, TARG_NOCREATE);
-
+ GNode *gn = Targ_FindNode(target);
if (gn == NULL)
return FALSE;
diff --git a/job.h b/job.h
index 45fc1b7f8df4..a412365de572 100644
--- a/job.h
+++ b/job.h
@@ -1,4 +1,4 @@
-/* $NetBSD: job.h,v 1.47 2020/08/29 12:20:17 rillig Exp $ */
+/* $NetBSD: job.h,v 1.58 2020/10/26 21:34:10 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -73,10 +73,10 @@
* from: @(#)job.h 8.1 (Berkeley) 6/6/93
*/
-/*-
- * job.h --
- * Definitions pertaining to the running of jobs in parallel mode.
+/*
+ * Running of jobs in parallel mode.
*/
+
#ifndef MAKE_JOB_H
#define MAKE_JOB_H
@@ -110,28 +110,6 @@ emul_poll(struct pollfd *fd, int nfd, int timeout);
*/
#define POLL_MSEC 5000
-/*-
- * Job Table definitions.
- *
- * Each job has several things associated with it:
- * 1) The process id of the child shell
- * 2) The graph node describing the target being made by this job
- * 3) A LstNode for the first command to be saved after the job
- * completes. This is NULL if there was no "..." in the job's
- * commands.
- * 4) An FILE* for writing out the commands. This is only
- * used before the job is actually started.
- * 5) The output is being caught via a pipe and
- * the descriptors of our pipe, an array in which output is line
- * buffered and the current position in that buffer are all
- * maintained for each job.
- * 6) A word of flags which determine how the module handles errors,
- * echoing, etc. for the job
- *
- * When a job is finished, the Make_Update function is called on each of the
- * parents of the node which was just remade. This takes care of the upward
- * traversal of the dependency graph.
- */
struct pollfd;
@@ -139,122 +117,90 @@ struct pollfd;
# include "meta.h"
#endif
-#define JOB_BUFSIZE 1024
+typedef enum JobState {
+ JOB_ST_FREE = 0, /* Job is available */
+ JOB_ST_SETUP = 1, /* Job is allocated but otherwise invalid */
+ JOB_ST_RUNNING = 3, /* Job is running, pid valid */
+ JOB_ST_FINISHED = 4 /* Job is done (ie after SIGCHILD) */
+} JobState;
+
+typedef enum JobFlags {
+ /* Ignore non-zero exits */
+ JOB_IGNERR = 0x001,
+ /* no output */
+ JOB_SILENT = 0x002,
+ /* Target is a special one. i.e. run it locally
+ * if we can't export it and maxLocal is 0 */
+ JOB_SPECIAL = 0x004,
+ /* Ignore "..." lines when processing commands */
+ JOB_IGNDOTS = 0x008,
+ /* we've sent 'set -x' */
+ JOB_TRACED = 0x400
+} JobFlags;
+
+/* A Job manages the shell commands that are run to create a single target.
+ * Each job is run in a separate subprocess by a shell. Several jobs can run
+ * in parallel.
+ *
+ * The shell commands for the target are written to a temporary file,
+ * then the shell is run with the temporary file as stdin, and the output
+ * of that shell is captured via a pipe.
+ *
+ * When a job is finished, Make_Update updates all parents of the node
+ * that was just remade, marking them as ready to be made next if all
+ * other dependencies are finished as well. */
typedef struct Job {
- int pid; /* The child's process ID */
- GNode *node; /* The target the child is making */
- LstNode tailCmds; /* The node of the first command to be
- * saved when the job has been run */
- FILE *cmdFILE; /* When creating the shell script, this is
- * where the commands go */
- int exit_status; /* from wait4() in signal handler */
- char job_state; /* status of the job entry */
-#define JOB_ST_FREE 0 /* Job is available */
-#define JOB_ST_SETUP 1 /* Job is allocated but otherwise invalid */
-#define JOB_ST_RUNNING 3 /* Job is running, pid valid */
-#define JOB_ST_FINISHED 4 /* Job is done (ie after SIGCHILD) */
- char job_suspended;
- short flags; /* Flags to control treatment of job */
-#define JOB_IGNERR 0x001 /* Ignore non-zero exits */
-#define JOB_SILENT 0x002 /* no output */
-#define JOB_SPECIAL 0x004 /* Target is a special one. i.e. run it locally
- * if we can't export it and maxLocal is 0 */
-#define JOB_IGNDOTS 0x008 /* Ignore "..." lines when processing
- * commands */
-#define JOB_TRACED 0x400 /* we've sent 'set -x' */
+ /* The process ID of the shell running the commands */
+ int pid;
+
+ /* 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. */
+ StringListNode *tailCmds;
+
+ /* When creating the shell script, this is where the commands go.
+ * This is only used before the job is actually started. */
+ FILE *cmdFILE;
- int jobPipe[2]; /* Pipe for reading output from job */
+ int exit_status; /* from wait4() in signal handler */
+
+ JobState job_state; /* status of the job entry */
+
+ char job_suspended;
+
+ JobFlags flags; /* Flags to control treatment of job */
+
+ int inPipe; /* Pipe for reading output from job */
+ int outPipe; /* Pipe for writing control commands */
struct pollfd *inPollfd; /* pollfd associated with inPipe */
- char outBuf[JOB_BUFSIZE + 1];
- /* Buffer for storing the output of the
- * job, line by line */
- int curPos; /* Current position in op_outBuf */
+
+#define JOB_BUFSIZE 1024
+ /* Buffer for storing the output of the job, line by line. */
+ char outBuf[JOB_BUFSIZE + 1];
+ size_t curPos; /* Current position in outBuf. */
#ifdef USE_META
- struct BuildMon bm;
+ struct BuildMon bm;
#endif
} Job;
-#define inPipe jobPipe[0]
-#define outPipe jobPipe[1]
-
-/*-
- * Shell Specifications:
- * Each shell type has associated with it the following information:
- * 1) The string which must match the last character of the shell name
- * for the shell to be considered of this type. The longest match
- * wins.
- * 2) A command to issue to turn off echoing of command lines
- * 3) A command to issue to turn echoing back on again
- * 4) What the shell prints, and its length, when given the echo-off
- * command. This line will not be printed when received from the shell
- * 5) A boolean to tell if the shell has the ability to control
- * error checking for individual commands.
- * 6) The string to turn this checking on.
- * 7) The string to turn it off.
- * 8) The command-flag to give to cause the shell to start echoing
- * commands right away.
- * 9) The command-flag to cause the shell to Lib_Exit when an error is
- * detected in one of the commands.
- *
- * Some special stuff goes on if a shell doesn't have error control. In such
- * a case, errCheck becomes a printf template for echoing the command,
- * should echoing be on and ignErr becomes another printf template for
- * executing the command while ignoring the return status. Finally errOut
- * is a printf template for running the command and causing the shell to
- * exit on error. If any of these strings are empty when hasErrCtl is FALSE,
- * the command will be executed anyway as is and if it causes an error, so be
- * it. Any templates setup to echo the command will escape any '$ ` \ "'i
- * characters in the command string to avoid common problems with
- * echo "%s\n" as a template.
- */
-typedef struct Shell {
- const char *name; /* the name of the shell. For Bourne and C
- * shells, this is used only to find the
- * shell description when used as the single
- * source of a .SHELL target. For user-defined
- * shells, this is the full path of the shell.
- */
- Boolean hasEchoCtl; /* True if both echoOff and echoOn defined */
- const char *echoOff; /* command to turn off echo */
- const char *echoOn; /* command to turn it back on again */
- const char *noPrint; /* command to skip when printing output from
- * shell. This is usually the command which
- * was executed to turn off echoing */
- int noPLen; /* length of noPrint command */
- Boolean hasErrCtl; /* set if can control error checking for
- * individual commands */
- const char *errCheck; /* string to turn error checking on */
- const char *ignErr; /* string to turn off error checking */
- const char *errOut; /* string to use for testing exit code */
- const char *newline; /* string literal that results in a newline
- * character when it appears outside of any
- * 'quote' or "quote" characters */
- char commentChar; /* character used by shell for comment lines */
-
- /*
- * command-line flags
- */
- const char *echo; /* echo commands */
- const char *exit; /* exit on error */
-} Shell;
-
extern const char *shellPath;
extern const char *shellName;
extern char *shellErrFlag;
-extern int jobTokensRunning; /* tokens currently "out" */
-extern int maxJobs; /* Max jobs we can run */
+extern int jobTokensRunning; /* tokens currently "out" */
void Shell_Init(void);
const char *Shell_GetNewline(void);
void Job_Touch(GNode *, Boolean);
-Boolean Job_CheckCommands(GNode *, void (*abortProc )(const char *, ...));
+Boolean Job_CheckCommands(GNode *, void (*abortProc)(const char *, ...));
void Job_CatchChildren(void);
void Job_CatchOutput(void);
void Job_Make(GNode *);
void Job_Init(void);
-Boolean Job_Empty(void);
Boolean Job_ParseShell(char *);
int Job_Finish(void);
void Job_End(void);
diff --git a/lst.c b/lst.c
index 26b2457cc013..d8b2d0efd7ea 100644
--- a/lst.c
+++ b/lst.c
@@ -1,4 +1,4 @@
-/* $NetBSD: lst.c,v 1.60 2020/08/31 05:56:02 rillig Exp $ */
+/* $NetBSD: lst.c,v 1.91 2020/10/28 02:43:16 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -32,9 +32,9 @@
* SUCH DAMAGE.
*/
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
+#include "make.h"
+
+MAKE_RCSID("$NetBSD: lst.c,v 1.91 2020/10/28 02:43:16 rillig Exp $");
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
@@ -42,110 +42,34 @@
#include <stdint.h>
#endif
-#include "make.h"
-
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: lst.c,v 1.60 2020/08/31 05:56:02 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-__RCSID("$NetBSD: lst.c,v 1.60 2020/08/31 05:56:02 rillig Exp $");
-#endif /* not lint */
-#endif
-
-struct ListNode {
- struct ListNode *prev; /* previous element in list */
- struct ListNode *next; /* next in list */
- uint8_t useCount; /* Count of functions using the node.
- * node may not be deleted until count
- * goes to 0 */
- Boolean deleted; /* List node should be removed when done */
- union {
- void *datum; /* datum associated with this element */
- const GNode *gnode; /* alias, just for debugging */
- const char *str; /* alias, just for debugging */
- };
-};
-
-typedef enum {
- Head, Middle, Tail, Unknown
-} Where;
-
-struct List {
- LstNode first; /* first node in list */
- LstNode last; /* last node in list */
-
- /* fields for sequential access */
- Boolean isOpen; /* true if list has been Lst_Open'ed */
- Where lastAccess; /* Where in the list the last access was */
- LstNode curr; /* current node, if open. NULL if
- * *just* opened */
- LstNode prev; /* Previous node, if open. Used by Lst_Remove */
-};
-
-/* Allocate and initialize a list node.
- *
- * The fields 'prev' and 'next' must be initialized by the caller.
- */
-static LstNode
-LstNodeNew(void *datum)
+static ListNode *
+LstNodeNew(ListNode *prev, ListNode *next, void *datum)
{
- LstNode node = bmake_malloc(sizeof *node);
- node->useCount = 0;
- node->deleted = FALSE;
+ ListNode *node = bmake_malloc(sizeof *node);
+ node->prev = prev;
+ node->next = next;
node->datum = datum;
return node;
}
-static Boolean
-LstIsEmpty(Lst list)
-{
- return list->first == NULL;
-}
-
/* Create and initialize a new, empty list. */
-Lst
-Lst_Init(void)
+List *
+Lst_New(void)
{
- Lst list = bmake_malloc(sizeof *list);
+ List *list = bmake_malloc(sizeof *list);
list->first = NULL;
list->last = NULL;
- list->isOpen = FALSE;
- list->lastAccess = Unknown;
return list;
}
-/* Duplicate an entire list, usually by copying the datum pointers.
- * If copyProc is given, that function is used to create the new datum from the
- * old datum, usually by creating a copy of it. */
-Lst
-Lst_Copy(Lst list, LstCopyProc copyProc)
-{
- Lst newList;
- LstNode node;
-
- assert(list != NULL);
-
- newList = Lst_Init();
-
- for (node = list->first; node != NULL; node = node->next) {
- void *datum = copyProc != NULL ? copyProc(node->datum) : node->datum;
- Lst_Append(newList, datum);
- }
-
- return newList;
-}
-
-/* Free a list and all its nodes. The list data itself are not freed though. */
+/* Free a list and all its nodes. The node data are not freed though. */
void
-Lst_Free(Lst list)
+Lst_Free(List *list)
{
- LstNode node;
- LstNode next;
-
- assert(list != NULL);
+ ListNode *node;
+ ListNode *next;
for (node = list->first; node != NULL; node = next) {
next = node->next;
@@ -158,13 +82,10 @@ Lst_Free(Lst list)
/* Destroy a list and free all its resources. The freeProc is called with the
* datum from each node in turn before the node is freed. */
void
-Lst_Destroy(Lst list, LstFreeProc freeProc)
+Lst_Destroy(List *list, LstFreeProc freeProc)
{
- LstNode node;
- LstNode next;
-
- assert(list != NULL);
- assert(freeProc != NULL);
+ ListNode *node;
+ ListNode *next;
for (node = list->first; node != NULL; node = next) {
next = node->next;
@@ -179,44 +100,33 @@ Lst_Destroy(Lst list, LstFreeProc freeProc)
* Functions to modify a list
*/
-/* Insert a new node with the given piece of data before the given node in the
- * given list. */
+/* Insert a new node with the datum before the given node. */
void
-Lst_InsertBefore(Lst list, LstNode node, void *datum)
+Lst_InsertBefore(List *list, ListNode *node, void *datum)
{
- LstNode newNode;
+ ListNode *newNode;
- assert(list != NULL);
- assert(!LstIsEmpty(list));
- assert(node != NULL);
assert(datum != NULL);
- newNode = LstNodeNew(datum);
- newNode->prev = node->prev;
- newNode->next = node;
+ newNode = LstNodeNew(node->prev, node, datum);
- if (node->prev != NULL) {
+ if (node->prev != NULL)
node->prev->next = newNode;
- }
node->prev = newNode;
- if (node == list->first) {
+ if (node == list->first)
list->first = newNode;
- }
}
/* Add a piece of data at the start of the given list. */
void
-Lst_Prepend(Lst list, void *datum)
+Lst_Prepend(List *list, void *datum)
{
- LstNode node;
+ ListNode *node;
- assert(list != NULL);
assert(datum != NULL);
- node = LstNodeNew(datum);
- node->prev = NULL;
- node->next = list->first;
+ node = LstNodeNew(NULL, list->first, datum);
if (list->first == NULL) {
list->first = node;
@@ -229,16 +139,13 @@ Lst_Prepend(Lst list, void *datum)
/* Add a piece of data at the end of the given list. */
void
-Lst_Append(Lst list, void *datum)
+Lst_Append(List *list, void *datum)
{
- LstNode node;
+ ListNode *node;
- assert(list != NULL);
assert(datum != NULL);
- node = LstNodeNew(datum);
- node->prev = list->last;
- node->next = NULL;
+ node = LstNodeNew(list->last, NULL, datum);
if (list->last == NULL) {
list->first = node;
@@ -252,265 +159,83 @@ Lst_Append(Lst list, void *datum)
/* Remove the given node from the given list.
* The datum stored in the node must be freed by the caller, if necessary. */
void
-Lst_Remove(Lst list, LstNode node)
+Lst_Remove(List *list, ListNode *node)
{
- assert(list != NULL);
- assert(node != NULL);
-
- /*
- * unlink it from the list
- */
- if (node->next != NULL) {
+ /* unlink it from its neighbors */
+ if (node->next != NULL)
node->next->prev = node->prev;
- }
- if (node->prev != NULL) {
+ if (node->prev != NULL)
node->prev->next = node->next;
- }
- /*
- * if either the first or last of the list point to this node,
- * adjust them accordingly
- */
- if (list->first == node) {
+ /* unlink it from the list */
+ if (list->first == node)
list->first = node->next;
- }
- if (list->last == node) {
+ if (list->last == node)
list->last = node->prev;
- }
-
- /*
- * Sequential access stuff. If the node we're removing is the current
- * node in the list, reset the current node to the previous one. If the
- * previous one was non-existent (prev == NULL), we set the
- * end to be Unknown, since it is.
- */
- if (list->isOpen && list->curr == node) {
- list->curr = list->prev;
- if (list->curr == NULL) {
- list->lastAccess = Unknown;
- }
- }
-
- /*
- * note that the datum is unmolested. The caller must free it as
- * necessary and as expected.
- */
- if (node->useCount == 0) {
- free(node);
- } else {
- node->deleted = TRUE;
- }
}
/* Replace the datum in the given node with the new datum. */
void
-LstNode_Set(LstNode node, void *datum)
+LstNode_Set(ListNode *node, void *datum)
{
- assert(node != NULL);
assert(datum != NULL);
node->datum = datum;
}
-/* Replace the datum in the given node to NULL. */
+/* Replace the datum in the given node to NULL.
+ * Having NULL values in a list is unusual though. */
void
-LstNode_SetNull(LstNode node)
+LstNode_SetNull(ListNode *node)
{
- assert(node != NULL);
-
node->datum = NULL;
}
-
-/*
- * Node-specific functions
- */
-
-/* Return the first node from the given list, or NULL if the list is empty. */
-LstNode
-Lst_First(Lst list)
-{
- assert(list != NULL);
-
- return list->first;
-}
-
-/* Return the last node from the given list, or NULL if the list is empty. */
-LstNode
-Lst_Last(Lst list)
-{
- assert(list != NULL);
-
- return list->last;
-}
-
-/* Return the successor to the given node on its list, or NULL. */
-LstNode
-LstNode_Next(LstNode node)
-{
- assert(node != NULL);
-
- return node->next;
-}
-
-/* Return the predecessor to the given node on its list, or NULL. */
-LstNode
-LstNode_Prev(LstNode node)
-{
- assert(node != NULL);
- return node->prev;
-}
-
-/* Return the datum stored in the given node. */
-void *
-LstNode_Datum(LstNode node)
-{
- assert(node != NULL);
- return node->datum;
-}
-
-
/*
* Functions for entire lists
*/
-/* Return TRUE if the given list is empty. */
-Boolean
-Lst_IsEmpty(Lst list)
-{
- assert(list != NULL);
-
- return LstIsEmpty(list);
-}
-
-/* Return the first node from the list for which the match function returns
- * TRUE, or NULL if none of the nodes matched. */
-LstNode
-Lst_Find(Lst list, LstFindProc match, const void *matchArgs)
-{
- return Lst_FindFrom(list, Lst_First(list), match, matchArgs);
-}
-
-/* Return the first node from the list, starting at the given node, for which
- * the match function returns TRUE, or NULL if none of the nodes matches.
- *
- * The start node may be NULL, in which case nothing is found. This allows
- * for passing Lst_First or LstNode_Next as the start node. */
-LstNode
-Lst_FindFrom(Lst list, LstNode node, LstFindProc match, const void *matchArgs)
-{
- LstNode tln;
-
- assert(list != NULL);
- assert(match != NULL);
-
- for (tln = node; tln != NULL; tln = tln->next) {
- if (match(tln->datum, matchArgs))
- return tln;
- }
-
- return NULL;
-}
-
/* Return the first node that contains the given datum, or NULL. */
-LstNode
-Lst_FindDatum(Lst list, const void *datum)
+ListNode *
+Lst_FindDatum(List *list, const void *datum)
{
- LstNode node;
+ ListNode *node;
- assert(list != NULL);
assert(datum != NULL);
- for (node = list->first; node != NULL; node = node->next) {
- if (node->datum == datum) {
+ for (node = list->first; node != NULL; node = node->next)
+ if (node->datum == datum)
return node;
- }
- }
return NULL;
}
-/* Apply the given function to each element of the given list. The function
- * should return 0 if traversal should continue and non-zero if it should
- * abort. */
-int
-Lst_ForEach(Lst list, LstActionProc proc, void *procData)
-{
- if (LstIsEmpty(list))
- return 0; /* XXX: Document what this value means. */
- return Lst_ForEachFrom(list, Lst_First(list), proc, procData);
-}
-
-/* Apply the given function to each element of the given list, starting from
- * the given node. The function should return 0 if traversal should continue,
- * and non-zero if it should abort. */
int
-Lst_ForEachFrom(Lst list, LstNode node,
- LstActionProc proc, void *procData)
+Lst_ForEachUntil(List *list, LstActionUntilProc proc, void *procData)
{
- LstNode tln = node;
- LstNode next;
- Boolean done;
- int result;
-
- assert(list != NULL);
- assert(node != NULL);
- assert(proc != NULL);
-
- do {
- /*
- * Take care of having the current element deleted out from under
- * us.
- */
-
- next = tln->next;
-
- /*
- * We're done with the traversal if
- * - the next node to examine doesn't exist and
- * - nothing's been added after the current node (check this
- * after proc() has been called).
- */
- done = next == NULL;
-
- tln->useCount++;
- result = (*proc)(tln->datum, procData);
- tln->useCount--;
-
- /*
- * Now check whether a node has been added.
- * Note: this doesn't work if this node was deleted before
- * the new node was added.
- */
- if (next != tln->next) {
- next = tln->next;
- done = 0;
- }
-
- if (tln->deleted) {
- free((char *)tln);
- }
- tln = next;
- } while (!result && !LstIsEmpty(list) && !done);
+ ListNode *node;
+ int result = 0;
+ for (node = list->first; node != NULL; node = node->next) {
+ result = proc(node->datum, procData);
+ if (result != 0)
+ break;
+ }
return result;
}
/* Move all nodes from list2 to the end of list1.
* List2 is destroyed and freed. */
void
-Lst_MoveAll(Lst list1, Lst list2)
+Lst_MoveAll(List *list1, List *list2)
{
- assert(list1 != NULL);
- assert(list2 != NULL);
-
if (list2->first != NULL) {
list2->first->prev = list1->last;
- if (list1->last != NULL) {
+ if (list1->last != NULL)
list1->last->next = list2->first;
- } else {
+ else
list1->first = list2->first;
- }
+
list1->last = list2->last;
}
free(list2);
@@ -518,124 +243,77 @@ Lst_MoveAll(Lst list1, Lst list2)
/* Copy the element data from src to the start of dst. */
void
-Lst_PrependAll(Lst dst, Lst src)
+Lst_PrependAll(List *dst, List *src)
{
- LstNode node;
+ ListNode *node;
for (node = src->last; node != NULL; node = node->prev)
Lst_Prepend(dst, node->datum);
}
/* Copy the element data from src to the end of dst. */
void
-Lst_AppendAll(Lst dst, Lst src)
+Lst_AppendAll(List *dst, List *src)
{
- LstNode node;
+ ListNode *node;
for (node = src->first; node != NULL; node = node->next)
Lst_Append(dst, node->datum);
}
/*
- * these functions are for dealing with a list as a table, of sorts.
- * An idea of the "current element" is kept and used by all the functions
- * between Lst_Open() and Lst_Close().
- *
- * The sequential functions access the list in a slightly different way.
- * CurPtr points to their idea of the current node in the list and they
- * access the list based on it.
+ * for using the list as a queue
*/
-/* Open a list for sequential access. A list can still be searched, etc.,
- * without confusing these functions. */
+/* Add the datum to the tail of the given list. */
void
-Lst_Open(Lst list)
+Lst_Enqueue(List *list, void *datum)
{
- assert(list != NULL);
- assert(!list->isOpen);
-
- list->isOpen = TRUE;
- list->lastAccess = LstIsEmpty(list) ? Head : Unknown;
- list->curr = NULL;
+ Lst_Append(list, datum);
}
-/* Return the next node for the given list, or NULL if the end has been
- * reached. */
-LstNode
-Lst_Next(Lst list)
+/* Remove and return the datum at the head of the given list. */
+void *
+Lst_Dequeue(List *list)
{
- LstNode node;
-
- assert(list != NULL);
- assert(list->isOpen);
-
- list->prev = list->curr;
-
- if (list->curr == NULL) {
- if (list->lastAccess == Unknown) {
- /*
- * If we're just starting out, lastAccess will be Unknown.
- * Then we want to start this thing off in the right
- * direction -- at the start with lastAccess being Middle.
- */
- list->curr = node = list->first;
- list->lastAccess = Middle;
- } else {
- node = NULL;
- list->lastAccess = Tail;
- }
- } else {
- node = list->curr->next;
- list->curr = node;
-
- if (node == list->first || node == NULL) {
- /*
- * If back at the front, then we've hit the end...
- */
- list->lastAccess = Tail;
- } else {
- /*
- * Reset to Middle if gone past first.
- */
- list->lastAccess = Middle;
- }
- }
-
- return node;
+ void *datum = list->first->datum;
+ Lst_Remove(list, list->first);
+ assert(datum != NULL); /* since NULL would mean end of the list */
+ return datum;
}
-/* Close a list which was opened for sequential access. */
void
-Lst_Close(Lst list)
+Vector_Init(Vector *v, size_t itemSize)
{
- assert(list != NULL);
- assert(list->isOpen);
-
- list->isOpen = FALSE;
- list->lastAccess = Unknown;
+ v->len = 0;
+ v->priv_cap = 10;
+ v->itemSize = itemSize;
+ v->items = bmake_malloc(v->priv_cap * v->itemSize);
}
-
-/*
- * for using the list as a queue
- */
-
-/* Add the datum to the tail of the given list. */
-void
-Lst_Enqueue(Lst list, void *datum)
+/* Add space for a new item to the vector and return a pointer to that space.
+ * The returned data is valid until the next modifying operation. */
+void *
+Vector_Push(Vector *v)
{
- Lst_Append(list, datum);
+ if (v->len >= v->priv_cap) {
+ v->priv_cap *= 2;
+ v->items = bmake_realloc(v->items, v->priv_cap * v->itemSize);
+ }
+ v->len++;
+ return Vector_Get(v, v->len - 1);
}
-/* Remove and return the datum at the head of the given list. */
+/* Return the pointer to the last item in the vector.
+ * The returned data is valid until the next modifying operation. */
void *
-Lst_Dequeue(Lst list)
+Vector_Pop(Vector *v)
{
- void *datum;
-
- assert(list != NULL);
- assert(!LstIsEmpty(list));
+ assert(v->len > 0);
+ v->len--;
+ return Vector_Get(v, v->len);
+}
- datum = list->first->datum;
- Lst_Remove(list, list->first);
- assert(datum != NULL);
- return datum;
+void
+Vector_Done(Vector *v)
+{
+ free(v->items);
}
diff --git a/lst.h b/lst.h
index d9aa9feb3b65..85418e0d0531 100644
--- a/lst.h
+++ b/lst.h
@@ -1,4 +1,4 @@
-/* $NetBSD: lst.h,v 1.60 2020/09/02 23:33:13 rillig Exp $ */
+/* $NetBSD: lst.h,v 1.84 2020/10/28 02:43:16 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -79,101 +79,109 @@
#define MAKE_LST_H
#include <sys/param.h>
+#include <stdint.h>
#include <stdlib.h>
/* A doubly-linked list of pointers. */
-typedef struct List *Lst;
+typedef struct List List;
/* A single node in the doubly-linked list. */
-typedef struct ListNode *LstNode;
+typedef struct ListNode ListNode;
+
+struct ListNode {
+ ListNode *prev; /* previous node in list, or NULL */
+ ListNode *next; /* next node in list, or NULL */
+ union {
+ void *datum; /* datum associated with this element */
+ const struct GNode *priv_gnode; /* alias, just for debugging */
+ const char *priv_str; /* alias, just for debugging */
+ };
+};
+
+struct List {
+ ListNode *first; /* first node in list */
+ ListNode *last; /* last node in list */
+};
-/* Copy a node, usually by allocating a copy of the given object.
- * For reference-counted objects, the original object may need to be
- * modified, therefore the parameter is not const. */
-typedef void *LstCopyProc(void *);
/* Free the datum of a node, called before freeing the node itself. */
typedef void LstFreeProc(void *);
-/* Return TRUE if the datum matches the args, for Lst_Find. */
-typedef Boolean LstFindProc(const void *datum, const void *args);
-/* An action for Lst_ForEach. */
-typedef int LstActionProc(void *datum, void *args);
+/* An action for Lst_ForEachUntil and Lst_ForEachUntilConcurrent. */
+typedef int LstActionUntilProc(void *datum, void *args);
/* Create or destroy a list */
/* Create a new list. */
-Lst Lst_Init(void);
-/* Duplicate an existing list. */
-Lst Lst_Copy(Lst, LstCopyProc);
+List *Lst_New(void);
/* Free the list, leaving the node data unmodified. */
-void Lst_Free(Lst);
+void Lst_Free(List *);
/* Free the list, freeing the node data using the given function. */
-void Lst_Destroy(Lst, LstFreeProc);
+void Lst_Destroy(List *, LstFreeProc);
/* Get information about a list */
-Boolean Lst_IsEmpty(Lst);
-/* Return the first node of the list, or NULL. */
-LstNode Lst_First(Lst);
-/* Return the last node of the list, or NULL. */
-LstNode Lst_Last(Lst);
-/* Find the first node for which the function returns TRUE, or NULL. */
-LstNode Lst_Find(Lst, LstFindProc, const void *);
-/* Find the first node for which the function returns TRUE, or NULL.
- * The search starts at the given node, towards the end of the list. */
-LstNode Lst_FindFrom(Lst, LstNode, LstFindProc, const void *);
+static inline MAKE_ATTR_UNUSED Boolean
+Lst_IsEmpty(List *list) { return list->first == NULL; }
+
/* Find the first node that contains the given datum, or NULL. */
-LstNode Lst_FindDatum(Lst, const void *);
+ListNode *Lst_FindDatum(List *, const void *);
/* Modify a list */
/* Insert a datum before the given node. */
-void Lst_InsertBefore(Lst, LstNode, void *);
+void Lst_InsertBefore(List *, ListNode *, void *);
/* Place a datum at the front of the list. */
-void Lst_Prepend(Lst, void *);
+void Lst_Prepend(List *, void *);
/* Place a datum at the end of the list. */
-void Lst_Append(Lst, void *);
+void Lst_Append(List *, void *);
/* Remove the node from the list. */
-void Lst_Remove(Lst, LstNode);
-void Lst_PrependAll(Lst, Lst);
-void Lst_AppendAll(Lst, Lst);
-void Lst_MoveAll(Lst, Lst);
+void Lst_Remove(List *, ListNode *);
+void Lst_PrependAll(List *, List *);
+void Lst_AppendAll(List *, List *);
+void Lst_MoveAll(List *, List *);
/* Node-specific functions */
-/* Return the successor of the node, or NULL. */
-LstNode LstNode_Next(LstNode);
-/* Return the predecessor of the node, or NULL. */
-LstNode LstNode_Prev(LstNode);
-/* Return the datum of the node. Usually not NULL. */
-void *LstNode_Datum(LstNode);
/* Replace the value of the node. */
-void LstNode_Set(LstNode, void *);
+void LstNode_Set(ListNode *, void *);
/* Set the value of the node to NULL. Having NULL in a list is unusual. */
-void LstNode_SetNull(LstNode);
+void LstNode_SetNull(ListNode *);
/* Iterating over a list, using a callback function */
-/* Apply a function to each datum of the list, until the callback function
- * returns non-zero. */
-int Lst_ForEach(Lst, LstActionProc, void *);
-/* Apply a function to each datum of the list, starting at the node,
- * until the callback function returns non-zero. */
-int Lst_ForEachFrom(Lst, LstNode, LstActionProc, void *);
-
-/* Iterating over a list while keeping track of the current node and possible
- * concurrent modifications */
-
-/* Start iterating the list. */
-void Lst_Open(Lst);
-/* Return the next node, or NULL. */
-LstNode Lst_Next(Lst);
-/* Finish iterating the list. */
-void Lst_Close(Lst);
+/* Run the action for each datum of the list, until the action returns
+ * non-zero.
+ *
+ * During this iteration, the list must not be modified structurally. */
+int Lst_ForEachUntil(List *, LstActionUntilProc, void *);
/* Using the list as a queue */
/* Add a datum at the tail of the queue. */
-void Lst_Enqueue(Lst, void *);
+void Lst_Enqueue(List *, void *);
/* Remove the head node of the queue and return its datum. */
-void *Lst_Dequeue(Lst);
+void *Lst_Dequeue(List *);
+
+/* A vector is an ordered collection of items, allowing for fast indexed
+ * access. */
+typedef struct Vector {
+ void *items; /* memory holding the items */
+ size_t itemSize; /* size of a single item in bytes */
+ size_t len; /* number of actually usable elements */
+ size_t priv_cap; /* capacity */
+} Vector;
+
+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. */
+static inline MAKE_ATTR_UNUSED void *
+Vector_Get(Vector *v, size_t i)
+{
+ unsigned char *items = v->items;
+ return items + i * v->itemSize;
+}
+
+void *Vector_Push(Vector *);
+void *Vector_Pop(Vector *);
+void Vector_Done(Vector *);
#endif /* MAKE_LST_H */
diff --git a/main.c b/main.c
index f27320c57451..729c225d4bc4 100644
--- a/main.c
+++ b/main.c
@@ -1,4 +1,4 @@
-/* $NetBSD: main.c,v 1.331 2020/08/30 19:56:02 rillig Exp $ */
+/* $NetBSD: main.c,v 1.421 2020/11/01 00:24:57 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -68,24 +68,6 @@
* SUCH DAMAGE.
*/
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: main.c,v 1.331 2020/08/30 19:56:02 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993\
- The Regents of the University of California. All rights reserved.");
-#endif /* not lint */
-
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)main.c 8.3 (Berkeley) 3/19/94";
-#else
-__RCSID("$NetBSD: main.c,v 1.331 2020/08/30 19:56:02 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
/*-
* main.c --
* The main file for this entire program. Exit routines etc
@@ -124,69 +106,47 @@ __RCSID("$NetBSD: main.c,v 1.331 2020/08/30 19:56:02 rillig Exp $");
#include <sys/utsname.h>
#include "wait.h"
-#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <time.h>
#include "make.h"
-#include "hash.h"
#include "dir.h"
#include "job.h"
#include "pathnames.h"
#include "trace.h"
-#ifdef USE_IOVEC
-#include <sys/uio.h>
+/* "@(#)main.c 8.3 (Berkeley) 3/19/94" */
+MAKE_RCSID("$NetBSD: main.c,v 1.421 2020/11/01 00:24:57 rillig Exp $");
+#if defined(MAKE_NATIVE) && !defined(lint)
+__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 "
+ "The Regents of the University of California. "
+ "All rights reserved.");
#endif
#ifndef DEFMAXLOCAL
#define DEFMAXLOCAL DEFMAXJOBS
-#endif /* DEFMAXLOCAL */
+#endif
#ifndef __arraycount
# define __arraycount(__x) (sizeof(__x) / sizeof(__x[0]))
#endif
-Lst create; /* Targets to be made */
+CmdOpts opts;
time_t now; /* Time at start of make */
GNode *DEFAULT; /* .DEFAULT node */
Boolean allPrecious; /* .PRECIOUS given on line by itself */
Boolean deleteOnError; /* .DELETE_ON_ERROR: set */
-static Boolean noBuiltins; /* -r flag */
-static Lst makefiles; /* ordered list of makefiles to read */
-static int printVars; /* -[vV] argument */
-#define COMPAT_VARS 1
-#define EXPAND_VARS 2
-static Lst variables; /* list of variables to print
- * (for -v and -V) */
-int maxJobs; /* -j argument */
static int maxJobTokens; /* -j argument */
-Boolean compatMake; /* -B argument */
-int debug; /* -d argument */
-Boolean debugVflag; /* -dV */
-Boolean noExecute; /* -n flag */
-Boolean noRecursiveExecute; /* -N flag */
-Boolean keepgoing; /* -k flag */
-Boolean queryFlag; /* -q flag */
-Boolean touchFlag; /* -t flag */
-Boolean enterFlag; /* -w flag */
Boolean enterFlagObj; /* -w and objdir != srcdir */
-Boolean ignoreErrors; /* -i flag */
-Boolean beSilent; /* -s flag */
+
Boolean oldVars; /* variable substitution style */
-Boolean checkEnvFirst; /* -e flag */
-Boolean parseWarnFatal; /* -W flag */
static int jp_0 = -1, jp_1 = -1; /* ends of parent job pipe */
-Boolean varNoExportEnv; /* -X flag */
Boolean doing_depend; /* Set while reading .depend */
static Boolean jobsRunning; /* TRUE if the jobs might be running */
static const char * tracefile;
-static void MainParseArgs(int, char **);
static int ReadMakefile(const char *);
static void usage(void) MAKE_ATTR_DEAD;
static void purge_cached_realpaths(void);
@@ -199,9 +159,8 @@ char *makeDependfile;
pid_t myPid;
int makelevel;
-FILE *debug_file;
-
Boolean forceJobs = FALSE;
+static int errors = 0;
/*
* On some systems MACHINE is defined as something other than
@@ -212,7 +171,7 @@ Boolean forceJobs = FALSE;
# define MACHINE FORCE_MACHINE
#endif
-extern Lst parseIncPath;
+extern SearchPath *parseIncPath;
/*
* For compatibility with the POSIX version of MAKEFLAGS that includes
@@ -229,7 +188,7 @@ explode(const char *flags)
return NULL;
for (f = flags; *f; f++)
- if (!isalpha((unsigned char)*f))
+ if (!ch_isalpha(*f))
break;
if (*f)
@@ -247,118 +206,133 @@ explode(const char *flags)
}
static void
+parse_debug_option_F(const char *modules)
+{
+ const char *mode;
+ size_t len;
+ char *fname;
+
+ if (opts.debug_file != stdout && opts.debug_file != stderr)
+ fclose(opts.debug_file);
+
+ if (*modules == '+') {
+ modules++;
+ mode = "a";
+ } else
+ mode = "w";
+
+ if (strcmp(modules, "stdout") == 0) {
+ opts.debug_file = stdout;
+ return;
+ }
+ if (strcmp(modules, "stderr") == 0) {
+ opts.debug_file = stderr;
+ return;
+ }
+
+ len = strlen(modules);
+ fname = bmake_malloc(len + 20);
+ memcpy(fname, modules, len + 1);
+
+ /* Let the filename be modified by the pid */
+ if (strcmp(fname + len - 3, ".%d") == 0)
+ snprintf(fname + len - 2, 20, "%d", getpid());
+
+ opts.debug_file = fopen(fname, mode);
+ if (!opts.debug_file) {
+ fprintf(stderr, "Cannot open debug file %s\n",
+ fname);
+ usage();
+ }
+ free(fname);
+}
+
+static void
parse_debug_options(const char *argvalue)
{
const char *modules;
- const char *mode;
- char *fname;
- int len;
for (modules = argvalue; *modules; ++modules) {
switch (*modules) {
+ case '0': /* undocumented, only intended for tests */
+ opts.debug &= DEBUG_LINT;
+ break;
case 'A':
- debug = ~(0|DEBUG_LINT);
+ opts.debug = ~(0|DEBUG_LINT);
break;
case 'a':
- debug |= DEBUG_ARCH;
+ opts.debug |= DEBUG_ARCH;
break;
case 'C':
- debug |= DEBUG_CWD;
+ opts.debug |= DEBUG_CWD;
break;
case 'c':
- debug |= DEBUG_COND;
+ opts.debug |= DEBUG_COND;
break;
case 'd':
- debug |= DEBUG_DIR;
+ opts.debug |= DEBUG_DIR;
break;
case 'e':
- debug |= DEBUG_ERROR;
+ opts.debug |= DEBUG_ERROR;
break;
case 'f':
- debug |= DEBUG_FOR;
+ opts.debug |= DEBUG_FOR;
break;
case 'g':
if (modules[1] == '1') {
- debug |= DEBUG_GRAPH1;
+ opts.debug |= DEBUG_GRAPH1;
++modules;
}
else if (modules[1] == '2') {
- debug |= DEBUG_GRAPH2;
+ opts.debug |= DEBUG_GRAPH2;
++modules;
}
else if (modules[1] == '3') {
- debug |= DEBUG_GRAPH3;
+ opts.debug |= DEBUG_GRAPH3;
++modules;
}
break;
case 'h':
- debug |= DEBUG_HASH;
+ opts.debug |= DEBUG_HASH;
break;
case 'j':
- debug |= DEBUG_JOB;
+ opts.debug |= DEBUG_JOB;
break;
case 'L':
- debug |= DEBUG_LINT;
+ opts.debug |= DEBUG_LINT;
break;
case 'l':
- debug |= DEBUG_LOUD;
+ opts.debug |= DEBUG_LOUD;
break;
case 'M':
- debug |= DEBUG_META;
+ opts.debug |= DEBUG_META;
break;
case 'm':
- debug |= DEBUG_MAKE;
+ opts.debug |= DEBUG_MAKE;
break;
case 'n':
- debug |= DEBUG_SCRIPT;
+ opts.debug |= DEBUG_SCRIPT;
break;
case 'p':
- debug |= DEBUG_PARSE;
+ opts.debug |= DEBUG_PARSE;
break;
case 's':
- debug |= DEBUG_SUFF;
+ opts.debug |= DEBUG_SUFF;
break;
case 't':
- debug |= DEBUG_TARG;
+ opts.debug |= DEBUG_TARG;
break;
case 'V':
- debugVflag = TRUE;
+ opts.debugVflag = TRUE;
break;
case 'v':
- debug |= DEBUG_VAR;
+ opts.debug |= DEBUG_VAR;
break;
case 'x':
- debug |= DEBUG_SHELL;
+ opts.debug |= DEBUG_SHELL;
break;
case 'F':
- if (debug_file != stdout && debug_file != stderr)
- fclose(debug_file);
- if (*++modules == '+') {
- modules++;
- mode = "a";
- } else
- mode = "w";
- if (strcmp(modules, "stdout") == 0) {
- debug_file = stdout;
- goto debug_setbuf;
- }
- if (strcmp(modules, "stderr") == 0) {
- debug_file = stderr;
- goto debug_setbuf;
- }
- len = strlen(modules);
- fname = bmake_malloc(len + 20);
- memcpy(fname, modules, len + 1);
- /* Let the filename be modified by the pid */
- if (strcmp(fname + len - 3, ".%d") == 0)
- snprintf(fname + len - 2, 20, "%d", getpid());
- debug_file = fopen(fname, mode);
- if (!debug_file) {
- fprintf(stderr, "Cannot open debug file %s\n",
- fname);
- usage();
- }
- free(fname);
+ parse_debug_option_F(modules + 1);
goto debug_setbuf;
default:
(void)fprintf(stderr,
@@ -372,8 +346,8 @@ debug_setbuf:
* Make the debug_file unbuffered, and make
* stdout line buffered (unless debugfile == stdout).
*/
- setvbuf(debug_file, NULL, _IONBF, 0);
- if (debug_file != stdout) {
+ setvbuf(opts.debug_file, NULL, _IONBF, 0);
+ if (opts.debug_file != stdout) {
setvbuf(stdout, NULL, _IOLBF, 0);
}
}
@@ -401,48 +375,241 @@ is_relpath(const char *path)
return FALSE;
}
-/*-
- * MainParseArgs --
- * Parse a given argument vector. Called from main() and from
- * Main_ParseArgLine() when the .MAKEFLAGS target is used.
- *
- * XXX: Deal with command line overriding .MAKEFLAGS in makefile
+static void
+MainParseArgChdir(const char *argvalue)
+{
+ struct stat sa, sb;
+
+ if (chdir(argvalue) == -1) {
+ (void)fprintf(stderr, "%s: chdir %s: %s\n",
+ progname, argvalue, strerror(errno));
+ exit(1);
+ }
+ if (getcwd(curdir, MAXPATHLEN) == NULL) {
+ (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno));
+ exit(2);
+ }
+ if (!is_relpath(argvalue) &&
+ stat(argvalue, &sa) != -1 &&
+ stat(curdir, &sb) != -1 &&
+ sa.st_ino == sb.st_ino &&
+ sa.st_dev == sb.st_dev)
+ strncpy(curdir, argvalue, MAXPATHLEN);
+ ignorePWD = TRUE;
+}
+
+static void
+MainParseArgJobsInternal(const char *argvalue)
+{
+ if (sscanf(argvalue, "%d,%d", &jp_0, &jp_1) != 2) {
+ (void)fprintf(stderr,
+ "%s: internal error -- J option malformed (%s)\n",
+ progname, argvalue);
+ usage();
+ }
+ 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;
+ } else {
+ Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
+ }
+}
+
+static void
+MainParseArgJobs(const char *argvalue)
+{
+ char *p;
+
+ forceJobs = TRUE;
+ opts.maxJobs = (int)strtol(argvalue, &p, 0);
+ if (*p != '\0' || opts.maxJobs < 1) {
+ (void)fprintf(stderr,
+ "%s: illegal argument to -j -- must be positive integer!\n",
+ progname);
+ exit(1); /* XXX: why not 2? */
+ }
+ Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
+ Var_Set(".MAKE.JOBS", argvalue, VAR_GLOBAL);
+ maxJobTokens = opts.maxJobs;
+}
+
+static void
+MainParseArgSysInc(const char *argvalue)
+{
+ /* look for magic parent directory search string */
+ if (strncmp(".../", argvalue, 4) == 0) {
+ char *found_path = Dir_FindHereOrAbove(curdir, argvalue + 4);
+ if (found_path == NULL)
+ return;
+ (void)Dir_AddDir(sysIncPath, found_path);
+ free(found_path);
+ } else {
+ (void)Dir_AddDir(sysIncPath, argvalue);
+ }
+ Var_Append(MAKEFLAGS, "-m", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
+}
+
+static Boolean
+MainParseArg(char c, const char *argvalue)
+{
+ switch (c) {
+ case '\0':
+ break;
+ case 'B':
+ opts.compatMake = TRUE;
+ Var_Append(MAKEFLAGS, "-B", VAR_GLOBAL);
+ Var_Set(MAKE_MODE, "compat", VAR_GLOBAL);
+ break;
+ case 'C':
+ MainParseArgChdir(argvalue);
+ break;
+ case 'D':
+ if (argvalue[0] == '\0') return FALSE;
+ Var_Set(argvalue, "1", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, "-D", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
+ break;
+ case 'I':
+ Parse_AddIncludeDir(argvalue);
+ Var_Append(MAKEFLAGS, "-I", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
+ break;
+ case 'J':
+ MainParseArgJobsInternal(argvalue);
+ break;
+ case 'N':
+ opts.noExecute = TRUE;
+ opts.noRecursiveExecute = TRUE;
+ Var_Append(MAKEFLAGS, "-N", VAR_GLOBAL);
+ break;
+ case 'S':
+ opts.keepgoing = FALSE;
+ Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL);
+ break;
+ case 'T':
+ tracefile = bmake_strdup(argvalue);
+ Var_Append(MAKEFLAGS, "-T", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
+ break;
+ case 'V':
+ case 'v':
+ opts.printVars = c == 'v' ? EXPAND_VARS : COMPAT_VARS;
+ Lst_Append(opts.variables, bmake_strdup(argvalue));
+ /* XXX: Why always -V? */
+ Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
+ break;
+ case 'W':
+ opts.parseWarnFatal = TRUE;
+ break;
+ case 'X':
+ opts.varNoExportEnv = TRUE;
+ Var_Append(MAKEFLAGS, "-X", VAR_GLOBAL);
+ break;
+ case 'd':
+ /* If '-d-opts' don't pass to children */
+ if (argvalue[0] == '-')
+ argvalue++;
+ else {
+ Var_Append(MAKEFLAGS, "-d", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
+ }
+ parse_debug_options(argvalue);
+ break;
+ case 'e':
+ opts.checkEnvFirst = TRUE;
+ Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL);
+ break;
+ case 'f':
+ Lst_Append(opts.makefiles, bmake_strdup(argvalue));
+ break;
+ case 'i':
+ opts.ignoreErrors = TRUE;
+ Var_Append(MAKEFLAGS, "-i", VAR_GLOBAL);
+ break;
+ case 'j':
+ MainParseArgJobs(argvalue);
+ break;
+ case 'k':
+ opts.keepgoing = TRUE;
+ Var_Append(MAKEFLAGS, "-k", VAR_GLOBAL);
+ break;
+ case 'm':
+ MainParseArgSysInc(argvalue);
+ break;
+ case 'n':
+ opts.noExecute = TRUE;
+ Var_Append(MAKEFLAGS, "-n", VAR_GLOBAL);
+ break;
+ case 'q':
+ opts.queryFlag = TRUE;
+ /* Kind of nonsensical, wot? */
+ Var_Append(MAKEFLAGS, "-q", VAR_GLOBAL);
+ break;
+ case 'r':
+ opts.noBuiltins = TRUE;
+ Var_Append(MAKEFLAGS, "-r", VAR_GLOBAL);
+ break;
+ case 's':
+ opts.beSilent = TRUE;
+ Var_Append(MAKEFLAGS, "-s", VAR_GLOBAL);
+ break;
+ case 't':
+ opts.touchFlag = TRUE;
+ Var_Append(MAKEFLAGS, "-t", VAR_GLOBAL);
+ break;
+ case 'w':
+ opts.enterFlag = TRUE;
+ Var_Append(MAKEFLAGS, "-w", VAR_GLOBAL);
+ break;
+ default:
+ case '?':
+ usage();
+ }
+ return TRUE;
+}
+
+/* Parse the given arguments. Called from main() and from
+ * Main_ParseArgLine() when the .MAKEFLAGS target is used.
*
- * Results:
- * None
+ * The arguments must be treated as read-only and will be freed after the
+ * call.
*
- * Side Effects:
- * Various global and local flags will be set depending on the flags
- * given
- */
+ * XXX: Deal with command line overriding .MAKEFLAGS in makefile */
static void
MainParseArgs(int argc, char **argv)
{
- char *p;
- char c = '?';
+ char c;
int arginc;
char *argvalue;
- const char *getopt_def;
- struct stat sa, sb;
char *optscan;
Boolean inOption, dashDash = FALSE;
- char found_path[MAXPATHLEN + 1]; /* for searching for sys.mk */
-#define OPTFLAGS "BC:D:I:J:NST:V:WXd:ef:ij:km:nqrstv:w"
+ const char *optspecs = "BC:D:I:J:NST:V:WXd:ef:ij:km:nqrstv:w";
/* Can't actually use getopt(3) because rescanning is not portable */
- getopt_def = OPTFLAGS;
rearg:
inOption = FALSE;
optscan = NULL;
- while(argc > 1) {
- char *getopt_spec;
- if(!inOption)
+ while (argc > 1) {
+ const char *optspec;
+ if (!inOption)
optscan = argv[1];
c = *optscan++;
arginc = 0;
- if(inOption) {
- if(c == '\0') {
+ if (inOption) {
+ if (c == '\0') {
++argv;
--argc;
inOption = FALSE;
@@ -455,13 +622,13 @@ rearg:
c = *optscan++;
}
/* '-' found at some earlier point */
- getopt_spec = strchr(getopt_def, c);
- if(c != '\0' && getopt_spec != NULL && getopt_spec[1] == ':') {
+ optspec = strchr(optspecs, c);
+ if (c != '\0' && optspec != NULL && optspec[1] == ':') {
/* -<something> found, and <something> should have an arg */
inOption = FALSE;
arginc = 1;
argvalue = optscan;
- if(*argvalue == '\0') {
+ if (*argvalue == '\0') {
if (argc < 3)
goto noarg;
argvalue = argv[2];
@@ -470,192 +637,17 @@ rearg:
} else {
argvalue = NULL;
}
- switch(c) {
+ switch (c) {
case '\0':
arginc = 1;
inOption = FALSE;
break;
- case 'B':
- compatMake = TRUE;
- Var_Append(MAKEFLAGS, "-B", VAR_GLOBAL);
- Var_Set(MAKE_MODE, "compat", VAR_GLOBAL);
- break;
- case 'C':
- if (chdir(argvalue) == -1) {
- (void)fprintf(stderr,
- "%s: chdir %s: %s\n",
- progname, argvalue,
- strerror(errno));
- exit(1);
- }
- if (getcwd(curdir, MAXPATHLEN) == NULL) {
- (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno));
- exit(2);
- }
- if (!is_relpath(argvalue) &&
- stat(argvalue, &sa) != -1 &&
- stat(curdir, &sb) != -1 &&
- sa.st_ino == sb.st_ino &&
- sa.st_dev == sb.st_dev)
- strncpy(curdir, argvalue, MAXPATHLEN);
- ignorePWD = TRUE;
- break;
- case 'D':
- if (argvalue == NULL || argvalue[0] == 0) goto noarg;
- Var_Set(argvalue, "1", VAR_GLOBAL);
- Var_Append(MAKEFLAGS, "-D", VAR_GLOBAL);
- Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
- break;
- case 'I':
- if (argvalue == NULL) goto noarg;
- Parse_AddIncludeDir(argvalue);
- Var_Append(MAKEFLAGS, "-I", VAR_GLOBAL);
- Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
- break;
- case 'J':
- if (argvalue == NULL) goto noarg;
- if (sscanf(argvalue, "%d,%d", &jp_0, &jp_1) != 2) {
- (void)fprintf(stderr,
- "%s: internal error -- J option malformed (%s)\n",
- progname, argvalue);
- usage();
- }
- 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;
- compatMake = TRUE;
- } else {
- Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL);
- Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
- }
- break;
- case 'N':
- noExecute = TRUE;
- noRecursiveExecute = TRUE;
- Var_Append(MAKEFLAGS, "-N", VAR_GLOBAL);
- break;
- case 'S':
- keepgoing = FALSE;
- Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL);
- break;
- case 'T':
- if (argvalue == NULL) goto noarg;
- tracefile = bmake_strdup(argvalue);
- Var_Append(MAKEFLAGS, "-T", VAR_GLOBAL);
- Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
- break;
- case 'V':
- case 'v':
- if (argvalue == NULL) goto noarg;
- printVars = c == 'v' ? EXPAND_VARS : COMPAT_VARS;
- Lst_Append(variables, argvalue);
- Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL);
- Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
- break;
- case 'W':
- parseWarnFatal = TRUE;
- break;
- case 'X':
- varNoExportEnv = TRUE;
- Var_Append(MAKEFLAGS, "-X", VAR_GLOBAL);
- break;
- case 'd':
- if (argvalue == NULL) goto noarg;
- /* If '-d-opts' don't pass to children */
- if (argvalue[0] == '-')
- argvalue++;
- else {
- Var_Append(MAKEFLAGS, "-d", VAR_GLOBAL);
- Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
- }
- parse_debug_options(argvalue);
- break;
- case 'e':
- checkEnvFirst = TRUE;
- Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL);
- break;
- case 'f':
- if (argvalue == NULL) goto noarg;
- Lst_Append(makefiles, argvalue);
- break;
- case 'i':
- ignoreErrors = TRUE;
- Var_Append(MAKEFLAGS, "-i", VAR_GLOBAL);
- break;
- case 'j':
- if (argvalue == NULL) goto noarg;
- forceJobs = TRUE;
- maxJobs = strtol(argvalue, &p, 0);
- if (*p != '\0' || maxJobs < 1) {
- (void)fprintf(stderr, "%s: illegal argument to -j -- must be positive integer!\n",
- progname);
- exit(1);
- }
- Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL);
- Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
- Var_Set(".MAKE.JOBS", argvalue, VAR_GLOBAL);
- maxJobTokens = maxJobs;
- break;
- case 'k':
- keepgoing = TRUE;
- Var_Append(MAKEFLAGS, "-k", VAR_GLOBAL);
- break;
- case 'm':
- if (argvalue == NULL) goto noarg;
- /* look for magic parent directory search string */
- if (strncmp(".../", argvalue, 4) == 0) {
- if (!Dir_FindHereOrAbove(curdir, argvalue+4,
- found_path, sizeof(found_path)))
- break; /* nothing doing */
- (void)Dir_AddDir(sysIncPath, found_path);
- } else {
- (void)Dir_AddDir(sysIncPath, argvalue);
- }
- Var_Append(MAKEFLAGS, "-m", VAR_GLOBAL);
- Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
- break;
- case 'n':
- noExecute = TRUE;
- Var_Append(MAKEFLAGS, "-n", VAR_GLOBAL);
- break;
- case 'q':
- queryFlag = TRUE;
- /* Kind of nonsensical, wot? */
- Var_Append(MAKEFLAGS, "-q", VAR_GLOBAL);
- break;
- case 'r':
- noBuiltins = TRUE;
- Var_Append(MAKEFLAGS, "-r", VAR_GLOBAL);
- break;
- case 's':
- beSilent = TRUE;
- Var_Append(MAKEFLAGS, "-s", VAR_GLOBAL);
- break;
- case 't':
- touchFlag = TRUE;
- Var_Append(MAKEFLAGS, "-t", VAR_GLOBAL);
- break;
- case 'w':
- enterFlag = TRUE;
- Var_Append(MAKEFLAGS, "-w", VAR_GLOBAL);
- break;
case '-':
dashDash = TRUE;
break;
default:
- case '?':
-#ifndef MAKE_NATIVE
- fprintf(stderr, "getopt(%s) -> %d (%c)\n",
- OPTFLAGS, c, c);
-#endif
- usage();
+ if (!MainParseArg(c, argvalue))
+ goto noarg;
}
argv += arginc;
argc -= arginc;
@@ -668,16 +660,18 @@ rearg:
* perform them if so. Else take them to be targets and stuff them
* on the end of the "create" list.
*/
- for (; argc > 1; ++argv, --argc)
- if (Parse_IsVar(argv[1])) {
- Parse_DoVar(argv[1], VAR_CMD);
+ for (; argc > 1; ++argv, --argc) {
+ VarAssign var;
+ if (Parse_IsVar(argv[1], &var)) {
+ Parse_DoVar(&var, VAR_CMDLINE);
} else {
if (!*argv[1])
Punt("illegal (null) argument.");
if (*argv[1] == '-' && !dashDash)
goto rearg;
- Lst_Append(create, bmake_strdup(argv[1]));
+ Lst_Append(opts.create, bmake_strdup(argv[1]));
}
+ }
return;
noarg:
@@ -686,29 +680,15 @@ noarg:
usage();
}
-/*-
- * Main_ParseArgLine --
- * Used by the parse module when a .MFLAGS or .MAKEFLAGS target
- * is encountered and by main() when reading the .MAKEFLAGS envariable.
- * Takes a line of arguments and breaks it into its
- * component words and passes those words and the number of them to the
- * MainParseArgs function.
- * The line should have all its leading whitespace removed.
- *
- * Input:
- * line Line to fracture
+/* Break a line of arguments into words and parse them.
*
- * Results:
- * None
- *
- * Side Effects:
- * Only those that come from the various arguments.
- */
+ * Used when a .MFLAGS or .MAKEFLAGS target is encountered during parsing and
+ * by main() when reading the MAKEFLAGS environment variable. */
void
Main_ParseArgLine(const char *line)
{
Words words;
- char *p1;
+ void *p1;
const char *argv0 = Var_Value(".MAKE", VAR_GLOBAL, &p1);
char *buf;
@@ -769,7 +749,9 @@ Main_SetObjdir(const char *fmt, ...)
/* look for the directory and try to chdir there */
if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
- if (chdir(path)) {
+ /* if not .CURDIR it must be writable */
+ if ((strcmp(path, curdir) != 0 && access(path, W_OK) != 0) ||
+ chdir(path)) {
(void)fprintf(stderr, "make warning: %s: %s.\n",
path, strerror(errno));
} else {
@@ -779,7 +761,7 @@ Main_SetObjdir(const char *fmt, ...)
Dir_InitDot();
purge_cached_realpaths();
rc = TRUE;
- if (enterFlag && strcmp(objdir, curdir) != 0)
+ if (opts.enterFlag && strcmp(objdir, curdir) != 0)
enterFlagObj = TRUE;
}
}
@@ -790,8 +772,8 @@ Main_SetObjdir(const char *fmt, ...)
static Boolean
Main_SetVarObjdir(const char *var, const char *suffix)
{
- char *path_freeIt;
- const char *path = Var_Value(var, VAR_CMD, &path_freeIt);
+ void *path_freeIt;
+ const char *path = Var_Value(var, VAR_CMDLINE, &path_freeIt);
const char *xpath;
char *xpath_freeIt;
@@ -803,9 +785,11 @@ Main_SetVarObjdir(const char *var, const char *suffix)
/* expand variable substitutions */
xpath = path;
xpath_freeIt = NULL;
- if (strchr(path, '$') != 0)
- xpath = xpath_freeIt = Var_Subst(path, VAR_GLOBAL,
- VARE_WANTRES);
+ if (strchr(path, '$') != 0) {
+ (void)Var_Subst(path, VAR_GLOBAL, VARE_WANTRES, &xpath_freeIt);
+ /* TODO: handle errors */
+ xpath = xpath_freeIt;
+ }
(void)Main_SetObjdir("%s%s", xpath, suffix);
@@ -815,23 +799,15 @@ Main_SetVarObjdir(const char *var, const char *suffix)
}
/* Read and parse the makefile.
- * Return TRUE if reading the makefile succeeded, for Lst_Find. */
-static Boolean
-ReadMakefileSucceeded(const void *fname, const void *unused)
+ * Return TRUE if reading the makefile succeeded. */
+static int
+ReadMakefileSucceeded(void *fname, void *unused)
{
return ReadMakefile(fname) == 0;
}
-/* Read and parse the makefile.
- * Return TRUE if reading the makefile failed, for Lst_Find. */
-static Boolean
-ReadMakefileFailed(const void *fname, const void *unused)
-{
- return ReadMakefile(fname) != 0;
-}
-
int
-str2Lst_Append(Lst lp, char *str, const char *sep)
+str2Lst_Append(StringList *lp, char *str, const char *sep)
{
char *cp;
int n;
@@ -870,13 +846,16 @@ MakeMode(const char *mode)
{
char *mode_freeIt = NULL;
- if (mode == NULL)
- mode = mode_freeIt = Var_Subst("${" MAKE_MODE ":tl}",
- VAR_GLOBAL, VARE_WANTRES);
+ if (mode == NULL) {
+ (void)Var_Subst("${" MAKE_MODE ":tl}",
+ VAR_GLOBAL, VARE_WANTRES, &mode_freeIt);
+ /* TODO: handle errors */
+ mode = mode_freeIt;
+ }
if (mode[0] != '\0') {
if (strstr(mode, "compat")) {
- compatMake = TRUE;
+ opts.compatMake = TRUE;
forceJobs = FALSE;
}
#if USE_META
@@ -889,46 +868,56 @@ MakeMode(const char *mode)
}
static void
+PrintVar(const char *varname, Boolean expandVars)
+{
+ if (strchr(varname, '$')) {
+ char *evalue;
+ (void)Var_Subst(varname, VAR_GLOBAL, VARE_WANTRES, &evalue);
+ /* TODO: handle errors */
+ printf("%s\n", evalue);
+ bmake_free(evalue);
+
+ } else if (expandVars) {
+ char *expr = str_concat3("${", varname, "}");
+ char *evalue;
+ (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &evalue);
+ /* TODO: handle errors */
+ free(expr);
+ printf("%s\n", evalue);
+ bmake_free(evalue);
+
+ } else {
+ void *freeIt;
+ const char *value = Var_Value(varname, VAR_GLOBAL, &freeIt);
+ printf("%s\n", value ? value : "");
+ bmake_free(freeIt);
+ }
+}
+
+static void
doPrintVars(void)
{
- LstNode ln;
+ StringListNode *ln;
Boolean expandVars;
- if (printVars == EXPAND_VARS)
+ if (opts.printVars == EXPAND_VARS)
expandVars = TRUE;
- else if (debugVflag)
+ else if (opts.debugVflag)
expandVars = FALSE;
else
expandVars = getBoolean(".MAKE.EXPAND_VARIABLES", FALSE);
- for (ln = Lst_First(variables); ln != NULL; ln = LstNode_Next(ln)) {
- char *var = LstNode_Datum(ln);
- const char *value;
- char *p1;
-
- if (strchr(var, '$')) {
- value = p1 = Var_Subst(var, VAR_GLOBAL, VARE_WANTRES);
- } else if (expandVars) {
- char tmp[128];
- int len = snprintf(tmp, sizeof(tmp), "${%s}", var);
-
- if (len >= (int)sizeof(tmp))
- Fatal("%s: variable name too big: %s",
- progname, var);
- value = p1 = Var_Subst(tmp, VAR_GLOBAL, VARE_WANTRES);
- } else {
- value = Var_Value(var, VAR_GLOBAL, &p1);
- }
- printf("%s\n", value ? value : "");
- bmake_free(p1);
+ for (ln = opts.variables->first; ln != NULL; ln = ln->next) {
+ const char *varname = ln->datum;
+ PrintVar(varname, expandVars);
}
}
static Boolean
runTargets(void)
{
- Lst targs; /* target nodes to create -- passed to Make_Init */
- Boolean outOfDate; /* FALSE if all targets up to date */
+ GNodeList *targs; /* target nodes to create -- passed to Make_Init */
+ Boolean outOfDate; /* FALSE if all targets up to date */
/*
* Have now read the entire graph and need to make a list of
@@ -936,12 +925,12 @@ runTargets(void)
* we consult the parsing module to find the main target(s)
* to create.
*/
- if (Lst_IsEmpty(create))
+ if (Lst_IsEmpty(opts.create))
targs = Parse_MainName();
else
- targs = Targ_FindList(create, TARG_CREATE);
+ targs = Targ_FindList(opts.create);
- if (!compatMake) {
+ if (!opts.compatMake) {
/*
* Initialize job module before traversing the graph
* now that any .BEGIN and .END targets have been read.
@@ -949,7 +938,7 @@ runTargets(void)
* (to prevent the .BEGIN from being executed should
* it exist).
*/
- if (!queryFlag) {
+ if (!opts.queryFlag) {
Job_Init();
jobsRunning = TRUE;
}
@@ -968,6 +957,397 @@ runTargets(void)
return outOfDate;
}
+/*
+ * 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.
+ */
+static void
+InitVarTargets(void)
+{
+ StringListNode *ln;
+
+ if (Lst_IsEmpty(opts.create)) {
+ Var_Set(".TARGETS", "", VAR_GLOBAL);
+ return;
+ }
+
+ for (ln = opts.create->first; ln != NULL; ln = ln->next) {
+ char *name = ln->datum;
+ Var_Append(".TARGETS", name, VAR_GLOBAL);
+ }
+}
+
+static void
+InitRandom(void)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ srandom((unsigned int)(tv.tv_sec + tv.tv_usec));
+}
+
+static const char *
+init_machine(const struct utsname *utsname)
+{
+#ifdef FORCE_MACHINE
+ const char *machine = FORCE_MACHINE;
+#else
+ const char *machine = getenv("MACHINE");
+#endif
+ if (machine != NULL)
+ return machine;
+
+#ifdef MAKE_NATIVE
+ return utsname->machine;
+#else
+#ifdef MAKE_MACHINE
+ return MAKE_MACHINE;
+#else
+ return "unknown";
+#endif
+#endif
+}
+
+static const char *
+init_machine_arch(void)
+{
+ const char *env = getenv("MACHINE_ARCH");
+ if (env != NULL)
+ return env;
+
+#if defined(MAKE_NATIVE) && defined(CTL_HW)
+ {
+ struct utsname utsname;
+ static char machine_arch_buf[sizeof(utsname.machine)];
+ const int mib[2] = { CTL_HW, HW_MACHINE_ARCH };
+ size_t len = sizeof(machine_arch_buf);
+
+ if (sysctl(mib, __arraycount(mib), machine_arch_buf,
+ &len, NULL, 0) < 0) {
+ (void)fprintf(stderr, "%s: sysctl failed (%s).\n", progname,
+ strerror(errno));
+ exit(2);
+ }
+
+ return machine_arch_buf;
+ }
+#else
+#ifndef MACHINE_ARCH
+#ifdef MAKE_MACHINE_ARCH
+ return MAKE_MACHINE_ARCH;
+#else
+ return "unknown";
+#endif
+#else
+ return MACHINE_ARCH;
+#endif
+#endif
+}
+
+#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.
+ *
+ * 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
+ * not provide it.
+ *
+ * So, to stop it breaking this case only, we ignore PWD if
+ * MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains a variable expression.
+ */
+static void
+HandlePWD(const struct stat *curdir_st)
+{
+ char *pwd;
+ void *prefix_freeIt, *makeobjdir_freeIt;
+ const char *makeobjdir;
+ struct stat pwd_st;
+
+ if (ignorePWD || (pwd = getenv("PWD")) == NULL)
+ return;
+
+ if (Var_Value("MAKEOBJDIRPREFIX", VAR_CMDLINE, &prefix_freeIt) != NULL) {
+ bmake_free(prefix_freeIt);
+ return;
+ }
+
+ makeobjdir = Var_Value("MAKEOBJDIR", VAR_CMDLINE, &makeobjdir_freeIt);
+ if (makeobjdir != NULL && strchr(makeobjdir, '$') != NULL)
+ goto ignore_pwd;
+
+ if (stat(pwd, &pwd_st) == 0 &&
+ curdir_st->st_ino == pwd_st.st_ino &&
+ curdir_st->st_dev == pwd_st.st_dev)
+ (void)strncpy(curdir, pwd, MAXPATHLEN);
+
+ignore_pwd:
+ bmake_free(makeobjdir_freeIt);
+}
+#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.
+ *
+ * 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.
+ */
+static void
+InitObjdir(const char *machine, const char *machine_arch)
+{
+ Dir_InitDir(curdir);
+ (void)Main_SetObjdir("%s", curdir);
+
+ if (!Main_SetVarObjdir("MAKEOBJDIRPREFIX", curdir) &&
+ !Main_SetVarObjdir("MAKEOBJDIR", "") &&
+ !Main_SetObjdir("%s.%s-%s", _PATH_OBJDIR, machine, machine_arch) &&
+ !Main_SetObjdir("%s.%s", _PATH_OBJDIR, machine) &&
+ !Main_SetObjdir("%s", _PATH_OBJDIR))
+ (void)Main_SetObjdir("%s%s", _PATH_OBJDIRPREFIX, curdir);
+}
+
+/* get rid of resource limit on file descriptors */
+static void
+UnlimitFiles(void)
+{
+#if defined(MAKE_NATIVE) || (defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE))
+ struct rlimit rl;
+ if (getrlimit(RLIMIT_NOFILE, &rl) != -1 &&
+ rl.rlim_cur != rl.rlim_max) {
+ rl.rlim_cur = rl.rlim_max;
+ (void)setrlimit(RLIMIT_NOFILE, &rl);
+ }
+#endif
+}
+
+static void
+CmdOpts_Init(void)
+{
+ opts.compatMake = FALSE; /* No compat mode */
+ opts.debug = 0; /* No debug verbosity, please. */
+ /* opts.debug_file has been initialized earlier */
+ opts.debugVflag = FALSE;
+ opts.checkEnvFirst = FALSE;
+ opts.makefiles = Lst_New();
+ opts.ignoreErrors = FALSE; /* Pay attention to non-zero returns */
+ opts.maxJobs = DEFMAXLOCAL; /* Set default local max concurrency */
+ opts.keepgoing = FALSE; /* Stop on error */
+ opts.noRecursiveExecute = FALSE; /* Execute all .MAKE targets */
+ opts.noExecute = FALSE; /* Execute all commands */
+ opts.queryFlag = FALSE; /* This is not just a check-run */
+ opts.noBuiltins = FALSE; /* Read the built-in rules */
+ opts.beSilent = FALSE; /* Print commands as executed */
+ opts.touchFlag = FALSE; /* Actually update targets */
+ opts.printVars = 0;
+ opts.variables = Lst_New();
+ opts.parseWarnFatal = FALSE;
+ opts.enterFlag = FALSE;
+ opts.varNoExportEnv = FALSE;
+ opts.create = Lst_New();
+}
+
+/* Initialize MAKE and .MAKE to the path of the executable, so that it can be
+ * found by execvp(3) and the shells, even after a chdir.
+ *
+ * If it's a relative path and contains a '/', resolve it to an absolute path.
+ * Otherwise keep it as is, assuming it will be found in the PATH. */
+static void
+InitVarMake(const char *argv0)
+{
+ const char *make = argv0;
+
+ if (argv0[0] != '/' && strchr(argv0, '/') != NULL) {
+ char pathbuf[MAXPATHLEN];
+ const char *abs = cached_realpath(argv0, pathbuf);
+ struct stat st;
+ if (abs != NULL && abs[0] == '/' && stat(make, &st) == 0)
+ make = abs;
+ }
+
+ Var_Set("MAKE", make, VAR_GLOBAL);
+ Var_Set(".MAKE", make, VAR_GLOBAL);
+}
+
+static void
+InitDefSysIncPath(char *syspath)
+{
+ static char defsyspath[] = _PATH_DEFSYSPATH;
+ char *start, *cp;
+
+ /*
+ * If no user-supplied system path was given (through the -m option)
+ * add the directories from the DEFSYSPATH (more than one may be given
+ * as dir1:...:dirn) to the system include path.
+ */
+ /* XXX: mismatch: the -m option sets sysIncPath, not syspath */
+ if (syspath == NULL || syspath[0] == '\0')
+ syspath = defsyspath;
+ else
+ syspath = bmake_strdup(syspath);
+
+ for (start = syspath; *start != '\0'; start = cp) {
+ for (cp = start; *cp != '\0' && *cp != ':'; cp++)
+ continue;
+ if (*cp == ':') {
+ *cp++ = '\0';
+ }
+ /* look for magic parent directory search string */
+ if (strncmp(".../", start, 4) != 0) {
+ (void)Dir_AddDir(defSysIncPath, start);
+ } else {
+ char *dir = Dir_FindHereOrAbove(curdir, start + 4);
+ if (dir != NULL) {
+ (void)Dir_AddDir(defSysIncPath, dir);
+ free(dir);
+ }
+ }
+ }
+
+ if (syspath != defsyspath)
+ free(syspath);
+}
+
+static void
+ReadBuiltinRules(void)
+{
+ StringList *sysMkPath = Lst_New();
+ Dir_Expand(_PATH_DEFSYSMK,
+ Lst_IsEmpty(sysIncPath) ? defSysIncPath : sysIncPath,
+ sysMkPath);
+ if (Lst_IsEmpty(sysMkPath))
+ Fatal("%s: no system rules (%s).", progname, _PATH_DEFSYSMK);
+ if (!Lst_ForEachUntil(sysMkPath, ReadMakefileSucceeded, NULL))
+ Fatal("%s: cannot open %s.", progname,
+ (char *)sysMkPath->first->datum);
+ /* XXX: sysMkPath is not freed */
+}
+
+static void
+InitMaxJobs(void)
+{
+ char *value;
+ int n;
+
+ if (forceJobs || opts.compatMake ||
+ !Var_Exists(".MAKE.JOBS", VAR_GLOBAL))
+ return;
+
+ (void)Var_Subst("${.MAKE.JOBS}", VAR_GLOBAL, VARE_WANTRES, &value);
+ /* TODO: handle errors */
+ n = (int)strtol(value, NULL, 0);
+ if (n < 1) {
+ (void)fprintf(stderr,
+ "%s: illegal value for .MAKE.JOBS "
+ "-- must be positive integer!\n",
+ progname);
+ exit(1);
+ }
+
+ if (n != opts.maxJobs) {
+ Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, value, VAR_GLOBAL);
+ }
+
+ opts.maxJobs = n;
+ maxJobTokens = opts.maxJobs;
+ forceJobs = TRUE;
+ free(value);
+}
+
+/*
+ * For compatibility, look at the directories in the VPATH variable
+ * and add them to the search path, if the variable is defined. The
+ * variable's value is in the same format as the PATH environment
+ * variable, i.e. <directory>:<directory>:<directory>...
+ */
+static void
+InitVpath(void)
+{
+ char *vpath, savec, *path;
+ if (!Var_Exists("VPATH", VAR_CMDLINE))
+ return;
+
+ (void)Var_Subst("${VPATH}", VAR_CMDLINE, VARE_WANTRES, &vpath);
+ /* TODO: handle errors */
+ path = vpath;
+ do {
+ char *cp;
+ /* skip to end of directory */
+ for (cp = path; *cp != ':' && *cp != '\0'; cp++)
+ continue;
+ /* Save terminator character so know when to stop */
+ savec = *cp;
+ *cp = '\0';
+ /* Add directory to search path */
+ (void)Dir_AddDir(dirSearchPath, path);
+ *cp = savec;
+ path = cp + 1;
+ } while (savec == ':');
+ free(vpath);
+}
+
+static void
+ReadMakefiles(void)
+{
+ if (opts.makefiles->first != NULL) {
+ StringListNode *ln;
+
+ for (ln = opts.makefiles->first; ln != NULL; ln = ln->next) {
+ if (ReadMakefile(ln->datum) != 0)
+ Fatal("%s: cannot open %s.",
+ progname, (char *)ln->datum);
+ }
+ } else {
+ char *p1;
+ (void)Var_Subst("${" MAKEFILE_PREFERENCE "}",
+ VAR_CMDLINE, VARE_WANTRES, &p1);
+ /* TODO: handle errors */
+ (void)str2Lst_Append(opts.makefiles, p1, NULL);
+ (void)Lst_ForEachUntil(opts.makefiles,
+ ReadMakefileSucceeded, NULL);
+ free(p1);
+ }
+}
+
+static void
+CleanUp(void)
+{
+#ifdef CLEANUP
+ Lst_Destroy(opts.variables, free);
+ Lst_Free(opts.makefiles); /* don't free, may be used in GNodes */
+ Lst_Destroy(opts.create, free);
+#endif
+
+ /* print the graph now it's been processed if the user requested it */
+ if (DEBUG(GRAPH2))
+ Targ_PrintGraph(2);
+
+ Trace_Log(MAKEEND, 0);
+
+ if (enterFlagObj)
+ printf("%s: Leaving directory `%s'\n", progname, objdir);
+ if (opts.enterFlag)
+ printf("%s: Leaving directory `%s'\n", progname, curdir);
+
+#ifdef USE_META
+ meta_finish();
+#endif
+ Suff_End();
+ Targ_End();
+ Arch_End();
+ Var_End();
+ Parse_End();
+ Dir_End();
+ Job_End();
+ Trace_End();
+}
+
/*-
* main --
* The main function, for obvious reasons. Initializes variables
@@ -988,55 +1368,28 @@ runTargets(void)
int
main(int argc, char **argv)
{
- Boolean outOfDate; /* FALSE if all targets up to date */
- struct stat sb, sa;
- char *p1, *path;
- char mdpath[MAXPATHLEN];
-#ifdef FORCE_MACHINE
- const char *machine = FORCE_MACHINE;
-#else
- const char *machine = getenv("MACHINE");
-#endif
- const char *machine_arch = getenv("MACHINE_ARCH");
+ Boolean outOfDate; /* FALSE if all targets up to date */
+ struct stat sa;
+ const char *machine;
+ const char *machine_arch;
char *syspath = getenv("MAKESYSPATH");
- Lst sysMkPath; /* Path of sys.mk */
- char *cp = NULL, *start;
- /* avoid faults on read-only strings */
- static char defsyspath[] = _PATH_DEFSYSPATH;
- char found_path[MAXPATHLEN + 1]; /* for searching for sys.mk */
- struct timeval rightnow; /* to initialize random seed */
struct utsname utsname;
/* default to writing debug to stderr */
- debug_file = stderr;
+ opts.debug_file = stderr;
#ifdef SIGINFO
(void)bmake_signal(SIGINFO, siginfo);
#endif
- /*
- * Set the seed to produce a different random sequence
- * on each program execution.
- */
- gettimeofday(&rightnow, NULL);
- srandom(rightnow.tv_sec + rightnow.tv_usec);
+
+ InitRandom();
if ((progname = strrchr(argv[0], '/')) != NULL)
progname++;
else
progname = argv[0];
-#if defined(MAKE_NATIVE) || (defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE))
- /*
- * get rid of resource limit on file descriptors
- */
- {
- struct rlimit rl;
- if (getrlimit(RLIMIT_NOFILE, &rl) != -1 &&
- rl.rlim_cur != rl.rlim_max) {
- rl.rlim_cur = rl.rlim_max;
- (void)setrlimit(RLIMIT_NOFILE, &rl);
- }
- }
-#endif
+
+ UnlimitFiles();
if (uname(&utsname) == -1) {
(void)fprintf(stderr, "%s: uname failed (%s).\n", progname,
@@ -1052,44 +1405,8 @@ main(int argc, char **argv)
* Note that both MACHINE and MACHINE_ARCH are decided at
* run-time.
*/
- if (!machine) {
-#ifdef MAKE_NATIVE
- machine = utsname.machine;
-#else
-#ifdef MAKE_MACHINE
- machine = MAKE_MACHINE;
-#else
- machine = "unknown";
-#endif
-#endif
- }
-
- if (!machine_arch) {
-#if defined(MAKE_NATIVE) && defined(HAVE_SYSCTL) && defined(CTL_HW) && defined(HW_MACHINE_ARCH)
- static char machine_arch_buf[sizeof(utsname.machine)];
- int mib[2] = { CTL_HW, HW_MACHINE_ARCH };
- size_t len = sizeof(machine_arch_buf);
-
- if (sysctl(mib, __arraycount(mib), machine_arch_buf,
- &len, NULL, 0) < 0) {
- (void)fprintf(stderr, "%s: sysctl failed (%s).\n", progname,
- strerror(errno));
- exit(2);
- }
-
- machine_arch = machine_arch_buf;
-#else
-#ifndef MACHINE_ARCH
-#ifdef MAKE_MACHINE_ARCH
- machine_arch = MAKE_MACHINE_ARCH;
-#else
- machine_arch = "unknown";
-#endif
-#else
- machine_arch = MACHINE_ARCH;
-#endif
-#endif
- }
+ machine = init_machine(&utsname);
+ machine_arch = init_machine_arch();
myPid = getpid(); /* remember this for vFork() */
@@ -1115,27 +1432,12 @@ main(int argc, char **argv)
VAR_GLOBAL);
Var_Set(MAKE_DEPENDFILE, ".depend", VAR_GLOBAL);
- create = Lst_Init();
- makefiles = Lst_Init();
- printVars = 0;
- debugVflag = FALSE;
- variables = Lst_Init();
- beSilent = FALSE; /* Print commands as executed */
- ignoreErrors = FALSE; /* Pay attention to non-zero returns */
- noExecute = FALSE; /* Execute all commands */
- noRecursiveExecute = FALSE; /* Execute all .MAKE targets */
- keepgoing = FALSE; /* Stop on error */
+ CmdOpts_Init();
allPrecious = FALSE; /* Remove targets when interrupted */
deleteOnError = FALSE; /* Historical default behavior */
- queryFlag = FALSE; /* This is not just a check-run */
- noBuiltins = FALSE; /* Read the built-in rules */
- touchFlag = FALSE; /* Actually update targets */
- debug = 0; /* No debug verbosity, please. */
jobsRunning = FALSE;
- maxJobs = DEFMAXLOCAL; /* Set default local max concurrency */
- maxJobTokens = maxJobs;
- compatMake = FALSE; /* No compat mode */
+ maxJobTokens = opts.maxJobs;
ignorePWD = FALSE;
/*
@@ -1151,30 +1453,13 @@ main(int argc, char **argv)
* MFLAGS also gets initialized empty, for compatibility.
*/
Parse_Init();
- if (argv[0][0] == '/' || strchr(argv[0], '/') == NULL) {
- /*
- * Leave alone if it is an absolute path, or if it does
- * not contain a '/' in which case we need to find it in
- * the path, like execvp(3) and the shells do.
- */
- p1 = argv[0];
- } else {
- /*
- * A relative path, canonicalize it.
- */
- p1 = cached_realpath(argv[0], mdpath);
- if (!p1 || *p1 != '/' || stat(p1, &sb) < 0) {
- p1 = argv[0]; /* realpath failed */
- }
- }
- Var_Set("MAKE", p1, VAR_GLOBAL);
- Var_Set(".MAKE", p1, VAR_GLOBAL);
+ InitVarMake(argv[0]);
Var_Set(MAKEFLAGS, "", VAR_GLOBAL);
Var_Set(MAKEOVERRIDES, "", VAR_GLOBAL);
Var_Set("MFLAGS", "", VAR_GLOBAL);
Var_Set(".ALLTARGETS", "", VAR_GLOBAL);
/* some makefiles need to know this */
- Var_Set(MAKE_LEVEL ".ENV", MAKE_LEVEL_ENV, VAR_CMD);
+ Var_Set(MAKE_LEVEL ".ENV", MAKE_LEVEL_ENV, VAR_CMDLINE);
/*
* Set some other useful macros
@@ -1209,9 +1494,11 @@ main(int argc, char **argv)
* in a different format).
*/
#ifdef POSIX
- p1 = explode(getenv("MAKEFLAGS"));
- Main_ParseArgLine(p1);
- free(p1);
+ {
+ char *p1 = explode(getenv("MAKEFLAGS"));
+ Main_ParseArgLine(p1);
+ free(p1);
+ }
#else
Main_ParseArgLine(getenv("MAKE"));
#endif
@@ -1228,7 +1515,7 @@ main(int argc, char **argv)
MainParseArgs(argc, argv);
- if (enterFlag)
+ if (opts.enterFlag)
printf("%s: Entering directory `%s'\n", progname, curdir);
/*
@@ -1240,57 +1527,12 @@ main(int argc, char **argv)
exit(2);
}
- /*
- * All this code is so that we know where we are when we start up
- * on a different machine with pmake.
- * 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
- * not provide it.
- * So, to stop it breaking this case only, we ignore PWD if
- * MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains a transform.
- */
#ifndef NO_PWD_OVERRIDE
- if (!ignorePWD) {
- char *pwd, *ptmp1 = NULL, *ptmp2 = NULL;
-
- if ((pwd = getenv("PWD")) != NULL &&
- Var_Value("MAKEOBJDIRPREFIX", VAR_CMD, &ptmp1) == NULL) {
- const char *makeobjdir = Var_Value("MAKEOBJDIR",
- VAR_CMD, &ptmp2);
-
- if (makeobjdir == NULL || !strchr(makeobjdir, '$')) {
- if (stat(pwd, &sb) == 0 &&
- sa.st_ino == sb.st_ino &&
- sa.st_dev == sb.st_dev)
- (void)strncpy(curdir, pwd, MAXPATHLEN);
- }
- }
- bmake_free(ptmp1);
- bmake_free(ptmp2);
- }
+ HandlePWD(&sa);
#endif
Var_Set(".CURDIR", curdir, VAR_GLOBAL);
- /*
- * 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.
- */
- Dir_InitDir(curdir);
- (void)Main_SetObjdir("%s", curdir);
-
- if (!Main_SetVarObjdir("MAKEOBJDIRPREFIX", curdir) &&
- !Main_SetVarObjdir("MAKEOBJDIR", "") &&
- !Main_SetObjdir("%s.%s-%s", _PATH_OBJDIR, machine, machine_arch) &&
- !Main_SetObjdir("%s.%s", _PATH_OBJDIR, machine) &&
- !Main_SetObjdir("%s", _PATH_OBJDIR))
- (void)Main_SetObjdir("%s%s", _PATH_OBJDIRPREFIX, curdir);
+ InitObjdir(machine, machine_arch);
/*
* Initialize archive, target and suffix modules in preparation for
@@ -1306,95 +1548,30 @@ main(int argc, char **argv)
Trace_Log(MAKESTART, NULL);
- /*
- * 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.
- */
- if (!Lst_IsEmpty(create)) {
- LstNode ln;
-
- for (ln = Lst_First(create); ln != NULL; ln = LstNode_Next(ln)) {
- char *name = LstNode_Datum(ln);
- Var_Append(".TARGETS", name, VAR_GLOBAL);
- }
- } else
- Var_Set(".TARGETS", "", VAR_GLOBAL);
-
-
- /*
- * If no user-supplied system path was given (through the -m option)
- * add the directories from the DEFSYSPATH (more than one may be given
- * as dir1:...:dirn) to the system include path.
- */
- /* XXX: mismatch: the -m option sets sysIncPath, not syspath */
- if (syspath == NULL || syspath[0] == '\0')
- syspath = defsyspath;
- else
- syspath = bmake_strdup(syspath);
+ InitVarTargets();
- for (start = syspath; *start != '\0'; start = cp) {
- for (cp = start; *cp != '\0' && *cp != ':'; cp++)
- continue;
- if (*cp == ':') {
- *cp++ = '\0';
- }
- /* look for magic parent directory search string */
- if (strncmp(".../", start, 4) != 0) {
- (void)Dir_AddDir(defIncPath, start);
- } else {
- if (Dir_FindHereOrAbove(curdir, start+4,
- found_path, sizeof(found_path))) {
- (void)Dir_AddDir(defIncPath, found_path);
- }
- }
- }
- if (syspath != defsyspath)
- free(syspath);
+ InitDefSysIncPath(syspath);
/*
* Read in the built-in rules first, followed by the specified
* makefiles, or the default makefile and Makefile, in that order,
* if no makefiles were given on the command line.
*/
- if (!noBuiltins) {
- LstNode ln;
-
- sysMkPath = Lst_Init();
- Dir_Expand(_PATH_DEFSYSMK,
- Lst_IsEmpty(sysIncPath) ? defIncPath : sysIncPath,
- sysMkPath);
- if (Lst_IsEmpty(sysMkPath))
- Fatal("%s: no system rules (%s).", progname,
- _PATH_DEFSYSMK);
- ln = Lst_Find(sysMkPath, ReadMakefileSucceeded, NULL);
- if (ln == NULL)
- Fatal("%s: cannot open %s.", progname,
- (char *)LstNode_Datum(Lst_First(sysMkPath)));
- }
-
- if (!Lst_IsEmpty(makefiles)) {
- LstNode ln;
-
- ln = Lst_Find(makefiles, ReadMakefileFailed, NULL);
- if (ln != NULL)
- Fatal("%s: cannot open %s.", progname,
- (char *)LstNode_Datum(ln));
- } else {
- p1 = Var_Subst("${" MAKEFILE_PREFERENCE "}",
- VAR_CMD, VARE_WANTRES);
- (void)str2Lst_Append(makefiles, p1, NULL);
- (void)Lst_Find(makefiles, ReadMakefileSucceeded, NULL);
- free(p1);
- }
-
+ if (!opts.noBuiltins)
+ ReadBuiltinRules();
+ ReadMakefiles();
+
/* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */
- if (!noBuiltins || !printVars) {
- makeDependfile = Var_Subst("${.MAKE.DEPENDFILE:T}",
- VAR_CMD, VARE_WANTRES);
- doing_depend = TRUE;
- (void)ReadMakefile(makeDependfile);
- doing_depend = FALSE;
+ if (!opts.noBuiltins || !opts.printVars) {
+ /* ignore /dev/null and anything starting with "no" */
+ (void)Var_Subst("${.MAKE.DEPENDFILE:N/dev/null:Nno*:T}",
+ VAR_CMDLINE, VARE_WANTRES, &makeDependfile);
+ if (makeDependfile[0] != '\0') {
+ /* TODO: handle errors */
+ doing_depend = TRUE;
+ (void)ReadMakefile(makeDependfile);
+ doing_depend = FALSE;
+ }
}
if (enterFlagObj)
@@ -1402,81 +1579,33 @@ main(int argc, char **argv)
MakeMode(NULL);
- Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL, &p1), VAR_GLOBAL);
- bmake_free(p1);
-
- if (!forceJobs && !compatMake &&
- Var_Exists(".MAKE.JOBS", VAR_GLOBAL)) {
- char *value;
- int n;
+ {
+ void *freeIt;
+ Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL, &freeIt),
+ VAR_GLOBAL);
+ bmake_free(freeIt);
- value = Var_Subst("${.MAKE.JOBS}", VAR_GLOBAL, VARE_WANTRES);
- n = strtol(value, NULL, 0);
- if (n < 1) {
- (void)fprintf(stderr, "%s: illegal value for .MAKE.JOBS -- must be positive integer!\n",
- progname);
- exit(1);
- }
- if (n != maxJobs) {
- Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL);
- Var_Append(MAKEFLAGS, value, VAR_GLOBAL);
- }
- maxJobs = n;
- maxJobTokens = maxJobs;
- forceJobs = TRUE;
- free(value);
}
+ InitMaxJobs();
+
/*
* Be compatible if user did not specify -j and did not explicitly
* turned compatibility on
*/
- if (!compatMake && !forceJobs) {
- compatMake = TRUE;
+ if (!opts.compatMake && !forceJobs) {
+ opts.compatMake = TRUE;
}
- if (!compatMake)
+ if (!opts.compatMake)
Job_ServerStart(maxJobTokens, jp_0, jp_1);
- if (DEBUG(JOB))
- fprintf(debug_file,
- "job_pipe %d %d, maxjobs %d, tokens %d, compat %d\n",
- jp_0, jp_1, maxJobs, maxJobTokens, compatMake ? 1 : 0);
+ DEBUG5(JOB, "job_pipe %d %d, maxjobs %d, tokens %d, compat %d\n",
+ jp_0, jp_1, opts.maxJobs, maxJobTokens, opts.compatMake ? 1 : 0);
- if (!printVars)
+ if (!opts.printVars)
Main_ExportMAKEFLAGS(TRUE); /* initial export */
-
- /*
- * For compatibility, look at the directories in the VPATH variable
- * and add them to the search path, if the variable is defined. The
- * variable's value is in the same format as the PATH envariable, i.e.
- * <directory>:<directory>:<directory>...
- */
- if (Var_Exists("VPATH", VAR_CMD)) {
- char *vpath, savec;
- /*
- * GCC stores string constants in read-only memory, but
- * Var_Subst will want to write this thing, so store it
- * in an array
- */
- static char VPATH[] = "${VPATH}";
-
- vpath = Var_Subst(VPATH, VAR_CMD, VARE_WANTRES);
- path = vpath;
- do {
- /* skip to end of directory */
- for (cp = path; *cp != ':' && *cp != '\0'; cp++)
- continue;
- /* Save terminator character so know when to stop */
- savec = *cp;
- *cp = '\0';
- /* Add directory to search path */
- (void)Dir_AddDir(dirSearchPath, path);
- *cp = savec;
- path = cp + 1;
- } while (savec == ':');
- free(vpath);
- }
+ InitVpath();
/*
* Now that all search paths have been read for suffixes et al, it's
@@ -1494,42 +1623,17 @@ main(int argc, char **argv)
Targ_PrintGraph(1);
/* print the values of any variables requested by the user */
- if (printVars) {
+ if (opts.printVars) {
doPrintVars();
outOfDate = FALSE;
} else {
outOfDate = runTargets();
}
-#ifdef CLEANUP
- Lst_Free(variables);
- Lst_Free(makefiles);
- Lst_Destroy(create, free);
-#endif
-
- /* print the graph now it's been processed if the user requested it */
- if (DEBUG(GRAPH2))
- Targ_PrintGraph(2);
-
- Trace_Log(MAKEEND, 0);
-
- if (enterFlagObj)
- printf("%s: Leaving directory `%s'\n", progname, objdir);
- if (enterFlag)
- printf("%s: Leaving directory `%s'\n", progname, curdir);
-
-#ifdef USE_META
- meta_finish();
-#endif
- Suff_End();
- Targ_End();
- Arch_End();
- Var_End();
- Parse_End();
- Dir_End();
- Job_End();
- Trace_End();
+ CleanUp();
+ if (DEBUG(LINT) && (errors > 0 || Parse_GetFatals() > 0))
+ return 2; /* Not 1 so -q can distinguish error */
return outOfDate ? 1 : 0;
}
@@ -1572,9 +1676,11 @@ ReadMakefile(const char *fname)
}
/* look in -I and system include directories. */
name = Dir_FindFile(fname, parseIncPath);
- if (!name)
- name = Dir_FindFile(fname,
- Lst_IsEmpty(sysIncPath) ? defIncPath : sysIncPath);
+ if (!name) {
+ SearchPath *sysInc = Lst_IsEmpty(sysIncPath)
+ ? defSysIncPath : sysIncPath;
+ name = Dir_FindFile(fname, sysInc);
+ }
if (!name || (fd = open(name, O_RDONLY)) == -1) {
free(name);
free(path);
@@ -1613,10 +1719,10 @@ found:
char *
Cmd_Exec(const char *cmd, const char **errfmt)
{
- const char *args[4]; /* Args for invoking the shell */
- int fds[2]; /* Pipe streams */
- int cpid; /* Child PID */
- int pid; /* PID from wait() */
+ const char *args[4]; /* Args for invoking the shell */
+ int fds[2]; /* Pipe streams */
+ int cpid; /* Child PID */
+ int pid; /* PID from wait() */
WAIT_T status; /* command exit status */
Buffer buf; /* buffer to store the result */
ssize_t bytes_read;
@@ -1704,7 +1810,7 @@ Cmd_Exec(const char *cmd, const char **errfmt)
JobReapChild(pid, status, FALSE);
continue;
}
- res_len = Buf_Size(&buf);
+ res_len = Buf_Len(&buf);
res = Buf_Destroy(&buf, FALSE);
if (savederr != 0)
@@ -1728,24 +1834,17 @@ bad:
return bmake_strdup("");
}
-/*-
- * Error --
- * Print an error message given its format.
+/* Print a printf-style error message.
*
- * Results:
- * None.
- *
- * Side Effects:
- * The message is printed.
- */
-/* VARARGS */
+ * This error message has no consequences, in particular it does not affect
+ * the exit status. */
void
Error(const char *fmt, ...)
{
va_list ap;
FILE *err_file;
- err_file = debug_file;
+ err_file = opts.debug_file;
if (err_file == stdout)
err_file = stderr;
(void)fflush(stdout);
@@ -1760,20 +1859,12 @@ Error(const char *fmt, ...)
break;
err_file = stderr;
}
+ errors++;
}
-/*-
- * Fatal --
- * Produce a Fatal error message. If jobs are running, waits for them
- * to finish.
- *
- * Results:
- * None
+/* Produce a Fatal error message, then exit immediately.
*
- * Side Effects:
- * The program exits
- */
-/* VARARGS */
+ * If jobs are running, wait for them to finish. */
void
Fatal(const char *fmt, ...)
{
@@ -1797,18 +1888,8 @@ Fatal(const char *fmt, ...)
exit(2); /* Not 1 so -q can distinguish error */
}
-/*
- * Punt --
- * Major exception once jobs are being created. Kills all jobs, prints
- * a message and exits.
- *
- * Results:
- * None
- *
- * Side Effects:
- * All children are killed indiscriminately and the program Lib_Exits
- */
-/* VARARGS */
+/* Major exception once jobs are being created.
+ * Kills all jobs, prints a message and exits. */
void
Punt(const char *fmt, ...)
{
@@ -1827,16 +1908,7 @@ Punt(const char *fmt, ...)
DieHorribly();
}
-/*-
- * DieHorribly --
- * Exit without giving a message.
- *
- * Results:
- * None
- *
- * Side Effects:
- * A big one...
- */
+/* Exit without giving a message. */
void
DieHorribly(void)
{
@@ -1848,24 +1920,15 @@ DieHorribly(void)
exit(2); /* Not 1, so -q can distinguish error */
}
-/*
- * Finish --
- * Called when aborting due to errors in child shell to signal
- * abnormal exit.
- *
- * Results:
- * None
- *
- * Side Effects:
- * The program exits
- */
+/* Called when aborting due to errors in child shell to signal abnormal exit.
+ * The program exits.
+ * Errors is the number of errors encountered in Make_Make. */
void
-Finish(int errors)
- /* number of errors encountered in Make_Make */
+Finish(int errs)
{
if (dieQuietly(NULL, -1))
exit(2);
- Fatal("%d error%s", errors, errors == 1 ? "" : "s");
+ Fatal("%d error%s", errs, errs == 1 ? "" : "s");
}
/*
@@ -1887,37 +1950,45 @@ eunlink(const char *file)
return unlink(file);
}
+static void
+write_all(int fd, const void *data, size_t n)
+{
+ const char *mem = data;
+
+ while (n > 0) {
+ ssize_t written = write(fd, mem, n);
+ if (written == -1 && errno == EAGAIN)
+ continue;
+ if (written == -1)
+ break;
+ mem += written;
+ n -= (size_t)written;
+ }
+}
+
/*
- * execError --
+ * execDie --
* Print why exec failed, avoiding stdio.
*/
-void
-execError(const char *af, const char *av)
+void MAKE_ATTR_DEAD
+execDie(const char *af, const char *av)
{
-#ifdef USE_IOVEC
- int i = 0;
- struct iovec iov[8];
-#define IOADD(s) \
- (void)(iov[i].iov_base = UNCONST(s), \
- iov[i].iov_len = strlen(iov[i].iov_base), \
- i++)
-#else
-#define IOADD(s) (void)write(2, s, strlen(s))
-#endif
+ Buffer buf;
- IOADD(progname);
- IOADD(": ");
- IOADD(af);
- IOADD("(");
- IOADD(av);
- IOADD(") failed (");
- IOADD(strerror(errno));
- IOADD(")\n");
-
-#ifdef USE_IOVEC
- while (writev(2, iov, 8) == -1 && errno == EAGAIN)
- continue;
-#endif
+ Buf_Init(&buf, 0);
+ Buf_AddStr(&buf, progname);
+ Buf_AddStr(&buf, ": ");
+ Buf_AddStr(&buf, af);
+ Buf_AddStr(&buf, "(");
+ Buf_AddStr(&buf, av);
+ Buf_AddStr(&buf, ") failed (");
+ Buf_AddStr(&buf, strerror(errno));
+ Buf_AddStr(&buf, ")\n");
+
+ write_all(STDERR_FILENO, Buf_GetAll(&buf, NULL), Buf_Len(&buf));
+
+ Buf_Destroy(&buf, TRUE);
+ _exit(1);
}
/*
@@ -1964,16 +2035,17 @@ static void
purge_cached_realpaths(void)
{
GNode *cache = get_cached_realpaths();
- Hash_Entry *he, *nhe;
- Hash_Search hs;
-
- he = Hash_EnumFirst(&cache->context, &hs);
- while (he) {
- nhe = Hash_EnumNext(&hs);
- if (he->name[0] != '/') {
+ HashEntry *he, *nhe;
+ HashIter hi;
+
+ HashIter_Init(&hi, &cache->context);
+ he = HashIter_Next(&hi);
+ while (he != NULL) {
+ nhe = HashIter_Next(&hi);
+ if (he->key[0] != '/') {
if (DEBUG(DIR))
- fprintf(stderr, "cached_realpath: purging %s\n", he->name);
- Hash_DeleteEntry(&cache->context, he);
+ fprintf(stderr, "cached_realpath: purging %s\n", he->key);
+ HashTable_DeleteEntry(&cache->context, he);
}
he = nhe;
}
@@ -1984,41 +2056,24 @@ cached_realpath(const char *pathname, char *resolved)
{
GNode *cache;
const char *rp;
- char *cp;
+ void *freeIt;
if (!pathname || !pathname[0])
return NULL;
cache = get_cached_realpaths();
- if ((rp = Var_Value(pathname, cache, &cp)) != NULL) {
+ if ((rp = Var_Value(pathname, cache, &freeIt)) != NULL) {
/* a hit */
strlcpy(resolved, rp, MAXPATHLEN);
} else if ((rp = realpath(pathname, resolved)) != NULL) {
Var_Set(pathname, rp, cache);
} /* else should we negative-cache? */
- bmake_free(cp);
+ bmake_free(freeIt);
return rp ? resolved : NULL;
}
-int
-PrintAddr(void *a, void *b)
-{
- printf("%lx ", (unsigned long) a);
- return b ? 0 : 0;
-}
-
-
-static int
-addErrorCMD(void *cmdp, void *gnp MAKE_ATTR_UNUSED)
-{
- if (cmdp == NULL)
- return 1; /* stop */
- Var_Append(".ERROR_CMD", cmdp, VAR_GLOBAL);
- return 0;
-}
-
/*
* Return true if we should die without noise.
* For example our failing child was a sub-make
@@ -2030,16 +2085,36 @@ dieQuietly(GNode *gn, int bf)
static int quietly = -1;
if (quietly < 0) {
- if (DEBUG(JOB) || getBoolean(".MAKE.DIE_QUIETLY", 1) == 0)
+ if (DEBUG(JOB) || !getBoolean(".MAKE.DIE_QUIETLY", TRUE))
quietly = 0;
else if (bf >= 0)
quietly = bf;
else
- quietly = (gn) ? ((gn->type & (OP_MAKE)) != 0) : 0;
+ quietly = gn != NULL ? ((gn->type & (OP_MAKE)) != 0) : 0;
}
return quietly;
}
+static void
+SetErrorVars(GNode *gn)
+{
+ StringListNode *ln;
+
+ /*
+ * We can print this even if there is no .ERROR target.
+ */
+ Var_Set(".ERROR_TARGET", gn->name, VAR_GLOBAL);
+ Var_Delete(".ERROR_CMD", VAR_GLOBAL);
+
+ for (ln = gn->commands->first; ln != NULL; ln = ln->next) {
+ const char *cmd = ln->datum;
+
+ if (cmd == NULL)
+ break;
+ Var_Append(".ERROR_CMD", cmd, VAR_GLOBAL);
+ }
+}
+
void
PrintOnError(GNode *gn, const char *s)
{
@@ -2063,16 +2138,11 @@ PrintOnError(GNode *gn, const char *s)
if (en)
return; /* we've been here! */
- if (gn) {
- /*
- * We can print this even if there is no .ERROR target.
- */
- Var_Set(".ERROR_TARGET", gn->name, VAR_GLOBAL);
- Var_Delete(".ERROR_CMD", VAR_GLOBAL);
- Lst_ForEach(gn->commands, addErrorCMD, gn);
- }
+ if (gn)
+ SetErrorVars(gn);
expr = "${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}";
- cp = Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES);
+ (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &cp);
+ /* TODO: handle errors */
printf("%s", cp);
free(cp);
fflush(stdout);
@@ -2080,7 +2150,7 @@ PrintOnError(GNode *gn, const char *s)
/*
* Finally, see if there is a .ERROR target, and run it if so.
*/
- en = Targ_FindNode(".ERROR", TARG_NOCREATE);
+ en = Targ_FindNode(".ERROR");
if (en) {
en->type |= OP_SPECIAL;
Compat_Make(en, en);
@@ -2099,7 +2169,8 @@ Main_ExportMAKEFLAGS(Boolean first)
once = FALSE;
expr = "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}";
- s = Var_Subst(expr, VAR_CMD, VARE_WANTRES);
+ (void)Var_Subst(expr, VAR_CMDLINE, VARE_WANTRES, &s);
+ /* TODO: handle errors */
if (s[0] != '\0') {
#ifdef POSIX
setenv("MAKEFLAGS", s, 1);
@@ -2121,8 +2192,9 @@ getTmpdir(void)
* Honor $TMPDIR but only if it is valid.
* Ensure it ends with /.
*/
- tmpdir = Var_Subst("${TMPDIR:tA:U" _PATH_TMP "}/", VAR_GLOBAL,
- VARE_WANTRES);
+ (void)Var_Subst("${TMPDIR:tA:U" _PATH_TMP "}/", VAR_GLOBAL,
+ VARE_WANTRES, &tmpdir);
+ /* TODO: handle errors */
if (stat(tmpdir, &st) < 0 || !S_ISDIR(st.st_mode)) {
free(tmpdir);
tmpdir = bmake_strdup(_PATH_TMP);
@@ -2133,19 +2205,19 @@ getTmpdir(void)
/*
* Create and open a temp file using "pattern".
- * If "fnamep" is provided set it to a copy of the filename created.
+ * If out_fname is provided, set it to a copy of the filename created.
* Otherwise unlink the file once open.
*/
int
-mkTempFile(const char *pattern, char **fnamep)
+mkTempFile(const char *pattern, char **out_fname)
{
static char *tmpdir = NULL;
char tfile[MAXPATHLEN];
int fd;
- if (!pattern)
+ if (pattern != NULL)
pattern = TMPPAT;
- if (!tmpdir)
+ if (tmpdir == NULL)
tmpdir = getTmpdir();
if (pattern[0] == '/') {
snprintf(tfile, sizeof(tfile), "%s", pattern);
@@ -2154,8 +2226,8 @@ mkTempFile(const char *pattern, char **fnamep)
}
if ((fd = mkstemp(tfile)) < 0)
Punt("Could not create temporary file %s: %s", tfile, strerror(errno));
- if (fnamep) {
- *fnamep = bmake_strdup(tfile);
+ if (out_fname) {
+ *out_fname = bmake_strdup(tfile);
} else {
unlink(tfile); /* we just want the descriptor */
}
@@ -2170,50 +2242,41 @@ mkTempFile(const char *pattern, char **fnamep)
Boolean
s2Boolean(const char *s, Boolean bf)
{
- if (s) {
- switch(*s) {
- case '\0': /* not set - the default wins */
- break;
- case '0':
- case 'F':
- case 'f':
- case 'N':
- case 'n':
- bf = FALSE;
- break;
- case 'O':
- case 'o':
- switch (s[1]) {
- case 'F':
- case 'f':
- bf = FALSE;
- break;
- default:
- bf = TRUE;
- break;
- }
- break;
- default:
- bf = TRUE;
- break;
- }
+ switch(s[0]) {
+ case '\0': /* not set - the default wins */
+ break;
+ case '0':
+ case 'F':
+ case 'f':
+ case 'N':
+ case 'n':
+ return FALSE;
+ case 'O':
+ case 'o':
+ return s[1] != 'F' && s[1] != 'f';
+ default:
+ return TRUE;
}
return bf;
}
/*
- * Return a Boolean based on setting of a knob.
+ * Return a Boolean based on a variable.
*
- * If the knob is not set, the supplied default is the return value.
- * If set, anything that looks or smells like "No", "False", "Off", "0" etc,
+ * If the knob is not set, return the fallback.
+ * If set, anything that looks or smells like "No", "False", "Off", "0", etc.
* is FALSE, otherwise TRUE.
*/
Boolean
-getBoolean(const char *name, Boolean fallback)
+getBoolean(const char *varname, Boolean fallback)
{
- char *expr = str_concat3("${", name, ":U:tl}");
- char *value = Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES);
- Boolean res = s2Boolean(value, fallback);
+ char *expr = str_concat3("${", varname, ":U}");
+ char *value;
+ Boolean res;
+
+ (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &value);
+ /* TODO: handle errors */
+ res = s2Boolean(value, fallback);
free(value);
free(expr);
return res;
diff --git a/make-bootstrap.sh.in b/make-bootstrap.sh.in
index ec100138b6d1..a75634b3dae6 100755
--- a/make-bootstrap.sh.in
+++ b/make-bootstrap.sh.in
@@ -58,22 +58,15 @@ do_link() {
${CC} ${LDSTATIC} ${LDFLAGS} -o "$output" "$@" ${LIBS}
}
-BASE_OBJECTS="arch.o buf.o compat.o cond.o dir.o for.o getopt hash.o \
-make.o make_malloc.o metachar.o parse.o sigcompat.o str.o strlist.o \
+BASE_OBJECTS="arch.o buf.o compat.o cond.o dir.o enum.o for.o getopt hash.o \
+lst.o make.o make_malloc.o metachar.o parse.o sigcompat.o str.o strlist.o \
suff.o targ.o trace.o var.o util.o"
-LST_OBJECTS="lstAppend.o lstDupl.o lstInit.o lstOpen.o \
-lstAtEnd.o lstEnQueue.o lstInsert.o lstAtFront.o lstIsAtEnd.o \
-lstClose.o lstFind.o lstIsEmpty.o lstRemove.o lstConcat.o \
-lstFindFrom.o lstLast.o lstReplace.o lstFirst.o lstDatum.o \
-lstForEach.o lstMember.o lstSucc.o lstDeQueue.o lstForEachFrom.o \
-lstDestroy.o lstNext.o lstPrev.o"
-
LIB_OBJECTS="@LIBOBJS@"
do_compile main.o ${MDEFS}
-for o in ${BASE_OBJECTS} ${LST_OBJECTS} ${LIB_OBJECTS}
+for o in ${BASE_OBJECTS} ${LIB_OBJECTS}
do
do_compile "$o"
done
diff --git a/make-conf.h b/make-conf.h
index 5b13e295ae0c..bc3b9e7e4915 100644
--- a/make-conf.h
+++ b/make-conf.h
@@ -1,4 +1,4 @@
-/* $NetBSD: config.h,v 1.22 2020/09/01 17:40:34 rillig Exp $ */
+/* $NetBSD: config.h,v 1.25 2020/10/19 23:43:55 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -76,10 +76,11 @@
* DEFMAXJOBS
* DEFMAXLOCAL
* These control the default concurrency. On no occasion will more
- * than DEFMAXJOBS targets be created at once (locally or remotely)
+ * than DEFMAXJOBS targets be created at once (locally or remotely).
+ *
* DEFMAXLOCAL is the highest number of targets which will be
* created on the local machine at once. Note that if you set this
- * to 0, nothing will ever happen...
+ * to 0, nothing will ever happen.
*/
#define DEFMAXJOBS 4
#define DEFMAXLOCAL 1
@@ -88,10 +89,12 @@
* 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 suff.c for more details.
+ * .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
@@ -108,11 +111,13 @@
* If defined, Make_Update will check a target for its current
* modification time after it has been re-made, setting it to the
* starting time of the make only if the target still doesn't exist.
+ *
* Unfortunately, under NFS the modification time often doesn't
* get updated in time, so a target will appear to not have been
- * re-made, causing later targets to appear up-to-date. On systems
- * that don't have this problem, you should define this. Under
- * NFS you probably should not, unless you aren't exporting jobs.
+ * re-made, causing later targets to appear up-to-date.
+ *
+ * On systems that don't have this problem, you should define this.
+ * Under NFS you probably should not, unless you aren't exporting jobs.
*/
#define RECHECK
@@ -128,8 +133,10 @@
/*
* 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
@@ -149,14 +156,6 @@
*/
#define SUNSHCMD
-/*
- * USE_IOVEC
- * We have writev(2)
- */
-#ifdef HAVE_SYS_UIO_H
-# define USE_IOVEC
-#endif
-
#if defined(MAKE_NATIVE) && !defined(__ELF__)
# ifndef RANLIBMAG
# define RANLIBMAG "__.SYMDEF"
diff --git a/make.1 b/make.1
index 9d83abe31d6c..4b1ef9ed0fe8 100644
--- a/make.1
+++ b/make.1
@@ -1,4 +1,4 @@
-.\" $NetBSD: make.1,v 1.289 2020/08/28 17:15:04 rillig Exp $
+.\" $NetBSD: make.1,v 1.290 2020/11/01 20:24:45 rillig 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 August 28, 2020
+.Dd November 1, 2020
.Dt MAKE 1
.Os
.Sh NAME
@@ -1872,7 +1872,7 @@ has been defined and has commands associated with it.
.Ar Expression
may also be an arithmetic or string comparison.
Variable expansion is
-performed on both sides of the comparison, after which the integral
+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.
@@ -1882,7 +1882,7 @@ variable expansion, either the left or right hand side of a
.Ql Ic ==
or
.Ql Ic "!="
-operator is not an integral value, then
+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
diff --git a/make.c b/make.c
index da2b8adf6efd..52bc75ac8467 100644
--- a/make.c
+++ b/make.c
@@ -1,4 +1,4 @@
-/* $NetBSD: make.c,v 1.133 2020/08/30 14:11:42 rillig Exp $ */
+/* $NetBSD: make.c,v 1.186 2020/11/01 17:47:26 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -68,85 +68,74 @@
* SUCH DAMAGE.
*/
-#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: make.c,v 1.133 2020/08/30 14:11:42 rillig Exp $";
-#else
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)make.c 8.1 (Berkeley) 6/6/93";
-#else
-__RCSID("$NetBSD: make.c,v 1.133 2020/08/30 14:11:42 rillig Exp $");
-#endif
-#endif /* not lint */
-#endif
-
/*-
* make.c --
* The functions which perform the examination of targets and
* their suitability for creation
*
* Interface:
- * Make_Run Initialize things for the module and recreate
- * whatever needs recreating. Returns TRUE if
- * work was (or would have been) done and FALSE
- * otherwise.
+ * Make_Run Initialize things for the module and recreate
+ * whatever needs recreating. Returns TRUE if
+ * work was (or would have been) done and FALSE
+ * otherwise.
*
- * Make_Update Update all parents of a given child. Performs
- * various bookkeeping chores like the updating
- * of the cmgn field of the parent, filling
- * of the IMPSRC context variable, etc. It will
- * place the parent on the toBeMade queue if it
- * should be.
+ * Make_Update Update all parents of a given child. Performs
+ * various bookkeeping chores like the updating
+ * of the youngestChild field of the parent, filling
+ * of the IMPSRC context variable, etc. It will
+ * place the parent on the toBeMade queue if it
+ * should be.
*
- * Make_TimeStamp Function to set the parent's cmgn field
- * based on a child's modification time.
+ * Make_TimeStamp Function to set the parent's youngestChild field
+ * based on a child's modification time.
*
- * Make_DoAllVar Set up the various local variables for a
- * target, including the .ALLSRC variable, making
- * sure that any variable that needs to exist
- * at the very least has the empty value.
+ * Make_DoAllVar Set up the various local variables for a
+ * target, including the .ALLSRC variable, making
+ * sure that any variable that needs to exist
+ * at the very least has the empty value.
*
- * Make_OODate Determine if a target is out-of-date.
+ * Make_OODate Determine if a target is out-of-date.
*
- * Make_HandleUse See if a child is a .USE node for a parent
- * and perform the .USE actions if so.
+ * Make_HandleUse See if a child is a .USE node for a parent
+ * and perform the .USE actions if so.
*
- * Make_ExpandUse Expand .USE nodes
+ * Make_ExpandUse Expand .USE nodes
*/
-#include "make.h"
-#include "enum.h"
-#include "dir.h"
-#include "job.h"
-
-static unsigned int checked = 1;/* Sequence # to detect recursion */
-static Lst toBeMade; /* The current fringe of the graph. These
- * are nodes which await examination by
- * MakeOODate. It is added to by
- * Make_Update and subtracted from by
- * MakeStartJobs */
-
-static int MakeAddChild(void *, void *);
-static int MakeFindChild(void *, void *);
-static int MakeUnmark(void *, void *);
-static int MakeAddAllSrc(void *, void *);
-static int MakeTimeStamp(void *, void *);
-static int MakeHandleUse(void *, void *);
-static Boolean MakeStartJobs(void);
-static int MakePrintStatus(void *, void *);
+#include "make.h"
+#include "dir.h"
+#include "job.h"
+
+/* "@(#)make.c 8.1 (Berkeley) 6/6/93" */
+MAKE_RCSID("$NetBSD: make.c,v 1.186 2020/11/01 17:47:26 rillig Exp $");
+
+/* Sequence # to detect recursion. */
+static unsigned int checked = 1;
+
+/* The current fringe of the graph.
+ * These are nodes which await examination by MakeOODate.
+ * It is added to by Make_Update and subtracted from by MakeStartJobs */
+static GNodeList *toBeMade;
+
static int MakeCheckOrder(void *, void *);
-static int MakeBuildChild(void *, void *);
static int MakeBuildParent(void *, void *);
+void
+debug_printf(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vfprintf(opts.debug_file, fmt, args);
+ va_end(args);
+}
+
MAKE_ATTR_DEAD static void
make_abort(GNode *gn, int line)
{
- static int two = 2;
-
- fprintf(debug_file, "make_abort from line %d\n", line);
- Targ_PrintNode(gn, &two);
- Lst_ForEach(toBeMade, Targ_PrintNode, &two);
+ debug_printf("make_abort from line %d\n", line);
+ Targ_PrintNode(gn, 2);
+ Targ_PrintNodes(toBeMade, 2);
Targ_PrintGraph(3);
abort();
}
@@ -188,65 +177,31 @@ GNode_FprintDetails(FILE *f, const char *prefix, const GNode *gn,
suffix);
}
-/*-
- *-----------------------------------------------------------------------
- * Make_TimeStamp --
- * Set the cmgn field of a parent node based on the mtime stamp in its
- * child. Called from MakeOODate via Lst_ForEach.
- *
- * Input:
- * pgn the current parent
- * cgn the child we've just examined
- *
- * Results:
- * Always returns 0.
- *
- * Side Effects:
- * The cmgn of the parent node will be changed if the mtime
- * field of the child is greater than it.
- *-----------------------------------------------------------------------
- */
-int
-Make_TimeStamp(GNode *pgn, GNode *cgn)
+Boolean
+GNode_ShouldExecute(GNode *gn)
{
- if (pgn->cmgn == NULL || cgn->mtime > pgn->cmgn->mtime) {
- pgn->cmgn = cgn;
- }
- return 0;
+ return !((gn->type & OP_MAKE) ? opts.noRecursiveExecute : opts.noExecute);
}
-/*
- * Input:
- * pgn the current parent
- * cgn the child we've just examined
- *
- */
-static int
-MakeTimeStamp(void *pgn, void *cgn)
+/* Update the youngest child of the node, according to the given child. */
+void
+Make_TimeStamp(GNode *pgn, GNode *cgn)
{
- return Make_TimeStamp((GNode *)pgn, (GNode *)cgn);
+ if (pgn->youngestChild == NULL || cgn->mtime > pgn->youngestChild->mtime) {
+ pgn->youngestChild = cgn;
+ }
}
-/*-
- *-----------------------------------------------------------------------
- * Make_OODate --
- * See if a given node is out of date with respect to its sources.
- * Used by Make_Run when deciding which nodes to place on the
- * toBeMade queue initially and by Make_Update to screen out USE and
- * EXEC nodes. In the latter case, however, any other sort of node
- * must be considered out-of-date since at least one of its children
- * will have been recreated.
- *
- * Input:
- * gn the node to check
+/* See if the node is out of date with respect to its sources.
*
- * Results:
- * TRUE if the node is out of date. FALSE otherwise.
+ * Used by Make_Run when deciding which nodes to place on the
+ * toBeMade queue initially and by Make_Update to screen out .USE and
+ * .EXEC nodes. In the latter case, however, any other sort of node
+ * must be considered out-of-date since at least one of its children
+ * will have been recreated.
*
- * Side Effects:
- * The mtime field of the node and the cmgn field of its parents
- * will/may be changed.
- *-----------------------------------------------------------------------
+ * The mtime field of the node and the youngestChild field of its parents
+ * may be changed.
*/
Boolean
Make_OODate(GNode *gn)
@@ -261,17 +216,17 @@ Make_OODate(GNode *gn)
(void)Dir_MTime(gn, 1);
if (DEBUG(MAKE)) {
if (gn->mtime != 0) {
- fprintf(debug_file, "modified %s...", Targ_FmtTime(gn->mtime));
+ debug_printf("modified %s...", Targ_FmtTime(gn->mtime));
} else {
- fprintf(debug_file, "non-existent...");
+ debug_printf("non-existent...");
}
}
}
/*
* A target is remade in one of the following circumstances:
- * its modification time is smaller than that of its youngest child
- * and it would actually be run (has commands or type OP_NOP)
+ * its modification time is smaller than that of its youngest child and
+ * it would actually be run (has commands or is not GNode_IsTarget)
* it's the object of a force operator
* it has no children, was on the lhs of an operator and doesn't exist
* already.
@@ -287,33 +242,25 @@ Make_OODate(GNode *gn)
* If the node is a USE node it is *never* out of date
* no matter *what*.
*/
- if (DEBUG(MAKE)) {
- fprintf(debug_file, ".USE node...");
- }
+ DEBUG0(MAKE, ".USE node...");
oodate = FALSE;
} else if ((gn->type & OP_LIB) &&
((gn->mtime==0) || Arch_IsLib(gn))) {
- if (DEBUG(MAKE)) {
- fprintf(debug_file, "library...");
- }
+ DEBUG0(MAKE, "library...");
/*
* always out of date if no children and :: target
* or non-existent.
*/
oodate = (gn->mtime == 0 || Arch_LibOODate(gn) ||
- (gn->cmgn == NULL && (gn->type & OP_DOUBLEDEP)));
+ (gn->youngestChild == NULL && (gn->type & OP_DOUBLEDEP)));
} else if (gn->type & OP_JOIN) {
/*
* A target with the .JOIN attribute is only considered
* out-of-date if any of its children was out-of-date.
*/
- if (DEBUG(MAKE)) {
- fprintf(debug_file, ".JOIN node...");
- }
- if (DEBUG(MAKE)) {
- fprintf(debug_file, "source %smade...", gn->flags & CHILDMADE ? "" : "not ");
- }
+ DEBUG0(MAKE, ".JOIN node...");
+ DEBUG1(MAKE, "source %smade...", gn->flags & CHILDMADE ? "" : "not ");
oodate = (gn->flags & CHILDMADE) ? TRUE : FALSE;
} else if (gn->type & (OP_FORCE|OP_EXEC|OP_PHONY)) {
/*
@@ -322,34 +269,36 @@ Make_OODate(GNode *gn)
*/
if (DEBUG(MAKE)) {
if (gn->type & OP_FORCE) {
- fprintf(debug_file, "! operator...");
+ debug_printf("! operator...");
} else if (gn->type & OP_PHONY) {
- fprintf(debug_file, ".PHONY node...");
+ debug_printf(".PHONY node...");
} else {
- fprintf(debug_file, ".EXEC node...");
+ debug_printf(".EXEC node...");
}
}
oodate = TRUE;
- } else if ((gn->cmgn != NULL && gn->mtime < gn->cmgn->mtime) ||
- (gn->cmgn == NULL &&
+ } else if ((gn->youngestChild != NULL &&
+ gn->mtime < gn->youngestChild->mtime) ||
+ (gn->youngestChild == NULL &&
((gn->mtime == 0 && !(gn->type & OP_OPTIONAL))
- || gn->type & OP_DOUBLEDEP)))
+ || gn->type & OP_DOUBLEDEP)))
{
/*
* A node whose modification time is less than that of its
- * youngest child or that has no children (cmgn == NULL) and
+ * youngest child or that has no children (youngestChild == NULL) and
* either doesn't exist (mtime == 0) and it isn't optional
* or was the object of a * :: operator is out-of-date.
* Why? Because that's the way Make does it.
*/
if (DEBUG(MAKE)) {
- if (gn->cmgn != NULL && gn->mtime < gn->cmgn->mtime) {
- fprintf(debug_file, "modified before source %s...",
- gn->cmgn->path ? gn->cmgn->path : gn->cmgn->name);
+ if (gn->youngestChild != NULL &&
+ gn->mtime < gn->youngestChild->mtime) {
+ debug_printf("modified before source %s...",
+ GNode_Path(gn->youngestChild));
} else if (gn->mtime == 0) {
- fprintf(debug_file, "non-existent and no sources...");
+ debug_printf("non-existent and no sources...");
} else {
- fprintf(debug_file, ":: operator and no sources...");
+ debug_printf(":: operator and no sources...");
}
}
oodate = TRUE;
@@ -363,7 +312,7 @@ Make_OODate(GNode *gn)
*/
if (DEBUG(MAKE)) {
if (gn->flags & FORCE)
- fprintf(debug_file, "non existing child...");
+ debug_printf("non existing child...");
}
oodate = (gn->flags & FORCE) ? TRUE : FALSE;
}
@@ -377,71 +326,47 @@ Make_OODate(GNode *gn)
/*
* If the target isn't out-of-date, the parents need to know its
* modification time. Note that targets that appear to be out-of-date
- * but aren't, because they have no commands and aren't of type OP_NOP,
+ * but aren't, because they have no commands and are GNode_IsTarget,
* have their mtime stay below their children's mtime to keep parents from
* thinking they're out-of-date.
*/
if (!oodate) {
- Lst_ForEach(gn->parents, MakeTimeStamp, gn);
+ GNodeListNode *ln;
+ for (ln = gn->parents->first; ln != NULL; ln = ln->next)
+ Make_TimeStamp(ln->datum, gn);
}
return oodate;
}
-/*-
- *-----------------------------------------------------------------------
- * MakeAddChild --
- * Function used by Make_Run to add a child to the list l.
- * It will only add the child if its make field is FALSE.
- *
- * Input:
- * gnp the node to add
- * lp the list to which to add it
- *
- * Results:
- * Always returns 0
- *
- * Side Effects:
- * The given list is extended
- *-----------------------------------------------------------------------
- */
+/* Add the node to the list if it needs to be examined. */
static int
MakeAddChild(void *gnp, void *lp)
{
- GNode *gn = (GNode *)gnp;
- Lst l = (Lst) lp;
+ GNode *gn = gnp;
+ GNodeList *l = lp;
if ((gn->flags & REMAKE) == 0 && !(gn->type & (OP_USE|OP_USEBEFORE))) {
- if (DEBUG(MAKE))
- fprintf(debug_file, "MakeAddChild: need to examine %s%s\n",
- gn->name, gn->cohort_num);
+ DEBUG2(MAKE, "MakeAddChild: need to examine %s%s\n",
+ gn->name, gn->cohort_num);
Lst_Enqueue(l, gn);
}
return 0;
}
-/*-
- *-----------------------------------------------------------------------
- * MakeFindChild --
- * Function used by Make_Run to find the pathname of a child
- * that was already made.
+/* Find the pathname of a child that was already made.
+ *
+ * The path and mtime of the node and the youngestChild of the parent are
+ * updated; the unmade children count of the parent is decremented.
*
* Input:
* gnp the node to find
- *
- * Results:
- * Always returns 0
- *
- * Side Effects:
- * The path and mtime of the node and the cmgn of the parent are
- * updated; the unmade children count of the parent is decremented.
- *-----------------------------------------------------------------------
*/
static int
MakeFindChild(void *gnp, void *pgnp)
{
- GNode *gn = (GNode *)gnp;
- GNode *pgn = (GNode *)pgnp;
+ GNode *gn = gnp;
+ GNode *pgn = pgnp;
(void)Dir_MTime(gn, 0);
Make_TimeStamp(pgn, gn);
@@ -459,17 +384,18 @@ MakeFindChild(void *gnp, void *pgnp)
* has commands.
*
* Input:
- * cgn The .USE node
- * pgn The target of the .USE node
+ * cgn The source node, which is either a .USE/.USEBEFORE
+ * node or a transformation node (OP_TRANSFORM).
+ * pgn The target node
*/
void
Make_HandleUse(GNode *cgn, GNode *pgn)
{
- LstNode ln; /* An element in the children list */
+ GNodeListNode *ln; /* An element in the children list */
#ifdef DEBUG_SRC
if ((cgn->type & (OP_USE|OP_USEBEFORE|OP_TRANSFORM)) == 0) {
- fprintf(debug_file, "Make_HandleUse: called for plain node %s\n", cgn->name);
+ debug_printf("Make_HandleUse: called for plain node %s\n", cgn->name);
return;
}
#endif
@@ -484,9 +410,8 @@ Make_HandleUse(GNode *cgn, GNode *pgn)
}
}
- Lst_Open(cgn->children);
- while ((ln = Lst_Next(cgn->children)) != NULL) {
- GNode *gn = LstNode_Datum(ln);
+ for (ln = cgn->children->first; ln != NULL; ln = ln->next) {
+ GNode *gn = ln->datum;
/*
* Expand variables in the .USE node's name
@@ -499,57 +424,44 @@ Make_HandleUse(GNode *cgn, GNode *pgn)
} else {
free(gn->name);
}
- gn->name = Var_Subst(gn->uname, pgn, VARE_WANTRES);
+ (void)Var_Subst(gn->uname, pgn, VARE_WANTRES, &gn->name);
+ /* TODO: handle errors */
if (gn->uname && strcmp(gn->name, gn->uname) != 0) {
/* See if we have a target for this node. */
- GNode *tgn = Targ_FindNode(gn->name, TARG_NOCREATE);
+ GNode *tgn = Targ_FindNode(gn->name);
if (tgn != NULL)
gn = tgn;
}
Lst_Append(pgn->children, gn);
Lst_Append(gn->parents, pgn);
- pgn->unmade += 1;
+ pgn->unmade++;
}
- Lst_Close(cgn->children);
pgn->type |= cgn->type & ~(OP_OPMASK|OP_USE|OP_USEBEFORE|OP_TRANSFORM);
}
-/*-
- *-----------------------------------------------------------------------
- * MakeHandleUse --
- * Callback function for Lst_ForEach, used by Make_Run on the downward
- * pass to handle .USE nodes. Should be called before the children
- * are enqueued to be looked at by MakeAddChild.
- * This function calls Make_HandleUse to copy the .USE node's commands,
- * type flags and children to the parent node.
- *
- * Input:
- * cgnp the child we've just examined
- * pgnp the current parent
- *
- * Results:
- * returns 0.
+/* Used by Make_Run on the downward pass to handle .USE nodes. Should be
+ * called before the children are enqueued to be looked at by MakeAddChild.
*
- * Side Effects:
- * After expansion, .USE child nodes are removed from the parent
+ * For a .USE child, the commands, type flags and children are copied to the
+ * parent node, and since the relation to the .USE node is then no longer
+ * needed, that relation is removed.
*
- *-----------------------------------------------------------------------
+ * Input:
+ * cgn the child, which may be a .USE node
+ * pgn the current parent
*/
-static int
-MakeHandleUse(void *cgnp, void *pgnp)
+static void
+MakeHandleUse(GNode *cgn, GNode *pgn, GNodeListNode *ln)
{
- GNode *cgn = (GNode *)cgnp;
- GNode *pgn = (GNode *)pgnp;
- LstNode ln; /* An element in the children list */
- int unmarked;
+ Boolean unmarked;
unmarked = ((cgn->type & OP_MARK) == 0);
cgn->type |= OP_MARK;
if ((cgn->type & (OP_USE|OP_USEBEFORE)) == 0)
- return 0;
+ return;
if (unmarked)
Make_HandleUse(cgn, pgn);
@@ -561,29 +473,23 @@ MakeHandleUse(void *cgnp, void *pgnp)
* children the parent has. This is used by Make_Run to decide
* whether to queue the parent or examine its children...
*/
- if ((ln = Lst_FindDatum(pgn->children, cgn)) != NULL) {
- Lst_Remove(pgn->children, ln);
- pgn->unmade--;
+ Lst_Remove(pgn->children, ln);
+ pgn->unmade--;
+}
+
+static void
+HandleUseNodes(GNode *gn)
+{
+ GNodeListNode *ln, *nln;
+ for (ln = gn->children->first; ln != NULL; ln = nln) {
+ nln = ln->next;
+ MakeHandleUse(ln->datum, gn, ln);
}
- return 0;
}
-/*-
- *-----------------------------------------------------------------------
- * Make_Recheck --
- * Check the modification time of a gnode, and update it as described
- * in the comments below.
- *
- * Results:
- * returns 0 if the gnode does not exist, or its filesystem
- * time if it does.
- *
- * Side Effects:
- * the gnode's modification time and path name are affected.
- *
- *-----------------------------------------------------------------------
- */
+/* Check the modification time of a gnode, and update it if necessary.
+ * Return 0 if the gnode does not exist, or its filesystem time if it does. */
time_t
Make_Recheck(GNode *gn)
{
@@ -598,10 +504,10 @@ Make_Recheck(GNode *gn)
* parse.h : parse.o
*
* parse.o : parse.y
- * yacc -d parse.y
- * cc -c y.tab.c
- * mv y.tab.o parse.o
- * cmp -s y.tab.h parse.h || mv y.tab.h parse.h
+ * yacc -d parse.y
+ * cc -c y.tab.c
+ * mv y.tab.o parse.o
+ * cmp -s y.tab.h parse.h || mv y.tab.h parse.h
*
* In this case, if the definitions produced by yacc haven't changed
* from before, parse.h won't have been updated and gn->mtime will
@@ -641,74 +547,74 @@ Make_Recheck(GNode *gn)
* the target is made now. Otherwise archives with ... rules
* don't work!
*/
- if (NoExecute(gn) || (gn->type & OP_SAVE_CMDS) ||
+ if (!GNode_ShouldExecute(gn) || (gn->type & OP_SAVE_CMDS) ||
(mtime == 0 && !(gn->type & OP_WAIT))) {
- if (DEBUG(MAKE)) {
- fprintf(debug_file, " recheck(%s): update time from %s to now\n",
- gn->name, Targ_FmtTime(gn->mtime));
- }
+ DEBUG2(MAKE, " recheck(%s): update time from %s to now\n",
+ gn->name, Targ_FmtTime(gn->mtime));
gn->mtime = now;
}
else {
- if (DEBUG(MAKE)) {
- fprintf(debug_file, " recheck(%s): current update time: %s\n",
- gn->name, Targ_FmtTime(gn->mtime));
- }
+ DEBUG2(MAKE, " recheck(%s): current update time: %s\n",
+ gn->name, Targ_FmtTime(gn->mtime));
}
#endif
return mtime;
}
-/*-
- *-----------------------------------------------------------------------
- * Make_Update --
- * Perform update on the parents of a node. Used by JobFinish once
- * a node has been dealt with and by MakeStartJobs if it finds an
- * up-to-date node.
- *
- * Input:
- * cgn the child node
- *
- * Results:
- * Always returns 0
- *
- * Side Effects:
- * The unmade field of pgn is decremented and pgn may be placed on
- * the toBeMade queue if this field becomes 0.
+/*
+ * Set the .PREFIX and .IMPSRC variables for all the implied parents
+ * of this node.
+ */
+static void
+UpdateImplicitParentsVars(GNode *cgn, const char *cname)
+{
+ GNodeListNode *ln;
+ const char *cpref = GNode_VarPrefix(cgn);
+
+ for (ln = cgn->implicitParents->first; ln != NULL; ln = ln->next) {
+ GNode *pgn = ln->datum;
+ if (pgn->flags & REMAKE) {
+ Var_Set(IMPSRC, cname, pgn);
+ if (cpref != NULL)
+ Var_Set(PREFIX, cpref, pgn);
+ }
+ }
+}
+
+/* Perform update on the parents of a node. Used by JobFinish once
+ * a node has been dealt with and by MakeStartJobs if it finds an
+ * up-to-date node.
*
- * If the child was made, the parent's flag CHILDMADE field will be
- * set true.
+ * The unmade field of pgn is decremented and pgn may be placed on
+ * the toBeMade queue if this field becomes 0.
*
- * If the child is not up-to-date and still does not exist,
- * set the FORCE flag on the parents.
+ * If the child was made, the parent's flag CHILDMADE field will be
+ * set true.
*
- * If the child wasn't made, the cmgn field of the parent will be
- * altered if the child's mtime is big enough.
+ * If the child is not up-to-date and still does not exist,
+ * set the FORCE flag on the parents.
*
- * Finally, if the child is the implied source for the parent, the
- * parent's IMPSRC variable is set appropriately.
+ * If the child wasn't made, the youngestChild field of the parent will be
+ * altered if the child's mtime is big enough.
*
- *-----------------------------------------------------------------------
+ * Finally, if the child is the implied source for the parent, the
+ * parent's IMPSRC variable is set appropriately.
*/
void
Make_Update(GNode *cgn)
{
- GNode *pgn; /* the parent node */
- const char *cname; /* the child's name */
- LstNode ln; /* Element in parents and implicitParents lists */
+ const char *cname; /* the child's name */
time_t mtime = -1;
- char *p1;
- Lst parents;
+ GNodeList *parents;
+ GNodeListNode *ln;
GNode *centurion;
/* It is save to re-examine any nodes again */
checked++;
- cname = Var_Value(TARGET, cgn, &p1);
- bmake_free(p1);
+ cname = GNode_VarTarget(cgn);
- if (DEBUG(MAKE))
- fprintf(debug_file, "Make_Update: %s%s\n", cgn->name, cgn->cohort_num);
+ DEBUG2(MAKE, "Make_Update: %s%s\n", cgn->name, cgn->cohort_num);
/*
* If the child was actually made, see what its modification time is
@@ -726,7 +632,7 @@ Make_Update(GNode *cgn)
if ((centurion = cgn->centurion) != NULL) {
if (!Lst_IsEmpty(cgn->parents))
Punt("%s%s: cohort has parents", cgn->name, cgn->cohort_num);
- centurion->unmade_cohorts -= 1;
+ centurion->unmade_cohorts--;
if (centurion->unmade_cohorts < 0)
Error("Graph cycles through centurion %s", centurion->name);
} else {
@@ -735,22 +641,21 @@ Make_Update(GNode *cgn)
parents = centurion->parents;
/* If this was a .ORDER node, schedule the RHS */
- Lst_ForEach(centurion->order_succ, MakeBuildParent, Lst_First(toBeMade));
+ Lst_ForEachUntil(centurion->order_succ, MakeBuildParent, toBeMade->first);
/* Now mark all the parents as having one less unmade child */
- Lst_Open(parents);
- while ((ln = Lst_Next(parents)) != NULL) {
- pgn = LstNode_Datum(ln);
+ for (ln = parents->first; ln != NULL; ln = ln->next) {
+ GNode *pgn = ln->datum;
+
if (DEBUG(MAKE))
- fprintf(debug_file, "inspect parent %s%s: flags %x, "
- "type %x, made %d, unmade %d ",
- pgn->name, pgn->cohort_num, pgn->flags,
- pgn->type, pgn->made, pgn->unmade-1);
+ debug_printf("inspect parent %s%s: flags %x, "
+ "type %x, made %d, unmade %d ",
+ pgn->name, pgn->cohort_num, pgn->flags,
+ pgn->type, pgn->made, pgn->unmade - 1);
if (!(pgn->flags & REMAKE)) {
/* This parent isn't needed */
- if (DEBUG(MAKE))
- fprintf(debug_file, "- not needed\n");
+ DEBUG0(MAKE, "- not needed\n");
continue;
}
if (mtime == 0 && !(cgn->type & OP_WAIT))
@@ -758,15 +663,14 @@ Make_Update(GNode *cgn)
/*
* If the parent has the .MADE attribute, its timestamp got
- * updated to that of its newest child, and its unmake
+ * updated to that of its newest child, and its unmade
* child count got set to zero in Make_ExpandUse().
* However other things might cause us to build one of its
* children - and so we mustn't do any processing here when
* the child build finishes.
*/
if (pgn->type & OP_MADE) {
- if (DEBUG(MAKE))
- fprintf(debug_file, "- .MADE\n");
+ DEBUG0(MAKE, "- .MADE\n");
continue;
}
@@ -781,19 +685,17 @@ Make_Update(GNode *cgn)
* of a `::' dependency.
*/
if (centurion->unmade_cohorts != 0 || centurion->made < MADE) {
- if (DEBUG(MAKE))
- fprintf(debug_file,
- "- centurion made %d, %d unmade cohorts\n",
- centurion->made, centurion->unmade_cohorts);
+ DEBUG2(MAKE, "- centurion made %d, %d unmade cohorts\n",
+ centurion->made, centurion->unmade_cohorts);
continue;
}
/* One more child of this parent is now made */
- pgn->unmade -= 1;
+ pgn->unmade--;
if (pgn->unmade < 0) {
if (DEBUG(MAKE)) {
- fprintf(debug_file, "Graph cycles through %s%s\n",
- pgn->name, pgn->cohort_num);
+ debug_printf("Graph cycles through %s%s\n",
+ pgn->name, pgn->cohort_num);
Targ_PrintGraph(2);
}
Error("Graph cycles through %s%s", pgn->name, pgn->cohort_num);
@@ -802,8 +704,7 @@ Make_Update(GNode *cgn)
/* We must always rescan the parents of .WAIT and .ORDER nodes. */
if (pgn->unmade != 0 && !(centurion->type & OP_WAIT)
&& !(centurion->flags & DONE_ORDER)) {
- if (DEBUG(MAKE))
- fprintf(debug_file, "- unmade children\n");
+ DEBUG0(MAKE, "- unmade children\n");
continue;
}
if (pgn->made != DEFERRED) {
@@ -812,113 +713,78 @@ Make_Update(GNode *cgn)
* or it on the RHS of a .WAIT directive
* or it is already on the toBeMade list.
*/
- if (DEBUG(MAKE))
- fprintf(debug_file, "- not deferred\n");
+ DEBUG0(MAKE, "- not deferred\n");
continue;
}
assert(pgn->order_pred != NULL);
- if (Lst_ForEach(pgn->order_pred, MakeCheckOrder, 0)) {
+ if (Lst_ForEachUntil(pgn->order_pred, MakeCheckOrder, 0)) {
/* A .ORDER rule stops us building this */
continue;
}
if (DEBUG(MAKE)) {
- static int two = 2;
- fprintf(debug_file, "- %s%s made, schedule %s%s (made %d)\n",
- cgn->name, cgn->cohort_num,
- pgn->name, pgn->cohort_num, pgn->made);
- Targ_PrintNode(pgn, &two);
+ debug_printf("- %s%s made, schedule %s%s (made %d)\n",
+ cgn->name, cgn->cohort_num,
+ pgn->name, pgn->cohort_num, pgn->made);
+ Targ_PrintNode(pgn, 2);
}
/* Ok, we can schedule the parent again */
pgn->made = REQUESTED;
Lst_Enqueue(toBeMade, pgn);
}
- Lst_Close(parents);
- /*
- * Set the .PREFIX and .IMPSRC variables for all the implied parents
- * of this node.
- */
- Lst_Open(cgn->implicitParents);
- {
- const char *cpref = Var_Value(PREFIX, cgn, &p1);
-
- while ((ln = Lst_Next(cgn->implicitParents)) != NULL) {
- pgn = LstNode_Datum(ln);
- if (pgn->flags & REMAKE) {
- Var_Set(IMPSRC, cname, pgn);
- if (cpref != NULL)
- Var_Set(PREFIX, cpref, pgn);
- }
- }
- bmake_free(p1);
- Lst_Close(cgn->implicitParents);
- }
+ UpdateImplicitParentsVars(cgn, cname);
}
-/*-
- *-----------------------------------------------------------------------
- * MakeAddAllSrc --
- * Add a child's name to the ALLSRC and OODATE variables of the given
- * node. Called from Make_DoAllVar via Lst_ForEach. A child is added only
- * if it has not been given the .EXEC, .USE or .INVISIBLE attributes.
- * .EXEC and .USE children are very rarely going to be files, so...
- * If the child is a .JOIN node, its ALLSRC is propagated to the parent.
- *
- * A child is added to the OODATE variable if its modification time is
- * later than that of its parent, as defined by Make, except if the
- * parent is a .JOIN node. In that case, it is only added to the OODATE
- * variable if it was actually made (since .JOIN nodes don't have
- * modification times, the comparison is rather unfair...)..
- *
- * Results:
- * Always returns 0
- *
- * Side Effects:
- * The ALLSRC variable for the given node is extended.
- *-----------------------------------------------------------------------
- */
-static int
-MakeUnmark(void *cgnp, void *pgnp MAKE_ATTR_UNUSED)
+static void
+UnmarkChildren(GNode *gn)
{
- GNode *cgn = (GNode *)cgnp;
+ GNodeListNode *ln;
- cgn->type &= ~OP_MARK;
- return 0;
+ for (ln = gn->children->first; ln != NULL; ln = ln->next) {
+ GNode *child = ln->datum;
+ child->type &= ~OP_MARK;
+ }
}
-/*
+/* Add a child's name to the ALLSRC and OODATE variables of the given
+ * node, but only if it has not been given the .EXEC, .USE or .INVISIBLE
+ * attributes. .EXEC and .USE children are very rarely going to be files,
+ * so...
+ *
+ * If the child is a .JOIN node, its ALLSRC is propagated to the parent.
+ *
+ * A child is added to the OODATE variable if its modification time is
+ * later than that of its parent, as defined by Make, except if the
+ * parent is a .JOIN node. In that case, it is only added to the OODATE
+ * variable if it was actually made (since .JOIN nodes don't have
+ * modification times, the comparison is rather unfair...)..
+ *
* Input:
- * cgnp The child to add
- * pgnp The parent to whose ALLSRC variable it should
+ * cgn The child to add
+ * pgn The parent to whose ALLSRC variable it should
* be added
- *
*/
-static int
-MakeAddAllSrc(void *cgnp, void *pgnp)
+static void
+MakeAddAllSrc(GNode *cgn, GNode *pgn)
{
- GNode *cgn = (GNode *)cgnp;
- GNode *pgn = (GNode *)pgnp;
-
if (cgn->type & OP_MARK)
- return 0;
+ return;
cgn->type |= OP_MARK;
if ((cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE|OP_INVISIBLE)) == 0) {
const char *child, *allsrc;
- char *p1 = NULL, *p2 = NULL;
if (cgn->type & OP_ARCHV)
- child = Var_Value(MEMBER, cgn, &p1);
+ child = GNode_VarMember(cgn);
else
- child = cgn->path ? cgn->path : cgn->name;
+ child = GNode_Path(cgn);
if (cgn->type & OP_JOIN) {
- allsrc = Var_Value(ALLSRC, cgn, &p2);
+ allsrc = GNode_VarAllsrc(cgn);
} else {
allsrc = child;
}
if (allsrc != NULL)
Var_Append(ALLSRC, allsrc, pgn);
- bmake_free(p2);
if (pgn->type & OP_JOIN) {
if (cgn->made == MADE) {
Var_Append(OODATE, child, pgn);
@@ -944,74 +810,46 @@ MakeAddAllSrc(void *cgnp, void *pgnp)
*/
Var_Append(OODATE, child, pgn);
}
- bmake_free(p1);
}
- return 0;
}
-/*-
- *-----------------------------------------------------------------------
- * Make_DoAllVar --
- * Set up the ALLSRC and OODATE variables. Sad to say, it must be
- * done separately, rather than while traversing the graph. This is
- * because Make defined OODATE to contain all sources whose modification
- * times were later than that of the target, *not* those sources that
- * were out-of-date. Since in both compatibility and native modes,
- * the modification time of the parent isn't found until the child
- * has been dealt with, we have to wait until now to fill in the
- * variable. As for ALLSRC, the ordering is important and not
- * guaranteed when in native mode, so it must be set here, too.
- *
- * Results:
- * None
+/* Set up the ALLSRC and OODATE variables. Sad to say, it must be
+ * done separately, rather than while traversing the graph. This is
+ * because Make defined OODATE to contain all sources whose modification
+ * times were later than that of the target, *not* those sources that
+ * were out-of-date. Since in both compatibility and native modes,
+ * the modification time of the parent isn't found until the child
+ * has been dealt with, we have to wait until now to fill in the
+ * variable. As for ALLSRC, the ordering is important and not
+ * guaranteed when in native mode, so it must be set here, too.
*
- * Side Effects:
- * The ALLSRC and OODATE variables of the given node is filled in.
- * If the node is a .JOIN node, its TARGET variable will be set to
- * match its ALLSRC variable.
- *-----------------------------------------------------------------------
+ * If the node is a .JOIN node, its TARGET variable will be set to
+ * match its ALLSRC variable.
*/
void
Make_DoAllVar(GNode *gn)
{
+ GNodeListNode *ln;
+
if (gn->flags & DONE_ALLSRC)
return;
- Lst_ForEach(gn->children, MakeUnmark, gn);
- Lst_ForEach(gn->children, MakeAddAllSrc, gn);
+ UnmarkChildren(gn);
+ for (ln = gn->children->first; ln != NULL; ln = ln->next)
+ MakeAddAllSrc(ln->datum, gn);
- if (!Var_Exists (OODATE, gn)) {
+ if (!Var_Exists(OODATE, gn)) {
Var_Set(OODATE, "", gn);
}
- if (!Var_Exists (ALLSRC, gn)) {
+ if (!Var_Exists(ALLSRC, gn)) {
Var_Set(ALLSRC, "", gn);
}
- if (gn->type & OP_JOIN) {
- char *p1;
- Var_Set(TARGET, Var_Value(ALLSRC, gn, &p1), gn);
- bmake_free(p1);
- }
+ if (gn->type & OP_JOIN)
+ Var_Set(TARGET, GNode_VarAllsrc(gn), gn);
gn->flags |= DONE_ALLSRC;
}
-/*-
- *-----------------------------------------------------------------------
- * MakeStartJobs --
- * Start as many jobs as possible.
- *
- * Results:
- * If the query flag was given to pmake, no job will be started,
- * but as soon as an out-of-date target is found, this function
- * returns TRUE. At all other times, this function returns FALSE.
- *
- * Side Effects:
- * Nodes are removed from the toBeMade queue and job table slots
- * are filled.
- *
- *-----------------------------------------------------------------------
- */
-
static int
MakeCheckOrder(void *v_bn, void *ignore MAKE_ATTR_UNUSED)
{
@@ -1019,9 +857,9 @@ MakeCheckOrder(void *v_bn, void *ignore MAKE_ATTR_UNUSED)
if (bn->made >= MADE || !(bn->flags & REMAKE))
return 0;
- if (DEBUG(MAKE))
- fprintf(debug_file, "MakeCheckOrder: Waiting for .ORDER node %s%s\n",
- bn->name, bn->cohort_num);
+
+ DEBUG2(MAKE, "MakeCheckOrder: Waiting for .ORDER node %s%s\n",
+ bn->name, bn->cohort_num);
return 1;
}
@@ -1030,23 +868,20 @@ MakeBuildChild(void *v_cn, void *toBeMade_next)
{
GNode *cn = v_cn;
- if (DEBUG(MAKE))
- fprintf(debug_file, "MakeBuildChild: inspect %s%s, made %d, type %x\n",
- cn->name, cn->cohort_num, cn->made, cn->type);
+ DEBUG4(MAKE, "MakeBuildChild: inspect %s%s, made %d, type %x\n",
+ cn->name, cn->cohort_num, cn->made, cn->type);
if (cn->made > DEFERRED)
return 0;
/* If this node is on the RHS of a .ORDER, check LHSs. */
assert(cn->order_pred);
- if (Lst_ForEach(cn->order_pred, MakeCheckOrder, 0)) {
+ if (Lst_ForEachUntil(cn->order_pred, MakeCheckOrder, 0)) {
/* Can't build this (or anything else in this child list) yet */
cn->made = DEFERRED;
return 0; /* but keep looking */
}
- if (DEBUG(MAKE))
- fprintf(debug_file, "MakeBuildChild: schedule %s%s\n",
- cn->name, cn->cohort_num);
+ DEBUG2(MAKE, "MakeBuildChild: schedule %s%s\n", cn->name, cn->cohort_num);
cn->made = REQUESTED;
if (toBeMade_next == NULL)
@@ -1055,10 +890,10 @@ MakeBuildChild(void *v_cn, void *toBeMade_next)
Lst_InsertBefore(toBeMade, toBeMade_next, cn);
if (cn->unmade_cohorts != 0)
- Lst_ForEach(cn->cohorts, MakeBuildChild, toBeMade_next);
+ Lst_ForEachUntil(cn->cohorts, MakeBuildChild, toBeMade_next);
/*
- * If this node is a .WAIT node with unmade chlidren
+ * If this node is a .WAIT node with unmade children
* then don't add the next sibling.
*/
return cn->type & OP_WAIT && cn->unmade > 0;
@@ -1081,6 +916,12 @@ MakeBuildParent(void *v_pn, void *toBeMade_next)
return 0;
}
+/* Start as many jobs as possible, taking them from the toBeMade queue.
+ *
+ * If the query flag was given to pmake, no job will be started,
+ * but as soon as an out-of-date target is found, this function
+ * returns TRUE. At all other times, this function returns FALSE.
+ */
static Boolean
MakeStartJobs(void)
{
@@ -1094,26 +935,21 @@ MakeStartJobs(void)
have_token = 1;
gn = Lst_Dequeue(toBeMade);
- if (DEBUG(MAKE))
- fprintf(debug_file, "Examining %s%s...\n",
- gn->name, gn->cohort_num);
+ DEBUG2(MAKE, "Examining %s%s...\n", gn->name, gn->cohort_num);
if (gn->made != REQUESTED) {
- if (DEBUG(MAKE))
- fprintf(debug_file, "state %d\n", gn->made);
+ DEBUG1(MAKE, "state %d\n", gn->made);
make_abort(gn, __LINE__);
}
- if (gn->checked == checked) {
+ if (gn->checked_seqno == checked) {
/* We've already looked at this node since a job finished... */
- if (DEBUG(MAKE))
- fprintf(debug_file, "already checked %s%s\n",
- gn->name, gn->cohort_num);
+ DEBUG2(MAKE, "already checked %s%s\n", gn->name, gn->cohort_num);
gn->made = DEFERRED;
continue;
}
- gn->checked = checked;
+ gn->checked_seqno = checked;
if (gn->unmade != 0) {
/*
@@ -1121,28 +957,23 @@ MakeStartJobs(void)
* just before the current first element.
*/
gn->made = DEFERRED;
- Lst_ForEach(gn->children, MakeBuildChild, Lst_First(toBeMade));
+ Lst_ForEachUntil(gn->children, MakeBuildChild, toBeMade->first);
/* and drop this node on the floor */
- if (DEBUG(MAKE))
- fprintf(debug_file, "dropped %s%s\n", gn->name, gn->cohort_num);
+ DEBUG2(MAKE, "dropped %s%s\n", gn->name, gn->cohort_num);
continue;
}
gn->made = BEINGMADE;
if (Make_OODate(gn)) {
- if (DEBUG(MAKE)) {
- fprintf(debug_file, "out-of-date\n");
- }
- if (queryFlag) {
+ DEBUG0(MAKE, "out-of-date\n");
+ if (opts.queryFlag) {
return TRUE;
}
Make_DoAllVar(gn);
Job_Make(gn);
have_token = 0;
} else {
- if (DEBUG(MAKE)) {
- fprintf(debug_file, "up-to-date\n");
- }
+ DEBUG0(MAKE, "up-to-date\n");
gn->made = UPTODATE;
if (gn->type & OP_JOIN) {
/*
@@ -1163,58 +994,44 @@ MakeStartJobs(void)
return FALSE;
}
-/*-
- *-----------------------------------------------------------------------
- * MakePrintStatus --
- * Print the status of a top-level node, viz. it being up-to-date
- * already or not created due to an error in a lower level.
- * Callback function for Make_Run via Lst_ForEach.
- *
- * Input:
- * gnp Node to examine
- * cyclep True if gn->unmade being non-zero implies a
- * cycle in the graph, not an error in an
- * inferior.
- *
- * Results:
- * Always returns 0.
- *
- * Side Effects:
- * A message may be printed.
- *
- *-----------------------------------------------------------------------
- */
-static int
-MakePrintStatusOrder(void *ognp, void *gnp)
+static void
+MakePrintStatusOrderNode(GNode *ogn, GNode *gn)
{
- GNode *ogn = ognp;
- GNode *gn = gnp;
-
if (!(ogn->flags & REMAKE) || ogn->made > REQUESTED)
/* not waiting for this one */
- return 0;
+ return;
printf(" `%s%s' has .ORDER dependency against %s%s ",
- gn->name, gn->cohort_num, ogn->name, ogn->cohort_num);
+ gn->name, gn->cohort_num, ogn->name, ogn->cohort_num);
GNode_FprintDetails(stdout, "(", ogn, ")\n");
- if (DEBUG(MAKE) && debug_file != stdout) {
- fprintf(debug_file, " `%s%s' has .ORDER dependency against %s%s ",
- gn->name, gn->cohort_num, ogn->name, ogn->cohort_num);
- GNode_FprintDetails(debug_file, "(", ogn, ")\n");
+ if (DEBUG(MAKE) && opts.debug_file != stdout) {
+ debug_printf(" `%s%s' has .ORDER dependency against %s%s ",
+ gn->name, gn->cohort_num, ogn->name, ogn->cohort_num);
+ GNode_FprintDetails(opts.debug_file, "(", ogn, ")\n");
}
- return 0;
}
-static int
-MakePrintStatus(void *gnp, void *v_errors)
+static void
+MakePrintStatusOrder(GNode *gn)
{
- GNode *gn = (GNode *)gnp;
- int *errors = v_errors;
+ GNodeListNode *ln;
+ for (ln = gn->order_pred->first; ln != NULL; ln = ln->next)
+ MakePrintStatusOrderNode(ln->datum, gn);
+}
+
+static void MakePrintStatusList(GNodeList *, int *);
+/* Print the status of a top-level node, viz. it being up-to-date already
+ * or not created due to an error in a lower level.
+ * Callback function for Make_Run via Lst_ForEachUntil.
+ */
+static Boolean
+MakePrintStatus(GNode *gn, int *errors)
+{
if (gn->flags & DONECYCLE)
/* We've completely processed this node before, don't do it again. */
- return 0;
+ return FALSE;
if (gn->unmade == 0) {
gn->flags |= DONECYCLE;
@@ -1231,29 +1048,27 @@ MakePrintStatus(void *gnp, void *v_errors)
(*errors)++;
printf("`%s%s' was not built", gn->name, gn->cohort_num);
GNode_FprintDetails(stdout, " (", gn, ")!\n");
- if (DEBUG(MAKE) && debug_file != stdout) {
- fprintf(debug_file, "`%s%s' was not built",
- gn->name, gn->cohort_num);
- GNode_FprintDetails(debug_file, " (", gn, ")!\n");
+ if (DEBUG(MAKE) && opts.debug_file != stdout) {
+ debug_printf("`%s%s' was not built", gn->name, gn->cohort_num);
+ GNode_FprintDetails(opts.debug_file, " (", gn, ")!\n");
}
/* Most likely problem is actually caused by .ORDER */
- Lst_ForEach(gn->order_pred, MakePrintStatusOrder, gn);
+ MakePrintStatusOrder(gn);
break;
default:
/* Errors - already counted */
printf("`%s%s' not remade because of errors.\n",
gn->name, gn->cohort_num);
- if (DEBUG(MAKE) && debug_file != stdout)
- fprintf(debug_file, "`%s%s' not remade because of errors.\n",
- gn->name, gn->cohort_num);
+ if (DEBUG(MAKE) && opts.debug_file != stdout)
+ debug_printf("`%s%s' not remade because of errors.\n",
+ gn->name, gn->cohort_num);
break;
}
- return 0;
+ return FALSE;
}
- if (DEBUG(MAKE))
- fprintf(debug_file, "MakePrintStatus: %s%s has %d unmade children\n",
- gn->name, gn->cohort_num, gn->unmade);
+ DEBUG3(MAKE, "MakePrintStatus: %s%s has %d unmade children\n",
+ gn->name, gn->cohort_num, gn->unmade);
/*
* If printing cycles and came to one that has unmade children,
* print out the cycle by recursing on its children.
@@ -1261,10 +1076,10 @@ MakePrintStatus(void *gnp, void *v_errors)
if (!(gn->flags & CYCLE)) {
/* Fist time we've seen this node, check all children */
gn->flags |= CYCLE;
- Lst_ForEach(gn->children, MakePrintStatus, errors);
+ MakePrintStatusList(gn->children, errors);
/* Mark that this node needn't be processed again */
gn->flags |= DONECYCLE;
- return 0;
+ return FALSE;
}
/* Only output the error once per node */
@@ -1272,32 +1087,41 @@ MakePrintStatus(void *gnp, void *v_errors)
Error("Graph cycles through `%s%s'", gn->name, gn->cohort_num);
if ((*errors)++ > 100)
/* Abandon the whole error report */
- return 1;
+ return TRUE;
/* Reporting for our children will give the rest of the loop */
- Lst_ForEach(gn->children, MakePrintStatus, errors);
- return 0;
+ MakePrintStatusList(gn->children, errors);
+ return FALSE;
}
+static void
+MakePrintStatusList(GNodeList *gnodes, int *errors)
+{
+ GNodeListNode *ln;
+ for (ln = gnodes->first; ln != NULL; ln = ln->next)
+ if (MakePrintStatus(ln->datum, errors))
+ break;
+}
-/*-
- *-----------------------------------------------------------------------
- * Make_ExpandUse --
- * Expand .USE nodes and create a new targets list
+/* Expand .USE nodes and create a new targets list.
*
* Input:
* targs the initial list of targets
- *
- * Side Effects:
- *-----------------------------------------------------------------------
*/
void
-Make_ExpandUse(Lst targs)
+Make_ExpandUse(GNodeList *targs)
{
- GNode *gn; /* a temporary pointer */
- Lst examine; /* List of targets to examine */
+ GNodeList *examine; /* List of targets to examine */
- examine = Lst_Copy(targs, NULL);
+ {
+ /* XXX: Why is it necessary to copy the list? There shouldn't be
+ * any modifications to the list, at least the function name
+ * ExpandUse doesn't suggest that. */
+ GNodeListNode *ln;
+ examine = Lst_New();
+ for (ln = targs->first; ln != NULL; ln = ln->next)
+ Lst_Append(examine, ln->datum);
+ }
/*
* Make an initial downward pass over the graph, marking nodes to be made
@@ -1308,15 +1132,14 @@ Make_ExpandUse(Lst targs)
* and go on about our business.
*/
while (!Lst_IsEmpty(examine)) {
- gn = Lst_Dequeue(examine);
+ GNode *gn = Lst_Dequeue(examine);
if (gn->flags & REMAKE)
/* We've looked at this one already */
continue;
gn->flags |= REMAKE;
- if (DEBUG(MAKE))
- fprintf(debug_file, "Make_ExpandUse: examine %s%s\n",
- gn->name, gn->cohort_num);
+ DEBUG2(MAKE, "Make_ExpandUse: examine %s%s\n",
+ gn->name, gn->cohort_num);
if (gn->type & OP_DOUBLEDEP)
Lst_PrependAll(examine, gn->cohorts);
@@ -1342,81 +1165,52 @@ Make_ExpandUse(Lst targs)
}
(void)Dir_MTime(gn, 0);
- Var_Set(TARGET, gn->path ? gn->path : gn->name, gn);
- Lst_ForEach(gn->children, MakeUnmark, gn);
- Lst_ForEach(gn->children, MakeHandleUse, gn);
+ Var_Set(TARGET, GNode_Path(gn), gn);
+ UnmarkChildren(gn);
+ HandleUseNodes(gn);
if ((gn->type & OP_MADE) == 0)
Suff_FindDeps(gn);
else {
/* Pretend we made all this node's children */
- Lst_ForEach(gn->children, MakeFindChild, gn);
+ Lst_ForEachUntil(gn->children, MakeFindChild, gn);
if (gn->unmade != 0)
printf("Warning: %s%s still has %d unmade children\n",
gn->name, gn->cohort_num, gn->unmade);
}
if (gn->unmade != 0)
- Lst_ForEach(gn->children, MakeAddChild, examine);
+ Lst_ForEachUntil(gn->children, MakeAddChild, examine);
}
Lst_Free(examine);
}
-/*-
- *-----------------------------------------------------------------------
- * Make_ProcessWait --
- * Convert .WAIT nodes into dependencies
- *
- * Input:
- * targs the initial list of targets
- *
- *-----------------------------------------------------------------------
- */
-
-static int
-link_parent(void *cnp, void *pnp)
-{
- GNode *cn = cnp;
- GNode *pn = pnp;
-
- Lst_Append(pn->children, cn);
- Lst_Append(cn->parents, pn);
- pn->unmade++;
- return 0;
-}
-
-static int
-add_wait_dep(void *v_cn, void *v_wn)
+/* Make the .WAIT node depend on the previous children */
+static void
+add_wait_dependency(GNodeListNode *owln, GNode *wn)
{
- GNode *cn = v_cn;
- GNode *wn = v_wn;
+ GNodeListNode *cln;
+ GNode *cn;
- if (cn == wn)
- return 1;
+ for (cln = owln; (cn = cln->datum) != wn; cln = cln->next) {
+ DEBUG3(MAKE, ".WAIT: add dependency %s%s -> %s\n",
+ cn->name, cn->cohort_num, wn->name);
- if (cn == NULL || wn == NULL) {
- printf("bad wait dep %p %p\n", cn, wn);
- exit(4);
+ /* XXX: This pattern should be factored out, it repeats often */
+ Lst_Append(wn->children, cn);
+ wn->unmade++;
+ Lst_Append(cn->parents, wn);
}
- if (DEBUG(MAKE))
- fprintf(debug_file, ".WAIT: add dependency %s%s -> %s\n",
- cn->name, cn->cohort_num, wn->name);
-
- Lst_Append(wn->children, cn);
- wn->unmade++;
- Lst_Append(cn->parents, wn);
- return 0;
}
+/* Convert .WAIT nodes into dependencies. */
static void
-Make_ProcessWait(Lst targs)
+Make_ProcessWait(GNodeList *targs)
{
- GNode *pgn; /* 'parent' node we are examining */
- GNode *cgn; /* Each child in turn */
- LstNode owln; /* Previous .WAIT node */
- Lst examine; /* List of targets to examine */
- LstNode ln;
+ GNode *pgn; /* 'parent' node we are examining */
+ GNodeListNode *owln; /* Previous .WAIT node */
+ GNodeList *examine; /* List of targets to examine */
/*
* We need all the nodes to have a common parent in order for the
@@ -1430,40 +1224,47 @@ Make_ProcessWait(Lst targs)
/* Get it displayed in the diag dumps */
Lst_Prepend(Targ_List(), pgn);
- Lst_ForEach(targs, link_parent, pgn);
+ {
+ GNodeListNode *ln;
+ for (ln = targs->first; ln != NULL; ln = ln->next) {
+ GNode *cgn = ln->datum;
+
+ Lst_Append(pgn->children, cgn);
+ Lst_Append(cgn->parents, pgn);
+ pgn->unmade++;
+ }
+ }
/* Start building with the 'dummy' .MAIN' node */
MakeBuildChild(pgn, NULL);
- examine = Lst_Init();
+ examine = Lst_New();
Lst_Append(examine, pgn);
while (!Lst_IsEmpty(examine)) {
+ GNodeListNode *ln;
+
pgn = Lst_Dequeue(examine);
/* We only want to process each child-list once */
if (pgn->flags & DONE_WAIT)
continue;
pgn->flags |= DONE_WAIT;
- if (DEBUG(MAKE))
- fprintf(debug_file, "Make_ProcessWait: examine %s\n", pgn->name);
+ DEBUG1(MAKE, "Make_ProcessWait: examine %s\n", pgn->name);
if (pgn->type & OP_DOUBLEDEP)
Lst_PrependAll(examine, pgn->cohorts);
- owln = Lst_First(pgn->children);
- Lst_Open(pgn->children);
- for (; (ln = Lst_Next(pgn->children)) != NULL; ) {
- cgn = LstNode_Datum(ln);
+ owln = pgn->children->first;
+ for (ln = pgn->children->first; ln != NULL; ln = ln->next) {
+ GNode *cgn = ln->datum;
if (cgn->type & OP_WAIT) {
- /* Make the .WAIT node depend on the previous children */
- Lst_ForEachFrom(pgn->children, owln, add_wait_dep, cgn);
+ add_wait_dependency(owln, cgn);
owln = ln;
} else {
Lst_Append(examine, cgn);
}
}
- Lst_Close(pgn->children);
}
Lst_Free(examine);
@@ -1494,22 +1295,22 @@ Make_ProcessWait(Lst targs)
*-----------------------------------------------------------------------
*/
Boolean
-Make_Run(Lst targs)
+Make_Run(GNodeList *targs)
{
- int errors; /* Number of errors the Job module reports */
+ int errors; /* Number of errors the Job module reports */
/* Start trying to make the current targets... */
- toBeMade = Lst_Init();
+ toBeMade = Lst_New();
Make_ExpandUse(targs);
Make_ProcessWait(targs);
if (DEBUG(MAKE)) {
- fprintf(debug_file, "#***# full graph\n");
+ debug_printf("#***# full graph\n");
Targ_PrintGraph(1);
}
- if (queryFlag) {
+ if (opts.queryFlag) {
/*
* We wouldn't do any work unless we could start some jobs in the
* next loop... (we won't actually start any, of course, this is just
@@ -1547,12 +1348,11 @@ Make_Run(Lst targs)
* Print the final status of each target. E.g. if it wasn't made
* because some inferior reported an error.
*/
- if (DEBUG(MAKE))
- fprintf(debug_file, "done: errors %d\n", errors);
+ DEBUG1(MAKE, "done: errors %d\n", errors);
if (errors == 0) {
- Lst_ForEach(targs, MakePrintStatus, &errors);
+ MakePrintStatusList(targs, &errors);
if (DEBUG(MAKE)) {
- fprintf(debug_file, "done: errors %d\n", errors);
+ debug_printf("done: errors %d\n", errors);
if (errors)
Targ_PrintGraph(4);
}
diff --git a/make.h b/make.h
index 520a6602518f..0c10706d3632 100644
--- a/make.h
+++ b/make.h
@@ -1,4 +1,4 @@
-/* $NetBSD: make.h,v 1.137 2020/09/02 23:42:58 rillig Exp $ */
+/* $NetBSD: make.h,v 1.179 2020/11/01 17:47:26 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -91,6 +91,7 @@
#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
@@ -142,23 +143,31 @@
#ifdef USE_DOUBLE_BOOLEAN
/* During development, to find type mismatches in function declarations. */
typedef double Boolean;
+#define TRUE 1.0
+#define FALSE 0.0
#elif defined(USE_UCHAR_BOOLEAN)
/* During development, to find code that depends on the exact value of TRUE or
* that stores other values in Boolean variables. */
typedef unsigned char Boolean;
#define TRUE ((unsigned char)0xFF)
#define FALSE ((unsigned char)0x00)
+#elif defined(USE_CHAR_BOOLEAN)
+/* During development, to find code that uses a boolean as array index, via
+ * -Wchar-subscripts. */
+typedef char Boolean;
+#define TRUE ((char)-1)
+#define FALSE ((char)0x00)
#elif defined(USE_ENUM_BOOLEAN)
-typedef enum { FALSE, TRUE} Boolean;
+typedef enum Boolean { FALSE, TRUE } Boolean;
#else
typedef int Boolean;
-#endif
#ifndef TRUE
#define TRUE 1
-#endif /* TRUE */
+#endif
#ifndef FALSE
#define FALSE 0
-#endif /* FALSE */
+#endif
+#endif
#include "lst.h"
#include "enum.h"
@@ -196,18 +205,20 @@ typedef enum {
* communicating to other parts of the program the way in which a target
* should be made.
*
- * These constants are bitwise-OR'ed together and placed in the 'type' field
- * of each node. Any node that has a 'type' field which satisfies the OP_NOP
- * function was never never on the left-hand side of an operator, though it
- * may have been on the right-hand side... */
-typedef enum {
- /* Execution of commands depends on children (:) */
+ * Some of the OP_ constants can be combined, others cannot. */
+typedef enum GNodeType {
+ /* 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,
- /* Always execute commands (!) */
+ /* The dependency operator '!' always executes its commands, even if
+ * its children are up-to-date. */
OP_FORCE = 1 << 1,
- /* Execution of commands depends on children per line (::) */
+ /* 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. */
OP_DOUBLEDEP = 1 << 2,
+ /* Matches the dependency operators ':', '!' and '::'. */
OP_OPMASK = OP_DEPENDS|OP_FORCE|OP_DOUBLEDEP,
/* Don't care if the target doesn't exist and can't be created */
@@ -216,8 +227,8 @@ typedef enum {
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 */
- OP_EXEC = 1 << 5,
- /* Ignore errors when creating the node */
+ OP_EXEC = 1 << 5,
+ /* 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,
@@ -259,23 +270,32 @@ typedef enum {
/* The node is a transformation rule */
OP_TRANSFORM = 1 << 31,
/* Target is a member of an archive */
+ /* XXX: How does this differ from OP_ARCHV? */
OP_MEMBER = 1 << 30,
- /* Target is a library */
+ /* The node is a library,
+ * its name has the form "-l<libname>" */
OP_LIB = 1 << 29,
- /* Target is an archive construct */
+ /* The node is an archive member,
+ * its name has the form "archive(member)" */
+ /* XXX: How does this differ from OP_MEMBER? */
OP_ARCHV = 1 << 28,
/* Target has all the commands it should. Used when parsing to catch
- * multiple commands for a target. */
+ * multiple command groups for a target. Only applies to the dependency
+ * operators ':' and '!', but not to '::'. */
OP_HAS_COMMANDS = 1 << 27,
- /* Saving commands on .END (Compat) */
+ /* 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 << 26,
/* Already processed by Suff_FindDeps */
OP_DEPS_FOUND = 1 << 25,
/* Node found while expanding .ALLSRC */
- OP_MARK = 1 << 24
+ OP_MARK = 1 << 24,
+
+ OP_NOTARGET = OP_NOTMAIN | OP_USE | OP_EXEC | OP_TRANSFORM
} GNodeType;
-typedef enum {
+typedef enum GNodeFlags {
REMAKE = 0x0001, /* this target needs to be (re)made */
CHILDMADE = 0x0002, /* children of this target were made */
FORCE = 0x0004, /* children don't exist, and we pretend made */
@@ -288,6 +308,14 @@ typedef enum {
INTERNAL = 0x4000 /* Internal use only */
} GNodeFlags;
+typedef struct List StringList;
+typedef struct ListNode StringListNode;
+
+typedef struct List GNodeList;
+typedef struct ListNode GNodeListNode;
+
+typedef struct List /* of CachedDir */ SearchPath;
+
/* A graph node represents a target that can possibly be made, including its
* relation to other targets and a lot of other details. */
typedef struct GNode {
@@ -302,40 +330,41 @@ typedef struct GNode {
/* 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. */
GNodeType type;
- /* whether it is involved in this invocation of make */
GNodeFlags flags;
/* The state of processing on this node */
GNodeMade made;
int unmade; /* The number of unmade children */
- time_t mtime; /* Its modification time */
- struct GNode *cmgn; /* The youngest child */
+ /* The modification time; 0 means the node does not have a corresponding
+ * file; see Make_OODate. */
+ 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. */
- Lst implicitParents;
+ GNodeList *implicitParents;
- /* Other nodes of the same name for the :: operator. */
- Lst cohorts;
+ /* Other nodes of the same name, for the '::' operator. */
+ GNodeList *cohorts;
/* The nodes that depend on this one, or in other words, the nodes for
* which this is a source. */
- Lst parents;
+ GNodeList *parents;
/* The nodes on which this one depends. */
- Lst children;
+ GNodeList *children;
/* .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. */
- Lst order_pred;
+ GNodeList *order_pred;
/* .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. */
- Lst order_succ;
+ GNodeList *order_succ;
- /* #n for this cohort */
+ /* The "#n" suffix for this cohort, or "" for other nodes */
char cohort_num[8];
/* The number of unmade instances on the cohorts list */
int unmade_cohorts;
@@ -344,14 +373,17 @@ typedef struct GNode {
struct GNode *centurion;
/* Last time (sequence number) we tried to make this node */
- unsigned int checked;
+ unsigned int checked_seqno;
/* The "local" variables that are specific to this target and this target
- * only, such as $@, $<, $?. */
- Hash_Table context;
+ * only, such as $@, $<, $?.
+ *
+ * Also used for the global variable scopes VAR_GLOBAL, VAR_CMDLINE,
+ * VAR_INTERNAL, which contain variables with arbitrary names. */
+ HashTable /* of Var pointer */ context;
/* The commands to be given to a shell to create this target. */
- Lst commands;
+ StringList *commands;
/* Suffix for the node (determined by Suff_FindDeps and opaque to everyone
* but the Suff module) */
@@ -363,40 +395,21 @@ typedef struct GNode {
int lineno;
} GNode;
-#define NoExecute(gn) ((gn->type & OP_MAKE) ? noRecursiveExecute : noExecute)
-/*
- * OP_NOP will return TRUE if the node with the given type was not the
- * object of a dependency operator
- */
-#define OP_NOP(t) (((t) & OP_OPMASK) == 0x00000000)
-
-#define OP_NOTARGET (OP_NOTMAIN|OP_USE|OP_EXEC|OP_TRANSFORM)
-
-/*
- * The TARG_ constants are used when calling the Targ_FindNode and
- * Targ_FindList functions in targ.c. They simply tell the functions what to
- * do if the desired node(s) is (are) not found. If the TARG_CREATE constant
- * is given, a new, empty node will be created for the target, placed in the
- * table of all targets and its address returned. If TARG_NOCREATE is given,
- * a NULL pointer will be returned.
- */
-#define TARG_NOCREATE 0x00 /* don't create it */
-#define TARG_CREATE 0x01 /* create node if not found */
-#define TARG_NOHASH 0x02 /* don't look in/add to hash table */
-
/*
* Error levels for parsing. PARSE_FATAL means the process cannot continue
- * once the makefile has been parsed. PARSE_WARNING means it can. Passed
- * as the first argument to Parse_Error.
+ * once the top-level makefile has been parsed. PARSE_WARNING and PARSE_INFO
+ * mean it can.
*/
-#define PARSE_INFO 3
-#define PARSE_WARNING 2
-#define PARSE_FATAL 1
+typedef enum ParseErrorLevel {
+ PARSE_FATAL = 1,
+ PARSE_WARNING,
+ PARSE_INFO
+} ParseErrorLevel;
/*
- * Values returned by Cond_Eval.
+ * Values returned by Cond_EvalLine and Cond_EvalCondition.
*/
-typedef enum {
+typedef enum CondEvalResult {
COND_PARSE, /* Parse the next lines */
COND_SKIP, /* Skip the next lines */
COND_INVALID /* Not a conditional statement */
@@ -405,77 +418,52 @@ typedef enum {
/*
* Definitions for the "local" variables. Used only for clarity.
*/
-#define TARGET "@" /* Target of dependency */
-#define OODATE "?" /* All out-of-date sources */
-#define ALLSRC ">" /* All sources */
-#define IMPSRC "<" /* Sour