diff options
author | Simon J. Gerraty <sjg@FreeBSD.org> | 2020-09-05 16:11:04 +0000 |
---|---|---|
committer | Simon J. Gerraty <sjg@FreeBSD.org> | 2020-09-05 16:11:04 +0000 |
commit | 6bbc783f48498b808e19db4441299dc7d85a278b (patch) | |
tree | be201219a56594c76537191ee91fdd3ef8cfb348 | |
parent | 367d32e2b15fe0397ddecccaa04cf9ed0164c969 (diff) |
Import bmake-20200902vendor/NetBSD/bmake/20200902
Lots of code refactoring, simplification and cleanup.
Lots of new unit-tests providing much higher code coverage.
All courtesy of rillig at netbsd.
Other significant changes:
o new read-only variable .SHELL which provides the path of the shell
used to run scripts (as defined by the .SHELL target).
o new debug option -dl: LINT mode, does the equivalent of := for all
variable assignments so that file and line number are reported for
variable parse errors.
Notes
Notes:
svn path=/vendor/NetBSD/bmake/dist/; revision=365361
svn path=/vendor/NetBSD/bmake/20200902/; revision=365363; tag=vendor/NetBSD/bmake/20200902
656 files changed, 14869 insertions, 10875 deletions
diff --git a/ChangeLog b/ChangeLog index 20e2628b3bed..847c2e4c0f90 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,243 @@ +2020-09-02 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200902 + Merge with NetBSD make, pick up + o use make_stat to ensure no confusion over valid fields + returned by cached_stat + o var.c: make VarQuote const-correct + o add unit tests for .for + +2020-09-01 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200901 + Merge with NetBSD make, pick up + o rename Hash_Table fields + o make data types in Dir_HasWildcards more precise + +2020-08-31 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200831 + Merge with NetBSD make, pick up + o suff.c: fix unbalanced Lst_Open/Lst_Close in SuffFindCmds + o lst.c: Lst_Open renable assert that list isn't open + o unit test for .TARGET dependent flags + o var.c: fix aliasing bug in VarUniq + o more unit tests for :u + +2020-08-30 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200830 + Merge with NetBSD make, pick up + o allow for strict type checking for Boolean + o Var_Parse never returns NULL + o Var_Subst never returns NULL + o Lst_Find now takes boolean match function + o rename Lst_Memeber to Lst_FindDatum + o rename LstNode functions to match their type + o rename GNode.iParents to implicitParents + o fix assertion failure for .SUFFIXES in archives + o compat.c: clean up documentation for CompatInterrupt and Compat_Run + remove unreachable code from CompatRunCommand + o main.c: simplify getBoolean + o stc.c: replace brk_string with simpler Str_Words + o suff.c: add debug macros + +2020-08-28 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200828 + Merge with NetBSD make, pick up + o lst.c: inline LstIsValid and LstNodeIsValid + o remove trailing S from Lst function names after migration complete + o more comment cleanup/clarification + o suff.c: clean up suffix handling + o more unit tests + +2020-08-26 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200826 + Merge with NetBSD make, pick up + o enum.c: distinguish between bitsets containing flags and + ordinary enums + o var.c: fix error message for ::!= modifier with shell error + o fix bugs in -DCLEANUP mode + +2020-08-24 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200824 + Merge with NetBSD make, pick up + o in debug mode, print GNode details in symbols + +2020-08-23 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200823 + Merge with NetBSD make, pick up + o lst.c: more asserts, + make args to Lst_Find match others. + o var.c: pass flags to VarAdd + o arch.c: use Buffer + o str.c: brk_string return size_t for nwords + o more unit tests + +2020-08-22 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): + Merge with NetBSD make, pick up + o var.c: support for read-only variables eg .SHELL + being the shell used to run scripts. + o lst.c: more simplification + o more documentation and style cleanup + o more unit tests + o ensure unit-test/Makefile is run by TEST_MAKE + o reduce duplication of header inclusion + +2020-08-21 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200821 + Merge with NetBSD make, pick up + o lst.c: revert invalid assertion - but document it + o dir.c: split Dir_Init into two functions + +2020-08-20 Simon J Gerraty <sjg@beast.crufty.net> + + * lst.c: needs inttypes.h on Linux + + * VERSION (_MAKE_VERSION): 20200820 + Merge with NetBSD make, pick up + o make.1: clarify some passages + o var.c: more cleanup, clarify comments + o make_malloc.c: remove unreachable code + o cond.c: make CondGetString easier to debug + o simplify list usage + o unit-tests: more + +2020-08-16 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200816 + Merge with NetBSD make, pick up + o refactor unit-tests to be more fine grained + not all tests moved yet + +2020-08-14 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200814 + Merge with NetBSD make, pick up + o more str_concat variants + o more enums for flags + o var.c: cleanup for higher warnings level + +2020-08-10 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200810 + Merge with NetBSD make, pick up + o more unit tests + o general comment and style cleanup + +2020-08-08 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200808 + Merge with NetBSD make, pick up + o enum.[ch]: streamline, enums for use in flags and debug output + o cond.c: cleanup + o var.c: reduce duplicate code for modifiers + debug logging for Var_Parse + more detailed debug output + o more unit tests + +2020-08-06 Simon J Gerraty <sjg@beast.crufty.net> + + * unit-tests/Makefile: -r for recursive and include Makefile.inc + so I can run tests in meta mode + supress extra noise if in meta mode + + * VERSION (_MAKE_VERSION): 20200806 + Merge with NetBSD make, pick up + o parse.c: remove VARE_WANTRES for LINT + we just want to check parsing (for now). + +2020-08-05 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200805 + Merge with NetBSD make, pick up + o make.1: Rework the description of dependence operators + +2020-08-03 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200803 + Merge with NetBSD make, pick up + o revert some C99 usage, for max portability + o unit-tests/lint + +2020-08-02 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200802 + Merge with NetBSD make, pick up + o more unit tests + +2020-08-01 Simon J Gerraty <sjg@beast.crufty.net> + + * Remove NetBSD specific plumbing from unit-tests/Makefile + + * VERSION (_MAKE_VERSION): 20200801 + Merge with NetBSD make, pick up + o make Var_Value return const + o size_t for buf sizes + o optimize some buffer operations - avoid strlen + +2020-07-31 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200731 + Merge with NetBSD make, pick up + o var.c: fix undefinded behavior for incomplete :t modifier + fixes unit-test/moderrs on Ubuntu + o parse.c: When parsing variable assignments other than := + if DEBUG(LINT) test substition of value, so we get a file and + line number in the resulting error. + o dir.c: fix parsing of nested braces in dependency lines + add unit-tests + +2020-07-30 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200730 + Merge with NetBSD make, pick up + o var.c: minor cleanup + o unit-tests: more tests to improve code coverage + +2020-07-28 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200728 + Merge with NetBSD make, pick up + o var.c: more optimizations + +2020-07-26 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200726 + Merge with NetBSD make, pick up + o collapse lsd.lib into lst.c - reduce code size and allow inlining + o lots of function comment updates + o var.c: more optimizations + o make return of Var_Parse const + +2020-07-20 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200720 + Merge with NetBSD make, pick up + o DEBUG_HASH report stats at end and tone down the noise + o var.c: each flag type gets its own prefix. + move SysV string matching to var.c + make ampersand in ${VAR:from=to&} an ordinary character + cleanup and simplify implementation of modifiers + o make.1: move documentation for assignment modifiers + +2020-07-18 Simon J Gerraty <sjg@beast.crufty.net> + + * VERSION (_MAKE_VERSION): 20200718 + Merge with NetBSD make, pick up + o DEBUG_HASH to see how well the hash tables are working + +2020-07-11 Simon J Gerraty <sjg@beast.crufty.net> + + * bsd.after-import.mk: make sure we update unit-tests/Makefile + 2020-07-10 Simon J Gerraty <sjg@beast.crufty.net> * configure.in: use AC_INCLUDES_DEFAULT rather than AC_HEADER_STDC @@ -23,6 +23,8 @@ configure.in dir.c dir.h dirname.c +enum.c +enum.h filemon/filemon.h filemon/filemon_dev.c filemon/filemon_ktrace.c @@ -34,36 +36,8 @@ hash.h install-sh job.c job.h +lst.c lst.h -lst.lib/Makefile -lst.lib/lstAppend.c -lst.lib/lstAtEnd.c -lst.lib/lstAtFront.c -lst.lib/lstClose.c -lst.lib/lstConcat.c -lst.lib/lstDatum.c -lst.lib/lstDeQueue.c -lst.lib/lstDestroy.c -lst.lib/lstDupl.c -lst.lib/lstEnQueue.c -lst.lib/lstFind.c -lst.lib/lstFindFrom.c -lst.lib/lstFirst.c -lst.lib/lstForEach.c -lst.lib/lstForEachFrom.c -lst.lib/lstInit.c -lst.lib/lstInsert.c -lst.lib/lstInt.h -lst.lib/lstIsAtEnd.c -lst.lib/lstIsEmpty.c -lst.lib/lstLast.c -lst.lib/lstMember.c -lst.lib/lstNext.c -lst.lib/lstOpen.c -lst.lib/lstPrev.c -lst.lib/lstRemove.c -lst.lib/lstReplace.c -lst.lib/lstSucc.c machine.sh main.c make-bootstrap.sh.in @@ -88,7 +62,6 @@ ranlib.h realpath.c setenv.c sigcompat.c -sprite.h str.c stresep.c strlcpy.c @@ -100,22 +73,234 @@ 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/cmd-interrupt.exp +unit-tests/cmd-interrupt.mk +unit-tests/cmdline.exp +unit-tests/cmdline.mk unit-tests/comment.exp unit-tests/comment.mk +unit-tests/cond-cmp-numeric-eq.exp +unit-tests/cond-cmp-numeric-eq.mk +unit-tests/cond-cmp-numeric-ge.exp +unit-tests/cond-cmp-numeric-ge.mk +unit-tests/cond-cmp-numeric-gt.exp +unit-tests/cond-cmp-numeric-gt.mk +unit-tests/cond-cmp-numeric-le.exp +unit-tests/cond-cmp-numeric-le.mk +unit-tests/cond-cmp-numeric-lt.exp +unit-tests/cond-cmp-numeric-lt.mk +unit-tests/cond-cmp-numeric-ne.exp +unit-tests/cond-cmp-numeric-ne.mk +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-func-commands.exp +unit-tests/cond-func-commands.mk +unit-tests/cond-func-defined.exp +unit-tests/cond-func-defined.mk +unit-tests/cond-func-empty.exp +unit-tests/cond-func-empty.mk +unit-tests/cond-func-exists.exp +unit-tests/cond-func-exists.mk +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-late.exp +unit-tests/cond-late.mk +unit-tests/cond-op-and.exp +unit-tests/cond-op-and.mk +unit-tests/cond-op-not.exp +unit-tests/cond-op-not.mk +unit-tests/cond-op-or.exp +unit-tests/cond-op-or.mk +unit-tests/cond-op-parentheses.exp +unit-tests/cond-op-parentheses.mk +unit-tests/cond-op.exp +unit-tests/cond-op.mk +unit-tests/cond-short.exp +unit-tests/cond-short.mk +unit-tests/cond-token-number.exp +unit-tests/cond-token-number.mk +unit-tests/cond-token-plain.exp +unit-tests/cond-token-plain.mk +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/cond1.exp unit-tests/cond1.mk unit-tests/cond2.exp unit-tests/cond2.mk -unit-tests/cond-late.mk -unit-tests/cond-late.exp -unit-tests/cond-short.mk -unit-tests/cond-short.exp +unit-tests/counter.exp +unit-tests/counter.mk +unit-tests/dep-colon.exp +unit-tests/dep-colon.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-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-exec.exp +unit-tests/depsrc-exec.mk +unit-tests/depsrc-ignore.exp +unit-tests/depsrc-ignore.mk +unit-tests/depsrc-made.exp +unit-tests/depsrc-made.mk +unit-tests/depsrc-make.exp +unit-tests/depsrc-make.mk +unit-tests/depsrc-meta.exp +unit-tests/depsrc-meta.mk +unit-tests/depsrc-nometa.exp +unit-tests/depsrc-nometa.mk +unit-tests/depsrc-nometa_cmp.exp +unit-tests/depsrc-nometa_cmp.mk +unit-tests/depsrc-nopath.exp +unit-tests/depsrc-nopath.mk +unit-tests/depsrc-notmain.exp +unit-tests/depsrc-notmain.mk +unit-tests/depsrc-optional.exp +unit-tests/depsrc-optional.mk +unit-tests/depsrc-phony.exp +unit-tests/depsrc-phony.mk +unit-tests/depsrc-precious.exp +unit-tests/depsrc-precious.mk +unit-tests/depsrc-recursive.exp +unit-tests/depsrc-recursive.mk +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-wait.exp +unit-tests/depsrc-wait.mk +unit-tests/depsrc.exp +unit-tests/depsrc.mk +unit-tests/deptgt-begin.exp +unit-tests/deptgt-begin.mk +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.exp +unit-tests/deptgt-end.mk +unit-tests/deptgt-error.exp +unit-tests/deptgt-error.mk +unit-tests/deptgt-ignore.exp +unit-tests/deptgt-ignore.mk +unit-tests/deptgt-interrupt.exp +unit-tests/deptgt-interrupt.mk +unit-tests/deptgt-main.exp +unit-tests/deptgt-main.mk +unit-tests/deptgt-makeflags.exp +unit-tests/deptgt-makeflags.mk +unit-tests/deptgt-no_parallel.exp +unit-tests/deptgt-no_parallel.mk +unit-tests/deptgt-nopath.exp +unit-tests/deptgt-nopath.mk +unit-tests/deptgt-notparallel.exp +unit-tests/deptgt-notparallel.mk +unit-tests/deptgt-objdir.exp +unit-tests/deptgt-objdir.mk +unit-tests/deptgt-order.exp +unit-tests/deptgt-order.mk +unit-tests/deptgt-path-suffix.exp +unit-tests/deptgt-path-suffix.mk +unit-tests/deptgt-path.exp +unit-tests/deptgt-path.mk +unit-tests/deptgt-phony.exp +unit-tests/deptgt-phony.mk +unit-tests/deptgt-precious.exp +unit-tests/deptgt-precious.mk +unit-tests/deptgt-shell.exp +unit-tests/deptgt-shell.mk +unit-tests/deptgt-silent.exp +unit-tests/deptgt-silent.mk +unit-tests/deptgt-stale.exp +unit-tests/deptgt-stale.mk +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/directive-elif.exp +unit-tests/directive-elif.mk +unit-tests/directive-elifdef.exp +unit-tests/directive-elifdef.mk +unit-tests/directive-elifmake.exp +unit-tests/directive-elifmake.mk +unit-tests/directive-elifndef.exp +unit-tests/directive-elifndef.mk +unit-tests/directive-elifnmake.exp +unit-tests/directive-elifnmake.mk +unit-tests/directive-else.exp +unit-tests/directive-else.mk +unit-tests/directive-endif.exp +unit-tests/directive-endif.mk +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-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-if.exp +unit-tests/directive-if.mk +unit-tests/directive-ifdef.exp +unit-tests/directive-ifdef.mk +unit-tests/directive-ifmake.exp +unit-tests/directive-ifmake.mk +unit-tests/directive-ifndef.exp +unit-tests/directive-ifndef.mk +unit-tests/directive-ifnmake.exp +unit-tests/directive-ifnmake.mk +unit-tests/directive-info.exp +unit-tests/directive-info.mk +unit-tests/directive-undef.exp +unit-tests/directive-undef.mk +unit-tests/directive-unexport-env.exp +unit-tests/directive-unexport-env.mk +unit-tests/directive-unexport.exp +unit-tests/directive-unexport.mk +unit-tests/directive-warning.exp +unit-tests/directive-warning.mk +unit-tests/directive.exp +unit-tests/directive.mk +unit-tests/directives.exp +unit-tests/directives.mk unit-tests/dollar.exp unit-tests/dollar.mk unit-tests/doterror.exp unit-tests/doterror.mk unit-tests/dotwait.exp unit-tests/dotwait.mk +unit-tests/envfirst.exp +unit-tests/envfirst.mk unit-tests/error.exp unit-tests/error.mk unit-tests/escape.exp @@ -124,6 +309,8 @@ unit-tests/export-all.exp unit-tests/export-all.mk unit-tests/export-env.exp unit-tests/export-env.mk +unit-tests/export-variants.exp +unit-tests/export-variants.mk unit-tests/export.exp unit-tests/export.mk unit-tests/forloop.exp @@ -138,6 +325,10 @@ unit-tests/include-main.exp unit-tests/include-main.mk unit-tests/include-sub.mk unit-tests/include-subsub.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 @@ -146,12 +337,62 @@ unit-tests/modmatch.exp unit-tests/modmatch.mk unit-tests/modmisc.exp unit-tests/modmisc.mk -unit-tests/modorder.exp -unit-tests/modorder.mk unit-tests/modts.exp unit-tests/modts.mk unit-tests/modword.exp unit-tests/modword.mk +unit-tests/opt-backwards.exp +unit-tests/opt-backwards.mk +unit-tests/opt-chdir.exp +unit-tests/opt-chdir.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 +unit-tests/opt-env.mk +unit-tests/opt-file.exp +unit-tests/opt-file.mk +unit-tests/opt-ignore.exp +unit-tests/opt-ignore.mk +unit-tests/opt-include-dir.exp +unit-tests/opt-include-dir.mk +unit-tests/opt-jobs-internal.exp +unit-tests/opt-jobs-internal.mk +unit-tests/opt-jobs.exp +unit-tests/opt-jobs.mk +unit-tests/opt-keep-going.exp +unit-tests/opt-keep-going.mk +unit-tests/opt-m-include-dir.exp +unit-tests/opt-m-include-dir.mk +unit-tests/opt-no-action-at-all.exp +unit-tests/opt-no-action-at-all.mk +unit-tests/opt-no-action.exp +unit-tests/opt-no-action.mk +unit-tests/opt-query.exp +unit-tests/opt-query.mk +unit-tests/opt-raw.exp +unit-tests/opt-raw.mk +unit-tests/opt-silent.exp +unit-tests/opt-silent.mk +unit-tests/opt-touch.exp +unit-tests/opt-touch.mk +unit-tests/opt-tracefile.exp +unit-tests/opt-tracefile.mk +unit-tests/opt-var-expanded.exp +unit-tests/opt-var-expanded.mk +unit-tests/opt-var-literal.exp +unit-tests/opt-var-literal.mk +unit-tests/opt-warnings-as-errors.exp +unit-tests/opt-warnings-as-errors.mk +unit-tests/opt-where-am-i.exp +unit-tests/opt-where-am-i.mk +unit-tests/opt-x-reduce-exported.exp +unit-tests/opt-x-reduce-exported.mk +unit-tests/opt.exp +unit-tests/opt.mk unit-tests/order.exp unit-tests/order.mk unit-tests/phony-end.exp @@ -162,6 +403,28 @@ unit-tests/posix1.exp unit-tests/posix1.mk unit-tests/qequals.exp unit-tests/qequals.mk +unit-tests/recursive.exp +unit-tests/recursive.mk +unit-tests/sh-dots.exp +unit-tests/sh-dots.mk +unit-tests/sh-jobs-error.exp +unit-tests/sh-jobs-error.mk +unit-tests/sh-jobs.exp +unit-tests/sh-jobs.mk +unit-tests/sh-leading-at.exp +unit-tests/sh-leading-at.mk +unit-tests/sh-leading-hyphen.exp +unit-tests/sh-leading-hyphen.mk +unit-tests/sh-leading-plus.exp +unit-tests/sh-leading-plus.mk +unit-tests/sh-meta-chars.exp +unit-tests/sh-meta-chars.mk +unit-tests/sh-multi-line.exp +unit-tests/sh-multi-line.mk +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/sunshcmd.exp @@ -174,12 +437,204 @@ unit-tests/unexport-env.exp unit-tests/unexport-env.mk unit-tests/unexport.exp unit-tests/unexport.mk +unit-tests/use-inference.exp +unit-tests/use-inference.mk +unit-tests/var-class-cmdline.exp +unit-tests/var-class-cmdline.mk +unit-tests/var-class-env.exp +unit-tests/var-class-env.mk +unit-tests/var-class-global.exp +unit-tests/var-class-global.mk +unit-tests/var-class-local-legacy.exp +unit-tests/var-class-local-legacy.mk +unit-tests/var-class-local.exp +unit-tests/var-class-local.mk +unit-tests/var-class.exp +unit-tests/var-class.mk +unit-tests/var-op-append.exp +unit-tests/var-op-append.mk +unit-tests/var-op-assign.exp +unit-tests/var-op-assign.mk +unit-tests/var-op-default.exp +unit-tests/var-op-default.mk +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.exp +unit-tests/var-op.mk unit-tests/varcmd.exp unit-tests/varcmd.mk +unit-tests/vardebug.exp +unit-tests/vardebug.mk +unit-tests/varfind.exp +unit-tests/varfind.mk unit-tests/varmisc.exp unit-tests/varmisc.mk +unit-tests/varmod-assign.exp +unit-tests/varmod-assign.mk +unit-tests/varmod-defined.exp +unit-tests/varmod-defined.mk unit-tests/varmod-edge.exp unit-tests/varmod-edge.mk +unit-tests/varmod-exclam-shell.exp +unit-tests/varmod-exclam-shell.mk +unit-tests/varmod-extension.exp +unit-tests/varmod-extension.mk +unit-tests/varmod-gmtime.exp +unit-tests/varmod-gmtime.mk +unit-tests/varmod-hash.exp +unit-tests/varmod-hash.mk +unit-tests/varmod-head.exp +unit-tests/varmod-head.mk +unit-tests/varmod-ifelse.exp +unit-tests/varmod-ifelse.mk +unit-tests/varmod-l-name-to-value.exp +unit-tests/varmod-l-name-to-value.mk +unit-tests/varmod-localtime.exp +unit-tests/varmod-localtime.mk +unit-tests/varmod-loop.exp +unit-tests/varmod-loop.mk +unit-tests/varmod-match-escape.exp +unit-tests/varmod-match-escape.mk +unit-tests/varmod-match.exp +unit-tests/varmod-match.mk +unit-tests/varmod-no-match.exp +unit-tests/varmod-no-match.mk +unit-tests/varmod-order-reverse.exp +unit-tests/varmod-order-reverse.mk +unit-tests/varmod-order-shuffle.exp +unit-tests/varmod-order-shuffle.mk +unit-tests/varmod-order.exp +unit-tests/varmod-order.mk +unit-tests/varmod-path.exp +unit-tests/varmod-path.mk +unit-tests/varmod-quote-dollar.exp +unit-tests/varmod-quote-dollar.mk +unit-tests/varmod-quote.exp +unit-tests/varmod-quote.mk +unit-tests/varmod-range.exp +unit-tests/varmod-range.mk +unit-tests/varmod-remember.exp +unit-tests/varmod-remember.mk +unit-tests/varmod-root.exp +unit-tests/varmod-root.mk +unit-tests/varmod-select-words.exp +unit-tests/varmod-select-words.mk +unit-tests/varmod-shell.exp +unit-tests/varmod-shell.mk +unit-tests/varmod-subst-regex.exp +unit-tests/varmod-subst-regex.mk +unit-tests/varmod-subst.exp +unit-tests/varmod-subst.mk +unit-tests/varmod-sysv.exp +unit-tests/varmod-sysv.mk +unit-tests/varmod-tail.exp +unit-tests/varmod-tail.mk +unit-tests/varmod-to-abs.exp +unit-tests/varmod-to-abs.mk +unit-tests/varmod-to-lower.exp +unit-tests/varmod-to-lower.mk +unit-tests/varmod-to-many-words.exp +unit-tests/varmod-to-many-words.mk +unit-tests/varmod-to-one-word.exp +unit-tests/varmod-to-one-word.mk +unit-tests/varmod-to-separator.exp +unit-tests/varmod-to-separator.mk +unit-tests/varmod-to-upper.exp +unit-tests/varmod-to-upper.mk +unit-tests/varmod-undefined.exp +unit-tests/varmod-undefined.mk +unit-tests/varmod-unique.exp +unit-tests/varmod-unique.mk +unit-tests/varmod.exp +unit-tests/varmod.mk +unit-tests/varname-dollar.exp +unit-tests/varname-dollar.mk +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-libs.exp +unit-tests/varname-dot-libs.mk +unit-tests/varname-dot-make-dependfile.exp +unit-tests/varname-dot-make-dependfile.mk +unit-tests/varname-dot-make-expand_variables.exp +unit-tests/varname-dot-make-expand_variables.mk +unit-tests/varname-dot-make-exported.exp +unit-tests/varname-dot-make-exported.mk +unit-tests/varname-dot-make-jobs-prefix.exp +unit-tests/varname-dot-make-jobs-prefix.mk +unit-tests/varname-dot-make-jobs.exp +unit-tests/varname-dot-make-jobs.mk +unit-tests/varname-dot-make-level.exp +unit-tests/varname-dot-make-level.mk +unit-tests/varname-dot-make-makefile_preference.exp +unit-tests/varname-dot-make-makefile_preference.mk +unit-tests/varname-dot-make-makefiles.exp +unit-tests/varname-dot-make-makefiles.mk +unit-tests/varname-dot-make-meta-bailiwick.exp +unit-tests/varname-dot-make-meta-bailiwick.mk +unit-tests/varname-dot-make-meta-created.exp +unit-tests/varname-dot-make-meta-created.mk +unit-tests/varname-dot-make-meta-files.exp +unit-tests/varname-dot-make-meta-files.mk +unit-tests/varname-dot-make-meta-ignore_filter.exp +unit-tests/varname-dot-make-meta-ignore_filter.mk +unit-tests/varname-dot-make-meta-ignore_paths.exp +unit-tests/varname-dot-make-meta-ignore_paths.mk +unit-tests/varname-dot-make-meta-ignore_patterns.exp +unit-tests/varname-dot-make-meta-ignore_patterns.mk +unit-tests/varname-dot-make-meta-prefix.exp +unit-tests/varname-dot-make-meta-prefix.mk +unit-tests/varname-dot-make-mode.exp +unit-tests/varname-dot-make-mode.mk +unit-tests/varname-dot-make-path_filemon.exp +unit-tests/varname-dot-make-path_filemon.mk +unit-tests/varname-dot-make-pid.exp +unit-tests/varname-dot-make-pid.mk +unit-tests/varname-dot-make-ppid.exp +unit-tests/varname-dot-make-ppid.mk +unit-tests/varname-dot-make-save_dollars.exp +unit-tests/varname-dot-make-save_dollars.mk +unit-tests/varname-dot-makeoverrides.exp +unit-tests/varname-dot-makeoverrides.mk +unit-tests/varname-dot-newline.exp +unit-tests/varname-dot-newline.mk +unit-tests/varname-dot-objdir.exp +unit-tests/varname-dot-objdir.mk +unit-tests/varname-dot-parsedir.exp +unit-tests/varname-dot-parsedir.mk +unit-tests/varname-dot-parsefile.exp +unit-tests/varname-dot-parsefile.mk +unit-tests/varname-dot-path.exp +unit-tests/varname-dot-path.mk +unit-tests/varname-dot-shell.exp +unit-tests/varname-dot-shell.mk +unit-tests/varname-dot-targets.exp +unit-tests/varname-dot-targets.mk +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.exp +unit-tests/varname-make_print_var_on_error.mk +unit-tests/varname-makeflags.exp +unit-tests/varname-makeflags.mk +unit-tests/varname-pwd.exp +unit-tests/varname-pwd.mk +unit-tests/varname-vpath.exp +unit-tests/varname-vpath.mk +unit-tests/varname.exp +unit-tests/varname.mk +unit-tests/varparse-dynamic.exp +unit-tests/varparse-dynamic.mk unit-tests/varquote.exp unit-tests/varquote.mk unit-tests/varshell.exp @@ -3,6 +3,7 @@ original contributors or assignees. Including: Copyright (c) 1993-2020, Simon J Gerraty + Copyright (c) 2020, Roland Illig <rillig@NetBSD.org> Copyright (c) 2009-2016, Juniper Networks, Inc. Copyright (c) 2009, John Birrell. Copyright (c) 1997-2020 The NetBSD Foundation, Inc. @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.107 2020/06/07 21:18:46 sjg Exp $ +# $Id: Makefile,v 1.112 2020/08/28 16:26:17 sjg Exp $ PROG= bmake @@ -8,9 +8,11 @@ SRCS= \ compat.c \ cond.c \ dir.c \ + enum.c \ for.c \ hash.c \ job.c \ + lst.c \ main.c \ make.c \ make_malloc.c \ @@ -25,36 +27,6 @@ SRCS= \ util.c \ var.c -# from lst.lib/ -SRCS+= \ - lstAppend.c \ - lstAtEnd.c \ - lstAtFront.c \ - lstClose.c \ - lstConcat.c \ - lstDatum.c \ - lstDeQueue.c \ - lstDestroy.c \ - lstDupl.c \ - lstEnQueue.c \ - lstFind.c \ - lstFindFrom.c \ - lstFirst.c \ - lstForEach.c \ - lstForEachFrom.c \ - lstInit.c \ - lstInsert.c \ - lstIsAtEnd.c \ - lstIsEmpty.c \ - lstLast.c \ - lstMember.c \ - lstNext.c \ - lstOpen.c \ - lstPrev.c \ - lstRemove.c \ - lstReplace.c \ - lstSucc.c - .-include "VERSION" .-include "Makefile.inc" @@ -97,7 +69,6 @@ COPTS.filemon_dev.c += -DHAVE_FILEMON_H -I${FILEMON_H:H} .endif # USE_FILEMON .PATH: ${srcdir} -.PATH: ${srcdir}/lst.lib .if make(obj) || make(clean) SUBDIR+= unit-tests @@ -109,7 +80,7 @@ SUBDIR+= unit-tests # list of OS's which are derrived from BSD4.4 BSD44_LIST= NetBSD FreeBSD OpenBSD DragonFly MirBSD Bitrig # we are... -OS!= uname -s +OS := ${.MAKE.OS:U${uname -s:L:sh}} # are we 4.4BSD ? isBSD44:=${BSD44_LIST:M${OS}} @@ -237,5 +208,8 @@ install-mk: # end-delete2 # A simple unit-test driver to help catch regressions +TEST_MAKE ?= ${.OBJDIR}/${PROG:T} accept test: - cd ${.CURDIR}/unit-tests && MAKEFLAGS= ${.MAKE} -r -m / TEST_MAKE=${TEST_MAKE:U${.OBJDIR}/${PROG:T}} ${.TARGET} + cd ${.CURDIR}/unit-tests && \ + MAKEFLAGS= ${TEST_MAKE} -r -m / ${.TARGET} ${TESTS:DTESTS=${TESTS:Q}} + @@ -1,2 +1,2 @@ # keep this compatible with sh and make -_MAKE_VERSION=20200710 +_MAKE_VERSION=20200902 @@ -1,4 +1,4 @@ -/* $NetBSD: arch.c,v 1.73 2020/07/03 08:02:55 rillig Exp $ */ +/* $NetBSD: arch.c,v 1.107 2020/08/30 11:15:05 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -69,14 +69,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: arch.c,v 1.73 2020/07/03 08:02:55 rillig Exp $"; +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.73 2020/07/03 08:02:55 rillig Exp $"); +__RCSID("$NetBSD: arch.c,v 1.107 2020/08/30 11:15:05 rillig Exp $"); #endif #endif /* not lint */ #endif @@ -94,7 +94,7 @@ __RCSID("$NetBSD: arch.c,v 1.73 2020/07/03 08:02:55 rillig Exp $"); * 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. - * FAILURE is returned if the specification is + * FALSE is returned if the specification is * invalid for some reason. * * Arch_Touch Alter the modification time of the archive @@ -184,12 +184,9 @@ typedef struct Arch { size_t fnamesize; /* Size of the string table */ } Arch; -static int ArchFindArchive(const void *, const void *); -#ifdef CLEANUP -static void ArchFree(void *); -#endif -static struct ar_hdr *ArchStatMember(char *, char *, Boolean); -static FILE *ArchFindMember(char *, char *, struct ar_hdr *, const char *); +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__) #define SVR4ARCHIVES static int ArchSVR4Entry(Arch *, char *, size_t, FILE *); @@ -225,19 +222,6 @@ static int ArchSVR4Entry(Arch *, char *, size_t, FILE *); #define AR_MAX_NAME_LEN (sizeof(arh.AR_NAME)-1) #ifdef CLEANUP -/*- - *----------------------------------------------------------------------- - * ArchFree -- - * Free memory used by an archive - * - * Results: - * None. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ static void ArchFree(void *ap) { @@ -272,23 +256,18 @@ ArchFree(void *ap) * ctxt Context in which to expand variables * * Results: - * SUCCESS if it was a valid specification. The linePtr is updated + * TRUE if it was a valid specification. The linePtr is updated * to point to the first non-space after the archive spec. The * nodes for the members are placed on the given list. - * - * Side Effects: - * Some nodes may be created. The given list is extended. - * *----------------------------------------------------------------------- */ -ReturnStatus +Boolean Arch_ParseArchive(char **linePtr, Lst 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 *nameBuf; /* temporary place for node name */ char saveChar; /* Ending delimiter of member-name */ Boolean subLibName; /* TRUE if libName should have/had * variable substitution performed on it */ @@ -304,26 +283,25 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt) * so we can safely advance beyond it... */ int length; - void *freeIt; - char *result; - - result = Var_Parse(cp, ctxt, VARF_UNDEFERR|VARF_WANTRES, - &length, &freeIt); - free(freeIt); - - if (result == var_Error) { - return FAILURE; - } else { - subLibName = TRUE; - } - - cp += length-1; + void *result_freeIt; + const char *result; + Boolean isError; + + result = Var_Parse(cp, ctxt, VARE_UNDEFERR|VARE_WANTRES, + &length, &result_freeIt); + isError = result == var_Error; + free(result_freeIt); + if (isError) + return FALSE; + + subLibName = TRUE; + cp += length - 1; } } *cp++ = '\0'; if (subLibName) { - libName = Var_Subst(NULL, libName, ctxt, VARF_UNDEFERR|VARF_WANTRES); + libName = Var_Subst(libName, ctxt, VARE_UNDEFERR|VARE_WANTRES); } @@ -347,18 +325,18 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt) */ int length; void *freeIt; - char *result; + const char *result; + Boolean isError; - result = Var_Parse(cp, ctxt, VARF_UNDEFERR|VARF_WANTRES, + result = Var_Parse(cp, ctxt, VARE_UNDEFERR|VARE_WANTRES, &length, &freeIt); + isError = result == var_Error; free(freeIt); - if (result == var_Error) { - return FAILURE; - } else { - doSubst = TRUE; - } + if (isError) + return FALSE; + doSubst = TRUE; cp += length; } else { cp++; @@ -372,7 +350,7 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt) */ if (*cp == '\0') { printf("No closing parenthesis in archive specification\n"); - return FAILURE; + return FALSE; } /* @@ -401,20 +379,15 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt) char *buf; char *sacrifice; char *oldMemName = memName; - size_t sz; - memName = Var_Subst(NULL, memName, ctxt, - VARF_UNDEFERR|VARF_WANTRES); + memName = Var_Subst(memName, ctxt, VARE_UNDEFERR | VARE_WANTRES); /* * Now form an archive spec and recurse to deal with nested * variables and multi-word variable values.... The results * are just placed at the end of the nodeLst we're returning. */ - sz = strlen(memName)+strlen(libName)+3; - buf = sacrifice = bmake_malloc(sz); - - snprintf(buf, sz, "%s(%s)", libName, memName); + buf = sacrifice = str_concat4(libName, "(", memName, ")"); if (strchr(memName, '$') && strcmp(memName, oldMemName) == 0) { /* @@ -426,42 +399,43 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt) if (gn == NULL) { free(buf); - return FAILURE; + return FALSE; } else { gn->type |= OP_ARCHV; - (void)Lst_AtEnd(nodeLst, gn); + Lst_Append(nodeLst, gn); } - } else if (Arch_ParseArchive(&sacrifice, nodeLst, ctxt)!=SUCCESS) { + } else if (!Arch_ParseArchive(&sacrifice, nodeLst, ctxt)) { /* - * Error in nested call -- free buffer and return FAILURE + * Error in nested call -- free buffer and return FALSE * ourselves. */ free(buf); - return FAILURE; + return FALSE; } /* * Free buffer and continue with our work. */ free(buf); } else if (Dir_HasWildcards(memName)) { - Lst members = Lst_Init(FALSE); - char *member; - size_t sz = MAXPATHLEN, nsz; - nameBuf = bmake_malloc(sz); + Lst members = Lst_Init(); + Buffer nameBuf; + Buf_Init(&nameBuf, 0); Dir_Expand(memName, dirSearchPath, members); while (!Lst_IsEmpty(members)) { - member = (char *)Lst_DeQueue(members); - nsz = strlen(libName) + strlen(member) + 3; - if (sz > nsz) - nameBuf = bmake_realloc(nameBuf, sz = nsz * 2); + char *member = Lst_Dequeue(members); - snprintf(nameBuf, sz, "%s(%s)", libName, member); + Buf_Empty(&nameBuf); + Buf_AddStr(&nameBuf, libName); + Buf_AddStr(&nameBuf, "("); + Buf_AddStr(&nameBuf, member); + Buf_AddStr(&nameBuf, ")"); free(member); - gn = Targ_FindNode(nameBuf, TARG_CREATE); + + gn = Targ_FindNode(Buf_GetAll(&nameBuf, NULL), TARG_CREATE); if (gn == NULL) { - free(nameBuf); - return FAILURE; + Buf_Destroy(&nameBuf, TRUE); + return FALSE; } else { /* * We've found the node, but have to make sure the rest of @@ -471,19 +445,24 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt) * end of the provided list. */ gn->type |= OP_ARCHV; - (void)Lst_AtEnd(nodeLst, gn); + Lst_Append(nodeLst, gn); } } - Lst_Destroy(members, NULL); - free(nameBuf); + Lst_Free(members); + Buf_Destroy(&nameBuf, TRUE); } else { - size_t sz = strlen(libName) + strlen(memName) + 3; - nameBuf = bmake_malloc(sz); - snprintf(nameBuf, sz, "%s(%s)", libName, memName); - gn = Targ_FindNode(nameBuf, TARG_CREATE); - free(nameBuf); + Buffer nameBuf; + + Buf_Init(&nameBuf, 0); + Buf_AddStr(&nameBuf, libName); + Buf_AddStr(&nameBuf, "("); + Buf_AddStr(&nameBuf, memName); + Buf_AddStr(&nameBuf, ")"); + + gn = Targ_FindNode(Buf_GetAll(&nameBuf, NULL), TARG_CREATE); + Buf_Destroy(&nameBuf, TRUE); if (gn == NULL) { - return FAILURE; + return FALSE; } else { /* * We've found the node, but have to make sure the rest of the @@ -493,7 +472,7 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt) * provided list. */ gn->type |= OP_ARCHV; - (void)Lst_AtEnd(nodeLst, gn); + Lst_Append(nodeLst, gn); } } if (doSubst) { @@ -520,31 +499,15 @@ Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt) } while (*cp != '\0' && isspace ((unsigned char)*cp)); *linePtr = cp; - return SUCCESS; + return TRUE; } -/*- - *----------------------------------------------------------------------- - * ArchFindArchive -- - * See if the given archive is the one we are looking for. Called - * From ArchStatMember and ArchFindMember via Lst_Find. - * - * Input: - * ar Current list element - * archName Name we want - * - * Results: - * 0 if it is, non-zero if it isn't. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -static int -ArchFindArchive(const void *ar, const void *archName) +/* 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(archName, ((const Arch *)ar)->name); + return strcmp(((const Arch *)ar)->name, desiredName) == 0; } /*- @@ -565,24 +528,20 @@ ArchFindArchive(const void *ar, const void *archName) * 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... - * - * Side Effects: - * *----------------------------------------------------------------------- */ static struct ar_hdr * -ArchStatMember(char *archive, char *member, Boolean hash) +ArchStatMember(const char *archive, const char *member, Boolean hash) { FILE * arch; /* Stream to archive */ - int size; /* Size of archive member */ - char *cp; /* Useful character pointer */ + 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. */ + /* Current member name while hashing. */ /* * Because of space constraints and similar things, files are archived @@ -590,14 +549,14 @@ ArchStatMember(char *archive, char *member, Boolean hash) * to point 'member' to the final component, if there is one, to make * the comparisons easier... */ - cp = strrchr(member, '/'); - if (cp != NULL) { - member = cp + 1; + const char *base = strrchr(member, '/'); + if (base != NULL) { + member = base + 1; } - ln = Lst_Find(archives, archive, ArchFindArchive); + ln = Lst_Find(archives, ArchFindArchive, archive); if (ln != NULL) { - ar = (Arch *)Lst_Datum(ln); + ar = LstNode_Datum(ln); he = Hash_FindEntry(&ar->members, member); @@ -610,8 +569,7 @@ ArchStatMember(char *archive, char *member, Boolean hash) if (len > AR_MAX_NAME_LEN) { len = AR_MAX_NAME_LEN; - strncpy(copy, member, AR_MAX_NAME_LEN); - copy[AR_MAX_NAME_LEN] = '\0'; + snprintf(copy, sizeof copy, "%s", member); } if ((he = Hash_FindEntry(&ar->members, copy)) != NULL) return (struct ar_hdr *)Hash_GetValue(he); @@ -653,7 +611,7 @@ ArchStatMember(char *archive, char *member, Boolean hash) * can handle... */ if ((fread(magic, SARMAG, 1, arch) != 1) || - (strncmp(magic, ARMAG, SARMAG) != 0)) { + (strncmp(magic, ARMAG, SARMAG) != 0)) { fclose(arch); return NULL; } @@ -673,6 +631,8 @@ ArchStatMember(char *archive, char *member, Boolean hash) */ goto badarch; } else { + char *nameend; + /* * We need to advance the stream's pointer to the start of the * next header. Files are padded with newlines to an even-byte @@ -680,13 +640,14 @@ ArchStatMember(char *archive, char *member, Boolean hash) * 'size' field of the header and round it up during the seek. */ arh.AR_SIZE[sizeof(arh.AR_SIZE)-1] = '\0'; - size = (int)strtol(arh.AR_SIZE, NULL, 10); + size = (size_t)strtol(arh.ar_size, NULL, 10); memcpy(memName, arh.AR_NAME, sizeof(arh.AR_NAME)); - for (cp = &memName[AR_MAX_NAME_LEN]; *cp == ' '; cp--) { - continue; + nameend = memName + AR_MAX_NAME_LEN; + while (*nameend == ' ') { + nameend--; } - cp[1] = '\0'; + nameend[1] = '\0'; #ifdef SVR4ARCHIVES /* @@ -706,8 +667,8 @@ ArchStatMember(char *archive, char *member, Boolean hash) } } else { - if (cp[0] == '/') - cp[0] = '\0'; + if (nameend[0] == '/') + nameend[0] = '\0'; } #endif @@ -719,11 +680,11 @@ ArchStatMember(char *archive, char *member, Boolean hash) if (strncmp(memName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 && isdigit((unsigned char)memName[sizeof(AR_EFMT1) - 1])) { - unsigned int elen = atoi(&memName[sizeof(AR_EFMT1)-1]); + int elen = atoi(&memName[sizeof(AR_EFMT1)-1]); - if (elen > MAXPATHLEN) + if ((unsigned int)elen > MAXPATHLEN) goto badarch; - if (fread(memName, elen, 1, arch) != 1) + if (fread(memName, (size_t)elen, 1, arch) != 1) goto badarch; memName[elen] = '\0'; if (fseek(arch, -elen, SEEK_CUR) != 0) @@ -738,13 +699,13 @@ ArchStatMember(char *archive, char *member, Boolean hash) Hash_SetValue(he, bmake_malloc(sizeof(struct ar_hdr))); memcpy(Hash_GetValue(he), &arh, sizeof(struct ar_hdr)); } - if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0) + if (fseek(arch, ((long)size + 1) & ~1, SEEK_CUR) != 0) goto badarch; } fclose(arch); - (void)Lst_AtEnd(archives, ar); + Lst_Append(archives, ar); /* * Now that the archive has been read and cached, we can look into @@ -774,17 +735,14 @@ badarch: * If it is "//", then load the table of filenames * If it is "/<offset>", then try to substitute the long file name * from offset of a table previously read. + * If a table is read, the file pointer is moved to the next archive + * member. * * Results: * -1: Bad data in archive * 0: A table was loaded from the file * 1: Name was successfully substituted from table * 2: Name was not successfully substituted from table - * - * Side Effects: - * If a table is read, the file pointer is moved to the next archive - * member - * *----------------------------------------------------------------------- */ static int @@ -834,7 +792,7 @@ ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch) } if (DEBUG(ARCH)) { fprintf(debug_file, "Found svr4 archive name table with %lu entries\n", - (unsigned long)entry); + (unsigned long)entry); } return 0; } @@ -861,8 +819,7 @@ ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch) fprintf(debug_file, "Replaced %s with %s\n", name, &ar->fnametab[entry]); } - (void)strncpy(name, &ar->fnametab[entry], MAXPATHLEN); - name[MAXPATHLEN] = '\0'; + snprintf(name, MAXPATHLEN + 1, "%s", &ar->fnametab[entry]); return 1; } #endif @@ -874,6 +831,7 @@ ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch) * Locate a member of an archive, given the path of the archive and * the path of the desired member. If the archive is to be modified, * the mode should be "r+", if not, it should be "r". + * The passed struct ar_hdr structure is filled in. * * Input: * archive Path to the archive @@ -886,21 +844,17 @@ ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch) * An FILE *, opened for reading and writing, positioned at the * start of the member's struct ar_hdr, or NULL if the member was * nonexistent. The current struct ar_hdr for member. - * - * Side Effects: - * The passed struct ar_hdr structure is filled in. - * *----------------------------------------------------------------------- */ static FILE * -ArchFindMember(char *archive, char *member, struct ar_hdr *arhPtr, +ArchFindMember(const char *archive, const char *member, struct ar_hdr *arhPtr, const char *mode) { FILE * arch; /* Stream to archive */ int size; /* Size of archive member */ - char *cp; /* Useful character pointer */ char magic[SARMAG]; size_t len, tlen; + const char * base; arch = fopen(archive, mode); if (arch == NULL) { @@ -912,7 +866,7 @@ ArchFindMember(char *archive, char *member, struct ar_hdr *arhPtr, * can handle... */ if ((fread(magic, SARMAG, 1, arch) != 1) || - (strncmp(magic, ARMAG, SARMAG) != 0)) { + (strncmp(magic, ARMAG, SARMAG) != 0)) { fclose(arch); return NULL; } @@ -923,9 +877,9 @@ ArchFindMember(char *archive, char *member, struct ar_hdr *arhPtr, * to point 'member' to the final component, if there is one, to make * the comparisons easier... */ - cp = strrchr(member, '/'); - if (cp != NULL) { - member = cp + 1; + base = strrchr(member, '/'); + if (base != NULL) { + member = base + 1; } len = tlen = strlen(member); if (len > sizeof(arhPtr->AR_NAME)) { @@ -958,7 +912,7 @@ ArchFindMember(char *archive, char *member, struct ar_hdr *arhPtr, * the file at the actual member, rather than its header, but * not here... */ - if (fseek(arch, -sizeof(struct ar_hdr), SEEK_CUR) != 0) { + if (fseek(arch, -(long)sizeof(struct ar_hdr), SEEK_CUR) != 0) { fclose(arch); return NULL; } @@ -974,14 +928,14 @@ ArchFindMember(char *archive, char *member, struct ar_hdr *arhPtr, sizeof(AR_EFMT1) - 1) == 0 && isdigit((unsigned char)arhPtr->AR_NAME[sizeof(AR_EFMT1) - 1])) { - unsigned int elen = atoi(&arhPtr->AR_NAME[sizeof(AR_EFMT1)-1]); + int elen = atoi(&arhPtr->AR_NAME[sizeof(AR_EFMT1)-1]); char ename[MAXPATHLEN + 1]; - if (elen > MAXPATHLEN) { + if ((unsigned int)elen > MAXPATHLEN) { fclose(arch); return NULL; } - if (fread(ename, elen, 1, arch) != 1) { + if (fread(ename, (size_t)elen, 1, arch) != 1) { fclose(arch); return NULL; } @@ -991,7 +945,7 @@ ArchFindMember(char *archive, char *member, struct ar_hdr *arhPtr, } if (strncmp(ename, member, len) == 0) { /* Found as extended name */ - if (fseek(arch, -sizeof(struct ar_hdr) - elen, + if (fseek(arch, -(long)sizeof(struct ar_hdr) - elen, SEEK_CUR) != 0) { fclose(arch); return NULL; @@ -1035,18 +989,15 @@ skip: *----------------------------------------------------------------------- * Arch_Touch -- * Touch a member of an archive. + * The modification time of the entire archive is also changed. + * For a library, this could necessitate the re-ranlib'ing of the + * whole thing. * * Input: * gn Node of member to touch * * Results: * The 'time' field of the member's header is updated. - * - * Side Effects: - * The modification time of the entire archive is also changed. - * For a library, this could necessitate the re-ranlib'ing of the - * whole thing. - * *----------------------------------------------------------------------- */ void @@ -1060,8 +1011,8 @@ Arch_Touch(GNode *gn) Var_Value(MEMBER, gn, &p2), &arh, "r+"); - free(p1); - free(p2); + bmake_free(p1); + bmake_free(p2); snprintf(arh.AR_DATE, sizeof(arh.AR_DATE), "%-12ld", (long) now); @@ -1071,37 +1022,24 @@ Arch_Touch(GNode *gn) } } -/*- - *----------------------------------------------------------------------- - * Arch_TouchLib -- - * Given a node which represents a library, touch the thing, making - * sure that the table of contents also is touched. +/* Given a node which represents a library, touch the thing, making sure that + * the table of contents also is touched. + * + * Both the modification time of the library and of the RANLIBMAG member are + * set to 'now'. * * Input: * gn The node of the library to touch - * - * Results: - * None. - * - * Side Effects: - * Both the modification time of the library and of the RANLIBMAG - * member are set to 'now'. - * - *----------------------------------------------------------------------- */ void -#if !defined(RANLIBMAG) -Arch_TouchLib(GNode *gn MAKE_ATTR_UNUSED) -#else Arch_TouchLib(GNode *gn) -#endif { #ifdef RANLIBMAG FILE * arch; /* Stream open to archive */ struct ar_hdr arh; /* Header describing table of contents */ struct utimbuf times; /* Times for utime() call */ - arch = ArchFindMember(gn->path, UNCONST(RANLIBMAG), &arh, "r+"); + arch = ArchFindMember(gn->path, RANLIBMAG, &arh, "r+"); snprintf(arh.AR_DATE, sizeof(arh.AR_DATE), "%-12ld", (long) now); if (arch != NULL) { @@ -1111,25 +1049,16 @@ Arch_TouchLib(GNode *gn) times.actime = times.modtime = now; utime(gn->path, ×); } +#else + (void)gn; #endif } -/*- - *----------------------------------------------------------------------- - * Arch_MTime -- - * Return the modification time of a member of an archive. +/* Return the modification time of a member of an archive. The mtime field + * of the given node is filled in with the value returned by the function. * * Input: * gn Node describing archive member - * - * Results: - * The modification time(seconds). - * - * Side Effects: - * The mtime field of the given node is filled in with the value - * returned by the function. - * - *----------------------------------------------------------------------- */ time_t Arch_MTime(GNode *gn) @@ -1142,8 +1071,8 @@ Arch_MTime(GNode *gn) Var_Value(MEMBER, gn, &p2), TRUE); - free(p1); - free(p2); + bmake_free(p1); + bmake_free(p2); if (arhPtr != NULL) { modTime = (time_t)strtol(arhPtr->AR_DATE, NULL, 10); @@ -1155,34 +1084,17 @@ Arch_MTime(GNode *gn) return modTime; } -/*- - *----------------------------------------------------------------------- - * Arch_MemMTime -- - * Given a non-existent archive member's node, get its modification - * time from its archived form, if it exists. - * - * Results: - * The modification time. - * - * Side Effects: - * The mtime field is filled in. - * - *----------------------------------------------------------------------- - */ +/* Given a non-existent archive member's node, get its modification time from + * its archived form, if it exists. gn->mtime is filled in as well. */ time_t Arch_MemMTime(GNode *gn) { LstNode ln; GNode *pgn; - char *nameStart, - *nameEnd; - if (Lst_Open(gn->parents) != SUCCESS) { - gn->mtime = 0; - return 0; - } + Lst_Open(gn->parents); while ((ln = Lst_Next(gn->parents)) != NULL) { - pgn = (GNode *)Lst_Datum(ln); + pgn = LstNode_Datum(ln); if (pgn->type & OP_ARCHV) { /* @@ -1192,12 +1104,13 @@ Arch_MemMTime(GNode *gn) * child. We keep searching its parents in case some other * parent requires this child to exist... */ - nameStart = strchr(pgn->name, '(') + 1; - nameEnd = strchr(nameStart, ')'); + const char *nameStart = strchr(pgn->name, '(') + 1; + const char *nameEnd = strchr(nameStart, ')'); + size_t nameLen = (size_t)(nameEnd - nameStart); if ((pgn->flags & REMAKE) && - strncmp(nameStart, gn->name, nameEnd - nameStart) == 0) { - gn->mtime = Arch_MTime(pgn); + strncmp(nameStart, gn->name, nameLen) == 0) { + gn->mtime = Arch_MTime(pgn); } } else if (pgn->flags & REMAKE) { /* @@ -1214,29 +1127,20 @@ Arch_MemMTime(GNode *gn) return gn->mtime; } -/*- - *----------------------------------------------------------------------- - * Arch_FindLib -- - * Search for a library along the given search path. +/* Search for a library along the given search path. + * + * The node's 'path' field is set to the found path (including the + * actual file name, not -l...). If the system can handle the -L + * flag when linking (or we cannot find the library), we assume that + * the user has placed the .LIBS variable in the final linking + * command (or the linker will know where to find it) and set the + * TARGET variable for this node to be the node's name. Otherwise, + * we set the TARGET variable to be the full path of the library, + * as returned by Dir_FindFile. * * Input: * gn Node of library to find * path Search path - * - * Results: - * None. - * - * Side Effects: - * The node's 'path' field is set to the found path (including the - * actual file name, not -l...). If the system can handle the -L - * flag when linking (or we cannot find the library), we assume that - * the user has placed the .LIBRARIES variable in the final linking - * command (or the linker will know where to find it) and set the - * TARGET variable for this node to be the node's name. Otherwise, - * we set the TARGET variable to be the full path of the library, - * as returned by Dir_FindFile. - * - *----------------------------------------------------------------------- */ void Arch_FindLib(GNode *gn, Lst path) @@ -1258,44 +1162,38 @@ Arch_FindLib(GNode *gn, Lst path) #endif /* LIBRARIES */ } -/*- - *----------------------------------------------------------------------- - * Arch_LibOODate -- - * Decide if a node with the OP_LIB attribute is out-of-date. Called - * from Make_OODate to make its life easier. - * - * There are several ways for a library to be out-of-date that are - * not available to ordinary files. In addition, there are ways - * that are open to regular files that are not available to - * libraries. A library that is only used as a source is never - * considered out-of-date by itself. This does not preclude the - * library's modification time from making its parent be out-of-date. - * A library will be considered out-of-date for any of these reasons, - * given that it is a target on a dependency line somewhere: - * Its modification time is less than that of one of its - * sources (gn->mtime < gn->cmgn->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 by archiving). - * The modification time of one of its sources is greater than - * the one of its RANLIBMAG member (i.e. its table of contents - * is out-of-date). We don't compare of the archive time - * vs. TOC time because they can be too close. In my - * opinion we should not bother with the TOC at all since - * this is used by 'ar' rules that affect the data contents - * of the archive, not by ranlib rules, which affect the - * TOC. +/* Decide if a node with the OP_LIB attribute is out-of-date. Called from + * Make_OODate to make its life easier. + * The library will be hashed if it hasn't been already. + * + * There are several ways for a library to be out-of-date that are + * not available to ordinary files. In addition, there are ways + * that are open to regular files that are not available to + * libraries. A library that is only used as a source is never + * considered out-of-date by itself. This does not preclude the + * library's modification time from making its parent be out-of-date. + * A library will be considered out-of-date for any of these reasons, + * given that it is a target on a dependency line somewhere: + * + * Its modification time is less than that of one of its sources + * (gn->mtime < gn->cmgn->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 + * by archiving). + * + * The modification time of one of its sources is greater than the one + * of its RANLIBMAG member (i.e. its table of contents is out-of-date). + * We don't compare of the archive time vs. TOC time because they can be + * too close. In my opinion we should not bother with the TOC at all + * since this is used by 'ar' rules that affect the data contents of the + * archive, not by ranlib rules, which affect the TOC. * * Input: * gn The library's graph node * * Results: * TRUE if the library is out-of-date. FALSE otherwise. - * - * Side Effects: - * The library will be hashed if it hasn't been already. - * - *----------------------------------------------------------------------- */ Boolean Arch_LibOODate(GNode *gn) @@ -1315,7 +1213,7 @@ Arch_LibOODate(GNode *gn) struct ar_hdr *arhPtr; /* Header for __.SYMDEF */ int modTimeTOC; /* The table-of-contents's mod time */ - arhPtr = ArchStatMember(gn->path, UNCONST(RANLIBMAG), FALSE); + arhPtr = ArchStatMember(gn->path, RANLIBMAG, FALSE); if (arhPtr != NULL) { modTimeTOC = (int)strtol(arhPtr->AR_DATE, NULL, 10); @@ -1340,40 +1238,14 @@ Arch_LibOODate(GNode *gn) return oodate; } -/*- - *----------------------------------------------------------------------- - * Arch_Init -- - * Initialize things for this module. - * - * Results: - * None. - * - * Side Effects: - * The 'archives' list is initialized. - * - *----------------------------------------------------------------------- - */ +/* Initialize things for this module. */ void Arch_Init(void) { - archives = Lst_Init(FALSE); + archives = Lst_Init(); } - - -/*- - *----------------------------------------------------------------------- - * Arch_End -- - * Cleanup things for this module. - * - * Results: - * None. - * - * Side Effects: - * The 'archives' list is freed - * - *----------------------------------------------------------------------- - */ +/* Clean up things for this module. */ void Arch_End(void) { @@ -1382,35 +1254,22 @@ Arch_End(void) #endif } -/*- - *----------------------------------------------------------------------- - * Arch_IsLib -- - * Check if the node is a library - * - * Results: - * True or False. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -int +Boolean Arch_IsLib(GNode *gn) { static const char armag[] = "!<arch>\n"; - char buf[sizeof(armag)-1]; + char buf[sizeof armag - 1]; int fd; if ((fd = open(gn->path, O_RDONLY)) == -1) return FALSE; - if (read(fd, buf, sizeof(buf)) != sizeof(buf)) { + if (read(fd, buf, sizeof buf) != sizeof buf) { (void)close(fd); return FALSE; } (void)close(fd); - return memcmp(buf, armag, sizeof(buf)) == 0; + return memcmp(buf, armag, sizeof buf) == 0; } @@ -1,4 +1,4 @@ -.\" $NetBSD: make.1,v 1.282 2020/06/06 20:28:42 wiz Exp $ +.\" $NetBSD: make.1,v 1.289 2020/08/28 17:15:04 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 June 5, 2020 +.Dd August 28, 2020 .Dt BMAKE 1 .Os .Sh NAME @@ -166,8 +166,15 @@ Print the input graph after making everything, or before exiting on error. .It Ar "g3" Print the input graph before exiting on error. +.It Ar h +Print debugging information about hash table operations. .It Ar j Print debugging information about running multiple shells. +.It Ar L +Turn on lint checks. +This will throw errors for variable assignments that do not parse +correctly, at the time of assignment so the file and line number +are available. .It Ar l Print commands in Makefiles regardless of whether or not they are prefixed by .Ql @ @@ -311,7 +318,8 @@ as an argument). .It Fl n Display the commands that would have been executed, but do not actually execute them unless the target depends on the .MAKE special -source (see below). +source (see below) or the command is prefixed with +.Ql Ic + . .It Fl N Display the commands which would have been executed, but do not actually execute any of them; useful for debugging top-level makefiles @@ -412,37 +420,44 @@ or more sources. This creates a relationship where the targets .Dq depend on the sources -and are usually created from them. -The exact relationship between the target and the source is determined -by the operator that separates them. -The three operators are as follows: +and are customarily created from them. +A target is considered out-of-date if it does not exist, or if its +modification time is less than that of any of its sources. +An out-of-date target will be re-created, but not until all sources +have been examined and themselves re-created as needed. +Three operators may be used: .Bl -tag -width flag .It Ic \&: -A target is considered out-of-date if its modification time is less than -those of any of its sources. -Sources for a target accumulate over dependency lines when this operator -is used. -The target is removed if +Many dependency lines may name this target but only one may have +attached shell commands. +All sources named in all dependency lines are considered together, +and if needed the attached shell commands are run to create or +re-create the target. +If .Nm -is interrupted. +is interrupted, the target is removed. .It Ic \&! -Targets are always re-created, but not until all sources have been -examined and re-created as necessary. -Sources for a target accumulate over dependency lines when this operator -is used. -The target is removed if -.Nm -is interrupted. +The same, but the target is always re-created whether or not it is out +of date. .It Ic \&:: -If no sources are specified, the target is always re-created. -Otherwise, a target is considered out-of-date if any of its sources has -been modified more recently than the target. -Sources for a target do not accumulate over dependency lines when this -operator is used. -The target will not be removed if +Any dependency line may have attached shell commands, but each one +is handled independently: its sources are considered and the attached +shell commands are run if the target is out of date with respect to +(only) those sources. +Thus, different groups of the attached shell commands may be run +depending on the circumstances. +Furthermore, unlike +.Ic \&:, +for dependency lines with no sources, the attached shell +commands are always run. +Also unlike +.Ic \&:, +the target will not be removed if .Nm is interrupted. .El +All dependency lines mentioning a particular target must use the same +operator. .Pp Targets and sources may contain the shell wildcard values .Ql \&? , @@ -608,7 +623,7 @@ This shorter form is not recommended. .Pp If the variable name contains a dollar, then the name itself is expanded first. This allows almost arbitrary variable names, however names containing dollar, -braces, parenthesis, or whitespace are really best avoided! +braces, parentheses, or whitespace are really best avoided! .Pp If the result of expanding a variable contains a dollar sign .Pq Ql \&$ @@ -1126,6 +1141,9 @@ is set to the value of for all programs which .Nm executes. +.It Ev .SHELL +The pathname of the shell used to run target scripts. +It is read-only. .It Ev .TARGETS The list of targets explicitly specified on the command line, if any. .It Ev VPATH @@ -1171,7 +1189,7 @@ Replaces each word in the variable with its suffix. .It Cm \&:H Replaces each word in the variable with everything but the last component. .It Cm \&:M Ns Ar pattern -Select only those words that match +Selects only those words that match .Ar pattern . The standard shell wildcard characters .Pf ( Ql * , @@ -1195,11 +1213,11 @@ This is identical to but selects all words which do not match .Ar pattern . .It Cm \&:O -Order every word in variable alphabetically. +Orders every word in variable alphabetically. .It Cm \&:Or -Order every word in variable in reverse alphabetical order. +Orders every word in variable in reverse alphabetical order. .It Cm \&:Ox -Randomize words in variable. +Shuffles the words in variable. The results will be different each time you are referring to the modified variable; use the assignment with expansion .Pq Ql Cm \&:= @@ -1249,7 +1267,7 @@ If a .Va utc value is not provided or is 0, the current time is used. .It Cm \&:hash -Compute a 32-bit hash of the value and encode it as hex digits. +Computes a 32-bit hash of the value and encode it as hex digits. .It Cm \&:localtime[=utc] The value is a format string for .Xr strftime 3 , @@ -1259,7 +1277,7 @@ If a .Va utc value is not provided or is 0, the current time is used. .It Cm \&:tA -Attempt to convert variable to an absolute path using +Attempts to convert variable to an absolute path using .Xr realpath 3 , if that fails, the value is unchanged. .It Cm \&:tl @@ -1271,7 +1289,7 @@ This modifier sets the separator to the character If .Ar c is omitted, then no separator is used. -The common escapes (including octal numeric codes), work as expected. +The common escapes (including octal numeric codes) work as expected. .It Cm \&:tu Converts variable to upper-case letters. .It Cm \&:tW @@ -1287,21 +1305,21 @@ See also .Sm off .It Cm \&:S No \&/ Ar old_string No \&/ Ar new_string No \&/ Op Cm 1gW .Sm on -Modify the first occurrence of +Modifies the first occurrence of .Ar old_string -in the variable's value, replacing it with +in each word of the variable's value, replacing it with .Ar new_string . If a .Ql g -is appended to the last slash of the pattern, all occurrences +is appended to the last delimiter of the pattern, all occurrences in each word are replaced. If a .Ql 1 -is appended to the last slash of the pattern, only the first word +is appended to the last delimiter of the pattern, only the first occurrence is affected. If a .Ql W -is appended to the last slash of the pattern, +is appended to the last delimiter of the pattern, then the value is treated as a single word (possibly containing embedded white space). If @@ -1370,13 +1388,6 @@ as occur in the word or words it is found in; the .Ql W modifier causes the value to be treated as a single word (possibly containing embedded white space). -Note that -.Ql 1 -and -.Ql g -are orthogonal; the former specifies whether multiple words are -potentially affected, the latter whether multiple substitutions can -potentially occur within each affected word. .Pp As for the .Cm \&:S @@ -1387,9 +1398,9 @@ and are subjected to variable expansion before being parsed as regular expressions. .It Cm \&:T -Replaces each word in the variable with its last component. +Replaces each word in the variable with its last path component. .It Cm \&:u -Remove adjacent duplicate words (like +Removes adjacent duplicate words (like .Xr uniq 1 ) . .Sm off .It Cm \&:\&? Ar true_string Cm \&: Ar false_string @@ -1405,7 +1416,7 @@ usually contain variable expansions. A common error is trying to use expressions like .Dl ${NUMBERS:M42:?match:no} which actually tests defined(NUMBERS), -to determine is any words match "42" you need to use something like: +to determine if any words match "42" you need to use something like: .Dl ${"${NUMBERS:M42}" != \&"\&":?match:no} . .It Ar :old_string=new_string This is the @@ -1449,7 +1460,7 @@ in either the or .Ar old_string , only the first instance is treated specially (as the pattern character); -all subsequent instances are treated as regular characters +all subsequent instances are treated as regular characters. .Pp Variable expansion occurs in the normal fashion inside both .Ar old_string @@ -1466,11 +1477,10 @@ This is the loop expansion mechanism from the OSF Development Environment (ODE) make. Unlike .Cm \&.for -loops expansion occurs at the time of -reference. -Assign +loops, expansion occurs at the time of reference. +Assigns .Ar temp -to each word in the variable and evaluate +to each word in the variable and evaluates .Ar string . The ODE convention is that .Ar temp @@ -1481,7 +1491,7 @@ For example. However a single character variable is often more readable: .Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@} .It Cm \&:_[=var] -Save the current variable value in +Saves the current variable value in .Ql $_ or the named .Va var @@ -1502,7 +1512,7 @@ is used to save the result of the modifier which is later referenced using the index values from .Ql :range . .It Cm \&:U Ns Ar newval -If the variable is undefined +If the variable is undefined, .Ar newval is the value. If the variable is defined, the existing value is returned. @@ -1512,7 +1522,7 @@ It is handy for setting per-target CFLAGS for instance: If a value is only required if the variable is undefined, use: .Dl ${VAR:D:Unewval} .It Cm \&:D Ns Ar newval -If the variable is defined +If the variable is defined, .Ar newval is the value. .It Cm \&:L @@ -1641,7 +1651,7 @@ Returns the number of words in the value. .El \" :[range] .El .Sh INCLUDE STATEMENTS, CONDITIONALS AND FOR LOOPS -Makefile inclusion, conditional structures and for loops reminiscent +Makefile inclusion, conditional structures and for loops reminiscent of the C programming language are provided in .Nm . All such structures are identified by a line beginning with a single @@ -1687,7 +1697,7 @@ The possible conditionals are as follows: The message is printed along with the name of the makefile and line number, then .Nm -will exit. +will exit immediately. .It Ic .export Ar variable ... Export the specified global variable. If no variable list is provided, all globals are exported @@ -1876,7 +1886,7 @@ operator is not an integral 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 +variable is being compared against 0, or an empty string in the case of a string comparison. .Pp When @@ -1917,7 +1927,7 @@ The syntax of a for loop is: .Pp .Bl -tag -compact -width Ds .It Ic \&.for Ar variable Oo Ar variable ... Oc Ic in Ar expression -.It Aq make-rules +.It Aq make-lines .It Ic \&.endfor .El .Pp @@ -1929,7 +1939,7 @@ On each iteration of the loop, one word is taken and assigned to each in order, and these .Ic variables are substituted into the -.Ic make-rules +.Ic make-lines inside the body of the for loop. The number of words must come out even; that is, if there are three iteration variables, the number of words provided must be a multiple diff --git a/bmake.cat1 b/bmake.cat1 index feb93698e34f..564e811737da 100644 --- a/bmake.cat1 +++ b/bmake.cat1 @@ -89,9 +89,15 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) [4mg3[24m Print the input graph before exiting on error. + [4mh[24m Print debugging information about hash table operations. + [4mj[24m Print debugging information about running multiple shells. + [4mL[24m Turn on lint checks. This will throw errors for variable + assignments that do not parse correctly, at the time of + assignment so the file and line number are available. + [4ml[24m Print commands in Makefiles regardless of whether or not they are prefixed by `@' or other "quiet" flags. Also known as "loud" behavior. @@ -188,7 +194,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) [1m-n [22mDisplay the commands that would have been executed, but do not actually execute them unless the target depends on the .MAKE spe- - cial source (see below). + cial source (see below) or the command is prefixed with `[1m+[22m'. [1m-N [22mDisplay the commands which would have been executed, but do not actually execute any of them; useful for debugging top-level @@ -260,25 +266,31 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) [1mFILE DEPENDENCY SPECIFICATIONS[0m Dependency lines consist of one or more targets, an operator, and zero or more sources. This creates a relationship where the targets ``depend'' - on the sources and are usually created from them. The exact relationship - between the target and the source is determined by the operator that sep- - arates them. The three operators are as follows: - - [1m: [22mA target is considered out-of-date if its modification time is less - than those of any of its sources. Sources for a target accumulate - over dependency lines when this operator is used. The target is - removed if [1mbmake [22mis interrupted. - - [1m! [22mTargets are always re-created, but not until all sources have been - examined and re-created as necessary. Sources for a target accumu- - late over dependency lines when this operator is used. The target - is removed if [1mbmake [22mis interrupted. - - [1m:: [22mIf no sources are specified, the target is always re-created. Oth- - erwise, a target is considered out-of-date if any of its sources - has been modified more recently than the target. Sources for a - target do not accumulate over dependency lines when this operator - is used. The target will not be removed if [1mbmake [22mis interrupted. + on the sources and are customarily created from them. A target is con- + sidered out-of-date if it does not exist, or if its modification time is + less than that of any of its sources. An out-of-date target will be re- + created, but not until all sources have been examined and themselves re- + created as needed. Three operators may be used: + + [1m: [22mMany dependency lines may name this target but only one may have + attached shell commands. All sources named in all dependency lines + are considered together, and if needed the attached shell commands + are run to create or re-create the target. If [1mbmake [22mis inter- + rupted, the target is removed. + + [1m! [22mThe same, but the target is always re-created whether or not it is + out of date. + + [1m:: [22mAny dependency line may have attached shell commands, but each one + is handled independently: its sources are considered and the + attached shell commands are run if the target is out of date with + respect to (only) those sources. Thus, different groups of the + attached shell commands may be run depending on the circumstances. + Furthermore, unlike [1m:, [22mfor dependency lines with no sources, the + attached shell commands are always run. Also unlike [1m:, [22mthe target + will not be removed if [1mbmake [22mis interrupted. + All dependency lines mentioning a particular target must use the same + operator. Targets and sources may contain the shell wildcard values `?', `*', `[]', and `{}'. The values `?', `*', and `[]' may only be used as part of the @@ -374,7 +386,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) If the variable name contains a dollar, then the name itself is expanded first. This allows almost arbitrary variable names, however names con- - taining dollar, braces, parenthesis, or whitespace are really best + taining dollar, braces, parentheses, or whitespace are really best avoided! If the result of expanding a variable contains a dollar sign (`$') the @@ -729,6 +741,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) contains a variable transform. `PWD' is set to the value of `[4m.OBJDIR[24m' for all programs which [1mbmake [22mexecutes. + .SHELL The pathname of the shell used to run target scripts. It + is read-only. + .TARGETS The list of targets explicitly specified on the command line, if any. @@ -765,7 +780,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) ponent. [1m:M[4m[22mpattern[0m - Select only those words that match [4mpattern[24m. The standard shell + Selects only those words that match [4mpattern[24m. The standard shell wildcard characters (`*', `?', and `[]') may be used. The wildcard characters may be escaped with a backslash (`\'). As a consequence of the way values are split into words, matched, and then joined, a @@ -779,11 +794,11 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) This is identical to `[1m:M[22m', but selects all words which do not match [4mpattern[24m. - [1m:O [22mOrder every word in variable alphabetically. + [1m:O [22mOrders every word in variable alphabetically. - [1m:Or [22mOrder every word in variable in reverse alphabetical order. + [1m:Or [22mOrders every word in variable in reverse alphabetical order. - [1m:Ox [22mRandomize words in variable. The results will be different each + [1m:Ox [22mShuffles the words in variable. The results will be different each time you are referring to the modified variable; use the assignment with expansion (`[1m:=[22m') to prevent such behavior. For example, @@ -821,13 +836,13 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) [4mutc[24m value is not provided or is 0, the current time is used. [1m:hash[0m - Compute a 32-bit hash of the value and encode it as hex digits. + Computes a 32-bit hash of the value and encode it as hex digits. [1m:localtime[=utc][0m The value is a format string for strftime(3), using localtime(3). If a [4mutc[24m value is not provided or is 0, the current time is used. - [1m:tA [22mAttempt to convert variable to an absolute path using realpath(3), + [1m:tA [22mAttempts to convert variable to an absolute path using realpath(3), if that fails, the value is unchanged. [1m:tl [22mConverts variable to lower-case letters. @@ -836,7 +851,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) Words in the variable are normally separated by a space on expan- sion. This modifier sets the separator to the character [4mc[24m. If [4mc[24m is omitted, then no separator is used. The common escapes (including - octal numeric codes), work as expected. + octal numeric codes) work as expected. [1m:tu [22mConverts variable to upper-case letters. @@ -847,20 +862,20 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) white space. See also `[1m:[@][22m'. [1m:S[22m/[4mold_string[24m/[4mnew_string[24m/[[1m1gW[22m] - Modify the first occurrence of [4mold_string[24m in the variable's value, - replacing it with [4mnew_string[24m. If a `g' is appended to the last - slash of the pattern, all occurrences in each word are replaced. If - a `1' is appended to the last slash of the pattern, only the first - word is affected. If a `W' is appended to the last slash of the - pattern, then the value is treated as a single word (possibly con- - taining embedded white space). If [4mold_string[24m begins with a caret - (`^'), [4mold_string[24m is anchored at the beginning of each word. If - [4mold_string[24m ends with a dollar sign (`$'), it is anchored at the end - of each word. Inside [4mnew_string[24m, an ampersand (`&') is replaced by - [4mold_string[24m (without any `^' or `$'). Any character may be used as a - delimiter for the parts of the modifier string. The anchoring, - ampersand and delimiter characters may be escaped with a backslash - (`\'). + Modifies the first occurrence of [4mold_string[24m in each word of the + variable's value, replacing it with [4mnew_string[24m. If a `g' is + appended to the last delimiter of the pattern, all occurrences in + each word are replaced. If a `1' is appended to the last delimiter + of the pattern, only the first occurrence is affected. If a `W' is + appended to the last delimiter of the pattern, then the value is + treated as a single word (possibly containing embedded white space). + If [4mold_string[24m begins with a caret (`^'), [4mold_string[24m is anchored at + the beginning of each word. If [4mold_string[24m ends with a dollar sign + (`$'), it is anchored at the end of each word. Inside [4mnew_string[24m, + an ampersand (`&') is replaced by [4mold_string[24m (without any `^' or + `$'). Any character may be used as a delimiter for the parts of the + modifier string. The anchoring, ampersand and delimiter characters + may be escaped with a backslash (`\'). Variable expansion occurs in the normal fashion inside both [4mold_string[24m and [4mnew_string[24m with the single exception that a backslash @@ -878,16 +893,13 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) instances of the search pattern [4mpattern[24m as occur in the word or words it is found in; the `W' modifier causes the value to be treated as a single word (possibly containing embedded white space). - Note that `1' and `g' are orthogonal; the former specifies whether - multiple words are potentially affected, the latter whether multiple - substitutions can potentially occur within each affected word. As for the [1m:S [22mmodifier, the [4mpattern[24m and [4mreplacement[24m are subjected to variable expansion before being parsed as regular expressions. - [1m:T [22mReplaces each word in the variable with its last component. + [1m:T [22mReplaces each word in the variable with its last path component. - [1m:u [22mRemove adjacent duplicate words (like uniq(1)). + [1m:u [22mRemoves adjacent duplicate words (like uniq(1)). [1m:?[4m[22mtrue_string[24m[1m:[4m[22mfalse_string[0m If the variable name (not its value), when parsed as a .if condi- @@ -898,7 +910,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) variable expansions. A common error is trying to use expressions like ${NUMBERS:M42:?match:no} - which actually tests defined(NUMBERS), to determine is any words + which actually tests defined(NUMBERS), to determine if any words match "42" you need to use something like: ${"${NUMBERS:M42}" != "":?match:no}. @@ -916,7 +928,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) pattern matching character ([4m%[24m) in either the [4mnew_string[24m or [4mold_string[24m, only the first instance is treated specially (as the pattern character); all subsequent instances are treated as regular - characters + characters. Variable expansion occurs in the normal fashion inside both [4mold_string[24m and [4mnew_string[24m with the single exception that a backslash @@ -925,17 +937,17 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) [1m:@[4m[22mtemp[24m[1m@[4m[22mstring[24m[1m@[0m This is the loop expansion mechanism from the OSF Development Envi- - ronment (ODE) make. Unlike [1m.for [22mloops expansion occurs at the time - of reference. Assign [4mtemp[24m to each word in the variable and evaluate - [4mstring[24m. The ODE convention is that [4mtemp[24m should start and end with a - period. For example. + ronment (ODE) make. Unlike [1m.for [22mloops, expansion occurs at the time + of reference. Assigns [4mtemp[24m to each word in the variable and evalu- + ates [4mstring[24m. The ODE convention is that [4mtemp[24m should start and end + with a period. For example. ${LINKS:@.LINK.@${LN} ${TARGET} ${.LINK.}@} However a single character variable is often more readable: ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@} [1m:_[=var][0m - Save the current variable value in `$_' or the named [4mvar[24m for later + Saves the current variable value in `$_' or the named [4mvar[24m for later reference. Example usage: M_cmpv.units = 1 1000 1000000 @@ -948,7 +960,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) later referenced using the index values from `:range'. [1m:U[4m[22mnewval[0m - If the variable is undefined [4mnewval[24m is the value. If the variable + If the variable is undefined, [4mnewval[24m is the value. If the variable is defined, the existing value is returned. This is another ODE make feature. It is handy for setting per-target CFLAGS for instance: @@ -957,7 +969,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) ${VAR:D:Unewval} [1m:D[4m[22mnewval[0m - If the variable is defined [4mnewval[24m is the value. + If the variable is defined, [4mnewval[24m is the value. [1m:L [22mThe name of the variable is the value. @@ -1033,7 +1045,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) [1m# [22mReturns the number of words in the value. [1mINCLUDE STATEMENTS, CONDITIONALS AND FOR LOOPS[0m - Makefile inclusion, conditional structures and for loops reminiscent of + Makefile inclusion, conditional structures and for loops reminiscent of the C programming language are provided in [1mbmake[22m. All such structures are identified by a line beginning with a single dot (`.') character. Files are included with either [1m.include <[4m[22mfile[24m[1m> [22mor [1m.include "[4m[22mfile[24m[1m"[22m. Vari- @@ -1057,7 +1069,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) [1m.error [4m[22mmessage[0m The message is printed along with the name of the makefile and - line number, then [1mbmake [22mwill exit. + line number, then [1mbmake [22mwill exit immediately. [1m.export [4m[22mvariable[24m [4m...[0m Export the specified global variable. If no variable list is @@ -1191,7 +1203,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) variable expansion, either the left or right hand side of a `[1m==[22m' or `[1m!=[22m' operator is not an integral 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 + is assumed that the expanded variable is being compared against 0, or an empty string in the case of a string comparison. When [1mbmake [22mis evaluating one of these conditional expressions, and it @@ -1210,12 +1222,12 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) The syntax of a for loop is: [1m.for [4m[22mvariable[24m [[4mvariable[24m [4m...[24m] [1min [4m[22mexpression[0m - <make-rules> + <make-lines> [1m.endfor[0m After the for [1mexpression [22mis evaluated, it is split into words. On each iteration of the loop, one word is taken and assigned to each [1mvariable[22m, - in order, and these [1mvariables [22mare substituted into the [1mmake-rules [22minside + in order, and these [1mvariables [22mare substituted into the [1mmake-lines [22minside the body of the for loop. The number of words must come out even; that is, if there are three iteration variables, the number of words provided must be a multiple of three. @@ -1556,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 June 5, 2020 FreeBSD 11.3 +FreeBSD 11.3 August 28, 2020 FreeBSD 11.3 diff --git a/bsd.after-import.mk b/bsd.after-import.mk index 23f30aa6c638..0d48f3c26648 100644 --- a/bsd.after-import.mk +++ b/bsd.after-import.mk @@ -1,4 +1,4 @@ -# $Id: bsd.after-import.mk,v 1.15 2018/12/30 17:14:24 sjg Exp $ +# $Id: bsd.after-import.mk,v 1.16 2020/07/12 03:39:01 sjg Exp $ # This makefile is for use when integrating bmake into a BSD build # system. Use this makefile after importing bmake. @@ -9,7 +9,7 @@ # The goal is to allow the benefits of autoconf without # the overhead of running configure. -all: _makefile +all: _makefile _utmakefile all: after-import # we rely on bmake @@ -37,7 +37,9 @@ SRCTOP := ${srctop} .endif # This lets us match what boot-strap does -.if !defined(HOST_OS) +.if defined(.MAKE.OS) +HOST_OS:= ${.MAKE.OS} +.elif !defined(HOST_OS) HOST_OS!= uname .endif @@ -107,5 +109,18 @@ _makefile: bootstrap ${MAKEFILE} @cmp -s ${.TARGET} ${.CURDIR}/Makefile || \ mv ${.TARGET} ${.CURDIR}/Makefile +_utmakefile: bootstrap ${MAKEFILE} + @echo Generating ${.CURDIR}/unit-tests/Makefile + @mkdir -p ${.CURDIR}/unit-tests + @(echo '# This is a generated file, do NOT edit!'; \ + echo '# See ${_this:S,${SRCTOP}/,,}'; \ + echo '#'; echo '# $$${HOST_OS}$$'; \ + ${MAKEFILE_SED} \ + -e '/^UNIT_TESTS/s,=.*,= $${srcdir},' \ + ${BMAKE_SRC}/unit-tests/Makefile ) > ${.TARGET} + @cmp -s ${.TARGET} ${.CURDIR}/unit-tests/Makefile || \ + mv ${.TARGET} ${.CURDIR}/unit-tests/Makefile + + .include <bsd.obj.mk> @@ -1,4 +1,4 @@ -/* $NetBSD: buf.c,v 1.26 2020/07/03 08:02:55 rillig Exp $ */ +/* $NetBSD: buf.c,v 1.37 2020/08/23 08:21:50 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -70,173 +70,123 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: buf.c,v 1.26 2020/07/03 08:02:55 rillig Exp $"; +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.26 2020/07/03 08:02:55 rillig Exp $"); +__RCSID("$NetBSD: buf.c,v 1.37 2020/08/23 08:21:50 rillig Exp $"); #endif #endif /* not lint */ #endif -/*- - * buf.c -- - * Functions for automatically-expanded buffers. - */ - -#include "make.h" -#include "buf.h" - -#ifndef max -#define max(a,b) ((a) > (b) ? (a) : (b)) -#endif +/* Functions for automatically-expanded null-terminated buffers. */ -#define BUF_DEF_SIZE 256 /* Default buffer size */ +#include <limits.h> +#include "make.h" -/*- - *----------------------------------------------------------------------- - * Buf_Expand_1 -- - * Extend buffer for single byte add. - * - *----------------------------------------------------------------------- - */ +/* Extend the buffer for adding a single byte. */ void Buf_Expand_1(Buffer *bp) { - bp->size += max(bp->size, 16); + bp->size += MAX(bp->size, 16); bp->buffer = bmake_realloc(bp->buffer, bp->size); } -/*- - *----------------------------------------------------------------------- - * Buf_AddBytes -- - * Add a number of bytes to the buffer. - * - * Results: - * None. - * - * Side Effects: - * Guess what? - * - *----------------------------------------------------------------------- - */ +/* Add the given bytes to the buffer. */ void -Buf_AddBytes(Buffer *bp, int numBytes, const Byte *bytesPtr) +Buf_AddBytes(Buffer *bp, const char *bytesPtr, size_t numBytes) { - int count = bp->count; - Byte *ptr; + size_t count = bp->count; + char *ptr; if (__predict_false(count + numBytes >= bp->size)) { - bp->size += max(bp->size, numBytes + 16); + bp->size += MAX(bp->size, numBytes + 16); bp->buffer = bmake_realloc(bp->buffer, bp->size); } ptr = bp->buffer + count; bp->count = count + numBytes; - ptr[numBytes] = 0; memcpy(ptr, bytesPtr, numBytes); + ptr[numBytes] = '\0'; } -/*- - *----------------------------------------------------------------------- - * Buf_GetAll -- - * Get all the available data at once. - * - * Results: - * A pointer to the data and the number of bytes available. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -Byte * -Buf_GetAll(Buffer *bp, int *numBytesPtr) +/* Add the bytes between start and end to the buffer. */ +void +Buf_AddBytesBetween(Buffer *bp, const char *start, const char *end) { + Buf_AddBytes(bp, start, (size_t)(end - start)); +} + +/* Add the given string to the buffer. */ +void +Buf_AddStr(Buffer *bp, const char *str) +{ + Buf_AddBytes(bp, str, strlen(str)); +} +/* Add the given number to the buffer. */ +void +Buf_AddInt(Buffer *bp, 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 + }; + char buf[buf_size]; + + size_t len = (size_t)snprintf(buf, sizeof buf, "%d", n); + Buf_AddBytes(bp, buf, 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. */ +char * +Buf_GetAll(Buffer *bp, size_t *numBytesPtr) +{ if (numBytesPtr != NULL) *numBytesPtr = bp->count; - return bp->buffer; } -/*- - *----------------------------------------------------------------------- - * Buf_Empty -- - * Throw away bytes in a buffer. - * - * Results: - * None. - * - * Side Effects: - * The bytes are discarded. - * - *----------------------------------------------------------------------- - */ +/* Mark the buffer as empty, so it can be filled with data again. */ void Buf_Empty(Buffer *bp) { - bp->count = 0; - *bp->buffer = 0; + bp->buffer[0] = '\0'; } -/*- - *----------------------------------------------------------------------- - * Buf_Init -- - * Initialize a buffer. If no initial size is given, a reasonable - * default is used. - * - * Input: - * size Initial size for the buffer - * - * Results: - * A buffer to be given to other functions in this library. - * - * Side Effects: - * The buffer is created, the space allocated and pointers - * initialized. - * - *----------------------------------------------------------------------- - */ +/* Initialize a buffer. + * If the given initial size is 0, a reasonable default is used. */ void -Buf_Init(Buffer *bp, int size) +Buf_Init(Buffer *bp, size_t size) { if (size <= 0) { - size = BUF_DEF_SIZE; + size = 256; } bp->size = size; bp->count = 0; bp->buffer = bmake_malloc(size); - *bp->buffer = 0; + bp->buffer[0] = '\0'; } -/*- - *----------------------------------------------------------------------- - * Buf_Destroy -- - * Nuke a buffer and all its resources. - * - * Input: - * buf Buffer to destroy - * freeData TRUE if the data should be destroyed - * - * Results: - * Data buffer, NULL if freed - * - * Side Effects: - * The buffer is freed. - * - *----------------------------------------------------------------------- - */ -Byte * +/* Reset the buffer. + * If freeData is TRUE, the data from the buffer is freed as well. + * Otherwise it is kept and returned. */ +char * Buf_Destroy(Buffer *buf, Boolean freeData) { - Byte *data; - - data = buf->buffer; + char *data = buf->buffer; if (freeData) { free(data); data = NULL; @@ -249,42 +199,24 @@ Buf_Destroy(Buffer *buf, Boolean freeData) return data; } - -/*- - *----------------------------------------------------------------------- - * Buf_DestroyCompact -- - * Nuke a buffer and return its data. - * - * Input: - * buf Buffer to destroy - * - * Results: - * Data buffer - * - * Side Effects: - * If the buffer size is much greater than its content, - * a new buffer will be allocated and the old one freed. - * - *----------------------------------------------------------------------- - */ #ifndef BUF_COMPACT_LIMIT -# define BUF_COMPACT_LIMIT 128 /* worthwhile saving */ +# define BUF_COMPACT_LIMIT 128 /* worthwhile saving */ #endif -Byte * +/* Reset the buffer and return its data. + * + * If the buffer size is much greater than its content, + * a new buffer will be allocated and the old one freed. */ +char * Buf_DestroyCompact(Buffer *buf) { #if BUF_COMPACT_LIMIT > 0 - Byte *data; - if (buf->size - buf->count >= BUF_COMPACT_LIMIT) { /* We trust realloc to be smart */ - data = bmake_realloc(buf->buffer, buf->count + 1); - if (data) { - data[buf->count] = 0; - Buf_Destroy(buf, FALSE); - return data; - } + char *data = bmake_realloc(buf->buffer, buf->count + 1); + data[buf->count] = '\0'; + Buf_Destroy(buf, FALSE); + return data; } #endif return Buf_Destroy(buf, FALSE); @@ -1,4 +1,4 @@ -/* $NetBSD: buf.h,v 1.19 2017/05/31 22:02:06 maya Exp $ */ +/* $NetBSD: buf.h,v 1.28 2020/09/01 17:38:26 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -72,48 +72,54 @@ * from: @(#)buf.h 8.1 (Berkeley) 6/6/93 */ -/*- - * buf.h -- - * Header for users of the buf library. - */ +/* Automatically growing null-terminated buffers of characters. */ #ifndef MAKE_BUF_H #define MAKE_BUF_H -typedef char Byte; +#include <stddef.h> +/* An automatically growing null-terminated buffer of characters. */ typedef struct Buffer { - int size; /* Current size of the buffer */ - int count; /* Number of bytes in buffer */ - Byte *buffer; /* The buffer itself (zero terminated) */ + 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) */ } Buffer; -/* If we aren't on netbsd, __predict_false() might not be defined. */ +/* If we aren't on NetBSD, __predict_false() might not be defined. */ #ifndef __predict_false #define __predict_false(x) (x) #endif -/* Buf_AddByte adds a single byte to a buffer. */ -#define Buf_AddByte(bp, byte) do { \ - int _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; \ - } while (0) +void Buf_Expand_1(Buffer *); -#define BUF_ERROR 256 +/* Buf_AddByte adds a single byte to a buffer. */ +static inline void MAKE_ATTR_UNUSED +Buf_AddByte(Buffer *bp, 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; +} -#define Buf_Size(bp) ((bp)->count) +static inline size_t MAKE_ATTR_UNUSED +Buf_Size(const Buffer *bp) +{ + return bp->count; +} -void Buf_Expand_1(Buffer *); -void Buf_AddBytes(Buffer *, int, const Byte *); -Byte *Buf_GetAll(Buffer *, int *); +void Buf_AddBytes(Buffer *, const char *, size_t); +void Buf_AddBytesBetween(Buffer *, const char *, const char *); +void Buf_AddStr(Buffer *, const char *); +void Buf_AddInt(Buffer *, int); +char *Buf_GetAll(Buffer *, size_t *); void Buf_Empty(Buffer *); -void Buf_Init(Buffer *, int); -Byte *Buf_Destroy(Buffer *, Boolean); -Byte *Buf_DestroyCompact(Buffer *); +void Buf_Init(Buffer *, size_t); +char *Buf_Destroy(Buffer *, Boolean); +char *Buf_DestroyCompact(Buffer *); #endif /* MAKE_BUF_H */ @@ -1,4 +1,4 @@ -/* $NetBSD: compat.c,v 1.113 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -70,14 +70,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: compat.c,v 1.113 2020/07/03 08:13:23 rillig Exp $"; +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.113 2020/07/03 08:13:23 rillig Exp $"); +__RCSID("$NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $"); #endif #endif /* not lint */ #endif @@ -129,34 +129,24 @@ static void CompatDeleteTarget(GNode *gn) { if ((gn != NULL) && !Targ_Precious (gn)) { - char *p1; - char *file = Var_Value(TARGET, gn, &p1); + char *p1; + const char *file = Var_Value(TARGET, gn, &p1); if (!noExecute && eunlink(file) != -1) { Error("*** %s removed", file); } - free(p1); + bmake_free(p1); } } -/*- - *----------------------------------------------------------------------- - * CompatInterrupt -- - * Interrupt the creation of the current target and remove it if - * it ain't precious. - * - * Results: - * None. +/* Interrupt the creation of the current target and remove it if it ain't + * precious. Then exit. * - * Side Effects: - * The target is removed and the process exits. If .INTERRUPT exists, - * its commands are run first WITH INTERRUPTS IGNORED.. + * If .INTERRUPT exists, its commands are run first WITH INTERRUPTS IGNORED. * * XXX: is .PRECIOUS supposed to inhibit .INTERRUPT? I doubt it, but I've * left the logic alone for now. - dholland 20160826 - * - *----------------------------------------------------------------------- */ static void CompatInterrupt(int signo) @@ -190,7 +180,7 @@ CompatInterrupt(int signo) kill(myPid, signo); } } - + /*- *----------------------------------------------------------------------- * CompatRunCommand -- @@ -215,7 +205,7 @@ CompatRunCommand(void *cmdp, void *gnp) char *cmdStart; /* Start of expanded command */ char *cp, *bp; Boolean silent, /* Don't print command */ - doIt; /* Execute even if -n */ + 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 */ @@ -224,21 +214,17 @@ CompatRunCommand(void *cmdp, void *gnp) 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 */ - int argc; /* Number of arguments in av or 0 if not - * dynamically allocated */ - Boolean local; /* TRUE if command should be executed - * locally */ Boolean useShell; /* TRUE if command should be executed * using a shell */ char * volatile cmd = (char *)cmdp; GNode *gn = (GNode *)gnp; - silent = gn->type & OP_SILENT; + silent = (gn->type & OP_SILENT) != 0; errCheck = !(gn->type & OP_IGNORE); doIt = FALSE; - cmdNode = Lst_Member(gn->commands, cmd); - cmdStart = Var_Subst(NULL, cmd, gn, VARF_WANTRES); + 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 @@ -252,10 +238,11 @@ CompatRunCommand(void *cmdp, void *gnp) return 0; } cmd = cmdStart; - Lst_Replace(cmdNode, cmdStart); + LstNode_Set(cmdNode, cmdStart); if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) { - (void)Lst_AtEnd(ENDNode->commands, cmdStart); + assert(ENDNode != NULL); + Lst_Append(ENDNode->commands, cmdStart); return 0; } if (strcmp(cmdStart, "...") == 0) { @@ -266,7 +253,7 @@ CompatRunCommand(void *cmdp, void *gnp) while ((*cmd == '@') || (*cmd == '-') || (*cmd == '+')) { switch (*cmd) { case '@': - silent = DEBUG(LOUD) ? FALSE : TRUE; + silent = !DEBUG(LOUD); break; case '-': errCheck = FALSE; @@ -330,7 +317,6 @@ CompatRunCommand(void *cmdp, void *gnp) if (DEBUG(JOB)) fprintf(debug_file, "Execute: '%s'\n", cmd); -again: if (useShell) { /* * We need to pass the command off to the shell, typically @@ -352,9 +338,8 @@ again: else shargv[shargc++] = "-c"; shargv[shargc++] = cmd; - shargv[shargc++] = NULL; + shargv[shargc] = NULL; av = shargv; - argc = 0; bp = NULL; mav = NULL; } else { @@ -362,16 +347,12 @@ again: * No meta-characters, so no need to exec a shell. Break the command * into words to form an argument vector we can execute. */ - mav = brk_string(cmd, &argc, TRUE, &bp); - if (mav == NULL) { - useShell = 1; - goto again; - } + Words words = Str_Words(cmd, FALSE); + mav = words.words; + bp = words.freeIt; av = (void *)mav; } - local = TRUE; - #ifdef USE_META if (useMeta) { meta_compat_start(); @@ -392,10 +373,7 @@ again: meta_compat_child(); } #endif - if (local) - (void)execvp(av[0], (char *const *)UNCONST(av)); - else - (void)execv(av[0], (char *const *)UNCONST(av)); + (void)execvp(av[0], (char *const *)UNCONST(av)); execError("exec", av[0]); _exit(1); } @@ -403,7 +381,9 @@ again: free(mav); free(bp); - Lst_Replace(cmdNode, NULL); + /* XXX: Memory management looks suspicious here. */ + /* XXX: Setting a list item to NULL is unexpected. */ + LstNode_SetNull(cmdNode); #ifdef USE_META if (useMeta) { @@ -436,18 +416,18 @@ again: #endif if (status != 0) { if (DEBUG(ERROR)) { - fprintf(debug_file, "\n*** Failed target: %s\n*** Failed command: ", + fprintf(debug_file, "\n*** Failed target: %s\n*** Failed command: ", gn->name); - for (cp = cmd; *cp; ) { - if (isspace((unsigned char)*cp)) { + for (cp = cmd; *cp; ) { + if (isspace((unsigned char)*cp)) { fprintf(debug_file, " "); - while (isspace((unsigned char)*cp)) + while (isspace((unsigned char)*cp)) cp++; } else { fprintf(debug_file, "%c", *cp); - cp++; + cp++; } - } + } fprintf(debug_file, "\n"); } printf("*** Error code %d", status); @@ -502,7 +482,7 @@ again: return status; } - + /*- *----------------------------------------------------------------------- * Compat_Make -- @@ -544,14 +524,14 @@ Compat_Make(void *gnp, void *pgnp) Lst_ForEach(gn->children, Compat_Make, gn); if ((gn->flags & REMAKE) == 0) { gn->made = ABORTED; - pgn->flags &= ~REMAKE; + pgn->flags &= ~(unsigned)REMAKE; goto cohorts; } - if (Lst_Member(gn->iParents, pgn) != NULL) { + if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) { char *p1; Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn); - free(p1); + bmake_free(p1); } /* @@ -614,7 +594,7 @@ Compat_Make(void *gnp, void *pgnp) Lst_ForEach(gn->commands, CompatRunCommand, gn); curTarg = NULL; } else { - Job_Touch(gn, gn->type & OP_SILENT); + Job_Touch(gn, (gn->type & OP_SILENT) != 0); } } else { gn->made = ERROR; @@ -640,7 +620,7 @@ Compat_Make(void *gnp, void *pgnp) Make_TimeStamp(pgn, gn); } } else if (keepgoing) { - pgn->flags &= ~REMAKE; + pgn->flags &= ~(unsigned)REMAKE; } else { PrintOnError(gn, "\nStop."); exit(1); @@ -650,18 +630,19 @@ Compat_Make(void *gnp, void *pgnp) * Already had an error when making this beastie. Tell the parent * to abort. */ - pgn->flags &= ~REMAKE; + pgn->flags &= ~(unsigned)REMAKE; } else { - if (Lst_Member(gn->iParents, pgn) != NULL) { + if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) { char *p1; - Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn); - free(p1); + const char *target = Var_Value(TARGET, gn, &p1); + Var_Set(IMPSRC, target != NULL ? target : "", pgn); + bmake_free(p1); } switch(gn->made) { case BEINGMADE: Error("Graph cycles through %s", gn->name); gn->made = ERROR; - pgn->flags &= ~REMAKE; + pgn->flags &= ~(unsigned)REMAKE; break; case MADE: if ((gn->type & OP_EXEC) == 0) { @@ -683,22 +664,11 @@ cohorts: Lst_ForEach(gn->cohorts, Compat_Make, pgnp); return 0; } - -/*- - *----------------------------------------------------------------------- - * Compat_Run -- - * Initialize this mode and start making. + +/* Initialize this module and start making. * * Input: - * targs List of target nodes to re-create - * - * Results: - * None. - * - * Side Effects: - * Guess what? - * - *----------------------------------------------------------------------- + * targs The target nodes to re-create */ void Compat_Run(Lst targs) @@ -732,10 +702,10 @@ Compat_Run(Lst targs) gn = Targ_FindNode(".BEGIN", TARG_NOCREATE); if (gn != NULL) { Compat_Make(gn, gn); - if (gn->made == ERROR) { - PrintOnError(gn, "\nStop."); - exit(1); - } + if (gn->made == ERROR) { + PrintOnError(gn, "\nStop."); + exit(1); + } } } @@ -756,8 +726,8 @@ Compat_Run(Lst targs) * could not be made due to errors. */ errors = 0; - while (!Lst_IsEmpty (targs)) { - gn = (GNode *)Lst_DeQueue(targs); + while (!Lst_IsEmpty(targs)) { + gn = Lst_Dequeue(targs); Compat_Make(gn, gn); if (gn->made == UPTODATE) { @@ -1,4 +1,4 @@ -/* $NetBSD: cond.c,v 1.79 2020/07/09 22:34:08 sjg Exp $ */ +/* $NetBSD: cond.c,v 1.106 2020/08/29 13:38:48 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -70,14 +70,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: cond.c,v 1.79 2020/07/09 22:34:08 sjg Exp $"; +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.79 2020/07/09 22:34:08 sjg Exp $"); +__RCSID("$NetBSD: cond.c,v 1.106 2020/08/29 13:38:48 rillig Exp $"); #endif #endif /* not lint */ #endif @@ -91,14 +91,10 @@ __RCSID("$NetBSD: cond.c,v 1.79 2020/07/09 22:34:08 sjg Exp $"); * */ -#include <assert.h> -#include <ctype.h> -#include <errno.h> /* For strtoul() error checking */ +#include <errno.h> -#include "make.h" -#include "hash.h" -#include "dir.h" -#include "buf.h" +#include "make.h" +#include "dir.h" /* * The parsing of conditional expressions is based on this grammar: @@ -121,8 +117,7 @@ __RCSID("$NetBSD: cond.c,v 1.79 2020/07/09 22:34:08 sjg Exp $"); * T -> ! T * op -> == | != | > | < | >= | <= * - * 'symbol' is some other symbol to which the default function (condDefProc) - * is applied. + * '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 '||', @@ -141,46 +136,16 @@ typedef enum { TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR } Token; -/*- - * Structures to handle elegantly the different forms of #if's. The - * last two fields are stored in condInvert and condDefProc, respectively. - */ -static void CondPushBack(Token); -static int CondGetArg(Boolean, char **, char **, const char *); -static Boolean CondDoDefined(int, const char *); -static int CondStrMatch(const void *, const void *); -static Boolean CondDoMake(int, const char *); -static Boolean CondDoExists(int, const char *); -static Boolean CondDoTarget(int, const char *); -static Boolean CondDoCommands(int, const char *); -static Boolean CondCvtArg(char *, double *); -static Token CondToken(Boolean); -static Token CondT(Boolean); -static Token CondF(Boolean); static Token CondE(Boolean); -static int do_Cond_EvalExpression(Boolean *); +static CondEvalResult do_Cond_EvalExpression(Boolean *); -static const struct If { - const char *form; /* Form of if */ - int formlen; /* Length of form */ - Boolean doNot; /* TRUE if default function should be negated */ - Boolean (*defProc)(int, 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 }, - { NULL, 0, FALSE, NULL } -}; - -static const struct If *if_info; /* Info for current statement */ -static char *condExpr; /* The expression to parse */ -static Token condPushBack=TOK_NONE; /* Single push-back token used in +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 unsigned int cond_depth = 0; /* current .if nesting level */ -static unsigned int cond_min_depth = 0; /* depth at makefile open */ +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. @@ -194,58 +159,38 @@ static Boolean lhsStrict; static int istoken(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 && !isalpha((unsigned char)str[len]); } -/*- - *----------------------------------------------------------------------- - * CondPushBack -- - * Push back the most recent token read. We only need one level of - * this, so the thing is just stored in 'condPushback'. - * - * Input: - * t Token to push back into the "stream" - * - * Results: - * None. - * - * Side Effects: - * condPushback is overwritten. - * - *----------------------------------------------------------------------- - */ +/* Push back the most recent token read. We only need one level of + * this, so the thing is just stored in 'condPushback'. */ static void CondPushBack(Token t) { condPushBack = t; } - + /*- - *----------------------------------------------------------------------- - * CondGetArg -- - * Find the argument of a built-in function. + * Parse the argument of a built-in function. * * Results: - * The length of the argument and the address of the argument. - * - * Side Effects: - * The pointer is set to point to the closing parenthesis of the - * function call. - * - *----------------------------------------------------------------------- + * 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, char **linePtr, char **argPtr, const char *func) +CondGetArg(Boolean doEval, const char **linePtr, char **argPtr, + const char *func) { - char *cp; - int argLen; - Buffer buf; - int paren_depth; - char ch; + const char *cp; + Buffer buf; + int paren_depth; + char ch; + size_t argLen; cp = *linePtr; if (func != NULL) - /* Skip opening '(' - verfied by caller */ + /* Skip opening '(' - verified by caller */ cp++; if (*cp == '\0') { @@ -283,23 +228,19 @@ CondGetArg(Boolean doEval, char **linePtr, char **argPtr, const char *func) * variable, so we don't do it too. Nor do we return an error, * though perhaps we should... */ - char *cp2; - int len; - void *freeIt; - - cp2 = Var_Parse(cp, VAR_CMD, VARF_UNDEFERR| - (doEval ? VARF_WANTRES : 0), - &len, &freeIt); - Buf_AddBytes(&buf, strlen(cp2), cp2); + int len; + void *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; continue; } if (ch == '(') paren_depth++; - else - if (ch == ')' && --paren_depth < 0) - break; + else if (ch == ')' && --paren_depth < 0) + break; Buf_AddByte(&buf, *cp); cp++; } @@ -313,105 +254,49 @@ CondGetArg(Boolean doEval, char **linePtr, char **argPtr, const char *func) if (func != NULL && *cp++ != ')') { Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()", - func); + func); return 0; } *linePtr = cp; return argLen; } - -/*- - *----------------------------------------------------------------------- - * CondDoDefined -- - * Handle the 'defined' function for conditionals. - * - * Results: - * TRUE if the given variable is defined. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ + +/* Test whether the given variable is defined. */ static Boolean CondDoDefined(int argLen MAKE_ATTR_UNUSED, const char *arg) { - char *p1; - Boolean result; - - if (Var_Value(arg, VAR_CMD, &p1) != NULL) { - result = TRUE; - } else { - result = FALSE; - } - - free(p1); + char *freeIt; + Boolean result = Var_Value(arg, VAR_CMD, &freeIt) != NULL; + bmake_free(freeIt); return result; } - -/*- - *----------------------------------------------------------------------- - * CondStrMatch -- - * Front-end for Str_Match so it returns 0 on match and non-zero - * on mismatch. Callback function for CondDoMake via Lst_Find - * - * Results: - * 0 if string matches pattern - * - * Side Effects: - * None - * - *----------------------------------------------------------------------- - */ -static int -CondStrMatch(const void *string, const void *pattern) + +/* Wrapper around Str_Match, to be used by Lst_Find. */ +static Boolean +CondFindStrMatch(const void *string, const void *pattern) { - return !Str_Match(string, pattern); + return Str_Match(string, pattern); } - -/*- - *----------------------------------------------------------------------- - * CondDoMake -- - * Handle the 'make' function for conditionals. - * - * Results: - * TRUE if the given target is being made. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ + +/* See if the given target is being made. */ static Boolean CondDoMake(int argLen MAKE_ATTR_UNUSED, const char *arg) { - return Lst_Find(create, arg, CondStrMatch) != NULL; + return Lst_Find(create, CondFindStrMatch, arg) != NULL; } - -/*- - *----------------------------------------------------------------------- - * CondDoExists -- - * See if the given file exists. - * - * Results: - * TRUE if the file exists and FALSE if it does not. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ + +/* See if the given file exists. */ static Boolean CondDoExists(int argLen MAKE_ATTR_UNUSED, const char *arg) { Boolean result; - char *path; + char *path; path = Dir_FindFile(arg, dirSearchPath); if (DEBUG(COND)) { fprintf(debug_file, "exists(%s) result is \"%s\"\n", - arg, path ? path : ""); + arg, path ? path : ""); } if (path != NULL) { result = TRUE; @@ -421,68 +306,39 @@ CondDoExists(int argLen MAKE_ATTR_UNUSED, const char *arg) } return result; } - -/*- - *----------------------------------------------------------------------- - * CondDoTarget -- - * See if the given node exists and is an actual target. - * - * Results: - * TRUE if the node exists as a target and FALSE if it does not. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ + +/* See if the given node exists and is an actual target. */ static Boolean CondDoTarget(int argLen MAKE_ATTR_UNUSED, const char *arg) { - GNode *gn; + GNode *gn; gn = Targ_FindNode(arg, TARG_NOCREATE); return gn != NULL && !OP_NOP(gn->type); } -/*- - *----------------------------------------------------------------------- - * CondDoCommands -- - * See if the given node exists and is an actual target with commands - * associated with it. - * - * Results: - * TRUE if the node exists as a target and has commands associated with - * it and FALSE if it does not. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ +/* 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) { - GNode *gn; + GNode *gn; gn = Targ_FindNode(arg, TARG_NOCREATE); return gn != NULL && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands); } - + /*- - *----------------------------------------------------------------------- - * CondCvtArg -- - * Convert the given number into a double. - * We try a base 10 or 16 integer conversion first, if that fails - * then we try a floating point conversion instead. + * Convert the given number into a double. + * We try a base 10 or 16 integer conversion first, if that fails + * then we try a floating point conversion instead. * * Results: * Sets 'value' to double value of string. - * Returns 'true' if the convertion suceeded - * - *----------------------------------------------------------------------- + * Returns TRUE if the conversion succeeded. */ static Boolean -CondCvtArg(char *str, double *value) +CondCvtArg(const char *str, double *value) { char *eptr, ech; unsigned long l_val; @@ -510,33 +366,28 @@ CondCvtArg(char *str, double *value) } /*- - *----------------------------------------------------------------------- - * CondGetString -- - * Get a string from a variable reference or an optionally quoted - * string. This is called for the lhs and rhs of string compares. + * Get a string from a variable reference or an optionally quoted + * string. This is called for the lhs and rhs of string compares. * * Results: - * Sets freeIt if needed, - * Sets quoted if string was quoted, - * Returns NULL on error, - * else returns string - absent any quotes. + * 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 to end of this token. - * - * - *----------------------------------------------------------------------- + * Moves condExpr past the end of this token. */ /* coverity:[+alloc : arg-*2] */ -static char * +static const char * CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS) { Buffer buf; - char *cp; - char *str; - int len; - int qt; - char *start; + const char *cp; + const char *str; + int len; + Boolean qt; + const char *start; + VarEvalFlags eflags; Buf_Init(&buf, 0); str = NULL; @@ -573,9 +424,9 @@ CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS) break; case '$': /* if we are in quotes, then an undefined variable is ok */ - str = Var_Parse(condExpr, VAR_CMD, - ((!qt && doEval) ? VARF_UNDEFERR : 0) | - (doEval ? VARF_WANTRES : 0), &len, freeIt); + eflags = ((!qt && doEval) ? VARE_UNDEFERR : 0) | + (doEval ? VARE_WANTRES : 0); + str = Var_Parse(condExpr, VAR_CMD, eflags, &len, freeIt); if (str == var_Error) { if (*freeIt) { free(*freeIt); @@ -596,7 +447,7 @@ CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS) */ if ((condExpr == start + len) && (*condExpr == '\0' || - isspace((unsigned char) *condExpr) || + isspace((unsigned char)*condExpr) || strchr("!=><)", *condExpr))) { goto cleanup; } @@ -610,12 +461,12 @@ CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS) free(*freeIt); *freeIt = NULL; } - str = NULL; /* not finished yet */ - condExpr--; /* don't skip over next char */ + str = NULL; /* not finished yet */ + condExpr--; /* don't skip over next char */ break; default: if (strictLHS && !qt && *start != '$' && - !isdigit((unsigned char) *start)) { + !isdigit((unsigned char)*start)) { /* lhs must be quoted, a variable reference or number */ if (*freeIt) { free(*freeIt); @@ -628,43 +479,51 @@ CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS) break; } } - got_str: - str = Buf_GetAll(&buf, NULL); - *freeIt = str; - cleanup: +got_str: + *freeIt = Buf_GetAll(&buf, NULL); + str = *freeIt; +cleanup: Buf_Destroy(&buf, FALSE); return str; } - + +/* The different forms of #if's. */ +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 */ +} ifs[] = { + { "def", 3, FALSE, CondDoDefined }, + { "ndef", 4, TRUE, CondDoDefined }, + { "make", 4, FALSE, CondDoMake }, + { "nmake", 5, TRUE, CondDoMake }, + { "", 0, FALSE, CondDoDefined }, + { NULL, 0, FALSE, NULL } +}; + /*- - *----------------------------------------------------------------------- - * CondToken -- - * Return the next token from the input. - * - * Results: - * A Token for the next lexical token in the stream. + * Return the next token from the input. * * Side Effects: * condPushback will be set back to TOK_NONE if it is used. - * - *----------------------------------------------------------------------- */ static Token compare_expression(Boolean doEval) { - Token t; - char *lhs; - char *rhs; - char *op; - void *lhsFree; - void *rhsFree; + Token t; + const char *lhs; + const char *rhs; + const char *op; + void *lhsFree; + void *rhsFree; Boolean lhsQuoted; Boolean rhsQuoted; - double left, right; + double left, right; t = TOK_ERROR; rhs = NULL; - lhsFree = rhsFree = FALSE; + lhsFree = rhsFree = NULL; lhsQuoted = rhsQuoted = FALSE; /* @@ -678,7 +537,7 @@ compare_expression(Boolean doEval) /* * Skip whitespace to get to the operator */ - while (isspace((unsigned char) *condExpr)) + while (isspace((unsigned char)*condExpr)) condExpr++; /* @@ -688,39 +547,39 @@ compare_expression(Boolean doEval) */ op = condExpr; switch (*condExpr) { - case '!': - case '=': - case '<': - case '>': - if (condExpr[1] == '=') { - condExpr += 2; - } else { - condExpr += 1; - } - 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; + case '!': + case '=': + case '<': + case '>': + if (condExpr[1] == '=') { + condExpr += 2; + } else { + condExpr += 1; + } + 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; + goto done; } while (isspace((unsigned char)*condExpr)) @@ -742,16 +601,16 @@ compare_expression(Boolean doEval) } if (rhsQuoted || lhsQuoted) { -do_string_compare: + do_string_compare: if (((*op != '!') && (*op != '=')) || (op[1] != '=')) { Parse_Error(PARSE_WARNING, - "String comparison operator should be either == or !="); + "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); + lhs, rhs, op); } /* * Null-terminate rhs and perform the comparison. @@ -773,9 +632,9 @@ do_string_compare: if (DEBUG(COND)) { fprintf(debug_file, "left = %f, right = %f, op = %.2s\n", left, - right, op); + right, op); } - switch(op[0]) { + switch (op[0]) { case '!': if (op[1] != '=') { Parse_Error(PARSE_WARNING, @@ -816,21 +675,23 @@ done: } static int -get_mpt_arg(Boolean doEval, char **linePtr, char **argPtr, const char *func MAKE_ATTR_UNUSED) +get_mpt_arg(Boolean doEval, const char **linePtr, char **argPtr, + const char *func MAKE_ATTR_UNUSED) { /* * Use Var_Parse to parse the spec in parens and return * TOK_TRUE if the resulting string is empty. */ - int length; - void *freeIt; - char *val; - char *cp = *linePtr; + int length; + void *val_freeIt; + const char *val; + const char *cp = *linePtr; /* We do all the work here and return the result as the length */ *argPtr = NULL; - val = Var_Parse(cp - 1, VAR_CMD, doEval ? VARF_WANTRES : 0, &length, &freeIt); + 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'. @@ -838,12 +699,12 @@ get_mpt_arg(Boolean doEval, char **linePtr, char **argPtr, const char *func MAKE *linePtr = cp - 1 + length; if (val == var_Error) { - free(freeIt); + free(val_freeIt); return -1; } /* A variable is empty when it just contains spaces... 4/15/92, christos */ - while (isspace(*(unsigned char *)val)) + while (isspace((unsigned char)val[0])) val++; /* @@ -851,7 +712,7 @@ get_mpt_arg(Boolean doEval, char **linePtr, char **argPtr, const char *func MAKE * true/false here. */ length = *val ? 2 : 1; - free(freeIt); + free(val_freeIt); return length; } @@ -865,32 +726,32 @@ static Token compare_function(Boolean doEval) { static const struct fn_def { - const char *fn_name; - int fn_name_len; - int (*fn_getarg)(Boolean, char **, char **, const char *); - Boolean (*fn_proc)(int, const char *); + const char *fn_name; + size_t fn_name_len; + int (*fn_getarg)(Boolean, const char **, char **, const char *); + Boolean (*fn_proc)(int, 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 }, - { NULL, 0, NULL, NULL }, + { "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 }, + { NULL, 0, NULL, NULL }, }; const struct fn_def *fn_def; - Token t; - char *arg = NULL; - int arglen; - char *cp = condExpr; - char *cp1; + Token t; + char *arg = NULL; + int arglen; + const char *cp = condExpr; + 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)) continue; cp += fn_def->fn_name_len; /* There can only be whitespace before the '(' */ - while (isspace(*(unsigned char *)cp)) + while (isspace((unsigned char)*cp)) cp++; if (*cp != '(') break; @@ -921,7 +782,7 @@ compare_function(Boolean doEval) * expression. */ arglen = CondGetArg(doEval, &cp, &arg, NULL); - for (cp1 = cp; isspace(*(unsigned char *)cp1); cp1++) + for (cp1 = cp; isspace((unsigned char)*cp1); cp1++) continue; if (*cp1 == '=' || *cp1 == '!') return compare_expression(doEval); @@ -1015,7 +876,7 @@ CondToken(Boolean doEval) static Token CondT(Boolean doEval) { - Token t; + Token t; t = CondToken(doEval); @@ -1045,7 +906,7 @@ CondT(Boolean doEval) } return t; } - + /*- *----------------------------------------------------------------------- * CondF -- @@ -1063,7 +924,7 @@ CondT(Boolean doEval) static Token CondF(Boolean doEval) { - Token l, o; + Token l, o; l = CondT(doEval); if (l != TOK_ERROR) { @@ -1091,7 +952,7 @@ CondF(Boolean doEval) } return l; } - + /*- *----------------------------------------------------------------------- * CondE -- @@ -1109,7 +970,7 @@ CondF(Boolean doEval) static Token CondE(Boolean doEval) { - Token l, o; + Token l, o; l = CondF(doEval); if (l != TOK_ERROR) { @@ -1139,6 +1000,31 @@ CondE(Boolean doEval) return l; } +static CondEvalResult +do_Cond_EvalExpression(Boolean *value) +{ + + 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; + } + + return COND_INVALID; +} + /*- *----------------------------------------------------------------------- * Cond_EvalExpression -- @@ -1153,16 +1039,16 @@ CondE(Boolean doEval) * (*value) is set to the boolean value of the condition * * Side Effects: - * None. - * + * Any effects from evaluating the variables. *----------------------------------------------------------------------- */ -int -Cond_EvalExpression(const struct If *info, char *line, Boolean *value, int eprint, Boolean strictLHS) +CondEvalResult +Cond_EvalExpression(const struct If *info, char *line, Boolean *value, + int eprint, Boolean strictLHS) { static const struct If *dflt_info; const struct If *sv_if_info = if_info; - char *sv_condExpr = condExpr; + const char *sv_condExpr = condExpr; Token sv_condPushBack = condPushBack; int rval; @@ -1173,7 +1059,7 @@ Cond_EvalExpression(const struct If *info, char *line, Boolean *value, int eprin if (info == NULL && (info = dflt_info) == NULL) { /* Scan for the entry for .if - it can't be first */ - for (info = ifs; ; info++) + for (info = ifs;; info++) if (info->form[0] == 0) break; dflt_info = info; @@ -1196,32 +1082,7 @@ Cond_EvalExpression(const struct If *info, char *line, Boolean *value, int eprin return rval; } -static int -do_Cond_EvalExpression(Boolean *value) -{ - - 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; - } - - return COND_INVALID; -} - /*- *----------------------------------------------------------------------- * Cond_Eval -- @@ -1241,35 +1102,31 @@ do_Cond_EvalExpression(Boolean *value) * COND_SKIP if should skip lines after the conditional * COND_INVALID if not a valid conditional. * - * Side Effects: - * None. - * * Note that the states IF_ACTIVE and ELSE_ACTIVE are only different in order - * to detect splurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF) + * to detect spurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF), * otherwise .else could be treated as '.elif 1'. - * *----------------------------------------------------------------------- */ -int +CondEvalResult Cond_Eval(char *line) { -#define MAXIF 128 /* maximum depth of .if'ing */ -#define MAXIF_BUMP 32 /* how much to grow by */ + enum { MAXIF = 128 }; /* maximum depth of .if'ing */ + enum { MAXIF_BUMP = 32 }; /* how much to grow by */ enum if_states { IF_ACTIVE, /* .if or .elif part active */ ELSE_ACTIVE, /* .else part active */ SEARCH_FOR_ELIF, /* searching for .elif/else to execute */ - SKIP_TO_ELSE, /* has been true, but not seen '.else' */ + SKIP_TO_ELSE, /* has been true, but not seen '.else' */ SKIP_TO_ENDIF /* nothing else to execute */ }; static enum if_states *cond_state = NULL; static unsigned int max_if_depth = MAXIF; const struct If *ifp; - Boolean isElif; - Boolean value; - int level; /* Level at which to report errors. */ - enum if_states state; + Boolean isElif; + Boolean value; + int level; /* Level at which to report errors. */ + enum if_states state; level = PARSE_FATAL; if (!cond_state) { @@ -1292,7 +1149,8 @@ Cond_Eval(char *line) } /* Return state for previous conditional */ cond_depth--; - return cond_state[cond_depth] <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP; + return cond_state[cond_depth] <= ELSE_ACTIVE + ? COND_PARSE : COND_SKIP; } /* Quite likely this is 'else' or 'elif' */ @@ -1336,7 +1194,7 @@ Cond_Eval(char *line) * function is, etc. -- by looking in the table of valid "ifs" */ line += 2; - for (ifp = ifs; ; ifp++) { + for (ifp = ifs;; ifp++) { if (ifp->form == NULL) return COND_INVALID; if (istoken(ifp->form, line, ifp->formlen)) { @@ -1372,8 +1230,8 @@ Cond_Eval(char *line) * can need more than the default. */ max_if_depth += MAXIF_BUMP; - cond_state = bmake_realloc(cond_state, max_if_depth * - sizeof(*cond_state)); + cond_state = bmake_realloc(cond_state, + max_if_depth * sizeof(*cond_state)); } state = cond_state[cond_depth]; cond_depth++; @@ -1400,21 +1258,6 @@ Cond_Eval(char *line) return COND_PARSE; } - - -/*- - *----------------------------------------------------------------------- - * Cond_End -- - * Make sure everything's clean at the end of a makefile. - * - * Results: - * None. - * - * Side Effects: - * Parse_Error will be called if open conditionals are around. - * - *----------------------------------------------------------------------- - */ void Cond_restore_depth(unsigned int saved_depth) { @@ -1,4 +1,4 @@ -/* $NetBSD: dir.c,v 1.76 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: dir.c,v 1.135 2020/09/02 04:32:13 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -70,14 +70,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: dir.c,v 1.76 2020/07/03 08:13:23 rillig Exp $"; +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.76 2020/07/03 08:13:23 rillig Exp $"); +__RCSID("$NetBSD: dir.c,v 1.135 2020/09/02 04:32:13 rillig Exp $"); #endif #endif /* not lint */ #endif @@ -143,10 +143,20 @@ __RCSID("$NetBSD: dir.c,v 1.76 2020/07/03 08:13:23 rillig Exp $"); #include <stdio.h> #include "make.h" -#include "hash.h" #include "dir.h" #include "job.h" + +#define DIR_DEBUG0(fmt) \ + if (!DEBUG(DIR)) (void) 0; else fprintf(debug_file, fmt) + +#define DIR_DEBUG1(fmt, arg1) \ + if (!DEBUG(DIR)) (void) 0; else fprintf(debug_file, fmt, arg1) + +#define DIR_DEBUG2(fmt, arg1, arg2) \ + if (!DEBUG(DIR)) (void) 0; else fprintf(debug_file, fmt, arg1, arg2) + + /* * 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 @@ -217,37 +227,35 @@ __RCSID("$NetBSD: dir.c,v 1.76 2020/07/03 08:13:23 rillig Exp $"); * in a cache for when Dir_MTime was actually called. */ -Lst dirSearchPath; /* main search path */ +Lst dirSearchPath; /* main search path */ -static Lst openDirectories; /* the list of all open directories */ +static Lst openDirectories; /* the list of all open directories */ /* * Variables for gathering statistics on the efficiency of the hashing * mechanism. */ -static int hits, /* Found in directory cache */ - misses, /* Sad, but not evil misses */ - nearmisses, /* Found under search path */ - 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 - * look for . last */ -static Hash_Table mtimes; /* Results of doing a last-resort stat in - * Dir_FindFile -- if we have to go to the - * system to find the file, we might as well - * have its mtime on record. XXX: If this is done - * way early, there's a chance other rules will - * have already updated the file, in which case - * we'll update it again. Generally, there won't - * be two rules to update a single file, so this - * should be ok, but... */ - -static Hash_Table lmtimes; /* same as mtimes but for lstat */ - -static int DirFindName(const void *, const void *); -static int DirMatchFiles(const char *, Path *, Lst); +static int hits; /* Found in directory cache */ +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 + * look for . last */ + +/* Results of doing a last-resort stat in Dir_FindFile -- if we have to go to + * the system to find the file, we might as well have its mtime on record. + * + * XXX: If this is done way early, there's a chance other rules will have + * already updated the file, in which case we'll update it again. Generally, + * there won't be two rules to update a single file, so this should be ok, + * but... */ +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 *); @@ -259,23 +267,28 @@ static char *DirLookupAbs(Path *, const char *, const char *); /* - * We use stat(2) a lot, cache the results + * We use stat(2) a lot, cache the results. * mtime and mode are all we care about. */ struct cache_st { - time_t lmtime; /* lstat */ - time_t mtime; /* stat */ - mode_t mode; + time_t lmtime; /* lstat */ + time_t mtime; /* stat */ + mode_t mode; }; /* minimize changes below */ -#define CST_LSTAT 1 -#define CST_UPDATE 2 +typedef enum { + 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 stat *st, int flags) +cached_stats(Hash_Table *htp, const char *pathname, struct make_stat *mst, + CachedStatsFlags flags) { Hash_Entry *entry; + struct stat sys_st; struct cache_st *cst; int rc; @@ -284,83 +297,74 @@ cached_stats(Hash_Table *htp, const char *pathname, struct stat *st, int flags) entry = Hash_FindEntry(htp, pathname); - if (entry && (flags & CST_UPDATE) == 0) { - cst = entry->clientPtr; + if (entry && !(flags & CST_UPDATE)) { + cst = Hash_GetValue(entry); - memset(st, 0, sizeof(*st)); - st->st_mode = cst->mode; - st->st_mtime = (flags & CST_LSTAT) ? cst->lmtime : cst->mtime; - if (st->st_mtime) { - if (DEBUG(DIR)) { - fprintf(debug_file, "Using cached time %s for %s\n", - Targ_FmtTime(st->st_mtime), pathname); - } + mst->mst_mode = cst->mode; + mst->mst_mtime = (flags & CST_LSTAT) ? cst->lmtime : cst->mtime; + if (mst->mst_mtime) { + DIR_DEBUG2("Using cached time %s for %s\n", + Targ_FmtTime(mst->mst_mtime), pathname); return 0; } } - rc = (flags & CST_LSTAT) ? lstat(pathname, st) : stat(pathname, st); + rc = (flags & CST_LSTAT) + ? lstat(pathname, &sys_st) + : stat(pathname, &sys_st); if (rc == -1) return -1; - if (st->st_mtime == 0) - st->st_mtime = 1; /* avoid confusion with missing file */ + if (sys_st.st_mtime == 0) + sys_st.st_mtime = 1; /* avoid confusion with missing file */ + + mst->mst_mode = sys_st.st_mode; + mst->mst_mtime = sys_st.st_mtime; - if (!entry) + if (entry == NULL) entry = Hash_CreateEntry(htp, pathname, NULL); - if (!entry->clientPtr) { - entry->clientPtr = bmake_malloc(sizeof(*cst)); - memset(entry->clientPtr, 0, sizeof(*cst)); + if (Hash_GetValue(entry) == NULL) { + Hash_SetValue(entry, bmake_malloc(sizeof(*cst))); + memset(Hash_GetValue(entry), 0, sizeof(*cst)); } - cst = entry->clientPtr; - if ((flags & CST_LSTAT)) { - cst->lmtime = st->st_mtime; + cst = Hash_GetValue(entry); + if (flags & CST_LSTAT) { + cst->lmtime = sys_st.st_mtime; } else { - cst->mtime = st->st_mtime; - } - cst->mode = st->st_mode; - if (DEBUG(DIR)) { - fprintf(debug_file, " Caching %s for %s\n", - Targ_FmtTime(st->st_mtime), pathname); + cst->mtime = sys_st.st_mtime; } + cst->mode = sys_st.st_mode; + DIR_DEBUG2(" Caching %s for %s\n", + Targ_FmtTime(sys_st.st_mtime), pathname); return 0; } int -cached_stat(const char *pathname, void *st) +cached_stat(const char *pathname, struct make_stat *st) { return cached_stats(&mtimes, pathname, st, 0); } int -cached_lstat(const char *pathname, void *st) +cached_lstat(const char *pathname, struct make_stat *st) { return cached_stats(&lmtimes, pathname, st, CST_LSTAT); } -/*- - *----------------------------------------------------------------------- - * Dir_Init -- - * initialize things for this module - * - * Results: - * none - * - * Side Effects: - * some directories may be opened. - *----------------------------------------------------------------------- - */ +/* Initialize things for this module. */ void -Dir_Init(const char *cdname) +Dir_Init(void) +{ + dirSearchPath = Lst_Init(); + openDirectories = Lst_Init(); + Hash_InitTable(&mtimes, 0); + Hash_InitTable(&lmtimes, 0); +} + +void +Dir_InitDir(const char *cdname) { - if (!cdname) { - dirSearchPath = Lst_Init(FALSE); - openDirectories = Lst_Init(FALSE); - Hash_InitTable(&mtimes, 0); - Hash_InitTable(&lmtimes, 0); - return; - } Dir_InitCur(cdname); dotLast = bmake_malloc(sizeof(Path)); @@ -371,7 +375,7 @@ Dir_Init(const char *cdname) } /* - * Called by Dir_Init() and whenever .CURDIR is assigned to. + * Called by Dir_InitDir and whenever .CURDIR is assigned to. */ void Dir_InitCur(const char *cdname) @@ -397,18 +401,8 @@ Dir_InitCur(const char *cdname) } } -/*- - *----------------------------------------------------------------------- - * Dir_InitDot -- - * (re)initialize "dot" (current/object directory) path hash - * - * Results: - * none - * - * Side Effects: - * some directories may be opened. - *----------------------------------------------------------------------- - */ +/* (Re)initialize "dot" (current/object directory) path hash. + * Some directories may be opened. */ void Dir_InitDot(void) { @@ -416,8 +410,8 @@ Dir_InitDot(void) LstNode ln; /* Remove old entry from openDirectories, but do not destroy. */ - ln = Lst_Member(openDirectories, dot); - (void)Lst_Remove(openDirectories, ln); + ln = Lst_FindDatum(openDirectories, dot); + Lst_Remove(openDirectories, ln); } dot = Dir_AddDir(NULL, "."); @@ -432,21 +426,10 @@ Dir_InitDot(void) * to make sure it's not destroyed. */ dot->refCount += 1; - Dir_SetPATH(); /* initialize */ + Dir_SetPATH(); /* initialize */ } -/*- - *----------------------------------------------------------------------- - * Dir_End -- - * cleanup things for this module - * - * Results: - * none - * - * Side Effects: - * none - *----------------------------------------------------------------------- - */ +/* Clean up things for this module. */ void Dir_End(void) { @@ -460,9 +443,9 @@ Dir_End(void) Dir_Destroy(dotLast); Dir_Destroy(dot); Dir_ClearPath(dirSearchPath); - Lst_Destroy(dirSearchPath, NULL); + Lst_Free(dirSearchPath); Dir_ClearPath(openDirectories); - Lst_Destroy(openDirectories, NULL); + Lst_Free(openDirectories); Hash_DeleteTable(&mtimes); #endif } @@ -475,122 +458,100 @@ Dir_End(void) void Dir_SetPATH(void) { - LstNode ln; /* a list element */ + LstNode ln; /* a list element */ Path *p; - Boolean hasLastDot = FALSE; /* true we should search dot last */ + Boolean hasLastDot = FALSE; /* true if we should search dot last */ Var_Delete(".PATH", VAR_GLOBAL); - if (Lst_Open(dirSearchPath) == SUCCESS) { - if ((ln = Lst_First(dirSearchPath)) != NULL) { - p = (Path *)Lst_Datum(ln); - if (p == dotLast) { - hasLastDot = TRUE; - Var_Append(".PATH", dotLast->name, VAR_GLOBAL); - } + Lst_Open(dirSearchPath); + if ((ln = Lst_First(dirSearchPath)) != NULL) { + p = LstNode_Datum(ln); + if (p == dotLast) { + hasLastDot = TRUE; + Var_Append(".PATH", dotLast->name, VAR_GLOBAL); } + } - if (!hasLastDot) { - if (dot) - Var_Append(".PATH", dot->name, VAR_GLOBAL); - if (cur) - Var_Append(".PATH", cur->name, VAR_GLOBAL); - } + if (!hasLastDot) { + if (dot) + Var_Append(".PATH", dot->name, VAR_GLOBAL); + if (cur) + Var_Append(".PATH", cur->name, VAR_GLOBAL); + } - while ((ln = Lst_Next(dirSearchPath)) != NULL) { - p = (Path *)Lst_Datum(ln); - if (p == dotLast) - continue; - if (p == dot && hasLastDot) - continue; - Var_Append(".PATH", p->name, VAR_GLOBAL); - } + while ((ln = Lst_Next(dirSearchPath)) != NULL) { + p = LstNode_Datum(ln); + if (p == dotLast) + continue; + if (p == dot && hasLastDot) + continue; + Var_Append(".PATH", p->name, VAR_GLOBAL); + } - if (hasLastDot) { - if (dot) - Var_Append(".PATH", dot->name, VAR_GLOBAL); - if (cur) - Var_Append(".PATH", cur->name, VAR_GLOBAL); - } - Lst_Close(dirSearchPath); + if (hasLastDot) { + if (dot) + Var_Append(".PATH", dot->name, VAR_GLOBAL); + if (cur) + Var_Append(".PATH", cur->name, VAR_GLOBAL); } + Lst_Close(dirSearchPath); } -/*- - *----------------------------------------------------------------------- - * DirFindName -- - * 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. - * - * Input: - * p Current name - * dname Desired name - * - * Results: - * 0 if it is the same. Non-zero otherwise - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -static int -DirFindName(const void *p, const void *dname) +/* 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, dname); + return strcmp(((const Path *)p)->name, desiredName) == 0; } -/*- - *----------------------------------------------------------------------- - * Dir_HasWildcards -- - * see if the given name has any wildcard characters in it - * be careful not to expand unmatching brackets or braces. - * 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! +/* See if the given name has any wildcard characters in it. Be careful not to + * expand unmatching brackets or braces. + * + * 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 - * - * Side Effects: - * none - *----------------------------------------------------------------------- */ Boolean -Dir_HasWildcards(char *name) +Dir_HasWildcards(const char *name) { - char *cp; - int wild = 0, brace = 0, bracket = 0; + const char *cp; + Boolean wild = FALSE; + int braces = 0, brackets = 0; for (cp = name; *cp; cp++) { - switch(*cp) { + switch (*cp) { case '{': - brace++; - wild = 1; - break; + braces++; + wild = TRUE; + break; case '}': - brace--; - break; + braces--; + break; case '[': - bracket++; - wild = 1; - break; + brackets++; + wild = TRUE; + break; case ']': - bracket--; - break; + brackets--; + break; case '?': case '*': - wild = 1; - break; + wild = TRUE; + break; default: - break; + break; } } - return wild && bracket == 0 && brace == 0; + return wild && brackets == 0 && braces == 0; } /*- @@ -607,20 +568,17 @@ Dir_HasWildcards(char *name) * p Directory to search * expansion Place to store the results * - * Results: - * Always returns 0 - * * Side Effects: * File names are added to the expansions lst. The directory will be * fully hashed when this is done. *----------------------------------------------------------------------- */ -static int +static void DirMatchFiles(const char *pattern, Path *p, Lst 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 . */ + 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 . */ isDot = (*p->name == '.' && p->name[1] == '\0'); @@ -638,13 +596,75 @@ DirMatchFiles(const char *pattern, Path *p, Lst expansions) ((entry->name[0] != '.') || (pattern[0] == '.'))) { - (void)Lst_AtEnd(expansions, - (isDot ? bmake_strdup(entry->name) : - str_concat(p->name, entry->name, - STR_ADDSLASH))); + Lst_Append(expansions, + (isDot ? bmake_strdup(entry->name) : + str_concat3(p->name, "/", entry->name))); } } - return 0; +} + +/* Find the next closing brace in the string, taking nested braces into + * account. */ +static const char * +closing_brace(const char *p) +{ + int nest = 0; + while (*p != '\0') { + if (*p == '}' && nest == 0) + break; + if (*p == '{') + nest++; + if (*p == '}') + nest--; + p++; + } + return p; +} + +/* Find the next closing brace or comma in the string, taking nested braces + * into account. */ +static const char * +separator_comma(const char *p) +{ + int nest = 0; + while (*p != '\0') { + if ((*p == '}' || *p == ',') && nest == 0) + break; + if (*p == '{') + nest++; + if (*p == '}') + nest--; + p++; + } + return p; +} + +static Boolean +contains_wildcard(const char *p) +{ + for (; *p != '\0'; p++) { + switch (*p) { + case '*': + case '?': + case '{': + case '[': + return TRUE; + } + } + return FALSE; +} + +static char * +concat3(const char *a, size_t a_len, const char *b, size_t b_len, + const char *c, size_t c_len) +{ + size_t s_len = a_len + b_len + c_len; + char *s = bmake_malloc(s_len + 1); + memcpy(s, a, a_len); + memcpy(s + a_len, b, b_len); + memcpy(s + a_len + b_len, c, c_len); + s[s_len] = '\0'; + return s; } /*- @@ -672,91 +692,41 @@ DirMatchFiles(const char *pattern, Path *p, Lst expansions) static void DirExpandCurly(const char *word, const char *brace, Lst path, Lst expansions) { - const char *end; /* Character after the closing brace */ - const char *cp; /* Current position in brace clause */ - const char *start; /* Start of current piece of brace clause */ - int bracelevel; /* Number of braces we've seen. If we see a - * right brace when this is 0, we've hit the - * end of the clause. */ - char *file; /* Current expansion */ - int otherLen; /* The length of the other pieces of the - * expansion (chars before and after the - * clause in 'word') */ - char *cp2; /* Pointer for checking for wildcards in - * expansion before calling Dir_Expand */ - - start = brace+1; + const char *prefix, *middle, *piece, *middle_end, *suffix; + size_t prefix_len, suffix_len; - /* - * Find the end of the brace clause first, being wary of nested brace - * clauses. - */ - for (end = start, bracelevel = 0; *end != '\0'; end++) { - if (*end == '{') { - bracelevel++; - } else if ((*end == '}') && (bracelevel-- == 0)) { - break; - } - } - if (*end == '\0') { - Error("Unterminated {} clause \"%s\"", start); + /* Split the word into prefix '{' middle '}' suffix. */ + + middle = brace + 1; + middle_end = closing_brace(middle); + if (*middle_end == '\0') { + Error("Unterminated {} clause \"%s\"", middle); return; - } else { - end++; } - otherLen = brace - word + strlen(end); - for (cp = start; cp < end; cp++) { - /* - * Find the end of this piece of the clause. - */ - bracelevel = 0; - while (*cp != ',') { - if (*cp == '{') { - bracelevel++; - } else if ((*cp == '}') && (bracelevel-- <= 0)) { - break; - } - cp++; - } - /* - * Allocate room for the combination and install the three pieces. - */ - file = bmake_malloc(otherLen + cp - start + 1); - if (brace != word) { - strncpy(file, word, brace-word); - } - if (cp != start) { - strncpy(&file[brace-word], start, cp-start); - } - strcpy(&file[(brace-word)+(cp-start)], end); + prefix = word; + prefix_len = (size_t)(brace - prefix); + suffix = middle_end + 1; + suffix_len = strlen(suffix); - /* - * See if the result has any wildcards in it. If we find one, call - * Dir_Expand right away, telling it to place the result on our list - * of expansions. - */ - for (cp2 = file; *cp2 != '\0'; cp2++) { - switch(*cp2) { - case '*': - case '?': - case '{': - case '[': - Dir_Expand(file, path, expansions); - goto next; - } - } - if (*cp2 == '\0') { - /* - * Hit the end w/o finding any wildcards, so stick the expansion - * on the end of the list. - */ - (void)Lst_AtEnd(expansions, file); - } else { - next: + /* Split the middle into pieces, separated by commas. */ + + piece = middle; + while (piece < middle_end + 1) { + const char *piece_end = separator_comma(piece); + size_t piece_len = (size_t)(piece_end - piece); + + char *file = concat3(prefix, prefix_len, piece, piece_len, + suffix, suffix_len); + + if (contains_wildcard(file)) { + Dir_Expand(file, path, expansions); free(file); + } else { + Lst_Append(expansions, file); } - start = cp+1; + + piece = piece_end + 1; /* skip over the comma or closing brace */ } } @@ -784,32 +754,18 @@ DirExpandCurly(const char *word, const char *brace, Lst path, Lst expansions) static void DirExpandInt(const char *word, Lst path, Lst expansions) { - LstNode ln; /* Current node */ - Path *p; /* Directory in the node */ + LstNode ln; /* Current node */ - if (Lst_Open(path) == SUCCESS) { - while ((ln = Lst_Next(path)) != NULL) { - p = (Path *)Lst_Datum(ln); - DirMatchFiles(word, p, expansions); - } - Lst_Close(path); + Lst_Open(path); + while ((ln = Lst_Next(path)) != NULL) { + Path *p = LstNode_Datum(ln); + DirMatchFiles(word, p, expansions); } + Lst_Close(path); } -/*- - *----------------------------------------------------------------------- - * DirPrintWord -- - * Print a word in the list of expansions. Callback for Dir_Expand - * when DEBUG(DIR), via Lst_ForEach. - * - * Results: - * === 0 - * - * Side Effects: - * The passed word is printed, followed by a space. - * - *----------------------------------------------------------------------- - */ +/* 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) { @@ -836,16 +792,18 @@ DirPrintWord(void *word, void *dummy MAKE_ATTR_UNUSED) * * 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) { - const char *cp; + const char *cp; - if (DEBUG(DIR)) { - fprintf(debug_file, "Expanding \"%s\"... ", word); - } + assert(path != NULL); + assert(expansions != NULL); + + DIR_DEBUG1("Expanding \"%s\"... ", word); cp = strchr(word, '{'); if (cp) { @@ -872,13 +830,12 @@ Dir_Expand(const char *word, Lst path, Lst expansions) /* * Back up to the start of the component */ - char *dirpath; - while (cp > word && *cp != '/') { cp--; } if (cp != word) { char sc; + char *dirpath; /* * If the glob isn't in the first component, try and find * all the components up to the one with a wildcard. @@ -898,10 +855,10 @@ Dir_Expand(const char *word, Lst path, Lst expansions) char *dp = &dirpath[strlen(dirpath) - 1]; if (*dp == '/') *dp = '\0'; - path = Lst_Init(FALSE); + path = Lst_Init(); (void)Dir_AddDir(path, dirpath); - DirExpandInt(cp+1, path, expansions); - Lst_Destroy(path, NULL); + DirExpandInt(cp + 1, path, expansions); + Lst_Free(path); } } else { /* @@ -948,21 +905,17 @@ Dir_Expand(const char *word, Lst path, Lst expansions) */ static char * DirLookup(Path *p, const char *name MAKE_ATTR_UNUSED, const char *cp, - Boolean hasSlash MAKE_ATTR_UNUSED) + Boolean hasSlash MAKE_ATTR_UNUSED) { - char *file; /* the current filename to check */ + char *file; /* the current filename to check */ - if (DEBUG(DIR)) { - fprintf(debug_file, " %s ...\n", p->name); - } + DIR_DEBUG1(" %s ...\n", p->name); if (Hash_FindEntry(&p->files, cp) == NULL) return NULL; - file = str_concat(p->name, cp, STR_ADDSLASH); - if (DEBUG(DIR)) { - fprintf(debug_file, " returning %s\n", file); - } + file = str_concat3(p->name, "/", cp); + DIR_DEBUG1(" returning %s\n", file); p->hits += 1; hits += 1; return file; @@ -986,11 +939,11 @@ DirLookup(Path *p, const char *name MAKE_ATTR_UNUSED, const char *cp, static char * DirLookupSubdir(Path *p, const char *name) { - struct stat stb; /* Buffer for stat, if necessary */ - char *file; /* the current filename to check */ + struct make_stat mst; + char *file; /* the current filename to check */ if (p != dot) { - file = str_concat(p->name, name, STR_ADDSLASH); + file = str_concat3(p->name, "/", name); } else { /* * Checking in dot -- DON'T put a leading ./ on the thing. @@ -998,11 +951,9 @@ DirLookupSubdir(Path *p, const char *name) file = bmake_strdup(name); } - if (DEBUG(DIR)) { - fprintf(debug_file, "checking %s ...\n", file); - } + DIR_DEBUG1("checking %s ...\n", file); - if (cached_stat(file, &stb) == 0) { + if (cached_stat(file, &mst) == 0) { nearmisses += 1; return file; } @@ -1028,40 +979,34 @@ DirLookupSubdir(Path *p, const char *name) static char * DirLookupAbs(Path *p, const char *name, const char *cp) { - char *p1; /* pointer into p->name */ - const char *p2; /* pointer into name */ + char *p1; /* pointer into p->name */ + const char *p2; /* pointer into name */ - if (DEBUG(DIR)) { - fprintf(debug_file, " %s ...\n", p->name); - } + DIR_DEBUG1(" %s ...\n", p->name); - /* - * If the file has a leading path component and that component - * exactly matches the entire name of the current search - * 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++) { - continue; - } - if (*p1 != '\0' || p2 != cp - 1) { - return NULL; - } + /* + * If the file has a leading path component and that component + * exactly matches the entire name of the current search + * 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++) { + continue; + } + if (*p1 != '\0' || p2 != cp - 1) { + return NULL; + } - if (Hash_FindEntry(&p->files, cp) == NULL) { - if (DEBUG(DIR)) { - fprintf(debug_file, " must be here but isn't -- returning\n"); - } - /* Return empty string: terminates search */ - return bmake_strdup(""); - } + if (Hash_FindEntry(&p->files, cp) == NULL) { + DIR_DEBUG0(" must be here but isn't -- returning\n"); + /* Return empty string: terminates search */ + return bmake_strdup(""); + } - p->hits += 1; - hits += 1; - if (DEBUG(DIR)) { - fprintf(debug_file, " returning %s\n", name); - } - return bmake_strdup(name); + p->hits += 1; + hits += 1; + DIR_DEBUG1(" returning %s\n", name); + return bmake_strdup(name); } /*- @@ -1081,25 +1026,20 @@ static char * DirFindDot(Boolean hasSlash MAKE_ATTR_UNUSED, const char *name, const char *cp) { - if (Hash_FindEntry(&dot->files, cp) != NULL) { - if (DEBUG(DIR)) { - fprintf(debug_file, " in '.'\n"); - } - hits += 1; - dot->hits += 1; - return bmake_strdup(name); - } - if (cur && - Hash_FindEntry(&cur->files, cp) != NULL) { - if (DEBUG(DIR)) { - fprintf(debug_file, " in ${.CURDIR} = %s\n", cur->name); - } - hits += 1; - cur->hits += 1; - return str_concat(cur->name, cp, STR_ADDSLASH); - } + if (Hash_FindEntry(&dot->files, cp) != NULL) { + DIR_DEBUG0(" in '.'\n"); + hits += 1; + dot->hits += 1; + return bmake_strdup(name); + } + if (cur && Hash_FindEntry(&cur->files, cp) != NULL) { + DIR_DEBUG1(" in ${.CURDIR} = %s\n", cur->name); + hits += 1; + cur->hits += 1; + return str_concat3(cur->name, "/", cp); + } - return NULL; + return NULL; } /*- @@ -1127,14 +1067,14 @@ DirFindDot(Boolean hasSlash MAKE_ATTR_UNUSED, const char *name, const char *cp) char * Dir_FindFile(const char *name, Lst path) { - LstNode ln; /* a list element */ - 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 */ - Boolean hasSlash; /* true if 'name' contains a / */ - struct stat stb; /* Buffer for stat, if necessary */ - const char *trailing_dot = "."; + LstNode ln; /* a list element */ + 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 */ + Boolean hasSlash; /* true if 'name' contains a / */ + struct make_stat mst; /* Buffer for stat, if necessary */ + const char *trailing_dot = "."; /* * Find the final component of the name and note whether it has a @@ -1149,29 +1089,23 @@ Dir_FindFile(const char *name, Lst path) cp = name; } - if (DEBUG(DIR)) { - fprintf(debug_file, "Searching for %s ...", name); - } + DIR_DEBUG1("Searching for %s ...", name); - if (Lst_Open(path) == FAILURE) { - if (DEBUG(DIR)) { - fprintf(debug_file, "couldn't open path, file not found\n"); - } + if (path == NULL) { + DIR_DEBUG0("couldn't open path, file not found\n"); misses += 1; return NULL; } + Lst_Open(path); if ((ln = Lst_First(path)) != NULL) { - p = (Path *)Lst_Datum(ln); + p = LstNode_Datum(ln); if (p == dotLast) { hasLastDot = TRUE; - if (DEBUG(DIR)) - fprintf(debug_file, "[dot last]..."); + DIR_DEBUG0("[dot last]..."); } } - if (DEBUG(DIR)) { - fprintf(debug_file, "\n"); - } + DIR_DEBUG0("\n"); /* * If there's no leading directory components or if the leading @@ -1179,41 +1113,39 @@ Dir_FindFile(const char *name, Lst path) * of each of the directories on the search path. */ if (!hasSlash || (cp - 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 - * is found, we concatenate the directory name and the final - * component and return the resulting string. If we don't find any - * such thing, we go on to phase two... - * - * No matter what, we always look for the file in the current - * directory before anywhere else (unless we found the magic - * DOTLAST path, in which case we search it last) and we *do not* - * add the ./ to it if it exists. - * 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); - return file; - } + /* + * We look through all the directories on the path seeking one which + * contains the final component of the given name. If such a beast + * is found, we concatenate the directory name and the final + * component and return the resulting string. If we don't find any + * such thing, we go on to phase two... + * + * No matter what, we always look for the file in the current + * directory before anywhere else (unless we found the magic + * DOTLAST path, in which case we search it last) and we *do not* + * add the ./ to it if it exists. + * 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); + return file; + } - while ((ln = Lst_Next(path)) != NULL) { - p = (Path *)Lst_Datum(ln); - if (p == dotLast) - continue; - if ((file = DirLookup(p, name, cp, hasSlash)) != NULL) { - Lst_Close(path); - return file; - } + while ((ln = Lst_Next(path)) != NULL) { + p = LstNode_Datum(ln); + if (p == dotLast) + continue; + if ((file = DirLookup(p, name, cp, hasSlash)) != NULL) { + Lst_Close(path); + return file; } + } - if (hasLastDot && - (file = DirFindDot(hasSlash, name, cp)) != NULL) { - Lst_Close(path); - return file; - } + if (hasLastDot && (file = DirFindDot(hasSlash, name, cp)) != NULL) { + Lst_Close(path); + return file; + } } Lst_Close(path); @@ -1232,9 +1164,7 @@ Dir_FindFile(const char *name, Lst path) * This phase is only performed if the file is *not* absolute. */ if (!hasSlash) { - if (DEBUG(DIR)) { - fprintf(debug_file, " failed.\n"); - } + DIR_DEBUG0(" failed.\n"); misses += 1; return NULL; } @@ -1245,30 +1175,28 @@ Dir_FindFile(const char *name, Lst path) } if (name[0] != '/') { - Boolean checkedDot = FALSE; + Boolean checkedDot = FALSE; - if (DEBUG(DIR)) { - fprintf(debug_file, " Trying subdirectories...\n"); - } + DIR_DEBUG0(" Trying subdirectories...\n"); if (!hasLastDot) { - if (dot) { - checkedDot = TRUE; - if ((file = DirLookupSubdir(dot, name)) != NULL) - return file; - } - if (cur && (file = DirLookupSubdir(cur, name)) != NULL) - return file; + if (dot) { + checkedDot = TRUE; + if ((file = DirLookupSubdir(dot, name)) != NULL) + return file; + } + if (cur && (file = DirLookupSubdir(cur, name)) != NULL) + return file; } - (void)Lst_Open(path); + Lst_Open(path); while ((ln = Lst_Next(path)) != NULL) { - p = (Path *)Lst_Datum(ln); + p = LstNode_Datum(ln); if (p == dotLast) continue; if (p == dot) { - if (checkedDot) - continue; + if (checkedDot) + continue; checkedDot = TRUE; } if ((file = DirLookupSubdir(p, name)) != NULL) { @@ -1279,13 +1207,13 @@ Dir_FindFile(const char *name, Lst path) Lst_Close(path); if (hasLastDot) { - if (dot && !checkedDot) { - checkedDot = TRUE; - if ((file = DirLookupSubdir(dot, name)) != NULL) - return file; - } - if (cur && (file = DirLookupSubdir(cur, name)) != NULL) - return file; + if (dot && !checkedDot) { + checkedDot = TRUE; + if ((file = DirLookupSubdir(dot, name)) != NULL) + return file; + } + if (cur && (file = DirLookupSubdir(cur, name)) != NULL) + return file; } if (checkedDot) { @@ -1293,9 +1221,7 @@ Dir_FindFile(const char *name, Lst path) * Already checked by the given name, since . was in the path, * so no point in proceeding... */ - if (DEBUG(DIR)) { - fprintf(debug_file, " Checked . already, returning NULL\n"); - } + DIR_DEBUG0(" Checked . already, returning NULL\n"); return NULL; } @@ -1310,12 +1236,10 @@ Dir_FindFile(const char *name, Lst path) * file does not exist at all. This is signified by DirLookupAbs() * returning an empty string. */ - if (DEBUG(DIR)) { - fprintf(debug_file, " Trying exact path matches...\n"); - } + DIR_DEBUG0(" Trying exact path matches...\n"); - if (!hasLastDot && cur && ((file = DirLookupAbs(cur, name, cp)) - != NULL)) { + if (!hasLastDot && cur && + ((file = DirLookupAbs(cur, name, cp)) != NULL)) { if (file[0] == '\0') { free(file); return NULL; @@ -1323,9 +1247,9 @@ Dir_FindFile(const char *name, Lst path) return file; } - (void)Lst_Open(path); + Lst_Open(path); while ((ln = Lst_Next(path)) != NULL) { - p = (Path *)Lst_Datum(ln); + p = LstNode_Datum(ln); if (p == dotLast) continue; if ((file = DirLookupAbs(p, name, cp)) != NULL) { @@ -1339,8 +1263,8 @@ Dir_FindFile(const char *name, Lst path) } Lst_Close(path); - if (hasLastDot && cur && ((file = DirLookupAbs(cur, name, cp)) - != NULL)) { + if (hasLastDot && cur && + ((file = DirLookupAbs(cur, name, cp)) != NULL)) { if (file[0] == '\0') { free(file); return NULL; @@ -1380,7 +1304,7 @@ Dir_FindFile(const char *name, Lst path) if (ln == NULL) { return NULL; } else { - p = (Path *)Lst_Datum(ln); + p = LstNode_Datum(ln); } if (Hash_FindEntry(&p->files, cp) != NULL) { @@ -1389,18 +1313,14 @@ Dir_FindFile(const char *name, Lst path) return NULL; } #else /* !notdef */ - if (DEBUG(DIR)) { - fprintf(debug_file, " Looking for \"%s\" ...\n", name); - } + DIR_DEBUG1(" Looking for \"%s\" ...\n", name); bigmisses += 1; - if (cached_stat(name, &stb) == 0) { + if (cached_stat(name, &mst) == 0) { return bmake_strdup(name); } - if (DEBUG(DIR)) { - fprintf(debug_file, " failed. Returning NULL\n"); - } + DIR_DEBUG0(" failed. Returning NULL\n"); return NULL; #endif /* notdef */ } @@ -1416,7 +1336,7 @@ Dir_FindFile(const char *name, Lst path) * here starting directory * search_path the path we are looking for * result the result of a successful search is placed here - * rlen the length of the result buffer + * result_len the length of the result buffer * (typically MAXPATHLEN + 1) * * Results: @@ -1426,62 +1346,57 @@ Dir_FindFile(const char *name, Lst path) * Side Effects: *----------------------------------------------------------------------- */ -int -Dir_FindHereOrAbove(char *here, char *search_path, char *result, int rlen) { - - struct stat st; - char dirbase[MAXPATHLEN + 1], *db_end; - char try[MAXPATHLEN + 1], *try_end; - - /* copy out our starting point */ - snprintf(dirbase, sizeof(dirbase), "%s", here); - db_end = dirbase + strlen(dirbase); - - /* loop until we determine a result */ - while (1) { +Boolean +Dir_FindHereOrAbove(const char *here, const char *search_path, + char *result, int result_len) +{ + struct make_stat mst; + char dirbase[MAXPATHLEN + 1], *dirbase_end; + char try[MAXPATHLEN + 1], *try_end; - /* try and stat(2) it ... */ - snprintf(try, sizeof(try), "%s/%s", dirbase, search_path); - if (cached_stat(try, &st) != -1) { - /* - * success! if we found a file, chop off - * the filename so we return a directory. - */ - if ((st.st_mode & S_IFMT) != S_IFDIR) { - try_end = try + strlen(try); - while (try_end > try && *try_end != '/') - try_end--; - if (try_end > try) - *try_end = 0; /* chop! */ - } + /* copy out our starting point */ + snprintf(dirbase, sizeof(dirbase), "%s", here); + dirbase_end = dirbase + strlen(dirbase); - /* - * done! - */ - snprintf(result, rlen, "%s", try); - return 1; - } + /* loop until we determine a result */ + while (TRUE) { - /* - * nope, we didn't find it. if we used up dirbase we've - * reached the root and failed. - */ - if (db_end == dirbase) - break; /* failed! */ + /* try and stat(2) it ... */ + snprintf(try, sizeof(try), "%s/%s", dirbase, search_path); + if (cached_stat(try, &mst) != -1) { + /* + * success! if we found a file, chop off + * the filename so we return a directory. + */ + if ((mst.mst_mode & S_IFMT) != S_IFDIR) { + try_end = try + strlen(try); + while (try_end > try && *try_end != '/') + try_end--; + if (try_end > try) + *try_end = '\0'; /* chop! */ + } - /* - * truncate dirbase from the end to move up a dir - */ - while (db_end > dirbase && *db_end != '/') - db_end--; - *db_end = 0; /* chop! */ + snprintf(result, result_len, "%s", try); + return TRUE; + } - } /* while (1) */ + /* + * nope, we didn't find it. if we used up dirbase we've + * reached the root and failed. + */ + if (dirbase_end == dirbase) + break; /* failed! */ /* - * we failed... + * truncate dirbase from the end to move up a dir */ - return 0; + while (dirbase_end > dirbase && *dirbase_end != '/') + dirbase_end--; + *dirbase_end = '\0'; /* chop! */ + + } /* while (TRUE) */ + + return FALSE; } /*- @@ -1505,8 +1420,8 @@ Dir_FindHereOrAbove(char *here, char *search_path, char *result, int rlen) { int Dir_MTime(GNode *gn, Boolean recheck) { - char *fullName; /* the full pathname of name */ - struct stat stb; /* buffer for finding the mod time */ + char *fullName; /* the full pathname of name */ + struct make_stat mst; /* buffer for finding the mod time */ if (gn->type & OP_ARCHV) { return Arch_MTime(gn); @@ -1519,7 +1434,7 @@ Dir_MTime(GNode *gn, Boolean recheck) else { fullName = Dir_FindFile(gn->name, Suff_FindPath(gn)); if (fullName == NULL && gn->flags & FROM_DEPEND && - !Lst_IsEmpty(gn->iParents)) { + !Lst_IsEmpty(gn->implicitParents)) { char *cp; cp = strrchr(gn->name, '/'); @@ -1539,15 +1454,15 @@ Dir_MTime(GNode *gn, Boolean recheck) gn->path = bmake_strdup(fullName); if (!Job_RunTarget(".STALE", gn->fname)) fprintf(stdout, - "%s: %s, %d: ignoring stale %s for %s, " - "found %s\n", progname, gn->fname, gn->lineno, - makeDependfile, gn->name, fullName); + "%s: %s, %d: ignoring stale %s for %s, " + "found %s\n", progname, gn->fname, + gn->lineno, + makeDependfile, gn->name, fullName); } } } - if (DEBUG(DIR)) - fprintf(debug_file, "Found '%s' as '%s'\n", - gn->name, fullName ? fullName : "(not found)" ); + DIR_DEBUG2("Found '%s' as '%s'\n", + gn->name, fullName ? fullName : "(not found)"); } } else { fullName = gn->path; @@ -1557,13 +1472,13 @@ Dir_MTime(GNode *gn, Boolean recheck) fullName = bmake_strdup(gn->name); } - if (cached_stats(&mtimes, fullName, &stb, recheck ? CST_UPDATE : 0) < 0) { + if (cached_stats(&mtimes, fullName, &mst, recheck ? CST_UPDATE : 0) < 0) { if (gn->type & OP_MEMBER) { if (fullName != gn->path) free(fullName); return Arch_MemMTime(gn); } else { - stb.st_mtime = 0; + mst.mst_mtime = 0; } } @@ -1571,7 +1486,7 @@ Dir_MTime(GNode *gn, Boolean recheck) gn->path = fullName; } - gn->mtime = stb.st_mtime; + gn->mtime = mst.mst_mtime; return gn->mtime; } @@ -1585,6 +1500,8 @@ Dir_MTime(GNode *gn, Boolean recheck) * 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 * * Results: @@ -1598,78 +1515,70 @@ Dir_MTime(GNode *gn, Boolean recheck) Path * Dir_AddDir(Lst 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 */ + 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 */ - if (strcmp(name, ".DOTLAST") == 0) { - ln = Lst_Find(path, name, DirFindName); + if (path != NULL && strcmp(name, ".DOTLAST") == 0) { + ln = Lst_Find(path, DirFindName, name); if (ln != NULL) - return (Path *)Lst_Datum(ln); - else { - dotLast->refCount += 1; - (void)Lst_AtFront(path, dotLast); - } + return LstNode_Datum(ln); + + dotLast->refCount++; + Lst_Prepend(path, dotLast); } - if (path) - ln = Lst_Find(openDirectories, name, DirFindName); + if (path != NULL) + ln = Lst_Find(openDirectories, DirFindName, name); if (ln != NULL) { - p = (Path *)Lst_Datum(ln); - if (path && Lst_Member(path, p) == NULL) { + p = LstNode_Datum(ln); + if (Lst_FindDatum(path, p) == NULL) { p->refCount += 1; - (void)Lst_AtEnd(path, p); - } - } else { - if (DEBUG(DIR)) { - fprintf(debug_file, "Caching %s ...", name); + Lst_Append(path, p); } + return p; + } + + 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); + 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); - while ((dp = readdir(d)) != NULL) { + while ((dp = readdir(d)) != NULL) { #if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */ - /* - * The sun directory library doesn't check for a 0 inode - * (0-inode slots just take up space), so we have to do - * it ourselves. - */ - if (dp->d_fileno == 0) { - continue; - } -#endif /* sun && d_ino */ - (void)Hash_CreateEntry(&p->files, dp->d_name, NULL); + /* + * The sun directory library doesn't check for a 0 inode + * (0-inode slots just take up space), so we have to do + * it ourselves. + */ + if (dp->d_fileno == 0) { + continue; } - (void)closedir(d); - (void)Lst_AtEnd(openDirectories, p); - if (path != NULL) - (void)Lst_AtEnd(path, p); - } - if (DEBUG(DIR)) { - fprintf(debug_file, "done\n"); +#endif /* sun && d_ino */ + (void)Hash_CreateEntry(&p->files, dp->d_name, NULL); } + (void)closedir(d); + Lst_Append(openDirectories, p); + if (path != NULL) + Lst_Append(path, p); } + DIR_DEBUG0("done\n"); return p; } /*- *----------------------------------------------------------------------- * Dir_CopyDir -- - * Callback function for duplicating a search path via Lst_Duplicate. + * Callback function for duplicating a search path via Lst_Copy. * Ups the reference count for the directory. * * Results: * Returns the Path it was given. - * - * Side Effects: - * The refCount of the path is incremented. - * *----------------------------------------------------------------------- */ void * @@ -1704,25 +1613,23 @@ Dir_CopyDir(void *p) char * Dir_MakeFlags(const char *flag, Lst path) { - char *str; /* the string which will be returned */ - char *s1, *s2;/* the current directory preceded by 'flag' */ - LstNode ln; /* the node of the current directory */ - Path *p; /* the structure describing the current directory */ + Buffer buf; + LstNode ln; /* the node of the current directory */ - str = bmake_strdup(""); + Buf_Init(&buf, 0); - if (Lst_Open(path) == SUCCESS) { + if (path != NULL) { + Lst_Open(path); while ((ln = Lst_Next(path)) != NULL) { - p = (Path *)Lst_Datum(ln); - s2 = str_concat(flag, p->name, 0); - str = str_concat(s1 = str, s2, STR_ADDSPACE); - free(s1); - free(s2); + Path *p = LstNode_Datum(ln); + Buf_AddStr(&buf, " "); + Buf_AddStr(&buf, flag); + Buf_AddStr(&buf, p->name); } Lst_Close(path); } - return str; + return Buf_Destroy(&buf, FALSE); } /*- @@ -1746,14 +1653,14 @@ Dir_MakeFlags(const char *flag, Lst path) void Dir_Destroy(void *pp) { - Path *p = (Path *)pp; + Path *p = (Path *)pp; p->refCount -= 1; if (p->refCount == 0) { - LstNode ln; + LstNode ln; - ln = Lst_Member(openDirectories, p); - (void)Lst_Remove(openDirectories, ln); + ln = Lst_FindDatum(openDirectories, p); + Lst_Remove(openDirectories, ln); Hash_DeleteTable(&p->files); free(p->name); @@ -1781,9 +1688,8 @@ Dir_Destroy(void *pp) void Dir_ClearPath(Lst path) { - Path *p; while (!Lst_IsEmpty(path)) { - p = (Path *)Lst_DeQueue(path); + Path *p = Lst_Dequeue(path); Dir_Destroy(p); } } @@ -1811,37 +1717,43 @@ void Dir_Concat(Lst path1, Lst path2) { LstNode ln; - Path *p; + Path *p; - for (ln = Lst_First(path2); ln != NULL; ln = Lst_Succ(ln)) { - p = (Path *)Lst_Datum(ln); - if (Lst_Member(path1, p) == NULL) { + for (ln = Lst_First(path2); ln != NULL; ln = LstNode_Next(ln)) { + p = LstNode_Datum(ln); + if (Lst_FindDatum(path1, p) == NULL) { p->refCount += 1; - (void)Lst_AtEnd(path1, p); + Lst_Append(path1, p); } } } +static int +percentage(int num, int den) +{ + return den != 0 ? num * 100 / den : 0; +} + /********** DEBUG INFO **********/ void Dir_PrintDirectories(void) { - LstNode ln; - Path *p; + 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, - (hits+bigmisses+nearmisses ? - hits * 100 / (hits + bigmisses + nearmisses) : 0)); + 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"); - if (Lst_Open(openDirectories) == SUCCESS) { - while ((ln = Lst_Next(openDirectories)) != NULL) { - p = (Path *)Lst_Datum(ln); - fprintf(debug_file, "# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits); - } - Lst_Close(openDirectories); + + 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); } + Lst_Close(openDirectories); } static int @@ -1,4 +1,4 @@ -/* $NetBSD: dir.h,v 1.18 2017/05/31 22:02:06 maya Exp $ */ +/* $NetBSD: dir.h,v 1.23 2020/09/02 04:08:54 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -72,29 +72,34 @@ * from: @(#)dir.h 8.1 (Berkeley) 6/6/93 */ -/* dir.h -- - */ - #ifndef MAKE_DIR_H #define MAKE_DIR_H -typedef struct Path { +/* 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 * directory has been found */ - Hash_Table files; /* Hash table of files in directory */ + Hash_Table files; /* Hash set of files in directory */ } Path; -void Dir_Init(const char *); +struct make_stat { + time_t mst_mtime; + mode_t mst_mode; +}; + +void Dir_Init(void); +void Dir_InitDir(const char *); void Dir_InitCur(const char *); void Dir_InitDot(void); void Dir_End(void); void Dir_SetPATH(void); -Boolean Dir_HasWildcards(char *); +Boolean Dir_HasWildcards(const char *); void Dir_Expand(const char *, Lst, Lst); char *Dir_FindFile(const char *, Lst); -int Dir_FindHereOrAbove(char *, char *, char *, int); +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); @@ -103,6 +108,9 @@ void Dir_Concat(Lst, Lst); void Dir_PrintDirectories(void); void Dir_PrintPath(Lst); void Dir_Destroy(void *); -void * Dir_CopyDir(void *); +void *Dir_CopyDir(void *); + +int cached_lstat(const char *, struct make_stat *); +int cached_stat(const char *, struct make_stat *); #endif /* MAKE_DIR_H */ diff --git a/enum.c b/enum.c new file mode 100755 index 000000000000..9dec4f3a5f6a --- /dev/null +++ b/enum.c @@ -0,0 +1,100 @@ +/* $NetBSD: enum.c,v 1.6 2020/09/01 20:34:51 rillig Exp $ */ + +/* + Copyright (c) 2020 Roland Illig <rillig@NetBSD.org> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MAKE_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 <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "enum.h" + +/* Convert a bitset into a string representation, showing the names of the + * individual bits. + * + * Optionally, shortcuts for groups of bits can be added. To have an effect, + * they need to be listed before their individual bits. */ +const char * +Enum_FlagsToString(char *buf, size_t buf_size, + int value, const EnumToStringSpec *spec) +{ + const char *buf_start = buf; + const char *sep = ""; + size_t sep_len = 0; + + for (; spec->es_value != 0; spec++) { + size_t name_len; + + if ((value & spec->es_value) != spec->es_value) + continue; + value &= ~spec->es_value; + + assert(buf_size >= sep_len + 1); + memcpy(buf, sep, sep_len); + buf += sep_len; + buf_size -= sep_len; + + name_len = strlen(spec->es_name); + assert(buf_size >= name_len + 1); + memcpy(buf, spec->es_name, name_len); + buf += name_len; + buf_size -= name_len; + + sep = ENUM__SEP; + sep_len = sizeof ENUM__SEP - 1; + } + + /* If this assertion fails, the listed enum values are incomplete. */ + assert(value == 0); + + if (buf == buf_start) + return "none"; + + assert(buf_size >= 1); + buf[0] = '\0'; + return buf_start; +} + +/* Convert a fixed-value enum into a string representation. */ +const char * +Enum_ValueToString(int value, const EnumToStringSpec *spec) +{ + for (; spec->es_name[0] != '\0'; spec++) { + if (value == spec->es_value) + return spec->es_name; + } + abort(/* unknown enum value */); +} diff --git a/enum.h b/enum.h new file mode 100755 index 000000000000..d6a94f11e7cd --- /dev/null +++ b/enum.h @@ -0,0 +1,193 @@ +/* $NetBSD: enum.h,v 1.9 2020/09/01 20:34:51 rillig Exp $ */ + +/* + Copyright (c) 2020 Roland Illig <rillig@NetBSD.org> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MAKE_ENUM_H +#define MAKE_ENUM_H + +/* Generate string representations for bitmasks and simple enums. */ + +#include <stddef.h> + +typedef struct { + int es_value; + const char *es_name; +} EnumToStringSpec; + +const char *Enum_FlagsToString(char *, size_t, int, const EnumToStringSpec *); +const char *Enum_ValueToString(int, const EnumToStringSpec *); + +/* For Enum_FlagsToString, the separator between flags. */ +#define ENUM__SEP "|" + +/* Generate the string that joins all possible flags, to see how large the + * buffer must be. */ +#define ENUM__JOIN_STR_1(v1) \ + #v1 +#define ENUM__JOIN_STR_2(v1, v2) \ + ENUM__JOIN_STR_1(v1) ENUM__SEP \ + ENUM__JOIN_STR_1(v2) +#define ENUM__JOIN_STR_4(v1, v2, v3, v4) \ + ENUM__JOIN_STR_2(v1, v2) ENUM__SEP \ + ENUM__JOIN_STR_2(v3, v4) +#define ENUM__JOIN_STR_8(v1, v2, v3, v4, v5, v6, v7, v8) \ + ENUM__JOIN_STR_4(v1, v2, v3, v4) ENUM__SEP \ + ENUM__JOIN_STR_4(v5, v6, v7, v8) +#define ENUM__JOIN_STR_16(v01, v02, v03, v04, v05, v06, v07, v08, \ + v09, v10, v11, v12, v13, v14, v15, v16) \ + ENUM__JOIN_STR_8(v01, v02, v03, v04, v05, v06, v07, v08) ENUM__SEP \ + ENUM__JOIN_STR_8(v09, v10, v11, v12, v13, v14, v15, v16) + +#define ENUM__JOIN_2(part1, part2) \ + part1 ENUM__SEP part2 +#define ENUM__JOIN_3(part1, part2, part3) \ + part1 ENUM__SEP part2 ENUM__SEP part3 +#define ENUM__JOIN_4(part1, part2, part3, part4) \ + part1 ENUM__SEP part2 ENUM__SEP part3 ENUM__SEP part4 +#define ENUM__JOIN_5(part1, part2, part3, part4, part5) \ + part1 ENUM__SEP part2 ENUM__SEP part3 ENUM__SEP part4 ENUM__SEP part5 + +/* List the pairs of enum value and corresponding name. */ +#define ENUM__SPEC_1(v1) \ + { v1, #v1 } +#define ENUM__SPEC_2(v1, v2) \ + ENUM__SPEC_1(v1), \ + ENUM__SPEC_1(v2) +#define ENUM__SPEC_4(v1, v2, v3, v4) \ + ENUM__SPEC_2(v1, v2), \ + ENUM__SPEC_2(v3, v4) +#define ENUM__SPEC_8(v1, v2, v3, v4, v5, v6, v7, v8) \ + ENUM__SPEC_4(v1, v2, v3, v4), \ + ENUM__SPEC_4(v5, v6, v7, v8) +#define ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \ + v09, v10, v11, v12, v13, v14, v15, v16) \ + ENUM__SPEC_8(v01, v02, v03, v04, v05, v06, v07, v08), \ + ENUM__SPEC_8(v09, v10, v11, v12, v13, v14, v15, v16) + +#define ENUM__SPECS_2(part1, part2) \ + { part1, part2, { 0, "" } } +#define ENUM__SPECS_3(part1, part2, part3) \ + { part1, part2, part3, { 0, "" } } +#define ENUM__SPECS_4(part1, part2, part3, part4) \ + { part1, part2, part3, part4, { 0, "" } } +#define ENUM__SPECS_5(part1, part2, part3, part4, part5) \ + { part1, part2, part3, part4, part5, { 0, "" } } + +/* Declare the necessary data structures for calling Enum_ValueToString. */ +#define ENUM__VALUE_RTTI(typnam, specs) \ + static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs + +/* Declare the necessary data structures for calling Enum_FlagsToString. */ +#define ENUM__FLAGS_RTTI(typnam, specs, joined) \ + static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs; \ + enum { typnam ## _ ## ToStringSize = sizeof joined } + +/* Declare the necessary data structures for calling Enum_FlagsToString + * for an enum with 3 flags. */ +#define ENUM_FLAGS_RTTI_3(typnam, v1, v2, v3) \ + ENUM__FLAGS_RTTI(typnam, \ + ENUM__SPECS_2( \ + ENUM__SPEC_2(v1, v2), \ + ENUM__SPEC_1(v3)), \ + ENUM__JOIN_2( \ + ENUM__JOIN_STR_2(v1, v2), \ + ENUM__JOIN_STR_1(v3))) + +/* Declare the necessary data structures for calling Enum_FlagsToString + * for an enum with 8 flags. */ +#define ENUM_FLAGS_RTTI_8(typnam, v1, v2, v3, v4, v5, v6, v7, v8) \ + ENUM__FLAGS_RTTI(typnam, \ + ENUM__SPECS_2( \ + ENUM__SPEC_4(v1, v2, v3, v4), \ + ENUM__SPEC_4(v5, v6, v7, v8)), \ + ENUM__JOIN_2( \ + ENUM__JOIN_STR_4(v1, v2, v3, v4), \ + ENUM__JOIN_STR_4(v5, v6, v7, v8))) + +/* Declare the necessary data structures for calling Enum_ValueToString + * for an enum with 8 constants. */ +#define ENUM_VALUE_RTTI_8(typnam, v1, v2, v3, v4, v5, v6, v7, v8) \ + ENUM__VALUE_RTTI(typnam, \ + ENUM__SPECS_2( \ + ENUM__SPEC_4(v1, v2, v3, v4), \ + ENUM__SPEC_4(v5, v6, v7, v8))) + +/* Declare the necessary data structures for calling Enum_FlagsToString + * for an enum with 10 flags. */ +#define ENUM_FLAGS_RTTI_10(typnam, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) \ + ENUM__FLAGS_RTTI(typnam, \ + ENUM__SPECS_2( \ + ENUM__SPEC_8(v1, v2, v3, v4, v5, v6, v7, v8), \ + ENUM__SPEC_2(v9, v10)), \ + ENUM__JOIN_2( \ + ENUM__JOIN_STR_8(v1, v2, v3, v4, v5, v6, v7, v8), \ + ENUM__JOIN_STR_2(v9, v10))) + +/* Declare the necessary data structures for calling Enum_FlagsToString + * for an enum with 31 flags. */ +#define ENUM_FLAGS_RTTI_31(typnam, \ + v01, v02, v03, v04, v05, v06, v07, v08, \ + v09, v10, v11, v12, v13, v14, v15, v16, \ + v17, v18, v19, v20, v21, v22, v23, v24, \ + v25, v26, v27, v28, v29, v30, v31) \ + ENUM__FLAGS_RTTI(typnam, \ + ENUM__SPECS_5( \ + ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \ + v09, v10, v11, v12, v13, v14, v15, v16), \ + ENUM__SPEC_8(v17, v18, v19, v20, v21, v22, v23, v24), \ + ENUM__SPEC_4(v25, v26, v27, v28), \ + ENUM__SPEC_2(v29, v30), \ + ENUM__SPEC_1(v31)), \ + ENUM__JOIN_5( \ + ENUM__JOIN_STR_16(v01, v02, v03, v04, v05, v06, v07, v08, \ + v09, v10, v11, v12, v13, v14, v15, v16), \ + ENUM__JOIN_STR_8(v17, v18, v19, v20, v21, v22, v23, v24), \ + ENUM__JOIN_STR_4(v25, v26, v27, v28), \ + ENUM__JOIN_STR_2(v29, v30), \ + ENUM__JOIN_STR_1(v31))) + +/* Declare the necessary data structures for calling Enum_FlagsToString + * for an enum with 32 flags. */ +#define ENUM_FLAGS_RTTI_32(typnam, \ + v01, v02, v03, v04, v05, v06, v07, v08, \ + v09, v10, v11, v12, v13, v14, v15, v16, \ + v17, v18, v19, v20, v21, v22, v23, v24, \ + v25, v26, v27, v28, v29, v30, v31, v32) \ + ENUM__FLAGS_RTTI(typnam, \ + 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)), \ + ENUM__JOIN_2( \ + 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))) + +#endif @@ -1,4 +1,4 @@ -/* $NetBSD: for.c,v 1.54 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: for.c,v 1.67 2020/08/30 19:56:02 rillig Exp $ */ /* * Copyright (c) 1992, The Regents of the University of California. @@ -30,14 +30,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: for.c,v 1.54 2020/07/03 08:13:23 rillig Exp $"; +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.54 2020/07/03 08:13:23 rillig Exp $"); +__RCSID("$NetBSD: for.c,v 1.67 2020/08/30 19:56:02 rillig Exp $"); #endif #endif /* not lint */ #endif @@ -52,13 +52,7 @@ __RCSID("$NetBSD: for.c,v 1.54 2020/07/03 08:13:23 rillig Exp $"); * */ -#include <assert.h> -#include <ctype.h> - #include "make.h" -#include "hash.h" -#include "dir.h" -#include "buf.h" #include "strlist.h" #define FOR_SUB_ESCAPE_CHAR 1 @@ -83,34 +77,22 @@ __RCSID("$NetBSD: for.c,v 1.54 2020/07/03 08:13:23 rillig Exp $"); * For_Run. */ -static int forLevel = 0; /* Nesting level */ +static int forLevel = 0; /* Nesting level */ /* * State of a for loop. */ -typedef struct _For { - 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 { + Buffer buf; /* Body of loop */ + strlist_t vars; /* Iteration variables */ + strlist_t items; /* Substitution items */ + char *parse_buf; + int short_var; + int sub_next; } For; -static For *accumFor; /* Loop being accumulated */ - - - -static char * -make_str(const char *ptr, int len) -{ - char *new_ptr; +static For *accumFor; /* Loop being accumulated */ - new_ptr = bmake_malloc(len + 1); - memcpy(new_ptr, ptr, len); - new_ptr[len] = 0; - return new_ptr; -} static void For_Free(For *arg) @@ -148,14 +130,13 @@ For_Eval(char *line) { For *new_for; char *ptr = line, *sub; - int len; + size_t len; int escapes; unsigned char ch; - char **words, *word_buf; - int n, nwords; + Words words; /* Skip the '.' and any following whitespace */ - for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) + for (ptr++; *ptr && isspace((unsigned char)*ptr); ptr++) continue; /* @@ -163,8 +144,8 @@ For_Eval(char *line) * 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) { + !isspace((unsigned char)ptr[3])) { + if (ptr[0] == 'e' && strncmp(ptr + 1, "ndfor", 5) == 0) { Parse_Error(PARSE_FATAL, "for-less endfor"); return -1; } @@ -181,7 +162,7 @@ For_Eval(char *line) /* Grab the variables. Terminate on "in". */ for (;; ptr += len) { - while (*ptr && isspace((unsigned char) *ptr)) + while (*ptr && isspace((unsigned char)*ptr)) ptr++; if (*ptr == '\0') { Parse_Error(PARSE_FATAL, "missing `in' in for"); @@ -196,7 +177,7 @@ For_Eval(char *line) } if (len == 1) new_for->short_var = 1; - strlist_add_str(&new_for->vars, make_str(ptr, len), len); + strlist_add_str(&new_for->vars, bmake_strldup(ptr, len), len); } if (strlist_num(&new_for->vars) == 0) { @@ -205,7 +186,7 @@ For_Eval(char *line) return -1; } - while (*ptr && isspace((unsigned char) *ptr)) + while (*ptr && isspace((unsigned char)*ptr)) ptr++; /* @@ -216,23 +197,25 @@ For_Eval(char *line) * We can't do the escapes here - because we don't know whether * we are substuting into ${...} or $(...). */ - sub = Var_Subst(NULL, ptr, VAR_GLOBAL, VARF_WANTRES); + sub = Var_Subst(ptr, VAR_GLOBAL, VARE_WANTRES); /* * Split into words allowing for quoted strings. */ - words = brk_string(sub, &nwords, FALSE, &word_buf); + words = Str_Words(sub, FALSE); free(sub); - if (words != NULL) { - for (n = 0; n < nwords; n++) { - ptr = words[n]; + { + size_t n; + + for (n = 0; n < words.len; n++) { + ptr = words.words[n]; if (!*ptr) continue; escapes = 0; while ((ch = *ptr++)) { - switch(ch) { + switch (ch) { case ':': case '$': case '\\': @@ -250,17 +233,17 @@ For_Eval(char *line) * We have to dup words[n] to maintain the semantics of * strlist. */ - strlist_add_str(&new_for->items, bmake_strdup(words[n]), escapes); + strlist_add_str(&new_for->items, bmake_strdup(words.words[n]), + escapes); } - free(words); - free(word_buf); + Words_Free(words); if ((len = strlist_num(&new_for->items)) > 0 && len % (n = strlist_num(&new_for->vars))) { Parse_Error(PARSE_FATAL, - "Wrong number of words (%d) in .for substitution list" - " with %d vars", len, n); + "Wrong number of words (%zu) in .for substitution list" + " with %zu vars", len, n); /* * Return 'success' so that the body of the .for loop is * accumulated. @@ -288,49 +271,35 @@ For_Accum(char *line) if (*ptr == '.') { - for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) + for (ptr++; *ptr && isspace((unsigned char)*ptr); ptr++) continue; if (strncmp(ptr, "endfor", 6) == 0 && - (isspace((unsigned char) ptr[6]) || !ptr[6])) { + (isspace((unsigned char)ptr[6]) || !ptr[6])) { if (DEBUG(FOR)) (void)fprintf(debug_file, "For: end for %d\n", forLevel); if (--forLevel <= 0) return 0; } else if (strncmp(ptr, "for", 3) == 0 && - isspace((unsigned char) ptr[3])) { + isspace((unsigned char)ptr[3])) { forLevel++; if (DEBUG(FOR)) (void)fprintf(debug_file, "For: new loop %d\n", forLevel); } } - Buf_AddBytes(&accumFor->buf, strlen(line), line); + Buf_AddStr(&accumFor->buf, line); Buf_AddByte(&accumFor->buf, '\n'); return 1; } - -/*- - *----------------------------------------------------------------------- - * For_Run -- - * Run the for loop, imitating the actions of an include file - * - * Results: - * None. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -static int +static size_t for_var_len(const char *var) { char ch, var_start, var_end; int depth; - int len; + size_t len; var_start = *var; if (var_start == 0) @@ -360,24 +329,24 @@ for_var_len(const char *var) static void for_substitute(Buffer *cmds, strlist_t *items, unsigned int item_no, char ech) { - const char *item = strlist_str(items, item_no); - int len; 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))) { - Buf_AddBytes(cmds, strlen(item), item); + (ech == ')' ? ~FOR_SUB_ESCAPE_BRACE : ~FOR_SUB_ESCAPE_PAREN))) { + Buf_AddStr(cmds, item); return; } /* Escape ':', '$', '\\' and 'ech' - removed by :U processing */ while ((ch = *item++) != 0) { if (ch == '$') { - len = for_var_len(item); + size_t len = for_var_len(item); if (len != 0) { - Buf_AddBytes(cmds, len + 1, item - 1); + Buf_AddBytes(cmds, item - 1, len + 1); item += len; continue; } @@ -392,13 +361,14 @@ static char * For_Iterate(void *v_arg, size_t *ret_len) { For *arg = v_arg; - int i, len; + int i; char *var; char *cp; char *cmd_cp; char *body_end; char ch; Buffer cmds; + size_t cmd_len; if (arg->sub_next + strlist_num(&arg->vars) > strlist_num(&arg->items)) { /* No more iterations */ @@ -421,9 +391,9 @@ For_Iterate(void *v_arg, size_t *ret_len) * to contrive a makefile where an unwanted substitution happens. */ - cmd_cp = Buf_GetAll(&arg->buf, &len); - body_end = cmd_cp + len; - Buf_Init(&cmds, len + 256); + 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; @@ -431,15 +401,15 @@ For_Iterate(void *v_arg, size_t *ret_len) cp++; /* Check variable name against the .for loop variables */ STRLIST_FOREACH(var, &arg->vars, i) { - len = strlist_info(&arg->vars, i); - if (memcmp(cp, var, len) != 0) + size_t vlen = strlist_info(&arg->vars, i); + if (memcmp(cp, var, vlen) != 0) continue; - if (cp[len] != ':' && cp[len] != ech && cp[len] != '\\') + if (cp[vlen] != ':' && cp[vlen] != ech && cp[vlen] != '\\') continue; /* Found a variable match. Replace with :U<value> */ - Buf_AddBytes(&cmds, cp - cmd_cp, cmd_cp); - Buf_AddBytes(&cmds, 2, ":U"); - cp += len; + 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; @@ -457,15 +427,15 @@ For_Iterate(void *v_arg, size_t *ret_len) if (var[0] != ch || var[1] != 0) continue; /* Found a variable match. Replace with ${:U<value>} */ - Buf_AddBytes(&cmds, cp - cmd_cp, cmd_cp); - Buf_AddBytes(&cmds, 3, "{:U"); + Buf_AddBytesBetween(&cmds, cmd_cp, cp); + Buf_AddStr(&cmds, "{:U"); cmd_cp = ++cp; for_substitute(&cmds, &arg->items, arg->sub_next + i, /*{*/ '}'); - Buf_AddBytes(&cmds, 1, "}"); + Buf_AddByte(&cmds, '}'); break; } } - Buf_AddBytes(&cmds, body_end - cmd_cp, cmd_cp); + Buf_AddBytesBetween(&cmds, cmd_cp, body_end); cp = Buf_Destroy(&cmds, FALSE); if (DEBUG(FOR)) @@ -478,6 +448,7 @@ For_Iterate(void *v_arg, size_t *ret_len) return cp; } +/* Run the for loop, imitating the actions of an include file. */ void For_Run(int lineno) { @@ -487,9 +458,9 @@ For_Run(int lineno) accumFor = NULL; if (strlist_num(&arg->items) == 0) { - /* Nothing to expand - possibly due to an earlier syntax error. */ - For_Free(arg); - return; + /* Nothing to expand - possibly due to an earlier syntax error. */ + For_Free(arg); + return; } Parse_SetInput(NULL, lineno, -1, For_Iterate, arg); @@ -1,4 +1,4 @@ -/* $NetBSD: hash.c,v 1.22 2020/07/03 17:03:09 rillig Exp $ */ +/* $NetBSD: hash.c,v 1.29 2020/09/01 21:11:31 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -70,14 +70,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: hash.c,v 1.22 2020/07/03 17:03:09 rillig Exp $"; +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.22 2020/07/03 17:03:09 rillig Exp $"); +__RCSID("$NetBSD: hash.c,v 1.29 2020/09/01 21:11:31 rillig Exp $"); #endif #endif /* not lint */ #endif @@ -89,9 +89,7 @@ __RCSID("$NetBSD: hash.c,v 1.22 2020/07/03 17:03:09 rillig Exp $"); * table. Hash tables grow automatically as the amount of * information increases. */ -#include "sprite.h" #include "make.h" -#include "hash.h" /* * Forward references to local procedures that are used before they're @@ -107,29 +105,26 @@ static void RebuildTable(Hash_Table *); #define rebuildLimit 3 -/* - *--------------------------------------------------------- - * - * Hash_InitTable -- - * - * This routine just sets up the hash table. +/* The hash function(s) */ + +#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) + +#endif + +/* Sets up the hash table. * * Input: - * t Structure to to hold table. + * 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. - * - * Results: - * None. - * - * Side Effects: - * Memory is allocated for the initial bucket area. - * - *--------------------------------------------------------- */ - void Hash_InitTable(Hash_Table *t, int numBuckets) { @@ -146,118 +141,85 @@ Hash_InitTable(Hash_Table *t, int numBuckets) continue; } t->numEntries = 0; - t->size = i; - t->mask = i - 1; - t->bucketPtr = hp = bmake_malloc(sizeof(*hp) * i); + t->maxchain = 0; + t->bucketsSize = i; + t->bucketsMask = i - 1; + t->buckets = hp = bmake_malloc(sizeof(*hp) * i); while (--i >= 0) *hp++ = NULL; } -/* - *--------------------------------------------------------- - * - * Hash_DeleteTable -- - * - * This routine removes everything from a hash table - * and frees up the memory space it occupied (except for - * the space in the Hash_Table structure). - * - * Results: - * None. - * - * Side Effects: - * Lots of memory is freed up. - * - *--------------------------------------------------------- - */ - +/* Removes everything from the hash table and frees up the memory space it + * occupied (except for the space in the Hash_Table structure). */ void Hash_DeleteTable(Hash_Table *t) { struct Hash_Entry **hp, *h, *nexth = NULL; int i; - for (hp = t->bucketPtr, i = t->size; --i >= 0;) { + for (hp = t->buckets, i = t->bucketsSize; --i >= 0;) { for (h = *hp++; h != NULL; h = nexth) { nexth = h->next; free(h); } } - free(t->bucketPtr); + free(t->buckets); /* * Set up the hash table to cause memory faults on any future access * attempts until re-initialization. */ - t->bucketPtr = NULL; + t->buckets = NULL; } -/* - *--------------------------------------------------------- - * - * Hash_FindEntry -- - * - * Searches a hash table for an entry corresponding to key. +/* Searches the hash table for an entry corresponding to the key. * * Input: * t Hash table to search. * key A hash key. * * Results: - * The return value is a pointer to the entry for key, - * if key was present in the table. If key was not - * present, NULL is returned. - * - * Side Effects: - * None. - * - *--------------------------------------------------------- + * 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) { Hash_Entry *e; unsigned h; const char *p; + int chainlen; - if (t == NULL || t->bucketPtr == NULL) { + if (t == NULL || t->buckets == NULL) { return NULL; } - for (h = 0, p = key; *p;) - h = (h << 5) - h + *p++; + HASH(h, key, p); p = key; - for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next) + 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) - return e; - return NULL; + break; + } + if (chainlen > t->maxchain) + t->maxchain = chainlen; + return e; } -/* - *--------------------------------------------------------- - * - * Hash_CreateEntry -- - * - * Searches a hash table for an entry corresponding to - * key. If no entry is found, then one is created. +/* 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 in with TRUE if new entry created, + * newPtr Filled with TRUE if new entry created, * FALSE otherwise. - * - * Results: - * The return value is a pointer to the entry. If *newPtr - * isn't NULL, then *newPtr is filled in with TRUE if a - * new entry was created, and FALSE if an entry already existed - * with the given key. - * - * Side Effects: - * Memory may be allocated, and the hash buckets may be modified. - *--------------------------------------------------------- */ - Hash_Entry * Hash_CreateEntry(Hash_Table *t, const char *key, Boolean *newPtr) { @@ -265,33 +227,44 @@ Hash_CreateEntry(Hash_Table *t, const char *key, Boolean *newPtr) 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. */ - for (h = 0, p = key; *p;) - h = (h << 5) - h + *p++; + HASH(h, key, p); keylen = p - key; p = key; - for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next) { + 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; - return e; + break; } } + 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). */ - if (t->numEntries >= rebuildLimit * t->size) + if (t->numEntries >= rebuildLimit * t->bucketsSize) RebuildTable(t); e = bmake_malloc(sizeof(*e) + keylen); - hp = &t->bucketPtr[h & t->mask]; + hp = &t->buckets[h & t->bucketsMask]; e->next = *hp; *hp = e; Hash_SetValue(e, NULL); @@ -304,23 +277,7 @@ Hash_CreateEntry(Hash_Table *t, const char *key, Boolean *newPtr) return e; } -/* - *--------------------------------------------------------- - * - * Hash_DeleteEntry -- - * - * Delete the given hash table entry and free memory associated with - * it. - * - * Results: - * None. - * - * Side Effects: - * Hash chain that entry lives in is modified and memory is freed. - * - *--------------------------------------------------------- - */ - +/* Delete the given hash table entry and free memory associated with it. */ void Hash_DeleteEntry(Hash_Table *t, Hash_Entry *e) { @@ -328,7 +285,7 @@ Hash_DeleteEntry(Hash_Table *t, Hash_Entry *e) if (e == NULL) return; - for (hp = &t->bucketPtr[e->namehash & t->mask]; + for (hp = &t->buckets[e->namehash & t->bucketsMask]; (p = *hp) != NULL; hp = &p->next) { if (p == e) { *hp = p->next; @@ -341,12 +298,7 @@ Hash_DeleteEntry(Hash_Table *t, Hash_Entry *e) abort(); } -/* - *--------------------------------------------------------- - * - * Hash_EnumFirst -- - * This procedure sets things up for a complete search - * of all entries recorded in the hash table. +/* Sets things up for enumerating all entries in the hash table. * * Input: * t Table to be searched. @@ -355,57 +307,34 @@ Hash_DeleteEntry(Hash_Table *t, Hash_Entry *e) * Results: * The return value is the address of the first entry in * the hash table, or NULL if the table is empty. - * - * Side Effects: - * The information in searchPtr is initialized so that successive - * calls to Hash_Next will return successive HashEntry's - * from the table. - * - *--------------------------------------------------------- */ - Hash_Entry * Hash_EnumFirst(Hash_Table *t, Hash_Search *searchPtr) { - searchPtr->tablePtr = t; - searchPtr->nextIndex = 0; - searchPtr->hashEntryPtr = NULL; + searchPtr->table = t; + searchPtr->nextBucket = 0; + searchPtr->entry = NULL; return Hash_EnumNext(searchPtr); } -/* - *--------------------------------------------------------- - * - * Hash_EnumNext -- - * This procedure returns successive entries in the hash table. +/* 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. - * - * Results: - * The return value is a pointer to the next HashEntry - * in the table, or NULL when the end of the table is - * reached. - * - * Side Effects: - * The information in searchPtr is modified to advance to the - * next entry. - * - *--------------------------------------------------------- */ - Hash_Entry * Hash_EnumNext(Hash_Search *searchPtr) { Hash_Entry *e; - Hash_Table *t = searchPtr->tablePtr; + Hash_Table *t = searchPtr->table; /* - * The hashEntryPtr field points to the most recently returned - * entry, or is nil if we are starting up. If not nil, we have + * 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->hashEntryPtr; + e = searchPtr->entry; if (e != NULL) e = e->next; /* @@ -413,59 +342,49 @@ Hash_EnumNext(Hash_Search *searchPtr) * find the next nonempty chain. */ while (e == NULL) { - if (searchPtr->nextIndex >= t->size) + if (searchPtr->nextBucket >= t->bucketsSize) return NULL; - e = t->bucketPtr[searchPtr->nextIndex++]; + e = t->buckets[searchPtr->nextBucket++]; } - searchPtr->hashEntryPtr = e; + searchPtr->entry = e; return e; } -/* - *--------------------------------------------------------- - * - * RebuildTable -- - * This local routine makes a new hash table that - * is larger than the old one. - * - * Results: - * None. - * - * Side Effects: - * The entire hash table is moved, so any bucket numbers - * from the old table are invalid. - * - *--------------------------------------------------------- - */ - +/* 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; + Hash_Entry **oldhp; int oldsize; - oldhp = t->bucketPtr; - oldsize = i = t->size; + oldhp = t->buckets; + oldsize = i = t->bucketsSize; i <<= 1; - t->size = i; - t->mask = mask = i - 1; - t->bucketPtr = hp = bmake_malloc(sizeof(*hp) * i); + 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->bucketPtr[e->namehash & mask]; + 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; } -void Hash_ForEach(Hash_Table *t, void (*action)(void *, void *), void *data) +void +Hash_ForEach(Hash_Table *t, void (*action)(void *, void *), void *data) { Hash_Search search; Hash_Entry *e; @@ -475,3 +394,11 @@ void Hash_ForEach(Hash_Table *t, void (*action)(void *, void *), void *data) e = Hash_EnumNext(&search)) action(Hash_GetValue(e), data); } + +void +Hash_DebugStats(Hash_Table *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); +} @@ -1,4 +1,4 @@ -/* $NetBSD: hash.h,v 1.13 2020/07/03 17:03:09 rillig Exp $ */ +/* $NetBSD: hash.h,v 1.21 2020/09/01 21:11:31 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -72,71 +72,51 @@ * from: @(#)hash.h 8.1 (Berkeley) 6/6/93 */ -/* hash.h -- - * - * This file contains definitions used by the hash module, - * which maintains hash tables. - */ - -#ifndef _HASH_H -#define _HASH_H +/* Hash tables with strings as keys and arbitrary pointers as values. */ -/* - * The following defines one entry in the hash table. - */ +#ifndef MAKE_HASH_H +#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 associated with the same - * bucket. */ - void *clientPtr; /* Arbitrary pointer */ - unsigned namehash; /* hash value of key */ - char name[1]; /* key string */ + struct Hash_Entry *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; +/* The hash table containing the entries. */ typedef struct Hash_Table { - struct Hash_Entry **bucketPtr;/* Pointers to Hash_Entry, one - * for each bucket in the table. */ - int size; /* Actual size of array. */ + Hash_Entry **buckets; /* Pointers to Hash_Entry, one + * for each bucket in the table. */ + int bucketsSize; int numEntries; /* Number of entries in the table. */ - int mask; /* Used to select bits for hashing. */ + int bucketsMask; /* Used to select the bucket for a hash. */ + int maxchain; /* max length of chain detected */ } Hash_Table; /* * The following structure is used by the searching routines * to record where we are in the search. */ - typedef struct Hash_Search { - Hash_Table *tablePtr; /* Table being searched. */ - int nextIndex; /* Next bucket to check (after current). */ - Hash_Entry *hashEntryPtr; /* Next entry to check in current bucket. */ + 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; -/* - * Macros. - */ - -/* - * void * Hash_GetValue(h) - * Hash_Entry *h; - */ - -#define Hash_GetValue(h) ((h)->clientPtr) - -/* - * Hash_SetValue(h, val); - * Hash_Entry *h; - * char *val; - */ - -#define Hash_SetValue(h, val) ((h)->clientPtr = (val)) - -/* - * Hash_Size(n) returns the number of words in an object of n bytes - */ +static inline void * MAKE_ATTR_UNUSED +Hash_GetValue(Hash_Entry *h) +{ + return h->value; +} -#define Hash_Size(n) (((n) + sizeof (int) - 1) / sizeof (int)) +static inline void MAKE_ATTR_UNUSED +Hash_SetValue(Hash_Entry *h, void *datum) +{ + h->value = datum; +} void Hash_InitTable(Hash_Table *, int); void Hash_DeleteTable(Hash_Table *); @@ -146,5 +126,6 @@ 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 *); -#endif /* _HASH_H */ +#endif /* MAKE_HASH_H */ @@ -1,4 +1,4 @@ -/* $NetBSD: job.c,v 1.201 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: job.c,v 1.227 2020/08/30 19:56:02 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -70,14 +70,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: job.c,v 1.201 2020/07/03 08:13:23 rillig Exp $"; +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.201 2020/07/03 08:13:23 rillig Exp $"); +__RCSID("$NetBSD: job.c,v 1.227 2020/08/30 19:56:02 rillig Exp $"); #endif #endif /* not lint */ #endif @@ -112,7 +112,7 @@ __RCSID("$NetBSD: job.c,v 1.201 2020/07/03 08:13:23 rillig Exp $"); * * Job_ParseShell Given the line following a .SHELL target, parse * the line as a shell specification. Returns - * FAILURE if the spec was incorrect. + * FALSE if the spec was incorrect. * * Job_Finish Perform any final processing which needs doing. * This includes the execution of any commands @@ -142,7 +142,6 @@ __RCSID("$NetBSD: job.c,v 1.201 2020/07/03 08:13:23 rillig Exp $"); #include <sys/time.h> #include "wait.h" -#include <assert.h> #include <errno.h> #if !defined(USE_SELECT) && defined(HAVE_POLL_H) #include <poll.h> @@ -155,15 +154,12 @@ __RCSID("$NetBSD: job.c,v 1.201 2020/07/03 08:13:23 rillig Exp $"); #endif #endif #include <signal.h> -#include <stdio.h> -#include <string.h> #include <utime.h> #if defined(HAVE_SYS_SOCKET_H) # include <sys/socket.h> #endif #include "make.h" -#include "hash.h" #include "dir.h" #include "job.h" #include "pathnames.h" @@ -184,7 +180,6 @@ static int aborting = 0; /* why is the make aborting? */ * this tracks the number of tokens currently "out" to build jobs. */ int jobTokensRunning = 0; -int not_parallel = 0; /* set if .NOT_PARALLEL */ /* * XXX: Avoid SunOS bug... FILENO() is fp->_file, and file @@ -311,9 +306,9 @@ static Shell *commandShell = &shells[DEFSHELL_INDEX]; /* this is the shell to * Job_ParseShell function */ const char *shellPath = NULL, /* full pathname of * executable image */ - *shellName = NULL; /* last component of shell */ + *shellName = NULL; /* last component of shell */ char *shellErrFlag = NULL; -static const char *shellArgv = NULL; /* Custom shell args */ +static char *shellArgv = NULL; /* Custom shell args */ STATIC Job *job_table; /* The structures that describe them */ @@ -370,11 +365,6 @@ static void JobSigLock(sigset_t *); static void JobSigUnlock(sigset_t *); static void JobSigReset(void); -#if !defined(MALLOC_OPTIONS) -# define MALLOC_OPTIONS "A" -#endif -const char *malloc_options= MALLOC_OPTIONS; - static unsigned nfds_per_job(void) { @@ -723,15 +713,14 @@ JobPrintCommand(void *cmdp, void *jobp) char *escCmd = NULL; /* Command with quotes/backticks escaped */ char *cmd = (char *)cmdp; Job *job = (Job *)jobp; - int i, j; noSpecials = NoExecute(job->node); if (strcmp(cmd, "...") == 0) { job->node->type |= OP_SAVE_CMDS; if ((job->flags & JOB_IGNDOTS) == 0) { - job->tailCmds = Lst_Succ(Lst_Member(job->node->commands, - cmd)); + LstNode dotsNode = Lst_FindDatum(job->node->commands, cmd); + job->tailCmds = dotsNode != NULL ? LstNode_Next(dotsNode) : NULL; return 1; } return 0; @@ -745,7 +734,7 @@ JobPrintCommand(void *cmdp, void *jobp) numCommands += 1; - cmdStart = cmd = Var_Subst(NULL, cmd, job->node, VARF_WANTRES); + cmdStart = cmd = Var_Subst(cmd, job->node, VARE_WANTRES); cmdTemplate = "%s\n"; @@ -785,15 +774,17 @@ JobPrintCommand(void *cmdp, void *jobp) */ 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]; + 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; + escCmd[j] = '\0'; } if (shutUp) { @@ -926,15 +917,15 @@ JobPrintCommand(void *cmdp, void *jobp) * Always returns 0 * * Side Effects: - * The command is tacked onto the end of postCommands's commands list. + * The command is tacked onto the end of postCommands' commands list. * *----------------------------------------------------------------------- */ static int JobSaveCommand(void *cmd, void *gn) { - cmd = Var_Subst(NULL, (char *)cmd, (GNode *)gn, VARF_WANTRES); - (void)Lst_AtEnd(postCommands->commands, cmd); + cmd = Var_Subst((char *)cmd, (GNode *)gn, VARE_WANTRES); + Lst_Append(postCommands->commands, cmd); return 0; } @@ -1124,7 +1115,7 @@ JobFinish (Job *job, WAIT_T status) if (job->tailCmds != NULL) { Lst_ForEachFrom(job->node->commands, job->tailCmds, JobSaveCommand, - job->node); + job->node); } job->node->made = MADE; if (!(job->flags & JOB_SPECIAL)) @@ -1275,7 +1266,7 @@ Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...)) */ Make_HandleUse(DEFAULT, gn); Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), gn); - free(p1); + 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 @@ -1302,7 +1293,7 @@ Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...)) (void)fprintf(stdout, "%s%s %s (continuing)\n", progname, msg, gn->name); (void)fflush(stdout); - return FALSE; + return FALSE; } else { (*abortProc)("%s%s %s. Stop", progname, msg, gn->name); return FALSE; @@ -1345,7 +1336,7 @@ JobExec(Job *job, char **argv) for (i = 0; argv[i] != NULL; i++) { (void)fprintf(debug_file, "%s ", argv[i]); } - (void)fprintf(debug_file, "\n"); + (void)fprintf(debug_file, "\n"); } /* @@ -1719,7 +1710,7 @@ JobStart(GNode *gn, int flags) * up the graph. */ job->cmdFILE = stdout; - Job_Touch(gn, job->flags&JOB_SILENT); + Job_Touch(gn, job->flags&JOB_SILENT); noExec = TRUE; } /* Just in case it isn't already... */ @@ -1748,8 +1739,8 @@ JobStart(GNode *gn, int flags) if (cmdsOK && aborting == 0) { if (job->tailCmds != NULL) { Lst_ForEachFrom(job->node->commands, job->tailCmds, - JobSaveCommand, - job->node); + JobSaveCommand, + job->node); } job->node->made = MADE; Make_Update(job->node); @@ -1990,8 +1981,8 @@ JobRun(GNode *targ) * and .INTERRUPT job in the parallel job module. This has * the nice side effect that it avoids a lot of other problems. */ - Lst lst = Lst_Init(FALSE); - Lst_AtEnd(lst, targ); + Lst lst = Lst_Init(); + Lst_Append(lst, targ); (void)Make_Run(lst); Lst_Destroy(lst, NULL); JobStart(targ, JOB_SPECIAL); @@ -2218,8 +2209,9 @@ Shell_Init(void) shellName++; } else #endif - shellPath = str_concat(_PATH_DEFSHELLDIR, shellName, STR_ADDSLASH); + shellPath = str_concat3(_PATH_DEFSHELLDIR, "/", shellName); } + Var_Set_with_flags(".SHELL", shellPath, VAR_CMD, VAR_SET_READONLY); if (commandShell->exit == NULL) { commandShell->exit = ""; } @@ -2253,38 +2245,23 @@ Shell_Init(void) const char * Shell_GetNewline(void) { - return commandShell->newline; } void Job_SetPrefix(void) { - if (targPrefix) { free(targPrefix); } else if (!Var_Exists(MAKE_JOB_PREFIX, VAR_GLOBAL)) { Var_Set(MAKE_JOB_PREFIX, "---", VAR_GLOBAL); } - targPrefix = Var_Subst(NULL, "${" MAKE_JOB_PREFIX "}", - VAR_GLOBAL, VARF_WANTRES); + targPrefix = Var_Subst("${" MAKE_JOB_PREFIX "}", + VAR_GLOBAL, VARE_WANTRES); } -/*- - *----------------------------------------------------------------------- - * Job_Init -- - * Initialize the process module - * - * Input: - * - * Results: - * none - * - * Side Effects: - * lists and counters are initialized - *----------------------------------------------------------------------- - */ +/* Initialize the process module. */ void Job_Init(void) { @@ -2390,19 +2367,7 @@ static void JobSigReset(void) (void)bmake_signal(SIGCHLD, SIG_DFL); } -/*- - *----------------------------------------------------------------------- - * JobMatchShell -- - * Find a shell in 'shells' given its name. - * - * Results: - * A pointer to the Shell structure. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ +/* Find a shell in 'shells' given its name, or return NULL. */ static Shell * JobMatchShell(const char *name) { @@ -2425,7 +2390,7 @@ JobMatchShell(const char *name) * line The shell spec * * Results: - * FAILURE if the specification was incorrect. + * FALSE if the specification was incorrect. * * Side Effects: * commandShell points to a Shell structure (either predefined or @@ -2462,12 +2427,13 @@ JobMatchShell(const char *name) * *----------------------------------------------------------------------- */ -ReturnStatus +Boolean Job_ParseShell(char *line) { + Words wordsList; char **words; char **argv; - int argc; + size_t argc; char *path; Shell newShell; Boolean fullSpec = FALSE; @@ -2477,17 +2443,20 @@ Job_ParseShell(char *line) line++; } - free(UNCONST(shellArgv)); + free(shellArgv); memset(&newShell, 0, sizeof(newShell)); /* * Parse the specification by keyword */ - words = brk_string(line, &argc, TRUE, &path); + wordsList = Str_Words(line, TRUE); + words = wordsList.words; + argc = wordsList.len; + path = wordsList.freeIt; if (words == NULL) { Error("Unterminated quoted string [%s]", line); - return FAILURE; + return FALSE; } shellArgv = path; @@ -2526,7 +2495,7 @@ Job_ParseShell(char *line) Parse_Error(PARSE_FATAL, "Unknown keyword \"%s\"", *argv); free(words); - return FAILURE; + return FALSE; } fullSpec = TRUE; } @@ -2542,13 +2511,13 @@ Job_ParseShell(char *line) if (newShell.name == NULL) { Parse_Error(PARSE_FATAL, "Neither path nor name specified"); free(words); - return FAILURE; + return FALSE; } else { if ((sh = JobMatchShell(newShell.name)) == NULL) { Parse_Error(PARSE_WARNING, "%s: No matching shell", newShell.name); free(words); - return FAILURE; + return FALSE; } commandShell = sh; shellName = newShell.name; @@ -2584,7 +2553,7 @@ Job_ParseShell(char *line) Parse_Error(PARSE_WARNING, "%s: No matching shell", shellName); free(words); - return FAILURE; + return FALSE; } commandShell = sh; } else { @@ -2613,7 +2582,7 @@ Job_ParseShell(char *line) * shell specification. */ free(words); - return SUCCESS; + return TRUE; } /*- @@ -2790,7 +2759,6 @@ Job_AbortAll(void) continue; } - /*- *----------------------------------------------------------------------- * JobRestartJobs -- @@ -3125,7 +3093,7 @@ emul_poll(struct pollfd *fd, int nfd, int timeout) usecs = timeout * 1000; tv.tv_sec = usecs / 1000000; tv.tv_usec = usecs % 1000000; - tvp = &tv; + tvp = &tv; } nselect = select(maxfd + 1, &rfds, &wfds, 0, tvp); @@ -1,4 +1,4 @@ -/* $NetBSD: job.h,v 1.43 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: job.h,v 1.47 2020/08/29 12:20:17 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -77,8 +77,8 @@ * job.h -- * Definitions pertaining to the running of jobs in parallel mode. */ -#ifndef _JOB_H_ -#define _JOB_H_ +#ifndef MAKE_JOB_H +#define MAKE_JOB_H #define TMPPAT "makeXXXXXX" /* relative to tmpdir */ @@ -110,7 +110,6 @@ emul_poll(struct pollfd *fd, int nfd, int timeout); */ #define POLL_MSEC 5000 - /*- * Job Table definitions. * @@ -164,7 +163,7 @@ typedef struct Job { * commands */ #define JOB_TRACED 0x400 /* we've sent 'set -x' */ - int jobPipe[2]; /* Pipe for readind output from job */ + int jobPipe[2]; /* Pipe for reading output from job */ struct pollfd *inPollfd; /* pollfd associated with inPipe */ char outBuf[JOB_BUFSIZE + 1]; /* Buffer for storing the output of the @@ -179,7 +178,6 @@ typedef struct Job { #define inPipe jobPipe[0] #define outPipe jobPipe[1] - /*- * Shell Specifications: * Each shell type has associated with it the following information: @@ -252,23 +250,20 @@ void Shell_Init(void); const char *Shell_GetNewline(void); void Job_Touch(GNode *, Boolean); Boolean Job_CheckCommands(GNode *, void (*abortProc )(const char *, ...)); -#define CATCH_BLOCK 1 void Job_CatchChildren(void); void Job_CatchOutput(void); void Job_Make(GNode *); void Job_Init(void); -Boolean Job_Full(void); Boolean Job_Empty(void); -ReturnStatus Job_ParseShell(char *); +Boolean Job_ParseShell(char *); int Job_Finish(void); void Job_End(void); void Job_Wait(void); void Job_AbortAll(void); -void JobFlagForMigration(int); void Job_TokenReturn(void); Boolean Job_TokenWithdraw(void); void Job_ServerStart(int, int, int); void Job_SetPrefix(void); Boolean Job_RunTarget(const char *, const char *); -#endif /* _JOB_H_ */ +#endif /* MAKE_JOB_H */ diff --git a/lst.c b/lst.c new file mode 100644 index 000000000000..26b2457cc013 --- /dev/null +++ b/lst.c @@ -0,0 +1,641 @@ +/* $NetBSD: lst.c,v 1.60 2020/08/31 05:56:02 rillig Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#elif defined(HAVE_STDINT_H) +#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) +{ + LstNode node = bmake_malloc(sizeof *node); + node->useCount = 0; + node->deleted = FALSE; + 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) +{ + Lst 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. */ +void +Lst_Free(Lst list) +{ + LstNode node; + LstNode next; + + assert(list != NULL); + + for (node = list->first; node != NULL; node = next) { + next = node->next; + free(node); + } + + free(list); +} + +/* Destroy a list and free all its resources. The freeProc is called with the + * datum from each node in turn before the node is freed. */ +void +Lst_Destroy(Lst list, LstFreeProc freeProc) +{ + LstNode node; + LstNode next; + + assert(list != NULL); + assert(freeProc != NULL); + + for (node = list->first; node != NULL; node = next) { + next = node->next; + freeProc(node->datum); + free(node); + } + + free(list); +} + +/* + * Functions to modify a list + */ + +/* Insert a new node with the given piece of data before the given node in the + * given list. */ +void +Lst_InsertBefore(Lst list, LstNode node, void *datum) +{ + LstNode newNode; + + assert(list != NULL); + assert(!LstIsEmpty(list)); + assert(node != NULL); + assert(datum != NULL); + + newNode = LstNodeNew(datum); + newNode->prev = node->prev; + newNode->next = node; + + if (node->prev != NULL) { + node->prev->next = newNode; + } + node->prev = newNode; + + 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) +{ + LstNode node; + + assert(list != NULL); + assert(datum != NULL); + + node = LstNodeNew(datum); + node->prev = NULL; + node->next = list->first; + + if (list->first == NULL) { + list->first = node; + list->last = node; + } else { + list->first->prev = node; + list->first = node; + } +} + +/* Add a piece of data at the end of the given list. */ +void +Lst_Append(Lst list, void *datum) +{ + LstNode node; + + assert(list != NULL); + assert(datum != NULL); + + node = LstNodeNew(datum); + node->prev = list->last; + node->next = NULL; + + if (list->last == NULL) { + list->first = node; + list->last = node; + } else { + list->last->next = node; + list->last = node; + } +} + +/* 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) +{ + assert(list != NULL); + assert(node != NULL); + + /* + * unlink it from the list + */ + if (node->next != NULL) { + node->next->prev = node->prev; + } + 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) { + list->first = node->next; + } + 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) +{ + assert(node != NULL); + assert(datum != NULL); + + node->datum = datum; +} + +/* Replace the datum in the given node to NULL. */ +void +LstNode_SetNull(LstNode 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) +{ + LstNode node; + + assert(list != NULL); + assert(datum != NULL); + + 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) +{ + 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); + + return result; +} + +/* Move all nodes from list2 to the end of list1. + * List2 is destroyed and freed. */ +void +Lst_MoveAll(Lst list1, Lst list2) +{ + assert(list1 != NULL); + assert(list2 != NULL); + + if (list2->first != NULL) { + list2->first->prev = list1->last; + if (list1->last != NULL) { + list1->last->next = list2->first; + } else { + list1->first = list2->first; + } + list1->last = list2->last; + } + free(list2); +} + +/* Copy the element data from src to the start of dst. */ +void +Lst_PrependAll(Lst dst, Lst src) +{ + LstNode 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) +{ + LstNode 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. + */ + +/* Open a list for sequential access. A list can still be searched, etc., + * without confusing these functions. */ +void +Lst_Open(Lst list) +{ + assert(list != NULL); + assert(!list->isOpen); + + list->isOpen = TRUE; + list->lastAccess = LstIsEmpty(list) ? Head : Unknown; + list->curr = NULL; +} + +/* Return the next node for the given list, or NULL if the end has been + * reached. */ +LstNode +Lst_Next(Lst 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; +} + +/* Close a list which was opened for sequential access. */ +void +Lst_Close(Lst list) +{ + assert(list != NULL); + assert(list->isOpen); + + list->isOpen = FALSE; + list->lastAccess = Unknown; +} + + +/* + * for using the list as a queue + */ + +/* Add the datum to the tail of the given list. */ +void +Lst_Enqueue(Lst list, void *datum) +{ + Lst_Append(list, datum); +} + +/* Remove and return the datum at the head of the given list. */ +void * +Lst_Dequeue(Lst list) +{ + void *datum; + + assert(list != NULL); + assert(!LstIsEmpty(list)); + + datum = list->first->datum; + Lst_Remove(list, list->first); + assert(datum != NULL); + return datum; +} @@ -1,4 +1,4 @@ -/* $NetBSD: lst.h,v 1.20 2014/09/07 20:55:34 joerg Exp $ */ +/* $NetBSD: lst.h,v 1.60 2020/09/02 23:33:13 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -73,117 +73,107 @@ * from: @(#)lst.h 8.1 (Berkeley) 6/6/93 */ -/*- - * lst.h -- - * Header for using the list library - */ -#ifndef _LST_H_ -#define _LST_H_ - -#include <sys/param.h> -#include <stdlib.h> +/* Doubly-linked lists of arbitrary pointers. */ -#include "sprite.h" +#ifndef MAKE_LST_H +#define MAKE_LST_H -/* - * basic typedef. This is what the Lst_ functions handle - */ +#include <sys/param.h> +#include <stdlib.h> +/* A doubly-linked list of pointers. */ typedef struct List *Lst; +/* A single node in the doubly-linked list. */ typedef struct ListNode *LstNode; -typedef void *DuplicateProc(void *); -typedef void FreeProc(void *); +/* 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); -#define LST_CONCNEW 0 /* create new LstNode's when using Lst_Concat */ -#define LST_CONCLINK 1 /* relink LstNode's when using Lst_Concat */ +/* Create or destroy a list */ -/* - * Creation/destruction functions - */ -/* Create a new list */ -Lst Lst_Init(Boolean); -/* Duplicate an existing list */ -Lst Lst_Duplicate(Lst, DuplicateProc *); -/* Destroy an old one */ -void Lst_Destroy(Lst, FreeProc *); -/* True if list is empty */ -Boolean Lst_IsEmpty(Lst); +/* Create a new list. */ +Lst Lst_Init(void); +/* Duplicate an existing list. */ +Lst Lst_Copy(Lst, LstCopyProc); +/* Free the list, leaving the node data unmodified. */ +void Lst_Free(Lst); +/* Free the list, freeing the node data using the given function. */ +void Lst_Destroy(Lst, LstFreeProc); -/* - * Functions to modify a list - */ -/* Insert an element before another */ -ReturnStatus Lst_InsertBefore(Lst, LstNode, void *); -/* Insert an element after another */ -ReturnStatus Lst_InsertAfter(Lst, LstNode, void *); -/* Place an element at the front of a lst. */ -ReturnStatus Lst_AtFront(Lst, void *); -/* Place an element at the end of a lst. */ -ReturnStatus Lst_AtEnd(Lst, void *); -/* Remove an element */ -ReturnStatus Lst_Remove(Lst, LstNode); -/* Replace a node with a new value */ -ReturnStatus Lst_Replace(LstNode, void *); -/* Concatenate two lists */ -ReturnStatus Lst_Concat(Lst, Lst, int); +/* Get information about a list */ -/* - * Node-specific functions - */ -/* Return first element in list */ -LstNode Lst_First(Lst); -/* Return last element in list */ -LstNode Lst_Last(Lst); -/* Return successor to given element */ -LstNode Lst_Succ(LstNode); -/* Return predecessor to given element */ -LstNode Lst_Prev(LstNode); -/* Get datum from LstNode */ -void *Lst_Datum(LstNode); +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 *); +/* Find the first node that contains the given datum, or NULL. */ +LstNode Lst_FindDatum(Lst, const void *); -/* - * Functions for entire lists - */ -/* Find an element in a list */ -LstNode Lst_Find(Lst, const void *, int (*)(const void *, const void *)); -/* Find an element starting from somewhere */ -LstNode Lst_FindFrom(Lst, LstNode, const void *, - int (*cProc)(const void *, const void *)); -/* - * See if the given datum is on the list. Returns the LstNode containing - * the datum - */ -LstNode Lst_Member(Lst, void *); -/* Apply a function to all elements of a lst */ -int Lst_ForEach(Lst, int (*)(void *, void *), void *); -/* - * Apply a function to all elements of a lst starting from a certain point. - * If the list is circular, the application will wrap around to the - * beginning of the list again. - */ -int Lst_ForEachFrom(Lst, LstNode, int (*)(void *, void *), - void *); -/* - * 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(). - */ -/* Open the list */ -ReturnStatus Lst_Open(Lst); -/* Next element please */ -LstNode Lst_Next(Lst); -/* Done yet? */ -Boolean Lst_IsAtEnd(Lst); -/* Finish table access */ -void Lst_Close(Lst); +/* Modify a list */ -/* - * for using the list as a queue - */ -/* Place an element at tail of queue */ -ReturnStatus Lst_EnQueue(Lst, void *); -/* Remove an element from head of queue */ -void *Lst_DeQueue(Lst); +/* Insert a datum before the given node. */ +void Lst_InsertBefore(Lst, LstNode, void *); +/* Place a datum at the front of the list. */ +void Lst_Prepend(Lst, void *); +/* Place a datum at the end of the list. */ +void Lst_Append(Lst, 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); + +/* 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 *); +/* Set the value of the node to NULL. Having NULL in a list is unusual. */ +void LstNode_SetNull(LstNode); + +/* 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); + +/* Using the list as a queue */ + +/* Add a datum at the tail of the queue. */ +void Lst_Enqueue(Lst, void *); +/* Remove the head node of the queue and return its datum. */ +void *Lst_Dequeue(Lst); -#endif /* _LST_H_ */ +#endif /* MAKE_LST_H */ diff --git a/lst.lib/Makefile b/lst.lib/Makefile deleted file mode 100644 index e69de29bb2d1..000000000000 --- a/lst.lib/Makefile +++ /dev/null diff --git a/lst.lib/lstAppend.c b/lst.lib/lstAppend.c deleted file mode 100644 index 97e60d959d7d..000000000000 --- a/lst.lib/lstAppend.c +++ /dev/null @@ -1,121 +0,0 @@ -/* $NetBSD: lstAppend.c,v 1.15 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstAppend.c,v 1.15 2020/07/03 08:37:56 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstAppend.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstAppend.c,v 1.15 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstAppend.c -- - * Add a new node with a new datum after an existing node - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_InsertAfter -- - * Create a new node and add it to the given list after the given node. - * - * Input: - * l affected list - * ln node after which to append the datum - * d said datum - * - * Results: - * SUCCESS if all went well. - * - * Side Effects: - * A new ListNode is created and linked in to the List. The lastPtr - * field of the List will be altered if ln is the last node in the - * list. lastPtr and firstPtr will alter if the list was empty and - * ln was NULL. - * - *----------------------------------------------------------------------- - */ -ReturnStatus -Lst_InsertAfter(Lst l, LstNode ln, void *d) -{ - List list; - ListNode lNode; - ListNode nLNode; - - if (LstValid (l) && (ln == NULL && LstIsEmpty (l))) { - goto ok; - } - - if (!LstValid (l) || LstIsEmpty (l) || ! LstNodeValid (ln, l)) { - return FAILURE; - } - ok: - - list = l; - lNode = ln; - - PAlloc (nLNode, ListNode); - nLNode->datum = d; - nLNode->useCount = nLNode->flags = 0; - - if (lNode == NULL) { - if (list->isCirc) { - nLNode->nextPtr = nLNode->prevPtr = nLNode; - } else { - nLNode->nextPtr = nLNode->prevPtr = NULL; - } - list->firstPtr = list->lastPtr = nLNode; - } else { - nLNode->prevPtr = lNode; - nLNode->nextPtr = lNode->nextPtr; - - lNode->nextPtr = nLNode; - if (nLNode->nextPtr != NULL) { - nLNode->nextPtr->prevPtr = nLNode; - } - - if (lNode == list->lastPtr) { - list->lastPtr = nLNode; - } - } - - return SUCCESS; -} diff --git a/lst.lib/lstAtEnd.c b/lst.lib/lstAtEnd.c deleted file mode 100644 index 4eadfdb0e7f3..000000000000 --- a/lst.lib/lstAtEnd.c +++ /dev/null @@ -1,79 +0,0 @@ -/* $NetBSD: lstAtEnd.c,v 1.14 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstAtEnd.c,v 1.14 2020/07/03 08:37:56 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstAtEnd.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstAtEnd.c,v 1.14 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstAtEnd.c -- - * Add a node at the end of the list - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_AtEnd -- - * Add a node to the end of the given list - * - * Input: - * l List to which to add the datum - * d Datum to add - * - * Results: - * SUCCESS if life is good. - * - * Side Effects: - * A new ListNode is created and added to the list. - * - *----------------------------------------------------------------------- - */ -ReturnStatus -Lst_AtEnd(Lst l, void *d) -{ - LstNode end; - - end = Lst_Last(l); - return Lst_InsertAfter(l, end, d); -} diff --git a/lst.lib/lstAtFront.c b/lst.lib/lstAtFront.c deleted file mode 100644 index 724713c092ed..000000000000 --- a/lst.lib/lstAtFront.c +++ /dev/null @@ -1,76 +0,0 @@ -/* $NetBSD: lstAtFront.c,v 1.14 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstAtFront.c,v 1.14 2020/07/03 08:37:56 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstAtFront.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstAtFront.c,v 1.14 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstAtFront.c -- - * Add a node at the front of the list - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_AtFront -- - * Place a piece of data at the front of a list - * - * Results: - * SUCCESS or FAILURE - * - * Side Effects: - * A new ListNode is created and stuck at the front of the list. - * hence, firstPtr (and possible lastPtr) in the list are altered. - * - *----------------------------------------------------------------------- - */ -ReturnStatus -Lst_AtFront(Lst l, void *d) -{ - LstNode front; - - front = Lst_First(l); - return Lst_InsertBefore(l, front, d); -} diff --git a/lst.lib/lstClose.c b/lst.lib/lstClose.c deleted file mode 100644 index a1a3e9da80de..000000000000 --- a/lst.lib/lstClose.c +++ /dev/null @@ -1,85 +0,0 @@ -/* $NetBSD: lstClose.c,v 1.12 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstClose.c,v 1.12 2020/07/03 08:37:56 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstClose.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstClose.c,v 1.12 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstClose.c -- - * Close a list for sequential access. - * 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. Because the list is circular, Lst_Next - * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be - * used to determine when to stop. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Close -- - * Close a list which was opened for sequential access. - * - * Input: - * l The list to close - * - * Results: - * None. - * - * Side Effects: - * The list is closed. - * - *----------------------------------------------------------------------- - */ -void -Lst_Close(Lst l) -{ - List list = l; - - if (LstValid(l) == TRUE) { - list->isOpen = FALSE; - list->atEnd = Unknown; - } -} diff --git a/lst.lib/lstConcat.c b/lst.lib/lstConcat.c deleted file mode 100644 index 2f667c5b0119..000000000000 --- a/lst.lib/lstConcat.c +++ /dev/null @@ -1,184 +0,0 @@ -/* $NetBSD: lstConcat.c,v 1.17 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstConcat.c,v 1.17 2020/07/03 08:37:56 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstConcat.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstConcat.c,v 1.17 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * listConcat.c -- - * Function to concatentate two lists. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Concat -- - * Concatenate two lists. New elements are created to hold the data - * elements, if specified, but the elements themselves are not copied. - * If the elements should be duplicated to avoid confusion with another - * list, the Lst_Duplicate function should be called first. - * If LST_CONCLINK is specified, the second list is destroyed since - * its pointers have been corrupted and the list is no longer useable. - * - * Input: - * l1 The list to which l2 is to be appended - * l2 The list to append to l1 - * flags LST_CONCNEW if LstNode's should be duplicated - * LST_CONCLINK if should just be relinked - * - * Results: - * SUCCESS if all went well. FAILURE otherwise. - * - * Side Effects: - * New elements are created and appended the first list. - *----------------------------------------------------------------------- - */ -ReturnStatus -Lst_Concat(Lst l1, Lst l2, int flags) -{ - ListNode ln; /* original LstNode */ - ListNode nln; /* new LstNode */ - ListNode last; /* the last element in the list. Keeps - * bookkeeping until the end */ - List list1 = l1; - List list2 = l2; - - if (!LstValid (l1) || !LstValid (l2)) { - return FAILURE; - } - - if (flags == LST_CONCLINK) { - if (list2->firstPtr != NULL) { - /* - * We set the nextPtr of the - * last element of list two to be NIL to make the loop easier and - * so we don't need an extra case should the first list turn - * out to be non-circular -- the final element will already point - * to NIL space and the first element will be untouched if it - * existed before and will also point to NIL space if it didn't. - */ - list2->lastPtr->nextPtr = NULL; - /* - * So long as the second list isn't empty, we just link the - * first element of the second list to the last element of the - * first list. If the first list isn't empty, we then link the - * last element of the list to the first element of the second list - * The last element of the second list, if it exists, then becomes - * the last element of the first list. - */ - list2->firstPtr->prevPtr = list1->lastPtr; - if (list1->lastPtr != NULL) { - list1->lastPtr->nextPtr = list2->firstPtr; - } else { - list1->firstPtr = list2->firstPtr; - } - list1->lastPtr = list2->lastPtr; - } - if (list1->isCirc && list1->firstPtr != NULL) { - /* - * If the first list is supposed to be circular and it is (now) - * non-empty, we must make sure it's circular by linking the - * first element to the last and vice versa - */ - list1->firstPtr->prevPtr = list1->lastPtr; - list1->lastPtr->nextPtr = list1->firstPtr; - } - free(l2); - } else if (list2->firstPtr != NULL) { - /* - * We set the nextPtr of the last element of list 2 to be nil to make - * the loop less difficult. The loop simply goes through the entire - * second list creating new LstNodes and filling in the nextPtr, and - * prevPtr to fit into l1 and its datum field from the - * datum field of the corresponding element in l2. The 'last' node - * follows the last of the new nodes along until the entire l2 has - * been appended. Only then does the bookkeeping catch up with the - * changes. During the first iteration of the loop, if 'last' is nil, - * the first list must have been empty so the newly-created node is - * made the first node of the list. - */ - list2->lastPtr->nextPtr = NULL; - for (last = list1->lastPtr, ln = list2->firstPtr; - ln != NULL; - ln = ln->nextPtr) - { - PAlloc (nln, ListNode); - nln->datum = ln->datum; - if (last != NULL) { - last->nextPtr = nln; - } else { - list1->firstPtr = nln; - } - nln->prevPtr = last; - nln->flags = nln->useCount = 0; - last = nln; - } - - /* - * Finish bookkeeping. The last new element becomes the last element - * of list one. - */ - list1->lastPtr = last; - - /* - * The circularity of both list one and list two must be corrected - * for -- list one because of the new nodes added to it; list two - * because of the alteration of list2->lastPtr's nextPtr to ease the - * above for loop. - */ - if (list1->isCirc) { - list1->lastPtr->nextPtr = list1->firstPtr; - list1->firstPtr->prevPtr = list1->lastPtr; - } else { - last->nextPtr = NULL; - } - - if (list2->isCirc) { - list2->lastPtr->nextPtr = list2->firstPtr; - } - } - - return SUCCESS; -} diff --git a/lst.lib/lstDatum.c b/lst.lib/lstDatum.c deleted file mode 100644 index c8ccb558f3dc..000000000000 --- a/lst.lib/lstDatum.c +++ /dev/null @@ -1,76 +0,0 @@ -/* $NetBSD: lstDatum.c,v 1.14 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstDatum.c,v 1.14 2020/07/03 08:37:56 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstDatum.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstDatum.c,v 1.14 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstDatum.c -- - * Return the datum associated with a list node. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Datum -- - * Return the datum stored in the given node. - * - * Results: - * The datum or NULL if the node is invalid. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -void * -Lst_Datum(LstNode ln) -{ - if (ln != NULL) { - return ln->datum; - } else { - return NULL; - } -} diff --git a/lst.lib/lstDeQueue.c b/lst.lib/lstDeQueue.c deleted file mode 100644 index 0f1452e4ac7e..000000000000 --- a/lst.lib/lstDeQueue.c +++ /dev/null @@ -1,86 +0,0 @@ -/* $NetBSD: lstDeQueue.c,v 1.15 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstDeQueue.c,v 1.15 2020/07/03 08:37:56 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstDeQueue.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstDeQueue.c,v 1.15 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstDeQueue.c -- - * Remove the node and return its datum from the head of the list - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_DeQueue -- - * Remove and return the datum at the head of the given list. - * - * Results: - * The datum in the node at the head or NULL if the list - * is empty. - * - * Side Effects: - * The head node is removed from the list. - * - *----------------------------------------------------------------------- - */ -void * -Lst_DeQueue(Lst l) -{ - void *rd; - ListNode tln; - - tln = Lst_First(l); - if (tln == NULL) { - return NULL; - } - - rd = tln->datum; - if (Lst_Remove(l, tln) == FAILURE) { - return NULL; - } else { - return rd; - } -} diff --git a/lst.lib/lstDestroy.c b/lst.lib/lstDestroy.c deleted file mode 100644 index 92c5b2b2050c..000000000000 --- a/lst.lib/lstDestroy.c +++ /dev/null @@ -1,101 +0,0 @@ -/* $NetBSD: lstDestroy.c,v 1.16 2008/12/13 15:19:29 dsl Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstDestroy.c,v 1.16 2008/12/13 15:19:29 dsl Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstDestroy.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstDestroy.c,v 1.16 2008/12/13 15:19:29 dsl Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstDestroy.c -- - * Nuke a list and all its resources - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Destroy -- - * Destroy a list and free all its resources. If the freeProc is - * given, it is called with the datum from each node in turn before - * the node is freed. - * - * Results: - * None. - * - * Side Effects: - * The given list is freed in its entirety. - * - *----------------------------------------------------------------------- - */ -void -Lst_Destroy(Lst list, FreeProc *freeProc) -{ - ListNode ln; - ListNode tln = NULL; - - if (list == NULL) - return; - - /* To ease scanning */ - if (list->lastPtr != NULL) - list->lastPtr->nextPtr = NULL; - else { - free(list); - return; - } - - if (freeProc) { - for (ln = list->firstPtr; ln != NULL; ln = tln) { - tln = ln->nextPtr; - freeProc(ln->datum); - free(ln); - } - } else { - for (ln = list->firstPtr; ln != NULL; ln = tln) { - tln = ln->nextPtr; - free(ln); - } - } - - free(list); -} diff --git a/lst.lib/lstDupl.c b/lst.lib/lstDupl.c deleted file mode 100644 index 6318ee4e462a..000000000000 --- a/lst.lib/lstDupl.c +++ /dev/null @@ -1,107 +0,0 @@ -/* $NetBSD: lstDupl.c,v 1.17 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstDupl.c,v 1.17 2020/07/03 08:37:56 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstDupl.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstDupl.c,v 1.17 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * listDupl.c -- - * Duplicate a list. This includes duplicating the individual - * elements. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Duplicate -- - * Duplicate an entire list. If a function to copy a void *is - * given, the individual client elements will be duplicated as well. - * - * Input: - * l the list to duplicate - * copyProc A function to duplicate each void * - * - * Results: - * The new Lst structure or NULL if failure. - * - * Side Effects: - * A new list is created. - *----------------------------------------------------------------------- - */ -Lst -Lst_Duplicate(Lst l, DuplicateProc *copyProc) -{ - Lst nl; - ListNode ln; - List list = l; - - if (!LstValid (l)) { - return NULL; - } - - nl = Lst_Init(list->isCirc); - if (nl == NULL) { - return NULL; - } - - ln = list->firstPtr; - while (ln != NULL) { - if (copyProc != NULL) { - if (Lst_AtEnd(nl, copyProc(ln->datum)) == FAILURE) { - return NULL; - } - } else if (Lst_AtEnd(nl, ln->datum) == FAILURE) { - return NULL; - } - - if (list->isCirc && ln == list->lastPtr) { - ln = NULL; - } else { - ln = ln->nextPtr; - } - } - - return nl; -} diff --git a/lst.lib/lstEnQueue.c b/lst.lib/lstEnQueue.c deleted file mode 100644 index c6941a8eb9d3..000000000000 --- a/lst.lib/lstEnQueue.c +++ /dev/null @@ -1,77 +0,0 @@ -/* $NetBSD: lstEnQueue.c,v 1.14 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstEnQueue.c,v 1.14 2020/07/03 08:37:56 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstEnQueue.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstEnQueue.c,v 1.14 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstEnQueue.c-- - * Treat the list as a queue and place a datum at its end - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_EnQueue -- - * Add the datum to the tail of the given list. - * - * Results: - * SUCCESS or FAILURE as returned by Lst_InsertAfter. - * - * Side Effects: - * the lastPtr field is altered all the time and the firstPtr field - * will be altered if the list used to be empty. - * - *----------------------------------------------------------------------- - */ -ReturnStatus -Lst_EnQueue(Lst l, void *d) -{ - if (LstValid (l) == FALSE) { - return FAILURE; - } - - return Lst_InsertAfter(l, Lst_Last(l), d); -} diff --git a/lst.lib/lstFind.c b/lst.lib/lstFind.c deleted file mode 100644 index a1d27d3ad686..000000000000 --- a/lst.lib/lstFind.c +++ /dev/null @@ -1,73 +0,0 @@ -/* $NetBSD: lstFind.c,v 1.16 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstFind.c,v 1.16 2020/07/03 08:37:56 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstFind.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstFind.c,v 1.16 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstFind.c -- - * Find a node on a list. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Find -- - * Find a node on the given list using the given comparison function - * and the given datum. - * - * Results: - * The found node or NULL if none matches. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -LstNode -Lst_Find(Lst l, const void *d, int (*cProc)(const void *, const void *)) -{ - return Lst_FindFrom(l, Lst_First(l), d, cProc); -} diff --git a/lst.lib/lstFindFrom.c b/lst.lib/lstFindFrom.c deleted file mode 100644 index 676c07392039..000000000000 --- a/lst.lib/lstFindFrom.c +++ /dev/null @@ -1,89 +0,0 @@ -/* $NetBSD: lstFindFrom.c,v 1.16 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstFindFrom.c,v 1.16 2020/07/03 08:37:56 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstFindFrom.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstFindFrom.c,v 1.16 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstFindFrom.c -- - * Find a node on a list from a given starting point. Used by Lst_Find. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_FindFrom -- - * Search for a node starting and ending with the given one on the - * given list using the passed datum and comparison function to - * determine when it has been found. - * - * Results: - * The found node or NULL - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -LstNode -Lst_FindFrom(Lst l, LstNode ln, const void *d, - int (*cProc)(const void *, const void *)) -{ - ListNode tln; - - if (!LstValid (l) || LstIsEmpty (l) || !LstNodeValid (ln, l)) { - return NULL; - } - - tln = ln; - - do { - if ((*cProc)(tln->datum, d) == 0) - return tln; - tln = tln->nextPtr; - } while (tln != ln && tln != NULL); - - return NULL; -} diff --git a/lst.lib/lstFirst.c b/lst.lib/lstFirst.c deleted file mode 100644 index a79db57120dd..000000000000 --- a/lst.lib/lstFirst.c +++ /dev/null @@ -1,76 +0,0 @@ -/* $NetBSD: lstFirst.c,v 1.13 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstFirst.c,v 1.13 2020/07/03 08:37:56 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstFirst.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstFirst.c,v 1.13 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstFirst.c -- - * Return the first node of a list - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_First -- - * Return the first node on the given list. - * - * Results: - * The first node or NULL if the list is empty. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -LstNode -Lst_First(Lst l) -{ - if (!LstValid (l) || LstIsEmpty (l)) { - return NULL; - } else { - return l->firstPtr; - } -} diff --git a/lst.lib/lstForEach.c b/lst.lib/lstForEach.c deleted file mode 100644 index dc2fdd8bfc57..000000000000 --- a/lst.lib/lstForEach.c +++ /dev/null @@ -1,75 +0,0 @@ -/* $NetBSD: lstForEach.c,v 1.14 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstForEach.c,v 1.14 2020/07/03 08:37:57 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstForEach.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstForEach.c,v 1.14 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstForeach.c -- - * Perform a given function on all elements of a list. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_ForEach -- - * Apply the given function to each element of the given list. The - * function should return 0 if Lst_ForEach should continue and non- - * zero if it should abort. - * - * Results: - * None. - * - * Side Effects: - * Only those created by the passed-in function. - * - *----------------------------------------------------------------------- - */ -/*VARARGS2*/ -int -Lst_ForEach(Lst l, int (*proc)(void *, void *), void *d) -{ - return Lst_ForEachFrom(l, Lst_First(l), proc, d); -} diff --git a/lst.lib/lstForEachFrom.c b/lst.lib/lstForEachFrom.c deleted file mode 100644 index a08ddf35935b..000000000000 --- a/lst.lib/lstForEachFrom.c +++ /dev/null @@ -1,124 +0,0 @@ -/* $NetBSD: lstForEachFrom.c,v 1.18 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstForEachFrom.c,v 1.18 2020/07/03 08:37:57 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstForEachFrom.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstForEachFrom.c,v 1.18 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * lstForEachFrom.c -- - * Perform a given function on all elements of a list starting from - * a given point. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_ForEachFrom -- - * 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. - * - * Results: - * None. - * - * Side Effects: - * Only those created by the passed-in function. - * - *----------------------------------------------------------------------- - */ -/*VARARGS2*/ -int -Lst_ForEachFrom(Lst l, LstNode ln, int (*proc)(void *, void *), - void *d) -{ - ListNode tln = ln; - List list = l; - ListNode next; - Boolean done; - int result; - - if (!LstValid (list) || LstIsEmpty (list)) { - return 0; - } - - do { - /* - * Take care of having the current element deleted out from under - * us. - */ - - next = tln->nextPtr; - - /* - * We're done with the traversal if - * - the next node to examine is the first in the queue or - * doesn't exist and - * - nothing's been added after the current node (check this - * after proc() has been called). - */ - done = (next == NULL || next == list->firstPtr); - - (void) tln->useCount++; - result = (*proc) (tln->datum, d); - (void) 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->nextPtr) { - next = tln->nextPtr; - done = 0; - } - - if (tln->flags & LN_DELETED) { - free((char *)tln); - } - tln = next; - } while (!result && !LstIsEmpty(list) && !done); - - return result; -} diff --git a/lst.lib/lstInit.c b/lst.lib/lstInit.c deleted file mode 100644 index 3255da7e59a1..000000000000 --- a/lst.lib/lstInit.c +++ /dev/null @@ -1,85 +0,0 @@ -/* $NetBSD: lstInit.c,v 1.13 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstInit.c,v 1.13 2020/07/03 08:37:57 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstInit.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstInit.c,v 1.13 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * init.c -- - * Initialize a new linked list. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Init -- - * Create and initialize a new list. - * - * Input: - * circ TRUE if the list should be made circular - * - * Results: - * The created list. - * - * Side Effects: - * A list is created, what else? - * - *----------------------------------------------------------------------- - */ -Lst -Lst_Init(Boolean circ) -{ - List nList; - - PAlloc (nList, List); - - nList->firstPtr = NULL; - nList->lastPtr = NULL; - nList->isOpen = FALSE; - nList->isCirc = circ; - nList->atEnd = Unknown; - - return nList; -} diff --git a/lst.lib/lstInsert.c b/lst.lib/lstInsert.c deleted file mode 100644 index 845b8899e03b..000000000000 --- a/lst.lib/lstInsert.c +++ /dev/null @@ -1,121 +0,0 @@ -/* $NetBSD: lstInsert.c,v 1.15 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstInsert.c,v 1.15 2020/07/03 08:37:57 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstInsert.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstInsert.c,v 1.15 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstInsert.c -- - * Insert a new datum before an old one - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_InsertBefore -- - * Insert a new node with the given piece of data before the given - * node in the given list. - * - * Input: - * l list to manipulate - * ln node before which to insert d - * d datum to be inserted - * - * Results: - * SUCCESS or FAILURE. - * - * Side Effects: - * the firstPtr field will be changed if ln is the first node in the - * list. - * - *----------------------------------------------------------------------- - */ -ReturnStatus -Lst_InsertBefore(Lst l, LstNode ln, void *d) -{ - ListNode nLNode; /* new lnode for d */ - ListNode lNode = ln; - List list = l; - - - /* - * check validity of arguments - */ - if (LstValid (l) && (LstIsEmpty (l) && ln == NULL)) - goto ok; - - if (!LstValid (l) || LstIsEmpty (l) || !LstNodeValid (ln, l)) { - return FAILURE; - } - - ok: - PAlloc (nLNode, ListNode); - - nLNode->datum = d; - nLNode->useCount = nLNode->flags = 0; - - if (ln == NULL) { - if (list->isCirc) { - nLNode->prevPtr = nLNode->nextPtr = nLNode; - } else { - nLNode->prevPtr = nLNode->nextPtr = NULL; - } - list->firstPtr = list->lastPtr = nLNode; - } else { - nLNode->prevPtr = lNode->prevPtr; - nLNode->nextPtr = lNode; - - if (nLNode->prevPtr != NULL) { - nLNode->prevPtr->nextPtr = nLNode; - } - lNode->prevPtr = nLNode; - - if (lNode == list->firstPtr) { - list->firstPtr = nLNode; - } - } - - return SUCCESS; -} diff --git a/lst.lib/lstInt.h b/lst.lib/lstInt.h deleted file mode 100644 index ac53dcb67d77..000000000000 --- a/lst.lib/lstInt.h +++ /dev/null @@ -1,105 +0,0 @@ -/* $NetBSD: lstInt.h,v 1.22 2014/09/07 20:55:34 joerg Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * from: @(#)lstInt.h 8.1 (Berkeley) 6/6/93 - */ - -/*- - * lstInt.h -- - * Internals for the list library - */ -#ifndef _LSTINT_H_ -#define _LSTINT_H_ - -#include "../lst.h" -#include "../make_malloc.h" - -typedef struct ListNode { - struct ListNode *prevPtr; /* previous element in list */ - struct ListNode *nextPtr; /* next in list */ - unsigned int useCount:8, /* Count of functions using the node. - * node may not be deleted until count - * goes to 0 */ - flags:8; /* Node status flags */ - void *datum; /* datum associated with this element */ -} *ListNode; -/* - * Flags required for synchronization - */ -#define LN_DELETED 0x0001 /* List node should be removed when done */ - -typedef enum { - Head, Middle, Tail, Unknown -} Where; - -typedef struct List { - ListNode firstPtr; /* first node in list */ - ListNode lastPtr; /* last node in list */ - Boolean isCirc; /* true if the list should be considered - * circular */ -/* - * fields for sequential access - */ - Where atEnd; /* Where in the list the last access was */ - Boolean isOpen; /* true if list has been Lst_Open'ed */ - ListNode curPtr; /* current node, if open. NULL if - * *just* opened */ - ListNode prevPtr; /* Previous node, if open. Used by - * Lst_Remove */ -} *List; - -/* - * PAlloc (var, ptype) -- - * Allocate a pointer-typedef structure 'ptype' into the variable 'var' - */ -#define PAlloc(var,ptype) var = (ptype) bmake_malloc(sizeof *(var)) - -/* - * LstValid (l) -- - * Return TRUE if the list l is valid - */ -#define LstValid(l) ((Lst)(l) != NULL) - -/* - * LstNodeValid (ln, l) -- - * Return TRUE if the LstNode ln is valid with respect to l - */ -#define LstNodeValid(ln, l) ((ln) != NULL) - -/* - * LstIsEmpty (l) -- - * TRUE if the list l is empty. - */ -#define LstIsEmpty(l) (((List)(l))->firstPtr == NULL) - -#endif /* _LSTINT_H_ */ diff --git a/lst.lib/lstIsAtEnd.c b/lst.lib/lstIsAtEnd.c deleted file mode 100644 index c5add4d9867f..000000000000 --- a/lst.lib/lstIsAtEnd.c +++ /dev/null @@ -1,86 +0,0 @@ -/* $NetBSD: lstIsAtEnd.c,v 1.14 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstIsAtEnd.c,v 1.14 2020/07/03 08:37:57 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstIsAtEnd.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstIsAtEnd.c,v 1.14 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstIsAtEnd.c -- - * Tell if the current node is at the end of the list. - * 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. Because the list is circular, Lst_Next - * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be - * used to determine when to stop. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_IsAtEnd -- - * Return true if have reached the end of the given list. - * - * Results: - * TRUE if at the end of the list (this includes the list not being - * open or being invalid) or FALSE if not. We return TRUE if the list - * is invalid or unopend so as to cause the caller to exit its loop - * asap, the assumption being that the loop is of the form - * while (!Lst_IsAtEnd (l)) { - * ... - * } - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -Boolean -Lst_IsAtEnd(Lst l) -{ - List list = l; - - return !LstValid (l) || !list->isOpen || - list->atEnd == Head || list->atEnd == Tail; -} diff --git a/lst.lib/lstIsEmpty.c b/lst.lib/lstIsEmpty.c deleted file mode 100644 index ccf4525a3506..000000000000 --- a/lst.lib/lstIsEmpty.c +++ /dev/null @@ -1,74 +0,0 @@ -/* $NetBSD: lstIsEmpty.c,v 1.12 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstIsEmpty.c,v 1.12 2020/07/03 08:37:57 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstIsEmpty.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstIsEmpty.c,v 1.12 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstIsEmpty.c -- - * A single function to decide if a list is empty - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_IsEmpty -- - * Return TRUE if the given list is empty. - * - * Results: - * TRUE if the list is empty, FALSE otherwise. - * - * Side Effects: - * None. - * - * A list is considered empty if its firstPtr == NULL (or if - * the list itself is NULL). - *----------------------------------------------------------------------- - */ -Boolean -Lst_IsEmpty(Lst l) -{ - return !LstValid(l) || LstIsEmpty(l); -} diff --git a/lst.lib/lstLast.c b/lst.lib/lstLast.c deleted file mode 100644 index 1d65bf19473e..000000000000 --- a/lst.lib/lstLast.c +++ /dev/null @@ -1,76 +0,0 @@ -/* $NetBSD: lstLast.c,v 1.13 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstLast.c,v 1.13 2020/07/03 08:37:57 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstLast.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstLast.c,v 1.13 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstLast.c -- - * Return the last element of a list - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Last -- - * Return the last node on the list l. - * - * Results: - * The requested node or NULL if the list is empty. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -LstNode -Lst_Last(Lst l) -{ - if (!LstValid(l) || LstIsEmpty (l)) { - return NULL; - } else { - return l->lastPtr; - } -} diff --git a/lst.lib/lstMember.c b/lst.lib/lstMember.c deleted file mode 100644 index e9046aca1436..000000000000 --- a/lst.lib/lstMember.c +++ /dev/null @@ -1,77 +0,0 @@ -/* $NetBSD: lstMember.c,v 1.14 2013/11/14 00:01:28 sjg Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstMember.c,v 1.14 2013/11/14 00:01:28 sjg Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstMember.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstMember.c,v 1.14 2013/11/14 00:01:28 sjg Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * lstMember.c -- - * See if a given datum is on a given list. - */ - -#include "lstInt.h" - -LstNode -Lst_Member(Lst l, void *d) -{ - List list = l; - ListNode lNode; - - if (list == NULL) { - return NULL; - } - lNode = list->firstPtr; - if (lNode == NULL) { - return NULL; - } - - do { - if (lNode->datum == d) { - return lNode; - } - lNode = lNode->nextPtr; - } while (lNode != NULL && lNode != list->firstPtr); - - return NULL; -} diff --git a/lst.lib/lstNext.c b/lst.lib/lstNext.c deleted file mode 100644 index 9c180d2cfad1..000000000000 --- a/lst.lib/lstNext.c +++ /dev/null @@ -1,119 +0,0 @@ -/* $NetBSD: lstNext.c,v 1.13 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstNext.c,v 1.13 2020/07/03 08:37:57 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstNext.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstNext.c,v 1.13 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstNext.c -- - * Return the next node for a list. - * 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. Because the list is circular, Lst_Next - * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be - * used to determine when to stop. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Next -- - * Return the next node for the given list. - * - * Results: - * The next node or NULL if the list has yet to be opened. Also - * if the list is non-circular and the end has been reached, NULL - * is returned. - * - * Side Effects: - * the curPtr field is updated. - * - *----------------------------------------------------------------------- - */ -LstNode -Lst_Next(Lst l) -{ - ListNode tln; - List list = l; - - if ((LstValid (l) == FALSE) || - (list->isOpen == FALSE)) { - return NULL; - } - - list->prevPtr = list->curPtr; - - if (list->curPtr == NULL) { - if (list->atEnd == Unknown) { - /* - * If we're just starting out, atEnd will be Unknown. - * Then we want to start this thing off in the right - * direction -- at the start with atEnd being Middle. - */ - list->curPtr = tln = list->firstPtr; - list->atEnd = Middle; - } else { - tln = NULL; - list->atEnd = Tail; - } - } else { - tln = list->curPtr->nextPtr; - list->curPtr = tln; - - if (tln == list->firstPtr || tln == NULL) { - /* - * If back at the front, then we've hit the end... - */ - list->atEnd = Tail; - } else { - /* - * Reset to Middle if gone past first. - */ - list->atEnd = Middle; - } - } - - return tln; -} diff --git a/lst.lib/lstOpen.c b/lst.lib/lstOpen.c deleted file mode 100644 index 919dd6d5000c..000000000000 --- a/lst.lib/lstOpen.c +++ /dev/null @@ -1,86 +0,0 @@ -/* $NetBSD: lstOpen.c,v 1.13 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstOpen.c,v 1.13 2020/07/03 08:37:57 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstOpen.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstOpen.c,v 1.13 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstOpen.c -- - * Open a list for sequential access. 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. - * If the list is circular, Lst_Next and Lst_Prev will go around - * the list forever. Lst_IsAtEnd must be used to determine when to stop. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Open -- - * Open a list for sequential access. A list can still be searched, - * etc., without confusing these functions. - * - * Results: - * SUCCESS or FAILURE. - * - * Side Effects: - * isOpen is set TRUE and curPtr is set to NULL so the - * other sequential functions no it was just opened and can choose - * the first element accessed based on this. - * - *----------------------------------------------------------------------- - */ -ReturnStatus -Lst_Open(Lst l) -{ - if (LstValid (l) == FALSE) { - return FAILURE; - } - (l)->isOpen = TRUE; - (l)->atEnd = LstIsEmpty (l) ? Head : Unknown; - (l)->curPtr = NULL; - - return SUCCESS; -} diff --git a/lst.lib/lstPrev.c b/lst.lib/lstPrev.c deleted file mode 100644 index b6c548d9a523..000000000000 --- a/lst.lib/lstPrev.c +++ /dev/null @@ -1,78 +0,0 @@ -/* $NetBSD: lstPrev.c,v 1.4 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstPrev.c,v 1.4 2020/07/03 08:37:57 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstSucc.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstPrev.c,v 1.4 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstPrev.c -- - * return the predecessor to a given node - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Prev -- - * Return the predecessor to the given node on its list. - * - * Results: - * The predecessor of the node, if it exists (note that on a circular - * list, if the node is the only one in the list, it is its own - * predecessor). - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -LstNode -Lst_Prev(LstNode ln) -{ - if (ln == NULL) { - return NULL; - } else { - return ln->prevPtr; - } -} diff --git a/lst.lib/lstRemove.c b/lst.lib/lstRemove.c deleted file mode 100644 index 59245499bdc4..000000000000 --- a/lst.lib/lstRemove.c +++ /dev/null @@ -1,134 +0,0 @@ -/* $NetBSD: lstRemove.c,v 1.17 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstRemove.c,v 1.17 2020/07/03 08:37:57 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstRemove.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstRemove.c,v 1.17 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstRemove.c -- - * Remove an element from a list - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Remove -- - * Remove the given node from the given list. - * - * Results: - * SUCCESS or FAILURE. - * - * Side Effects: - * The list's firstPtr will be set to NULL if ln is the last - * node on the list. firsPtr and lastPtr will be altered if ln is - * either the first or last node, respectively, on the list. - * - *----------------------------------------------------------------------- - */ -ReturnStatus -Lst_Remove(Lst l, LstNode ln) -{ - List list = l; - ListNode lNode = ln; - - if (!LstValid (l) || !LstNodeValid (ln, l)) { - return FAILURE; - } - - /* - * unlink it from the list - */ - if (lNode->nextPtr != NULL) { - lNode->nextPtr->prevPtr = lNode->prevPtr; - } - if (lNode->prevPtr != NULL) { - lNode->prevPtr->nextPtr = lNode->nextPtr; - } - - /* - * if either the firstPtr or lastPtr of the list point to this node, - * adjust them accordingly - */ - if (list->firstPtr == lNode) { - list->firstPtr = lNode->nextPtr; - } - if (list->lastPtr == lNode) { - list->lastPtr = lNode->prevPtr; - } - - /* - * 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 (prevPtr == NULL), we set the - * end to be Unknown, since it is. - */ - if (list->isOpen && (list->curPtr == lNode)) { - list->curPtr = list->prevPtr; - if (list->curPtr == NULL) { - list->atEnd = Unknown; - } - } - - /* - * the only way firstPtr can still point to ln is if ln is the last - * node on the list (the list is circular, so lNode->nextptr == lNode in - * this case). The list is, therefore, empty and is marked as such - */ - if (list->firstPtr == lNode) { - list->firstPtr = NULL; - } - - /* - * note that the datum is unmolested. The caller must free it as - * necessary and as expected. - */ - if (lNode->useCount == 0) { - free(ln); - } else { - lNode->flags |= LN_DELETED; - } - - return SUCCESS; -} diff --git a/lst.lib/lstReplace.c b/lst.lib/lstReplace.c deleted file mode 100644 index f30cb00855e3..000000000000 --- a/lst.lib/lstReplace.c +++ /dev/null @@ -1,77 +0,0 @@ -/* $NetBSD: lstReplace.c,v 1.14 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstReplace.c,v 1.14 2020/07/03 08:37:57 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstReplace.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstReplace.c,v 1.14 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstReplace.c -- - * Replace the datum in a node with a new datum - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Replace -- - * Replace the datum in the given node with the new datum - * - * Results: - * SUCCESS or FAILURE. - * - * Side Effects: - * The datum field fo the node is altered. - * - *----------------------------------------------------------------------- - */ -ReturnStatus -Lst_Replace(LstNode ln, void *d) -{ - if (ln == NULL) { - return FAILURE; - } else { - (ln)->datum = d; - return SUCCESS; - } -} diff --git a/lst.lib/lstSucc.c b/lst.lib/lstSucc.c deleted file mode 100644 index b3f73bb15fd2..000000000000 --- a/lst.lib/lstSucc.c +++ /dev/null @@ -1,78 +0,0 @@ -/* $NetBSD: lstSucc.c,v 1.14 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstSucc.c,v 1.14 2020/07/03 08:37:57 rillig Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstSucc.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstSucc.c,v 1.14 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstSucc.c -- - * return the successor to a given node - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Succ -- - * Return the successor to the given node on its list. - * - * Results: - * The successor of the node, if it exists (note that on a circular - * list, if the node is the only one in the list, it is its own - * successor). - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -LstNode -Lst_Succ(LstNode ln) -{ - if (ln == NULL) { - return NULL; - } else { - return ln->nextPtr; - } -} @@ -1,4 +1,4 @@ -/* $NetBSD: main.c,v 1.279 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: main.c,v 1.331 2020/08/30 19:56:02 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -69,7 +69,7 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: main.c,v 1.279 2020/07/03 08:13:23 rillig Exp $"; +static char rcsid[] = "$NetBSD: main.c,v 1.331 2020/08/30 19:56:02 rillig Exp $"; #else #include <sys/cdefs.h> #ifndef lint @@ -81,7 +81,7 @@ __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993\ #if 0 static char sccsid[] = "@(#)main.c 8.3 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: main.c,v 1.279 2020/07/03 08:13:23 rillig Exp $"); +__RCSID("$NetBSD: main.c,v 1.331 2020/08/30 19:56:02 rillig Exp $"); #endif #endif /* not lint */ #endif @@ -124,13 +124,13 @@ __RCSID("$NetBSD: main.c,v 1.279 2020/07/03 08:13:23 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 <ctype.h> #include "make.h" #include "hash.h" @@ -162,7 +162,8 @@ 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 */ +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 */ @@ -180,14 +181,13 @@ Boolean beSilent; /* -s flag */ Boolean oldVars; /* variable substitution style */ Boolean checkEnvFirst; /* -e flag */ Boolean parseWarnFatal; /* -W flag */ -Boolean jobServer; /* -J 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 void *, const void *); +static int ReadMakefile(const char *); static void usage(void) MAKE_ATTR_DEAD; static void purge_cached_realpaths(void); @@ -257,7 +257,7 @@ parse_debug_options(const char *argvalue) for (modules = argvalue; *modules; ++modules) { switch (*modules) { case 'A': - debug = ~0; + debug = ~(0|DEBUG_LINT); break; case 'a': debug |= DEBUG_ARCH; @@ -291,9 +291,15 @@ parse_debug_options(const char *argvalue) ++modules; } break; + case 'h': + debug |= DEBUG_HASH; + break; case 'j': debug |= DEBUG_JOB; break; + case 'L': + debug |= DEBUG_LINT; + break; case 'l': debug |= DEBUG_LOUD; break; @@ -375,7 +381,7 @@ debug_setbuf: /* * does path contain any relative components */ -static int +static Boolean is_relpath(const char *path) { const char *cp; @@ -383,10 +389,7 @@ is_relpath(const char *path) if (path[0] != '/') return TRUE; cp = path; - do { - cp = strstr(cp, "/."); - if (!cp) - break; + while ((cp = strstr(cp, "/.")) != NULL) { cp += 2; if (cp[0] == '/' || cp[0] == '\0') return TRUE; @@ -394,7 +397,7 @@ is_relpath(const char *path) if (cp[1] == '/' || cp[1] == '\0') return TRUE; } - } while (cp); + } return FALSE; } @@ -416,7 +419,7 @@ static void MainParseArgs(int argc, char **argv) { char *p; - int c = '?'; + char c = '?'; int arginc; char *argvalue; const char *getopt_def; @@ -531,7 +534,6 @@ rearg: } else { Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); - jobServer = TRUE; } break; case 'N': @@ -553,7 +555,7 @@ rearg: case 'v': if (argvalue == NULL) goto noarg; printVars = c == 'v' ? EXPAND_VARS : COMPAT_VARS; - (void)Lst_AtEnd(variables, argvalue); + Lst_Append(variables, argvalue); Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); break; @@ -581,7 +583,7 @@ rearg: break; case 'f': if (argvalue == NULL) goto noarg; - (void)Lst_AtEnd(makefiles, argvalue); + Lst_Append(makefiles, argvalue); break; case 'i': ignoreErrors = TRUE; @@ -674,7 +676,7 @@ rearg: Punt("illegal (null) argument."); if (*argv[1] == '-' && !dashDash) goto rearg; - (void)Lst_AtEnd(create, bmake_strdup(argv[1])); + Lst_Append(create, bmake_strdup(argv[1])); } return; @@ -705,12 +707,10 @@ noarg: void Main_ParseArgLine(const char *line) { - char **argv; /* Manufactured argument vector */ - int argc; /* Number of arguments in argv */ - char *args; /* Space used by the args */ - char *buf, *p1; - char *argv0 = Var_Value(".MAKE", VAR_GLOBAL, &p1); - size_t len; + Words words; + char *p1; + const char *argv0 = Var_Value(".MAKE", VAR_GLOBAL, &p1); + char *buf; if (line == NULL) return; @@ -733,21 +733,19 @@ Main_ParseArgLine(const char *line) return; } #endif - buf = bmake_malloc(len = strlen(line) + strlen(argv0) + 2); - (void)snprintf(buf, len, "%s %s", argv0, line); + buf = str_concat3(argv0, " ", line); free(p1); - argv = brk_string(buf, &argc, TRUE, &args); - if (argv == NULL) { + words = Str_Words(buf, TRUE); + if (words.words == NULL) { Error("Unterminated quoted string [%s]", buf); free(buf); return; } free(buf); - MainParseArgs(argc, argv); + MainParseArgs((int)words.len, words.words); - free(args); - free(argv); + Words_Free(words); } Boolean @@ -775,7 +773,7 @@ Main_SetObjdir(const char *fmt, ...) (void)fprintf(stderr, "make warning: %s: %s.\n", path, strerror(errno)); } else { - strncpy(objdir, path, MAXPATHLEN); + snprintf(objdir, sizeof objdir, "%s", path); Var_Set(".OBJDIR", objdir, VAR_GLOBAL); setenv("PWD", objdir, 1); Dir_InitDot(); @@ -792,37 +790,44 @@ Main_SetObjdir(const char *fmt, ...) static Boolean Main_SetVarObjdir(const char *var, const char *suffix) { - char *p, *path, *xpath; + char *path_freeIt; + const char *path = Var_Value(var, VAR_CMD, &path_freeIt); + const char *xpath; + char *xpath_freeIt; - if ((path = Var_Value(var, VAR_CMD, &p)) == NULL || - *path == '\0') + if (path == NULL || path[0] == '\0') { + bmake_free(path_freeIt); return FALSE; + } /* expand variable substitutions */ + xpath = path; + xpath_freeIt = NULL; if (strchr(path, '$') != 0) - xpath = Var_Subst(NULL, path, VAR_GLOBAL, VARF_WANTRES); - else - xpath = path; + xpath = xpath_freeIt = Var_Subst(path, VAR_GLOBAL, + VARE_WANTRES); (void)Main_SetObjdir("%s%s", xpath, suffix); - if (xpath != path) - free(xpath); - free(p); + bmake_free(xpath_freeIt); + bmake_free(path_freeIt); return TRUE; } -/*- - * ReadAllMakefiles -- - * wrapper around ReadMakefile() to read all. - * - * Results: - * TRUE if ok, FALSE on error - */ -static int -ReadAllMakefiles(const void *p, const void *q) +/* 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 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(p, q) == 0; + return ReadMakefile(fname) != 0; } int @@ -835,7 +840,7 @@ str2Lst_Append(Lst lp, char *str, const char *sep) sep = " \t"; for (n = 0, cp = strtok(str, sep); cp; cp = strtok(NULL, sep)) { - (void)Lst_AtEnd(lp, cp); + Lst_Append(lp, cp); n++; } return n; @@ -863,13 +868,13 @@ siginfo(int signo MAKE_ATTR_UNUSED) void MakeMode(const char *mode) { - char *mp = NULL; + char *mode_freeIt = NULL; - if (!mode) - mode = mp = Var_Subst(NULL, "${" MAKE_MODE ":tl}", - VAR_GLOBAL, VARF_WANTRES); + if (mode == NULL) + mode = mode_freeIt = Var_Subst("${" MAKE_MODE ":tl}", + VAR_GLOBAL, VARE_WANTRES); - if (mode && *mode) { + if (mode[0] != '\0') { if (strstr(mode, "compat")) { compatMake = TRUE; forceJobs = FALSE; @@ -880,7 +885,7 @@ MakeMode(const char *mode) #endif } - free(mp); + free(mode_freeIt); } static void @@ -896,15 +901,13 @@ doPrintVars(void) else expandVars = getBoolean(".MAKE.EXPAND_VARIABLES", FALSE); - for (ln = Lst_First(variables); ln != NULL; - ln = Lst_Succ(ln)) { - char *var = (char *)Lst_Datum(ln); - char *value; + 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(NULL, var, VAR_GLOBAL, - VARF_WANTRES); + value = p1 = Var_Subst(var, VAR_GLOBAL, VARE_WANTRES); } else if (expandVars) { char tmp[128]; int len = snprintf(tmp, sizeof(tmp), "${%s}", var); @@ -912,13 +915,12 @@ doPrintVars(void) if (len >= (int)sizeof(tmp)) Fatal("%s: variable name too big: %s", progname, var); - value = p1 = Var_Subst(NULL, tmp, VAR_GLOBAL, - VARF_WANTRES); + value = p1 = Var_Subst(tmp, VAR_GLOBAL, VARE_WANTRES); } else { value = Var_Value(var, VAR_GLOBAL, &p1); } printf("%s\n", value ? value : ""); - free(p1); + bmake_free(p1); } } @@ -962,7 +964,7 @@ runTargets(void) Compat_Run(targs); outOfDate = FALSE; } - Lst_Destroy(targs, NULL); + Lst_Free(targs); return outOfDate; } @@ -1079,7 +1081,7 @@ main(int argc, char **argv) #else #ifndef MACHINE_ARCH #ifdef MAKE_MACHINE_ARCH - machine_arch = MAKE_MACHINE_ARCH; + machine_arch = MAKE_MACHINE_ARCH; #else machine_arch = "unknown"; #endif @@ -1113,11 +1115,11 @@ main(int argc, char **argv) VAR_GLOBAL); Var_Set(MAKE_DEPENDFILE, ".depend", VAR_GLOBAL); - create = Lst_Init(FALSE); - makefiles = Lst_Init(FALSE); + create = Lst_Init(); + makefiles = Lst_Init(); printVars = 0; debugVflag = FALSE; - variables = Lst_Init(FALSE); + variables = Lst_Init(); beSilent = FALSE; /* Print commands as executed */ ignoreErrors = FALSE; /* Pay attention to non-zero returns */ noExecute = FALSE; /* Execute all commands */ @@ -1199,7 +1201,7 @@ main(int argc, char **argv) #ifdef USE_META meta_init(); #endif - Dir_Init(NULL); /* Dir_* safe to call from MainParseArgs */ + Dir_Init(); /* * First snag any flags out of the MAKE environment variable. @@ -1265,8 +1267,8 @@ main(int argc, char **argv) (void)strncpy(curdir, pwd, MAXPATHLEN); } } - free(ptmp1); - free(ptmp2); + bmake_free(ptmp1); + bmake_free(ptmp2); } #endif Var_Set(".CURDIR", curdir, VAR_GLOBAL); @@ -1280,7 +1282,7 @@ main(int argc, char **argv) * and * finally _PATH_OBJDIRPREFIX`pwd`, in that order. If none * of these paths exist, just use .CURDIR. */ - Dir_Init(curdir); + Dir_InitDir(curdir); (void)Main_SetObjdir("%s", curdir); if (!Main_SetVarObjdir("MAKEOBJDIRPREFIX", curdir) && @@ -1312,10 +1314,8 @@ main(int argc, char **argv) if (!Lst_IsEmpty(create)) { LstNode ln; - for (ln = Lst_First(create); ln != NULL; - ln = Lst_Succ(ln)) { - char *name = (char *)Lst_Datum(ln); - + for (ln = Lst_First(create); ln != NULL; ln = LstNode_Next(ln)) { + char *name = LstNode_Datum(ln); Var_Append(".TARGETS", name, VAR_GLOBAL); } } else @@ -1327,7 +1327,8 @@ main(int argc, char **argv) * add the directories from the DEFSYSPATH (more than one may be given * as dir1:...:dirn) to the system include path. */ - if (syspath == NULL || *syspath == '\0') + /* XXX: mismatch: the -m option sets sysIncPath, not syspath */ + if (syspath == NULL || syspath[0] == '\0') syspath = defsyspath; else syspath = bmake_strdup(syspath); @@ -1353,48 +1354,46 @@ main(int argc, char **argv) /* * Read in the built-in rules first, followed by the specified - * makefile, if it was (makefile != NULL), or the default - * makefile and Makefile, in that order, if it wasn't. + * 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(FALSE); + 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, NULL, ReadMakefile); + ln = Lst_Find(sysMkPath, ReadMakefileSucceeded, NULL); if (ln == NULL) Fatal("%s: cannot open %s.", progname, - (char *)Lst_Datum(ln)); + (char *)LstNode_Datum(Lst_First(sysMkPath))); } if (!Lst_IsEmpty(makefiles)) { LstNode ln; - ln = Lst_Find(makefiles, NULL, ReadAllMakefiles); + ln = Lst_Find(makefiles, ReadMakefileFailed, NULL); if (ln != NULL) Fatal("%s: cannot open %s.", progname, - (char *)Lst_Datum(ln)); + (char *)LstNode_Datum(ln)); } else { - p1 = Var_Subst(NULL, "${" MAKEFILE_PREFERENCE "}", - VAR_CMD, VARF_WANTRES); - if (p1) { + p1 = Var_Subst("${" MAKEFILE_PREFERENCE "}", + VAR_CMD, VARE_WANTRES); (void)str2Lst_Append(makefiles, p1, NULL); - (void)Lst_Find(makefiles, NULL, ReadMakefile); + (void)Lst_Find(makefiles, ReadMakefileSucceeded, NULL); free(p1); - } } /* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */ if (!noBuiltins || !printVars) { - makeDependfile = Var_Subst(NULL, "${.MAKE.DEPENDFILE:T}", - VAR_CMD, VARF_WANTRES); + makeDependfile = Var_Subst("${.MAKE.DEPENDFILE:T}", + VAR_CMD, VARE_WANTRES); doing_depend = TRUE; - (void)ReadMakefile(makeDependfile, NULL); + (void)ReadMakefile(makeDependfile); doing_depend = FALSE; } @@ -1404,14 +1403,14 @@ main(int argc, char **argv) MakeMode(NULL); Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL, &p1), VAR_GLOBAL); - free(p1); + bmake_free(p1); if (!forceJobs && !compatMake && Var_Exists(".MAKE.JOBS", VAR_GLOBAL)) { char *value; int n; - value = Var_Subst(NULL, "${.MAKE.JOBS}", VAR_GLOBAL, VARF_WANTRES); + 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", @@ -1439,8 +1438,9 @@ main(int argc, char **argv) if (!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); + fprintf(debug_file, + "job_pipe %d %d, maxjobs %d, tokens %d, compat %d\n", + jp_0, jp_1, maxJobs, maxJobTokens, compatMake ? 1 : 0); if (!printVars) Main_ExportMAKEFLAGS(TRUE); /* initial export */ @@ -1461,7 +1461,7 @@ main(int argc, char **argv) */ static char VPATH[] = "${VPATH}"; - vpath = Var_Subst(NULL, VPATH, VAR_CMD, VARF_WANTRES); + vpath = Var_Subst(VPATH, VAR_CMD, VARE_WANTRES); path = vpath; do { /* skip to end of directory */ @@ -1502,9 +1502,9 @@ main(int argc, char **argv) } #ifdef CLEANUP - Lst_Destroy(variables, NULL); - Lst_Destroy(makefiles, NULL); - Lst_Destroy(create, (FreeProc *)free); + Lst_Free(variables); + Lst_Free(makefiles); + Lst_Destroy(create, free); #endif /* print the graph now it's been processed if the user requested it */ @@ -1522,7 +1522,7 @@ main(int argc, char **argv) meta_finish(); #endif Suff_End(); - Targ_End(); + Targ_End(); Arch_End(); Var_End(); Parse_End(); @@ -1533,23 +1533,16 @@ main(int argc, char **argv) return outOfDate ? 1 : 0; } -/*- - * ReadMakefile -- - * Open and parse the given makefile. +/* Open and parse the given makefile, with all its side effects. * * Results: * 0 if ok. -1 if couldn't open file. - * - * Side Effects: - * lots */ static int -ReadMakefile(const void *p, const void *q MAKE_ATTR_UNUSED) +ReadMakefile(const char *fname) { - const char *fname = p; /* makefile to read */ int fd; - size_t len = MAXPATHLEN; - char *name, *path = bmake_malloc(len); + char *name, *path = NULL; if (!strcmp(fname, "-")) { Parse_File(NULL /*stdin*/, -1); @@ -1557,22 +1550,16 @@ ReadMakefile(const void *p, const void *q MAKE_ATTR_UNUSED) } else { /* if we've chdir'd, rebuild the path name */ if (strcmp(curdir, objdir) && *fname != '/') { - size_t plen = strlen(curdir) + strlen(fname) + 2; - if (len < plen) - path = bmake_realloc(path, len = 2 * plen); - - (void)snprintf(path, len, "%s/%s", curdir, fname); + path = str_concat3(curdir, "/", fname); fd = open(path, O_RDONLY); if (fd != -1) { fname = path; goto found; } + free(path); /* If curdir failed, try objdir (ala .depend) */ - plen = strlen(objdir) + strlen(fname) + 2; - if (len < plen) - path = bmake_realloc(path, len = 2 * plen); - (void)snprintf(path, len, "%s/%s", objdir, fname); + path = str_concat3(objdir, "/", fname); fd = open(path, O_RDONLY); if (fd != -1) { fname = path; @@ -1613,31 +1600,32 @@ found: /*- * Cmd_Exec -- * Execute the command in cmd, and return the output of that command - * in a string. + * in a string. In the output, newlines are replaced with spaces. * * Results: - * A string containing the output of the command, or the empty string - * If errnum is not NULL, it contains the reason for the command failure + * A string containing the output of the command, or the empty string. + * *errfmt returns a format string describing the command failure, + * if any, using a single %s conversion specification. * * Side Effects: * The string must be freed by the caller. */ char * -Cmd_Exec(const char *cmd, const char **errnum) +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() */ - char *res; /* result */ WAIT_T status; /* command exit status */ Buffer buf; /* buffer to store the result */ + ssize_t bytes_read; + char *res; /* result */ + size_t res_len; char *cp; - int cc; /* bytes read, or -1 */ int savederr; /* saved errno */ - - *errnum = NULL; + *errfmt = NULL; if (!shellName) Shell_Init(); @@ -1653,7 +1641,7 @@ Cmd_Exec(const char *cmd, const char **errnum) * Open a pipe for fetching its output */ if (pipe(fds) == -1) { - *errnum = "Couldn't create pipe for \"%s\""; + *errfmt = "Couldn't create pipe for \"%s\""; goto bad; } @@ -1682,7 +1670,7 @@ Cmd_Exec(const char *cmd, const char **errnum) /*NOTREACHED*/ case -1: - *errnum = "Couldn't exec \"%s\""; + *errfmt = "Couldn't exec \"%s\""; goto bad; default: @@ -1696,12 +1684,12 @@ Cmd_Exec(const char *cmd, const char **errnum) do { char result[BUFSIZ]; - cc = read(fds[0], result, sizeof(result)); - if (cc > 0) - Buf_AddBytes(&buf, cc, result); + bytes_read = read(fds[0], result, sizeof(result)); + if (bytes_read > 0) + Buf_AddBytes(&buf, result, (size_t)bytes_read); } - while (cc > 0 || (cc == -1 && errno == EINTR)); - if (cc == -1) + while (bytes_read > 0 || (bytes_read == -1 && errno == EINTR)); + if (bytes_read == -1) savederr = errno; /* @@ -1716,43 +1704,28 @@ Cmd_Exec(const char *cmd, const char **errnum) JobReapChild(pid, status, FALSE); continue; } - cc = Buf_Size(&buf); + res_len = Buf_Size(&buf); res = Buf_Destroy(&buf, FALSE); if (savederr != 0) - *errnum = "Couldn't read shell's output for \"%s\""; + *errfmt = "Couldn't read shell's output for \"%s\""; if (WIFSIGNALED(status)) - *errnum = "\"%s\" exited on a signal"; + *errfmt = "\"%s\" exited on a signal"; else if (WEXITSTATUS(status) != 0) - *errnum = "\"%s\" returned non-zero status"; - - /* - * Null-terminate the result, convert newlines to spaces and - * install it in the variable. - */ - res[cc] = '\0'; - cp = &res[cc]; + *errfmt = "\"%s\" returned non-zero status"; - if (cc > 0 && *--cp == '\n') { - /* - * A final newline is just stripped - */ - *cp-- = '\0'; - } - while (cp >= res) { - if (*cp == '\n') { + /* Convert newlines to spaces. A final newline is just stripped */ + if (res_len > 0 && res[res_len - 1] == '\n') + res[res_len - 1] = '\0'; + for (cp = res; *cp != '\0'; cp++) + if (*cp == '\n') *cp = ' '; - } - cp--; - } break; } return res; bad: - res = bmake_malloc(1); - *res = '\0'; - return res; + return bmake_strdup(""); } /*- @@ -1888,7 +1861,7 @@ DieHorribly(void) */ void Finish(int errors) - /* number of errors encountered in Make_Make */ + /* number of errors encountered in Make_Make */ { if (dieQuietly(NULL, -1)) exit(2); @@ -1956,13 +1929,13 @@ usage(void) { char *p; if ((p = strchr(progname, '[')) != NULL) - *p = '\0'; + *p = '\0'; (void)fprintf(stderr, -"usage: %s [-BeikNnqrstWwX] \n\ - [-C directory] [-D variable] [-d flags] [-f makefile]\n\ - [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file]\n\ - [-V variable] [-v variable] [variable=value] [target ...]\n", +"usage: %s [-BeikNnqrstWwX] \n" +" [-C directory] [-D variable] [-d flags] [-f makefile]\n" +" [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file]\n" +" [-V variable] [-v variable] [variable=value] [target ...]\n", progname); exit(2); } @@ -2010,7 +1983,8 @@ char * cached_realpath(const char *pathname, char *resolved) { GNode *cache; - char *rp, *cp; + const char *rp; + char *cp; if (!pathname || !pathname[0]) return NULL; @@ -2024,7 +1998,7 @@ cached_realpath(const char *pathname, char *resolved) Var_Set(pathname, rp, cache); } /* else should we negative-cache? */ - free(cp); + bmake_free(cp); return rp ? resolved : NULL; } @@ -2070,9 +2044,14 @@ void PrintOnError(GNode *gn, const char *s) { static GNode *en = NULL; - char tmp[64]; + const char *expr; char *cp; + if (DEBUG(HASH)) { + Targ_Stats(); + Var_Stats(); + } + /* we generally want to keep quiet if a sub-make died */ if (dieQuietly(gn, -1)) return; @@ -2092,14 +2071,10 @@ PrintOnError(GNode *gn, const char *s) Var_Delete(".ERROR_CMD", VAR_GLOBAL); Lst_ForEach(gn->commands, addErrorCMD, gn); } - strncpy(tmp, "${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}", - sizeof(tmp) - 1); - cp = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES); - if (cp) { - if (*cp) - printf("%s", cp); - free(cp); - } + expr = "${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}"; + cp = Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES); + printf("%s", cp); + free(cp); fflush(stdout); /* @@ -2115,18 +2090,17 @@ PrintOnError(GNode *gn, const char *s) void Main_ExportMAKEFLAGS(Boolean first) { - static int once = 1; - char tmp[64]; + static Boolean once = TRUE; + const char *expr; char *s; if (once != first) return; - once = 0; + once = FALSE; - strncpy(tmp, "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}", - sizeof(tmp)); - s = Var_Subst(NULL, tmp, VAR_CMD, VARF_WANTRES); - if (s && *s) { + expr = "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}"; + s = Var_Subst(expr, VAR_CMD, VARE_WANTRES); + if (s[0] != '\0') { #ifdef POSIX setenv("MAKEFLAGS", s, 1); #else @@ -2147,8 +2121,8 @@ getTmpdir(void) * Honor $TMPDIR but only if it is valid. * Ensure it ends with /. */ - tmpdir = Var_Subst(NULL, "${TMPDIR:tA:U" _PATH_TMP "}/", VAR_GLOBAL, - VARF_WANTRES); + tmpdir = Var_Subst("${TMPDIR:tA:U" _PATH_TMP "}/", VAR_GLOBAL, + VARE_WANTRES); if (stat(tmpdir, &st) < 0 || !S_ISDIR(st.st_mode)) { free(tmpdir); tmpdir = bmake_strdup(_PATH_TMP); @@ -2235,18 +2209,12 @@ s2Boolean(const char *s, Boolean bf) * is FALSE, otherwise TRUE. */ Boolean -getBoolean(const char *name, Boolean bf) +getBoolean(const char *name, Boolean fallback) { - char tmp[64]; - char *cp; - - if (snprintf(tmp, sizeof(tmp), "${%s:U:tl}", name) < (int)(sizeof(tmp))) { - cp = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES); - - if (cp) { - bf = s2Boolean(cp, bf); - free(cp); - } - } - return bf; + char *expr = str_concat3("${", name, ":U:tl}"); + char *value = Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES); + Boolean res = s2Boolean(value, fallback); + free(value); + free(expr); + return res; } diff --git a/make-conf.h b/make-conf.h index a85b86d3efb5..5b13e295ae0c 100644 --- a/make-conf.h +++ b/make-conf.h @@ -1,4 +1,4 @@ -/* $NetBSD: config.h,v 1.21 2012/03/31 00:12:24 christos Exp $ */ +/* $NetBSD: config.h,v 1.22 2020/09/01 17:40:34 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -100,7 +100,10 @@ * LIBSUFF * Is the suffix used to denote libraries and is used by the Suff module * to find the search path on which to seek any -l<xx> targets. - * + */ +#define LIBSUFF ".a" + +/* * RECHECK * If defined, Make_Update will check a target for its current * modification time after it has been re-made, setting it to the @@ -108,10 +111,9 @@ * 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 defined this. Under + * that don't have this problem, you should define this. Under * NFS you probably should not, unless you aren't exporting jobs. */ -#define LIBSUFF ".a" #define RECHECK /* @@ -1,4 +1,4 @@ -.\" $NetBSD: make.1,v 1.282 2020/06/06 20:28:42 wiz Exp $ +.\" $NetBSD: make.1,v 1.289 2020/08/28 17:15:04 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 June 5, 2020 +.Dd August 28, 2020 .Dt MAKE 1 .Os .Sh NAME @@ -166,8 +166,15 @@ Print the input graph after making everything, or before exiting on error. .It Ar "g3" Print the input graph before exiting on error. +.It Ar h +Print debugging information about hash table operations. .It Ar j Print debugging information about running multiple shells. +.It Ar L +Turn on lint checks. +This will throw errors for variable assignments that do not parse +correctly, at the time of assignment so the file and line number +are available. .It Ar l Print commands in Makefiles regardless of whether or not they are prefixed by .Ql @ @@ -311,7 +318,8 @@ as an argument). .It Fl n Display the commands that would have been executed, but do not actually execute them unless the target depends on the .MAKE special -source (see below). +source (see below) or the command is prefixed with +.Ql Ic + . .It Fl N Display the commands which would have been executed, but do not actually execute any of them; useful for debugging top-level makefiles @@ -412,37 +420,44 @@ or more sources. This creates a relationship where the targets .Dq depend on the sources -and are usually created from them. -The exact relationship between the target and the source is determined -by the operator that separates them. -The three operators are as follows: +and are customarily created from them. +A target is considered out-of-date if it does not exist, or if its +modification time is less than that of any of its sources. +An out-of-date target will be re-created, but not until all sources +have been examined and themselves re-created as needed. +Three operators may be used: .Bl -tag -width flag .It Ic \&: -A target is considered out-of-date if its modification time is less than -those of any of its sources. -Sources for a target accumulate over dependency lines when this operator -is used. -The target is removed if +Many dependency lines may name this target but only one may have +attached shell commands. +All sources named in all dependency lines are considered together, +and if needed the attached shell commands are run to create or +re-create the target. +If .Nm -is interrupted. +is interrupted, the target is removed. .It Ic \&! -Targets are always re-created, but not until all sources have been -examined and re-created as necessary. -Sources for a target accumulate over dependency lines when this operator -is used. -The target is removed if -.Nm -is interrupted. +The same, but the target is always re-created whether or not it is out +of date. .It Ic \&:: -If no sources are specified, the target is always re-created. -Otherwise, a target is considered out-of-date if any of its sources has -been modified more recently than the target. -Sources for a target do not accumulate over dependency lines when this -operator is used. -The target will not be removed if +Any dependency line may have attached shell commands, but each one +is handled independently: its sources are considered and the attached +shell commands are run if the target is out of date with respect to +(only) those sources. +Thus, different groups of the attached shell commands may be run +depending on the circumstances. +Furthermore, unlike +.Ic \&:, +for dependency lines with no sources, the attached shell +commands are always run. +Also unlike +.Ic \&:, +the target will not be removed if .Nm is interrupted. .El +All dependency lines mentioning a particular target must use the same +operator. .Pp Targets and sources may contain the shell wildcard values .Ql \&? , @@ -608,7 +623,7 @@ This shorter form is not recommended. .Pp If the variable name contains a dollar, then the name itself is expanded first. This allows almost arbitrary variable names, however names containing dollar, -braces, parenthesis, or whitespace are really best avoided! +braces, parentheses, or whitespace are really best avoided! .Pp If the result of expanding a variable contains a dollar sign .Pq Ql \&$ @@ -1126,6 +1141,9 @@ is set to the value of for all programs which .Nm executes. +.It Ev .SHELL +The pathname of the shell used to run target scripts. +It is read-only. .It Ev .TARGETS The list of targets explicitly specified on the command line, if any. .It Ev VPATH @@ -1171,7 +1189,7 @@ Replaces each word in the variable with its suffix. .It Cm \&:H Replaces each word in the variable with everything but the last component. .It Cm \&:M Ns Ar pattern -Select only those words that match +Selects only those words that match .Ar pattern . The standard shell wildcard characters .Pf ( Ql * , @@ -1195,11 +1213,11 @@ This is identical to but selects all words which do not match .Ar pattern . .It Cm \&:O -Order every word in variable alphabetically. +Orders every word in variable alphabetically. .It Cm \&:Or -Order every word in variable in reverse alphabetical order. +Orders every word in variable in reverse alphabetical order. .It Cm \&:Ox -Randomize words in variable. +Shuffles the words in variable. The results will be different each time you are referring to the modified variable; use the assignment with expansion .Pq Ql Cm \&:= @@ -1249,7 +1267,7 @@ If a .Va utc value is not provided or is 0, the current time is used. .It Cm \&:hash -Compute a 32-bit hash of the value and encode it as hex digits. +Computes a 32-bit hash of the value and encode it as hex digits. .It Cm \&:localtime[=utc] The value is a format string for .Xr strftime 3 , @@ -1259,7 +1277,7 @@ If a .Va utc value is not provided or is 0, the current time is used. .It Cm \&:tA -Attempt to convert variable to an absolute path using +Attempts to convert variable to an absolute path using .Xr realpath 3 , if that fails, the value is unchanged. .It Cm \&:tl @@ -1271,7 +1289,7 @@ This modifier sets the separator to the character If .Ar c is omitted, then no separator is used. -The common escapes (including octal numeric codes), work as expected. +The common escapes (including octal numeric codes) work as expected. .It Cm \&:tu Converts variable to upper-case letters. .It Cm \&:tW @@ -1287,21 +1305,21 @@ See also .Sm off .It Cm \&:S No \&/ Ar old_string No \&/ Ar new_string No \&/ Op Cm 1gW .Sm on -Modify the first occurrence of +Modifies the first occurrence of .Ar old_string -in the variable's value, replacing it with +in each word of the variable's value, replacing it with .Ar new_string . If a .Ql g -is appended to the last slash of the pattern, all occurrences +is appended to the last delimiter of the pattern, all occurrences in each word are replaced. If a .Ql 1 -is appended to the last slash of the pattern, only the first word +is appended to the last delimiter of the pattern, only the first occurrence is affected. If a .Ql W -is appended to the last slash of the pattern, +is appended to the last delimiter of the pattern, then the value is treated as a single word (possibly containing embedded white space). If @@ -1370,13 +1388,6 @@ as occur in the word or words it is found in; the .Ql W modifier causes the value to be treated as a single word (possibly containing embedded white space). -Note that -.Ql 1 -and -.Ql g -are orthogonal; the former specifies whether multiple words are -potentially affected, the latter whether multiple substitutions can -potentially occur within each affected word. .Pp As for the .Cm \&:S @@ -1387,9 +1398,9 @@ and are subjected to variable expansion before being parsed as regular expressions. .It Cm \&:T -Replaces each word in the variable with its last component. +Replaces each word in the variable with its last path component. .It Cm \&:u -Remove adjacent duplicate words (like +Removes adjacent duplicate words (like .Xr uniq 1 ) . .Sm off .It Cm \&:\&? Ar true_string Cm \&: Ar false_string @@ -1405,7 +1416,7 @@ usually contain variable expansions. A common error is trying to use expressions like .Dl ${NUMBERS:M42:?match:no} which actually tests defined(NUMBERS), -to determine is any words match "42" you need to use something like: +to determine if any words match "42" you need to use something like: .Dl ${"${NUMBERS:M42}" != \&"\&":?match:no} . .It Ar :old_string=new_string This is the @@ -1449,7 +1460,7 @@ in either the or .Ar old_string , only the first instance is treated specially (as the pattern character); -all subsequent instances are treated as regular characters +all subsequent instances are treated as regular characters. .Pp Variable expansion occurs in the normal fashion inside both .Ar old_string @@ -1466,11 +1477,10 @@ This is the loop expansion mechanism from the OSF Development Environment (ODE) make. Unlike .Cm \&.for -loops expansion occurs at the time of -reference. -Assign +loops, expansion occurs at the time of reference. +Assigns .Ar temp -to each word in the variable and evaluate +to each word in the variable and evaluates .Ar string . The ODE convention is that .Ar temp @@ -1481,7 +1491,7 @@ For example. However a single character variable is often more readable: .Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@} .It Cm \&:_[=var] -Save the current variable value in +Saves the current variable value in .Ql $_ or the named .Va var @@ -1502,7 +1512,7 @@ is used to save the result of the modifier which is later referenced using the index values from .Ql :range . .It Cm \&:U Ns Ar newval -If the variable is undefined +If the variable is undefined, .Ar newval is the value. If the variable is defined, the existing value is returned. @@ -1512,7 +1522,7 @@ It is handy for setting per-target CFLAGS for instance: If a value is only required if the variable is undefined, use: .Dl ${VAR:D:Unewval} .It Cm \&:D Ns Ar newval -If the variable is defined +If the variable is defined, .Ar newval is the value. .It Cm \&:L @@ -1641,7 +1651,7 @@ Returns the number of words in the value. .El \" :[range] .El .Sh INCLUDE STATEMENTS, CONDITIONALS AND FOR LOOPS -Makefile inclusion, conditional structures and for loops reminiscent +Makefile inclusion, conditional structures and for loops reminiscent of the C programming language are provided in .Nm . All such structures are identified by a line beginning with a single @@ -1687,7 +1697,7 @@ The possible conditionals are as follows: The message is printed along with the name of the makefile and line number, then .Nm -will exit. +will exit immediately. .It Ic .export Ar variable ... Export the specified global variable. If no variable list is provided, all globals are exported @@ -1876,7 +1886,7 @@ operator is not an integral 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 +variable is being compared against 0, or an empty string in the case of a string comparison. .Pp When @@ -1917,7 +1927,7 @@ The syntax of a for loop is: .Pp .Bl -tag -compact -width Ds .It Ic \&.for Ar variable Oo Ar variable ... Oc Ic in Ar expression -.It Aq make-rules +.It Aq make-lines .It Ic \&.endfor .El .Pp @@ -1929,7 +1939,7 @@ On each iteration of the loop, one word is taken and assigned to each in order, and these .Ic variables are substituted into the -.Ic make-rules +.Ic make-lines inside the body of the for loop. The number of words must come out even; that is, if there are three iteration variables, the number of words provided must be a multiple @@ -1,4 +1,4 @@ -/* $NetBSD: make.c,v 1.99 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: make.c,v 1.133 2020/08/30 14:11:42 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -69,14 +69,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: make.c,v 1.99 2020/07/03 08:13:23 rillig Exp $"; +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.99 2020/07/03 08:13:23 rillig Exp $"); +__RCSID("$NetBSD: make.c,v 1.133 2020/08/30 14:11:42 rillig Exp $"); #endif #endif /* not lint */ #endif @@ -116,7 +116,7 @@ __RCSID("$NetBSD: make.c,v 1.99 2020/07/03 08:13:23 rillig Exp $"); */ #include "make.h" -#include "hash.h" +#include "enum.h" #include "dir.h" #include "job.h" @@ -151,6 +151,43 @@ make_abort(GNode *gn, int line) abort(); } +ENUM_VALUE_RTTI_8(GNodeMade, + UNMADE, DEFERRED, REQUESTED, BEINGMADE, + MADE, UPTODATE, ERROR, ABORTED); + +ENUM_FLAGS_RTTI_31(GNodeType, + OP_DEPENDS, OP_FORCE, OP_DOUBLEDEP, + /* OP_OPMASK is omitted since it combines other flags */ + OP_OPTIONAL, OP_USE, OP_EXEC, OP_IGNORE, + OP_PRECIOUS, OP_SILENT, OP_MAKE, OP_JOIN, + OP_MADE, OP_SPECIAL, OP_USEBEFORE, OP_INVISIBLE, + OP_NOTMAIN, OP_PHONY, OP_NOPATH, OP_WAIT, + OP_NOMETA, OP_META, OP_NOMETA_CMP, OP_SUBMAKE, + OP_TRANSFORM, OP_MEMBER, OP_LIB, OP_ARCHV, + OP_HAS_COMMANDS, OP_SAVE_CMDS, OP_DEPS_FOUND, OP_MARK); + +ENUM_FLAGS_RTTI_10(GNodeFlags, + REMAKE, CHILDMADE, FORCE, DONE_WAIT, + DONE_ORDER, FROM_DEPEND, DONE_ALLSRC, CYCLE, + DONECYCLE, INTERNAL); + +void +GNode_FprintDetails(FILE *f, const char *prefix, const GNode *gn, + const char *suffix) +{ + char type_buf[GNodeType_ToStringSize]; + char flags_buf[GNodeFlags_ToStringSize]; + + fprintf(f, "%smade %s, type %s, flags %s%s", + prefix, + Enum_ValueToString(gn->made, GNodeMade_ToStringSpecs), + Enum_FlagsToString(type_buf, sizeof type_buf, + gn->type, GNodeType_ToStringSpecs), + Enum_FlagsToString(flags_buf, sizeof flags_buf, + gn->flags, GNodeFlags_ToStringSpecs), + suffix); +} + /*- *----------------------------------------------------------------------- * Make_TimeStamp -- @@ -189,7 +226,7 @@ MakeTimeStamp(void *pgn, void *cgn) { return Make_TimeStamp((GNode *)pgn, (GNode *)cgn); } - + /*- *----------------------------------------------------------------------- * Make_OODate -- @@ -350,7 +387,7 @@ Make_OODate(GNode *gn) return oodate; } - + /*- *----------------------------------------------------------------------- * MakeAddChild -- @@ -378,11 +415,11 @@ MakeAddChild(void *gnp, void *lp) if (DEBUG(MAKE)) fprintf(debug_file, "MakeAddChild: need to examine %s%s\n", gn->name, gn->cohort_num); - (void)Lst_EnQueue(l, gn); + Lst_Enqueue(l, gn); } return 0; } - + /*- *----------------------------------------------------------------------- * MakeFindChild -- @@ -412,31 +449,18 @@ MakeFindChild(void *gnp, void *pgnp) return 0; } - -/*- - *----------------------------------------------------------------------- - * Make_HandleUse -- - * Function called by Make_Run and SuffApplyTransform on the downward - * pass to handle .USE and transformation nodes. It implements the - * .USE and transformation functionality by copying the node's commands, - * type flags and children to the parent node. + +/* Called by Make_Run and SuffApplyTransform on the downward pass to handle + * .USE and transformation nodes, by copying the child node's commands, type + * flags and children to the parent node. * - * A .USE node is much like an explicit transformation rule, except - * its commands are always added to the target node, even if the - * target already has commands. + * A .USE node is much like an explicit transformation rule, except its + * commands are always added to the target node, even if the target already + * has commands. * * Input: * cgn The .USE node * pgn The target of the .USE node - * - * Results: - * none - * - * Side Effects: - * Children and commands may be added to the parent and the parent's - * type may be changed. - * - *----------------------------------------------------------------------- */ void Make_HandleUse(GNode *cgn, GNode *pgn) @@ -452,52 +476,42 @@ Make_HandleUse(GNode *cgn, GNode *pgn) if ((cgn->type & (OP_USE|OP_USEBEFORE)) || Lst_IsEmpty(pgn->commands)) { if (cgn->type & OP_USEBEFORE) { - /* - * .USEBEFORE -- - * prepend the child's commands to the parent. - */ - Lst cmds = pgn->commands; - pgn->commands = Lst_Duplicate(cgn->commands, NULL); - (void)Lst_Concat(pgn->commands, cmds, LST_CONCNEW); - Lst_Destroy(cmds, NULL); + /* .USEBEFORE */ + Lst_PrependAll(pgn->commands, cgn->commands); } else { - /* - * .USE or target has no commands -- - * append the child's commands to the parent. - */ - (void)Lst_Concat(pgn->commands, cgn->commands, LST_CONCNEW); + /* .USE, or target has no commands */ + Lst_AppendAll(pgn->commands, cgn->commands); } } - if (Lst_Open(cgn->children) == SUCCESS) { - while ((ln = Lst_Next(cgn->children)) != NULL) { - GNode *tgn, *gn = (GNode *)Lst_Datum(ln); + Lst_Open(cgn->children); + while ((ln = Lst_Next(cgn->children)) != NULL) { + GNode *gn = LstNode_Datum(ln); - /* - * Expand variables in the .USE node's name - * and save the unexpanded form. - * We don't need to do this for commands. - * They get expanded properly when we execute. - */ - if (gn->uname == NULL) { - gn->uname = gn->name; - } else { - free(gn->name); - } - gn->name = Var_Subst(NULL, gn->uname, pgn, VARF_WANTRES); - if (gn->name && gn->uname && strcmp(gn->name, gn->uname) != 0) { - /* See if we have a target for this node. */ - tgn = Targ_FindNode(gn->name, TARG_NOCREATE); - if (tgn != NULL) - gn = tgn; - } - - (void)Lst_AtEnd(pgn->children, gn); - (void)Lst_AtEnd(gn->parents, pgn); - pgn->unmade += 1; + /* + * Expand variables in the .USE node's name + * and save the unexpanded form. + * We don't need to do this for commands. + * They get expanded properly when we execute. + */ + if (gn->uname == NULL) { + gn->uname = gn->name; + } else { + free(gn->name); } - Lst_Close(cgn->children); + gn->name = Var_Subst(gn->uname, pgn, VARE_WANTRES); + 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); + if (tgn != NULL) + gn = tgn; + } + + Lst_Append(pgn->children, gn); + Lst_Append(gn->parents, pgn); + pgn->unmade += 1; } + Lst_Close(cgn->children); pgn->type |= cgn->type & ~(OP_OPMASK|OP_USE|OP_USEBEFORE|OP_TRANSFORM); } @@ -547,7 +561,7 @@ 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_Member(pgn->children, cgn)) != NULL) { + if ((ln = Lst_FindDatum(pgn->children, cgn)) != NULL) { Lst_Remove(pgn->children, ln); pgn->unmade--; } @@ -680,8 +694,8 @@ void Make_Update(GNode *cgn) { GNode *pgn; /* the parent node */ - char *cname; /* the child's name */ - LstNode ln; /* Element in parents and iParents lists */ + const char *cname; /* the child's name */ + LstNode ln; /* Element in parents and implicitParents lists */ time_t mtime = -1; char *p1; Lst parents; @@ -691,7 +705,7 @@ Make_Update(GNode *cgn) checked++; cname = Var_Value(TARGET, cgn, &p1); - free(p1); + bmake_free(p1); if (DEBUG(MAKE)) fprintf(debug_file, "Make_Update: %s%s\n", cgn->name, cgn->cohort_num); @@ -724,123 +738,123 @@ Make_Update(GNode *cgn) Lst_ForEach(centurion->order_succ, MakeBuildParent, Lst_First(toBeMade)); /* Now mark all the parents as having one less unmade child */ - if (Lst_Open(parents) == SUCCESS) { - while ((ln = Lst_Next(parents)) != NULL) { - pgn = (GNode *)Lst_Datum(ln); - 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); - - if (!(pgn->flags & REMAKE)) { - /* This parent isn't needed */ - if (DEBUG(MAKE)) - fprintf(debug_file, "- not needed\n"); - continue; - } - if (mtime == 0 && !(cgn->type & OP_WAIT)) - pgn->flags |= FORCE; + Lst_Open(parents); + while ((ln = Lst_Next(parents)) != NULL) { + pgn = LstNode_Datum(ln); + 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); - /* - * If the parent has the .MADE attribute, its timestamp got - * updated to that of its newest child, and its unmake - * 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"); - continue; - } + if (!(pgn->flags & REMAKE)) { + /* This parent isn't needed */ + if (DEBUG(MAKE)) + fprintf(debug_file, "- not needed\n"); + continue; + } + if (mtime == 0 && !(cgn->type & OP_WAIT)) + pgn->flags |= FORCE; - if ( ! (cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE))) { - if (cgn->made == MADE) - pgn->flags |= CHILDMADE; - (void)Make_TimeStamp(pgn, cgn); - } + /* + * If the parent has the .MADE attribute, its timestamp got + * updated to that of its newest child, and its unmake + * 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"); + continue; + } - /* - * A parent must wait for the completion of all instances - * 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); - continue; - } + if ( ! (cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE))) { + if (cgn->made == MADE) + pgn->flags |= CHILDMADE; + (void)Make_TimeStamp(pgn, cgn); + } - /* One more child of this parent is now made */ - pgn->unmade -= 1; - if (pgn->unmade < 0) { - if (DEBUG(MAKE)) { - fprintf(debug_file, "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); - } + /* + * A parent must wait for the completion of all instances + * 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); + continue; + } - /* 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"); - continue; - } - if (pgn->made != DEFERRED) { - /* - * Either this parent is on a different branch of the tree, - * 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"); - continue; - } - if (pgn->order_pred - && Lst_ForEach(pgn->order_pred, MakeCheckOrder, 0)) { - /* A .ORDER rule stops us building this */ - continue; - } + /* One more child of this parent is now made */ + pgn->unmade -= 1; + if (pgn->unmade < 0) { 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); + fprintf(debug_file, "Graph cycles through %s%s\n", + pgn->name, pgn->cohort_num); + Targ_PrintGraph(2); } - /* Ok, we can schedule the parent again */ - pgn->made = REQUESTED; - (void)Lst_EnQueue(toBeMade, pgn); + Error("Graph cycles through %s%s", pgn->name, pgn->cohort_num); + } + + /* 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"); + continue; + } + if (pgn->made != DEFERRED) { + /* + * Either this parent is on a different branch of the tree, + * 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"); + continue; } - Lst_Close(parents); + assert(pgn->order_pred != NULL); + if (Lst_ForEach(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); + } + /* 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. */ - if (Lst_Open(cgn->iParents) == SUCCESS) { - char *cpref = Var_Value(PREFIX, cgn, &p1); + Lst_Open(cgn->implicitParents); + { + const char *cpref = Var_Value(PREFIX, cgn, &p1); - while ((ln = Lst_Next(cgn->iParents)) != NULL) { - pgn = (GNode *)Lst_Datum(ln); + 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); } } - free(p1); - Lst_Close(cgn->iParents); + bmake_free(p1); + Lst_Close(cgn->implicitParents); } } - + /*- *----------------------------------------------------------------------- * MakeAddAllSrc -- @@ -890,7 +904,7 @@ MakeAddAllSrc(void *cgnp, void *pgnp) cgn->type |= OP_MARK; if ((cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE|OP_INVISIBLE)) == 0) { - char *child, *allsrc; + const char *child, *allsrc; char *p1 = NULL, *p2 = NULL; if (cgn->type & OP_ARCHV) @@ -904,7 +918,7 @@ MakeAddAllSrc(void *cgnp, void *pgnp) } if (allsrc != NULL) Var_Append(ALLSRC, allsrc, pgn); - free(p2); + bmake_free(p2); if (pgn->type & OP_JOIN) { if (cgn->made == MADE) { Var_Append(OODATE, child, pgn); @@ -930,11 +944,11 @@ MakeAddAllSrc(void *cgnp, void *pgnp) */ Var_Append(OODATE, child, pgn); } - free(p1); + bmake_free(p1); } return 0; } - + /*- *----------------------------------------------------------------------- * Make_DoAllVar -- @@ -976,11 +990,11 @@ Make_DoAllVar(GNode *gn) if (gn->type & OP_JOIN) { char *p1; Var_Set(TARGET, Var_Value(ALLSRC, gn, &p1), gn); - free(p1); + bmake_free(p1); } gn->flags |= DONE_ALLSRC; } - + /*- *----------------------------------------------------------------------- * MakeStartJobs -- @@ -1023,7 +1037,8 @@ MakeBuildChild(void *v_cn, void *toBeMade_next) return 0; /* If this node is on the RHS of a .ORDER, check LHSs. */ - if (cn->order_pred && Lst_ForEach(cn->order_pred, MakeCheckOrder, 0)) { + assert(cn->order_pred); + if (Lst_ForEach(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 */ @@ -1035,7 +1050,7 @@ MakeBuildChild(void *v_cn, void *toBeMade_next) cn->made = REQUESTED; if (toBeMade_next == NULL) - Lst_AtEnd(toBeMade, cn); + Lst_Append(toBeMade, cn); else Lst_InsertBefore(toBeMade, toBeMade_next, cn); @@ -1072,13 +1087,13 @@ MakeStartJobs(void) GNode *gn; int have_token = 0; - while (!Lst_IsEmpty (toBeMade)) { + while (!Lst_IsEmpty(toBeMade)) { /* Get token now to avoid cycling job-list when we only have 1 token */ if (!have_token && !Job_TokenWithdraw()) break; have_token = 1; - gn = (GNode *)Lst_DeQueue(toBeMade); + gn = Lst_Dequeue(toBeMade); if (DEBUG(MAKE)) fprintf(debug_file, "Examining %s%s...\n", gn->name, gn->cohort_num); @@ -1147,7 +1162,7 @@ MakeStartJobs(void) return FALSE; } - + /*- *----------------------------------------------------------------------- * MakePrintStatus -- @@ -1179,15 +1194,15 @@ MakePrintStatusOrder(void *ognp, void *gnp) /* not waiting for this one */ return 0; - printf(" `%s%s' has .ORDER dependency against %s%s " - "(made %d, flags %x, type %x)\n", - gn->name, gn->cohort_num, - ogn->name, ogn->cohort_num, ogn->made, ogn->flags, ogn->type); - if (DEBUG(MAKE) && debug_file != stdout) - fprintf(debug_file, " `%s%s' has .ORDER dependency against %s%s " - "(made %d, flags %x, type %x)\n", - gn->name, gn->cohort_num, - ogn->name, ogn->cohort_num, ogn->made, ogn->flags, ogn->type); + printf(" `%s%s' has .ORDER dependency against %s%s ", + 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"); + } return 0; } @@ -1214,12 +1229,13 @@ MakePrintStatus(void *gnp, void *v_errors) case REQUESTED: case BEINGMADE: (*errors)++; - printf("`%s%s' was not built (made %d, flags %x, type %x)!\n", - gn->name, gn->cohort_num, gn->made, gn->flags, gn->type); - if (DEBUG(MAKE) && debug_file != stdout) - fprintf(debug_file, - "`%s%s' was not built (made %d, flags %x, type %x)!\n", - gn->name, gn->cohort_num, gn->made, gn->flags, gn->type); + 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"); + } /* Most likely problem is actually caused by .ORDER */ Lst_ForEach(gn->order_pred, MakePrintStatusOrder, gn); break; @@ -1262,7 +1278,7 @@ MakePrintStatus(void *gnp, void *v_errors) Lst_ForEach(gn->children, MakePrintStatus, errors); return 0; } - + /*- *----------------------------------------------------------------------- @@ -1281,7 +1297,7 @@ Make_ExpandUse(Lst targs) GNode *gn; /* a temporary pointer */ Lst examine; /* List of targets to examine */ - examine = Lst_Duplicate(targs, NULL); + examine = Lst_Copy(targs, NULL); /* * Make an initial downward pass over the graph, marking nodes to be made @@ -1291,8 +1307,8 @@ Make_ExpandUse(Lst targs) * be looked at in a minute, otherwise we add its children to our queue * and go on about our business. */ - while (!Lst_IsEmpty (examine)) { - gn = (GNode *)Lst_DeQueue(examine); + while (!Lst_IsEmpty(examine)) { + gn = Lst_Dequeue(examine); if (gn->flags & REMAKE) /* We've looked at this one already */ @@ -1302,13 +1318,8 @@ Make_ExpandUse(Lst targs) fprintf(debug_file, "Make_ExpandUse: examine %s%s\n", gn->name, gn->cohort_num); - if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (gn->cohorts)) { - /* Append all the 'cohorts' to the list of things to examine */ - Lst new; - new = Lst_Duplicate(gn->cohorts, NULL); - Lst_Concat(new, examine, LST_CONCLINK); - examine = new; - } + if (gn->type & OP_DOUBLEDEP) + Lst_PrependAll(examine, gn->cohorts); /* * Apply any .USE rules before looking for implicit dependencies @@ -1349,7 +1360,7 @@ Make_ExpandUse(Lst targs) Lst_ForEach(gn->children, MakeAddChild, examine); } - Lst_Destroy(examine, NULL); + Lst_Free(examine); } /*- @@ -1369,8 +1380,8 @@ link_parent(void *cnp, void *pnp) GNode *cn = cnp; GNode *pn = pnp; - Lst_AtEnd(pn->children, cn); - Lst_AtEnd(cn->parents, pn); + Lst_Append(pn->children, cn); + Lst_Append(cn->parents, pn); pn->unmade++; return 0; } @@ -1392,9 +1403,9 @@ add_wait_dep(void *v_cn, void *v_wn) fprintf(debug_file, ".WAIT: add dependency %s%s -> %s\n", cn->name, cn->cohort_num, wn->name); - Lst_AtEnd(wn->children, cn); + Lst_Append(wn->children, cn); wn->unmade++; - Lst_AtEnd(cn->parents, wn); + Lst_Append(cn->parents, wn); return 0; } @@ -1417,18 +1428,18 @@ Make_ProcessWait(Lst targs) pgn->flags = REMAKE; pgn->type = OP_PHONY | OP_DEPENDS; /* Get it displayed in the diag dumps */ - Lst_AtFront(Targ_List(), pgn); + Lst_Prepend(Targ_List(), pgn); Lst_ForEach(targs, link_parent, pgn); /* Start building with the 'dummy' .MAIN' node */ MakeBuildChild(pgn, NULL); - examine = Lst_Init(FALSE); - Lst_AtEnd(examine, pgn); + examine = Lst_Init(); + Lst_Append(examine, pgn); - while (!Lst_IsEmpty (examine)) { - pgn = Lst_DeQueue(examine); + while (!Lst_IsEmpty(examine)) { + pgn = Lst_Dequeue(examine); /* We only want to process each child-list once */ if (pgn->flags & DONE_WAIT) @@ -1437,30 +1448,25 @@ Make_ProcessWait(Lst targs) if (DEBUG(MAKE)) fprintf(debug_file, "Make_ProcessWait: examine %s\n", pgn->name); - if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (pgn->cohorts)) { - /* Append all the 'cohorts' to the list of things to examine */ - Lst new; - new = Lst_Duplicate(pgn->cohorts, NULL); - Lst_Concat(new, examine, LST_CONCLINK); - examine = new; - } + 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 = Lst_Datum(ln); + cgn = LstNode_Datum(ln); if (cgn->type & OP_WAIT) { /* Make the .WAIT node depend on the previous children */ Lst_ForEachFrom(pgn->children, owln, add_wait_dep, cgn); owln = ln; } else { - Lst_AtEnd(examine, cgn); + Lst_Append(examine, cgn); } } Lst_Close(pgn->children); } - Lst_Destroy(examine, NULL); + Lst_Free(examine); } /*- @@ -1493,7 +1499,7 @@ Make_Run(Lst targs) int errors; /* Number of errors the Job module reports */ /* Start trying to make the current targets... */ - toBeMade = Lst_Init(FALSE); + toBeMade = Lst_Init(); Make_ExpandUse(targs); Make_ProcessWait(targs); @@ -1,4 +1,4 @@ -/* $NetBSD: make.h,v 1.109 2020/07/02 15:14:38 rillig Exp $ */ +/* $NetBSD: make.h,v 1.137 2020/09/02 23:42:58 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -77,8 +77,8 @@ * The global definitions for pmake */ -#ifndef _MAKE_H_ -#define _MAKE_H_ +#ifndef MAKE_MAKE_H +#define MAKE_MAKE_H #ifdef HAVE_CONFIG_H # include "config.h" @@ -86,7 +86,9 @@ #include <sys/types.h> #include <sys/param.h> +#include <sys/stat.h> +#include <assert.h> #include <ctype.h> #include <fcntl.h> #include <stdio.h> @@ -132,8 +134,34 @@ #define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) /* delete */ #endif -#include "sprite.h" +/* + * A boolean type is defined as an integer, not an enum, for historic reasons. + * The only allowed values are the constants TRUE and FALSE (1 and 0). + */ + +#ifdef USE_DOUBLE_BOOLEAN +/* During development, to find type mismatches in function declarations. */ +typedef double Boolean; +#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_ENUM_BOOLEAN) +typedef enum { FALSE, TRUE} Boolean; +#else +typedef int Boolean; +#endif +#ifndef TRUE +#define TRUE 1 +#endif /* TRUE */ +#ifndef FALSE +#define FALSE 0 +#endif /* FALSE */ + #include "lst.h" +#include "enum.h" #include "hash.h" #include "make-conf.h" #include "buf.h" @@ -150,164 +178,191 @@ #define POSIX_SIGNALS #endif -/*- - * The structure for an individual graph node. Each node has several - * pieces of data associated with it. - * 1) the name of the target it describes - * 2) the location of the target file in the file system. - * 3) the type of operator used to define its sources (qv. parse.c) - * 4) whether it is involved in this invocation of make - * 5) whether the target has been remade - * 6) whether any of its children has been remade - * 7) the number of its children that are, as yet, unmade - * 8) its modification time - * 9) the modification time of its youngest child (qv. make.c) - * 10) a list of nodes for which this is a source (parents) - * 11) a list of nodes on which this depends (children) - * 12) a list of nodes that depend on this, as gleaned from the - * transformation rules (iParents) - * 13) a list of ancestor nodes, which includes parents, iParents, - * and recursive parents of parents - * 14) a list of nodes of the same name created by the :: operator - * 15) a list of nodes that must be made (if they're made) before - * this node can be, but that do not enter into the datedness of - * this node. - * 16) a list of nodes that must be made (if they're made) before - * this node or any child of this node can be, but that do not - * enter into the datedness of this node. - * 17) a list of nodes that must be made (if they're made) after - * this node is, but that do not depend on this node, in the - * normal sense. - * 18) a Lst of ``local'' variables that are specific to this target - * and this target only (qv. var.c [$@ $< $?, etc.]) - * 19) a Lst of strings that are commands to be given to a shell - * to create this target. - */ +typedef enum { + UNMADE, /* Not examined yet */ + DEFERRED, /* Examined once (building child) */ + REQUESTED, /* on toBeMade list */ + BEINGMADE, /* Target is already being made. + * Indicates a cycle in the graph. */ + MADE, /* Was out-of-date and has been made */ + UPTODATE, /* Was already up-to-date */ + ERROR, /* An error occurred while it was being + * made (used only in compat mode) */ + ABORTED /* The target was aborted due to an error + * making an inferior (compat). */ +} GNodeMade; + +/* The OP_ constants are used when parsing a dependency line as a way of + * 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 (:) */ + OP_DEPENDS = 1 << 0, + /* Always execute commands (!) */ + OP_FORCE = 1 << 1, + /* Execution of commands depends on children per line (::) */ + OP_DOUBLEDEP = 1 << 2, + + OP_OPMASK = OP_DEPENDS|OP_FORCE|OP_DOUBLEDEP, + + /* Don't care if the target doesn't exist and can't be created */ + OP_OPTIONAL = 1 << 3, + /* Use associated commands for parents */ + OP_USE = 1 << 4, + /* Target is never out of date, but always execute commands anyway. + * Its time doesn't matter, so it has none...sort of */ + OP_EXEC = 1 << 5, + /* Ignore errors when creating the node */ + OP_IGNORE = 1 << 6, + /* Don't remove the target when interrupted */ + OP_PRECIOUS = 1 << 7, + /* Don't echo commands when executed */ + OP_SILENT = 1 << 8, + /* Target is a recursive make so its commands should always be executed + * when it is out of date, regardless of the state of the -n or -t flags */ + OP_MAKE = 1 << 9, + /* Target is out-of-date only if any of its children was out-of-date */ + OP_JOIN = 1 << 10, + /* Assume the children of the node have been already made */ + OP_MADE = 1 << 11, + /* Special .BEGIN, .END, .INTERRUPT */ + OP_SPECIAL = 1 << 12, + /* Like .USE, only prepend commands */ + OP_USEBEFORE = 1 << 13, + /* The node is invisible to its parents. I.e. it doesn't show up in the + * parents' local variables. */ + OP_INVISIBLE = 1 << 14, + /* The node is exempt from normal 'main target' processing in parse.c */ + OP_NOTMAIN = 1 << 15, + /* Not a file target; run always */ + OP_PHONY = 1 << 16, + /* Don't search for file in the path */ + OP_NOPATH = 1 << 17, + /* .WAIT phony node */ + OP_WAIT = 1 << 18, + /* .NOMETA do not create a .meta file */ + OP_NOMETA = 1 << 19, + /* .META we _do_ want a .meta file */ + OP_META = 1 << 20, + /* Do not compare commands in .meta file */ + OP_NOMETA_CMP = 1 << 21, + /* Possibly a submake node */ + OP_SUBMAKE = 1 << 22, + + /* Attributes applied by PMake */ + + /* The node is a transformation rule */ + OP_TRANSFORM = 1 << 31, + /* Target is a member of an archive */ + OP_MEMBER = 1 << 30, + /* Target is a library */ + OP_LIB = 1 << 29, + /* Target is an archive construct */ + OP_ARCHV = 1 << 28, + /* Target has all the commands it should. Used when parsing to catch + * multiple commands for a target. */ + OP_HAS_COMMANDS = 1 << 27, + /* Saving commands on .END (Compat) */ + OP_SAVE_CMDS = 1 << 26, + /* Already processed by Suff_FindDeps */ + OP_DEPS_FOUND = 1 << 25, + /* Node found while expanding .ALLSRC */ + OP_MARK = 1 << 24 +} GNodeType; + +typedef enum { + 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 */ + DONE_WAIT = 0x0008, /* Set by Make_ProcessWait() */ + DONE_ORDER = 0x0010, /* Build requested by .ORDER processing */ + FROM_DEPEND = 0x0020, /* Node created from .depend */ + DONE_ALLSRC = 0x0040, /* We do it once only */ + CYCLE = 0x1000, /* Used by MakePrintStatus */ + DONECYCLE = 0x2000, /* Used by MakePrintStatus */ + INTERNAL = 0x4000 /* Internal use only */ +} GNodeFlags; + +/* 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 { - char *name; /* The target's name */ - char *uname; /* The unexpanded name of a .USE node */ - char *path; /* The full pathname of the file */ - int type; /* Its type (see the OP flags, below) */ - - int flags; -#define REMAKE 0x1 /* this target needs to be (re)made */ -#define CHILDMADE 0x2 /* children of this target were made */ -#define FORCE 0x4 /* children don't exist, and we pretend made */ -#define DONE_WAIT 0x8 /* Set by Make_ProcessWait() */ -#define DONE_ORDER 0x10 /* Build requested by .ORDER processing */ -#define FROM_DEPEND 0x20 /* Node created from .depend */ -#define DONE_ALLSRC 0x40 /* We do it once only */ -#define CYCLE 0x1000 /* Used by MakePrintStatus */ -#define DONECYCLE 0x2000 /* Used by MakePrintStatus */ -#define INTERNAL 0x4000 /* Internal use only */ - enum enum_made { - UNMADE, DEFERRED, REQUESTED, BEINGMADE, - MADE, UPTODATE, ERROR, ABORTED - } made; /* Set to reflect the state of processing - * on this node: - * UNMADE - Not examined yet - * DEFERRED - Examined once (building child) - * REQUESTED - on toBeMade list - * BEINGMADE - Target is already being made. - * Indicates a cycle in the graph. - * MADE - Was out-of-date and has been made - * UPTODATE - Was already up-to-date - * ERROR - An error occurred while it was being - * made (used only in compat mode) - * ABORTED - The target was aborted due to - * an error making an inferior (compat). - */ - int unmade; /* The number of unmade children */ - - time_t mtime; /* Its modification time */ - struct GNode *cmgn; /* The youngest child */ - - Lst iParents; /* Links to parents for which this is an - * implied source, if any */ - Lst cohorts; /* Other nodes for the :: operator */ - Lst parents; /* Nodes that depend on this one */ - Lst children; /* Nodes on which this one depends */ - Lst order_pred; /* .ORDER nodes we need made */ - Lst order_succ; /* .ORDER nodes who need us */ - - char cohort_num[8]; /* #n for this cohort */ - int unmade_cohorts;/* # of unmade instances on the - cohorts list */ - struct GNode *centurion; /* Pointer to the first instance of a :: - node; only set when on a cohorts list */ - unsigned int checked; /* Last time we tried to makle this node */ - - Hash_Table context; /* The local variables */ - Lst commands; /* Creation commands */ - - struct _Suff *suffix; /* Suffix for the node (determined by - * Suff_FindDeps and opaque to everyone - * but the Suff module) */ - const char *fname; /* filename where the GNode got defined */ - int lineno; /* line number where the GNode got defined */ + /* The target's name, such as "clean" or "make.c" */ + char *name; + /* The unexpanded name of a .USE node */ + char *uname; + /* The full pathname of the file belonging to the target. + * XXX: What about .PHONY targets? These don't have an associated path. */ + char *path; + + /* 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 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; + + /* Other nodes of the same name for the :: operator. */ + Lst cohorts; + + /* The nodes that depend on this one, or in other words, the nodes for + * which this is a source. */ + Lst parents; + /* The nodes on which this one depends. */ + Lst 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; + /* .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; + + /* #n for this cohort */ + char cohort_num[8]; + /* The number of unmade instances on the cohorts list */ + int unmade_cohorts; + /* Pointer to the first instance of a '::' node; only set when on a + * cohorts list */ + struct GNode *centurion; + + /* Last time (sequence number) we tried to make this node */ + unsigned int checked; + + /* The "local" variables that are specific to this target and this target + * only, such as $@, $<, $?. */ + Hash_Table context; + + /* The commands to be given to a shell to create this target. */ + Lst commands; + + /* Suffix for the node (determined by Suff_FindDeps and opaque to everyone + * but the Suff module) */ + struct Suff *suffix; + + /* filename where the GNode got defined */ + const char *fname; + /* line number where the GNode got defined */ + int lineno; } GNode; -/* - * The OP_ constants are used when parsing a dependency line as a way of - * 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 lefthand side of an operator, though it may have been on the - * righthand side... - */ -#define OP_DEPENDS 0x00000001 /* Execution of commands depends on - * kids (:) */ -#define OP_FORCE 0x00000002 /* Always execute commands (!) */ -#define OP_DOUBLEDEP 0x00000004 /* Execution of commands depends on kids - * per line (::) */ -#define OP_OPMASK (OP_DEPENDS|OP_FORCE|OP_DOUBLEDEP) - -#define OP_OPTIONAL 0x00000008 /* Don't care if the target doesn't - * exist and can't be created */ -#define OP_USE 0x00000010 /* Use associated commands for parents */ -#define OP_EXEC 0x00000020 /* Target is never out of date, but always - * execute commands anyway. Its time - * doesn't matter, so it has none...sort - * of */ -#define OP_IGNORE 0x00000040 /* Ignore errors when creating the node */ -#define OP_PRECIOUS 0x00000080 /* Don't remove the target when - * interrupted */ -#define OP_SILENT 0x00000100 /* Don't echo commands when executed */ -#define OP_MAKE 0x00000200 /* Target is a recursive make so its - * commands should always be executed when - * it is out of date, regardless of the - * state of the -n or -t flags */ -#define OP_JOIN 0x00000400 /* Target is out-of-date only if any of its - * children was out-of-date */ -#define OP_MADE 0x00000800 /* Assume the children of the node have - * been already made */ -#define OP_SPECIAL 0x00001000 /* Special .BEGIN, .END, .INTERRUPT */ -#define OP_USEBEFORE 0x00002000 /* Like .USE, only prepend commands */ -#define OP_INVISIBLE 0x00004000 /* The node is invisible to its parents. - * I.e. it doesn't show up in the parents's - * local variables. */ -#define OP_NOTMAIN 0x00008000 /* The node is exempt from normal 'main - * target' processing in parse.c */ -#define OP_PHONY 0x00010000 /* Not a file target; run always */ -#define OP_NOPATH 0x00020000 /* Don't search for file in the path */ -#define OP_WAIT 0x00040000 /* .WAIT phony node */ -#define OP_NOMETA 0x00080000 /* .NOMETA do not create a .meta file */ -#define OP_META 0x00100000 /* .META we _do_ want a .meta file */ -#define OP_NOMETA_CMP 0x00200000 /* Do not compare commands in .meta file */ -#define OP_SUBMAKE 0x00400000 /* Possibly a submake node */ -/* Attributes applied by PMake */ -#define OP_TRANSFORM 0x80000000 /* The node is a transformation rule */ -#define OP_MEMBER 0x40000000 /* Target is a member of an archive */ -#define OP_LIB 0x20000000 /* Target is a library */ -#define OP_ARCHV 0x10000000 /* Target is an archive construct */ -#define OP_HAS_COMMANDS 0x08000000 /* Target has all the commands it should. - * Used when parsing to catch multiple - * commands for a target */ -#define OP_SAVE_CMDS 0x04000000 /* Saving commands on .END (Compat) */ -#define OP_DEPS_FOUND 0x02000000 /* Already processed by Suff_FindDeps */ -#define OP_MARK 0x01000000 /* Node found while expanding .ALLSRC */ - #define NoExecute(gn) ((gn->type & OP_MAKE) ? noRecursiveExecute : noExecute) /* * OP_NOP will return TRUE if the node with the given type was not the @@ -330,18 +385,6 @@ typedef struct GNode { #define TARG_NOHASH 0x02 /* don't look in/add to hash table */ /* - * These constants are all used by the Str_Concat function to decide how the - * final string should look. If STR_ADDSPACE is given, a space will be - * placed between the two strings. If STR_ADDSLASH is given, a '/' will - * be used instead of a space. If neither is given, no intervening characters - * will be placed between the two strings in the final output. If the - * STR_DOFREE bit is set, the two input strings will be freed before - * Str_Concat returns. - */ -#define STR_ADDSPACE 0x01 /* add a space when Str_Concat'ing */ -#define STR_ADDSLASH 0x02 /* add a slash when Str_Concat'ing */ - -/* * 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. @@ -353,9 +396,11 @@ typedef struct GNode { /* * Values returned by Cond_Eval. */ -#define COND_PARSE 0 /* Parse the next lines */ -#define COND_SKIP 1 /* Skip the next lines */ -#define COND_INVALID 2 /* Not a conditional statement */ +typedef enum { + COND_PARSE, /* Parse the next lines */ + COND_SKIP, /* Skip the next lines */ + COND_INVALID /* Not a conditional statement */ +} CondEvalResult; /* * Definitions for the "local" variables. Used only for clarity. @@ -403,7 +448,6 @@ extern Boolean doing_depend; /* TRUE if processing .depend */ extern Boolean checkEnvFirst; /* TRUE if environment should be searched for * variables before the global context */ -extern Boolean jobServer; /* a jobServer already exists */ extern Boolean parseWarnFatal; /* TRUE if makefile parsing warnings are * treated as errors */ @@ -420,7 +464,6 @@ extern GNode *VAR_INTERNAL; /* Variables defined internally by make extern GNode *VAR_GLOBAL; /* Variables defined in a global context, e.g * in the Makefile itself */ extern GNode *VAR_CMD; /* Variables defined on the command line */ -extern GNode *VAR_FOR; /* Iteration variables */ extern char var_Error[]; /* Value returned by Var_Parse when an error * is encountered. It actually points to * an empty string, so naive callers needn't @@ -439,6 +482,8 @@ extern char *progname; /* The program name */ extern char *makeDependfile; /* .depend */ extern char **savedEnv; /* if we replaced environ this will be non-NULL */ +extern int makelevel; + /* * We cannot vfork() in a child of vfork(). * Most systems do not enforce this but some do. @@ -464,7 +509,7 @@ extern pid_t myPid; * There is one bit per module. It is up to the module what debug * information to print. */ -extern FILE *debug_file; /* Output written here - default stdout */ +extern FILE *debug_file; /* Output is written here - default stderr */ extern int debug; #define DEBUG_ARCH 0x00001 #define DEBUG_COND 0x00002 @@ -481,12 +526,15 @@ extern int debug; #define DEBUG_ERROR 0x01000 #define DEBUG_LOUD 0x02000 #define DEBUG_META 0x04000 +#define DEBUG_HASH 0x08000 #define DEBUG_GRAPH3 0x10000 #define DEBUG_SCRIPT 0x20000 #define DEBUG_PARSE 0x40000 #define DEBUG_CWD 0x80000 +#define DEBUG_LINT 0x100000 + #define CONCAT(a,b) a##b #define DEBUG(module) (debug & CONCAT(DEBUG_,module)) @@ -501,16 +549,13 @@ void Make_HandleUse(GNode *, GNode *); void Make_Update(GNode *); void Make_DoAllVar(GNode *); Boolean Make_Run(Lst); -char * Check_Cwd_Cmd(const char *); -void Check_Cwd(const char **); int dieQuietly(GNode *, int); void PrintOnError(GNode *, const char *); void Main_ExportMAKEFLAGS(Boolean); Boolean Main_SetObjdir(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2); int mkTempFile(const char *, char **); int str2Lst_Append(Lst, char *, const char *); -int cached_lstat(const char *, void *); -int cached_stat(const char *, void *); +void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *); #ifdef __GNUC__ #define UNCONST(ptr) ({ \ @@ -524,10 +569,10 @@ int cached_stat(const char *, void *); #endif #ifndef MIN -#define MIN(a, b) ((a < b) ? a : b) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif #ifndef MAX -#define MAX(a, b) ((a > b) ? a : b) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif /* At least GNU/Hurd systems lack hardcoded MAXPATHLEN/PATH_MAX */ @@ -547,4 +592,4 @@ int cached_stat(const char *, void *); #define KILLPG(pid, sig) killpg((pid), (sig)) #endif -#endif /* _MAKE_H_ */ +#endif /* MAKE_MAKE_H */ diff --git a/make_malloc.c b/make_malloc.c index 7e2f75ff85e0..ba9632b2b254 100644 --- a/make_malloc.c +++ b/make_malloc.c @@ -1,4 +1,4 @@ -/* $NetBSD: make_malloc.c,v 1.12 2020/07/03 08:02:55 rillig Exp $ */ +/* $NetBSD: make_malloc.c,v 1.18 2020/09/02 06:10:44 rillig Exp $ */ /*- * Copyright (c) 2009 The NetBSD Foundation, Inc. @@ -28,7 +28,7 @@ #ifdef MAKE_NATIVE #include <sys/cdefs.h> -__RCSID("$NetBSD: make_malloc.c,v 1.12 2020/07/03 08:02:55 rillig Exp $"); +__RCSID("$NetBSD: make_malloc.c,v 1.18 2020/09/02 06:10:44 rillig Exp $"); #endif #include <stdio.h> @@ -41,10 +41,7 @@ __RCSID("$NetBSD: make_malloc.c,v 1.12 2020/07/03 08:02:55 rillig Exp $"); #ifndef USE_EMALLOC static MAKE_ATTR_DEAD void enomem(void); -/* - * enomem -- - * die when out of memory. - */ +/* die when out of memory. */ static MAKE_ATTR_DEAD void enomem(void) { @@ -52,10 +49,7 @@ enomem(void) exit(2); } -/* - * bmake_malloc -- - * malloc, but die on error. - */ +/* malloc, but die on error. */ void * bmake_malloc(size_t len) { @@ -66,10 +60,7 @@ bmake_malloc(size_t len) return p; } -/* - * bmake_strdup -- - * strdup, but die on error. - */ +/* strdup, but die on error. */ char * bmake_strdup(const char *str) { @@ -82,33 +73,17 @@ bmake_strdup(const char *str) return memcpy(p, str, len); } -/* - * bmake_strndup -- - * strndup, but die on error. - */ +/* Allocate a string starting from str with exactly len characters. */ char * -bmake_strndup(const char *str, size_t max_len) +bmake_strldup(const char *str, size_t len) { - size_t len; - char *p; - - if (str == NULL) - return NULL; - - len = strlen(str); - if (len > max_len) - len = max_len; - p = bmake_malloc(len + 1); + char *p = bmake_malloc(len + 1); memcpy(p, str, len); p[len] = '\0'; - return p; } -/* - * bmake_realloc -- - * realloc, but die on error. - */ +/* realloc, but die on error. */ void * bmake_realloc(void *ptr, size_t size) { @@ -117,3 +92,10 @@ bmake_realloc(void *ptr, size_t size) return ptr; } #endif + +/* Allocate a string from start up to but excluding end. */ +char * +bmake_strsedup(const char *start, const char *end) +{ + return bmake_strldup(start, (size_t)(end - start)); +} diff --git a/make_malloc.h b/make_malloc.h index 36d3eff3c027..ac804d79c711 100644 --- a/make_malloc.h +++ b/make_malloc.h @@ -1,4 +1,4 @@ -/* $NetBSD: make_malloc.h,v 1.4 2009/01/24 14:43:29 dsl Exp $ */ +/* $NetBSD: make_malloc.h,v 1.10 2020/08/29 16:47:45 rillig Exp $ */ /*- * Copyright (c) 2009 The NetBSD Foundation, Inc. @@ -30,12 +30,25 @@ void *bmake_malloc(size_t); void *bmake_realloc(void *, size_t); char *bmake_strdup(const char *); -char *bmake_strndup(const char *, size_t); +char *bmake_strldup(const char *, size_t); #else #include <util.h> #define bmake_malloc(x) emalloc(x) #define bmake_realloc(x,y) erealloc(x,y) #define bmake_strdup(x) estrdup(x) -#define bmake_strndup(x,y) estrndup(x,y) +#define bmake_strldup(x,y) estrndup(x,y) #endif +char *bmake_strsedup(const char *, const char *); +/* Thin wrapper around free(3) to avoid the extra function call in case + * p is NULL, which on x86_64 costs about 12 machine instructions. + * Other platforms are similarly affected. + * + * The case of a NULL pointer happens especially often after Var_Value, + * since only environment variables need to be freed, but not others. */ +static inline void MAKE_ATTR_UNUSED +bmake_free(void *p) +{ + if (p != NULL) + free(p); +} @@ -1,4 +1,4 @@ -/* $NetBSD: meta.c,v 1.86 2020/07/11 00:39:53 sjg Exp $ */ +/* $NetBSD: meta.c,v 1.113 2020/09/02 04:08:54 rillig Exp $ */ /* * Implement 'meta' mode. @@ -47,6 +47,7 @@ char * dirname(char *); #endif #include "make.h" +#include "dir.h" #include "job.h" #ifdef USE_FILEMON @@ -318,7 +319,7 @@ meta_name(char *mname, size_t mnamelen, static int is_submake(void *cmdp, void *gnp) { - static char *p_make = NULL; + static const char *p_make = NULL; static int p_len; char *cmd = cmdp; GNode *gn = gnp; @@ -333,7 +334,7 @@ is_submake(void *cmdp, void *gnp) } cp = strchr(cmd, '$'); if ((cp)) { - mp = Var_Subst(NULL, cmd, gn, VARF_WANTRES); + mp = Var_Subst(cmd, gn, VARE_WANTRES); cmd = mp; } cp2 = strstr(cmd, p_make); @@ -372,13 +373,13 @@ printCMD(void *cmdp, void *mfpp) { meta_file_t *mfp = mfpp; char *cmd = cmdp; - char *cp = NULL; + char *cmd_freeIt = NULL; if (strchr(cmd, '$')) { - cmd = cp = Var_Subst(NULL, cmd, mfp->gn, VARF_WANTRES); + cmd = cmd_freeIt = Var_Subst(cmd, mfp->gn, VARE_WANTRES); } fprintf(mfp->fp, "CMD %s\n", cmd); - free(cp); + free(cmd_freeIt); return 0; } @@ -403,7 +404,7 @@ static Boolean meta_needed(GNode *gn, const char *dname, char *objdir, int verbose) { - struct stat fs; + struct make_stat mst; if (verbose) verbose = DEBUG(META); @@ -437,7 +438,7 @@ meta_needed(GNode *gn, const char *dname, } /* The object directory may not exist. Check it.. */ - if (cached_stat(dname, &fs) != 0) { + if (cached_stat(dname, &mst) != 0) { if (verbose) fprintf(debug_file, "Skipping meta for %s: no .OBJDIR\n", gn->name); @@ -488,7 +489,7 @@ meta_create(BuildMon *pbm, GNode *gn) char *mp; /* Describe the target we are building */ - mp = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", gn, VARF_WANTRES); + mp = Var_Subst("${" MAKE_META_PREFIX "}", gn, VARE_WANTRES); if (*mp) fprintf(stdout, "%s\n", mp); free(mp); @@ -546,7 +547,7 @@ meta_create(BuildMon *pbm, GNode *gn) } out: for (i--; i >= 0; i--) { - free(p[i]); + bmake_free(p[i]); } return mf.fp; @@ -629,24 +630,19 @@ meta_mode_init(const char *make_mode) /* * We consider ourselves master of all within ${.MAKE.META.BAILIWICK} */ - metaBailiwick = Lst_Init(FALSE); - metaBailiwickStr = Var_Subst(NULL, "${.MAKE.META.BAILIWICK:O:u:tA}", - VAR_GLOBAL, VARF_WANTRES); - if (metaBailiwickStr) { - str2Lst_Append(metaBailiwick, metaBailiwickStr, NULL); - } + metaBailiwick = Lst_Init(); + metaBailiwickStr = Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}", + VAR_GLOBAL, VARE_WANTRES); + str2Lst_Append(metaBailiwick, metaBailiwickStr, NULL); /* * We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS} */ - metaIgnorePaths = Lst_Init(FALSE); + metaIgnorePaths = Lst_Init(); Var_Append(MAKE_META_IGNORE_PATHS, "/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL); - metaIgnorePathsStr = Var_Subst(NULL, - "${" MAKE_META_IGNORE_PATHS ":O:u:tA}", VAR_GLOBAL, - VARF_WANTRES); - if (metaIgnorePathsStr) { - str2Lst_Append(metaIgnorePaths, metaIgnorePathsStr, NULL); - } + metaIgnorePathsStr = Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}", + VAR_GLOBAL, VARE_WANTRES); + str2Lst_Append(metaIgnorePaths, metaIgnorePathsStr, NULL); /* * We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS} @@ -654,12 +650,12 @@ meta_mode_init(const char *make_mode) cp = NULL; if (Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL, &cp)) { metaIgnorePatterns = TRUE; - free(cp); + bmake_free(cp); } cp = NULL; if (Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL, &cp)) { metaIgnoreFilter = TRUE; - free(cp); + bmake_free(cp); } } @@ -822,8 +818,8 @@ meta_job_output(Job *job, char *cp, const char *nl) if (!meta_prefix) { char *cp2; - meta_prefix = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", - VAR_GLOBAL, VARF_WANTRES); + meta_prefix = Var_Subst("${" MAKE_META_PREFIX "}", + VAR_GLOBAL, VARE_WANTRES); if ((cp2 = strchr(meta_prefix, '$'))) meta_prefix_len = cp2 - meta_prefix; else @@ -894,9 +890,11 @@ meta_job_finish(Job *job) void meta_finish(void) { - Lst_Destroy(metaBailiwick, NULL); + if (metaBailiwick != NULL) + Lst_Free(metaBailiwick); free(metaBailiwickStr); - Lst_Destroy(metaIgnorePaths, NULL); + if (metaIgnorePaths != NULL) + Lst_Free(metaIgnorePaths); free(metaIgnorePathsStr); } @@ -958,39 +956,23 @@ prefix_match(void *p, void *q) return strncmp(path, prefix, n) == 0; } -/* - * looking for exact or prefix/ match to - * Lst_Find wants 0 to stop search - */ -static int +/* See if the path equals prefix or starts with "prefix/". */ +static Boolean path_match(const void *p, const void *q) { - const char *prefix = q; const char *path = p; + const char *prefix = q; size_t n = strlen(prefix); - int rc; - if ((rc = strncmp(path, prefix, n)) == 0) { - switch (path[n]) { - case '\0': - case '/': - break; - default: - rc = 1; - break; - } - } - return rc; + if (strncmp(path, prefix, n) != 0) + return FALSE; + return path[n] == '\0' || path[n] == '/'; } -/* Lst_Find wants 0 to stop search */ -static int +static Boolean string_match(const void *p, const void *q) { - const char *p1 = p; - const char *p2 = q; - - return strcmp(p1, p2); + return strcmp(p, q) == 0; } @@ -1015,12 +997,12 @@ meta_ignore(GNode *gn, const char *p) } if (metaIgnorePatterns) { + const char *expr; char *pm; Var_Set(".p.", p, gn); - pm = Var_Subst(NULL, - "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}", - gn, VARF_WANTRES); + expr = "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}"; + pm = Var_Subst(expr, gn, VARE_WANTRES); if (*pm) { #ifdef DEBUG_META_MODE if (DEBUG(META)) @@ -1040,7 +1022,7 @@ meta_ignore(GNode *gn, const char *p) snprintf(fname, sizeof(fname), "${%s:L:${%s:ts:}}", p, MAKE_META_IGNORE_FILTER); - fm = Var_Subst(NULL, fname, gn, VARF_WANTRES); + fm = Var_Subst(fname, gn, VARE_WANTRES); if (*fm == '\0') { #ifdef DEBUG_META_MODE if (DEBUG(META)) @@ -1122,7 +1104,7 @@ meta_oodate(GNode *gn, Boolean oodate) goto oodate_out; dname = fname3; - missingFiles = Lst_Init(FALSE); + missingFiles = Lst_Init(); /* * We need to check if the target is out-of-date. This includes @@ -1147,7 +1129,7 @@ meta_oodate(GNode *gn, Boolean oodate) int pid; int x; LstNode ln; - struct stat fs; + struct make_stat mst; if (!buf) { bufsz = 8 * BUFSIZ; @@ -1240,7 +1222,7 @@ meta_oodate(GNode *gn, Boolean oodate) CHECK_VALID_META(p); pid = atoi(p); if (pid > 0 && pid != lastpid) { - char *ldir; + const char *ldir; char *tp; if (lastpid > 0) { @@ -1254,12 +1236,12 @@ meta_oodate(GNode *gn, Boolean oodate) ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp); if (ldir) { strlcpy(latestdir, ldir, sizeof(latestdir)); - free(tp); + bmake_free(tp); } ldir = Var_Value(lcwd_vname, VAR_GLOBAL, &tp); if (ldir) { strlcpy(lcwd, ldir, sizeof(lcwd)); - free(tp); + bmake_free(tp); } } /* Skip past the pid. */ @@ -1337,15 +1319,16 @@ meta_oodate(GNode *gn, Boolean oodate) case 'D': /* unlink */ if (*p == '/' && !Lst_IsEmpty(missingFiles)) { /* remove any missingFiles entries that match p */ - if ((ln = Lst_Find(missingFiles, p, - path_match)) != NULL) { + ln = Lst_Find(missingFiles, path_match, p); + if (ln != NULL) { LstNode nln; char *tp; do { - nln = Lst_FindFrom(missingFiles, Lst_Succ(ln), - p, path_match); - tp = Lst_Datum(ln); + nln = Lst_FindFrom(missingFiles, + LstNode_Next(ln), + path_match, p); + tp = LstNode_Datum(ln); Lst_Remove(missingFiles, ln); free(tp); } while ((ln = nln) != NULL); @@ -1411,11 +1394,11 @@ meta_oodate(GNode *gn, Boolean oodate) if ((strstr("tmp", p))) break; - if ((link_src != NULL && cached_lstat(p, &fs) < 0) || - (link_src == NULL && cached_stat(p, &fs) < 0)) { + if ((link_src != NULL && cached_lstat(p, &mst) < 0) || + (link_src == NULL && cached_stat(p, &mst) < 0)) { if (!meta_ignore(gn, p)) { - if (Lst_Find(missingFiles, p, string_match) == NULL) - Lst_AtEnd(missingFiles, bmake_strdup(p)); + if (Lst_Find(missingFiles, string_match, p) == NULL) + Lst_Append(missingFiles, bmake_strdup(p)); } } break; @@ -1475,7 +1458,7 @@ meta_oodate(GNode *gn, Boolean oodate) if (DEBUG(META)) fprintf(debug_file, "%s: %d: looking for: %s\n", fname, lineno, *sdp); #endif - if (cached_stat(*sdp, &fs) == 0) { + if (cached_stat(*sdp, &mst) == 0) { found = 1; p = *sdp; } @@ -1485,12 +1468,12 @@ meta_oodate(GNode *gn, Boolean oodate) if (DEBUG(META)) fprintf(debug_file, "%s: %d: found: %s\n", fname, lineno, p); #endif - if (!S_ISDIR(fs.st_mode) && - fs.st_mtime > gn->mtime) { + if (!S_ISDIR(mst.mst_mode) && + mst.mst_mtime > gn->mtime) { if (DEBUG(META)) fprintf(debug_file, "%s: %d: file '%s' is newer than the target...\n", fname, lineno, p); oodate = TRUE; - } else if (S_ISDIR(fs.st_mode)) { + } else if (S_ISDIR(mst.mst_mode)) { /* Update the latest directory. */ cached_realpath(p, latestdir); } @@ -1500,8 +1483,8 @@ meta_oodate(GNode *gn, Boolean oodate) * A referenced file outside of CWD is missing. * We cannot catch every eventuality here... */ - if (Lst_Find(missingFiles, p, string_match) == NULL) - Lst_AtEnd(missingFiles, bmake_strdup(p)); + if (Lst_Find(missingFiles, string_match, p) == NULL) + Lst_Append(missingFiles, bmake_strdup(p)); } } if (buf[0] == 'E') { @@ -1524,7 +1507,7 @@ meta_oodate(GNode *gn, Boolean oodate) fprintf(debug_file, "%s: %d: there were more build commands in the meta data file than there are now...\n", fname, lineno); oodate = TRUE; } else { - char *cmd = (char *)Lst_Datum(ln); + char *cmd = LstNode_Datum(ln); Boolean hasOODATE = FALSE; if (strstr(cmd, "$?")) @@ -1539,7 +1522,7 @@ meta_oodate(GNode *gn, Boolean oodate) if (DEBUG(META)) fprintf(debug_file, "%s: %d: cannot compare command using .OODATE\n", fname, lineno); } - cmd = Var_Subst(NULL, cmd, gn, VARF_WANTRES|VARF_UNDEFERR); + cmd = Var_Subst(cmd, gn, VARE_WANTRES|VARE_UNDEFERR); if ((cp = strchr(cmd, '\n'))) { int n; @@ -1576,7 +1559,7 @@ meta_oodate(GNode *gn, Boolean oodate) oodate = TRUE; } free(cmd); - ln = Lst_Succ(ln); + ln = LstNode_Next(ln); } } else if (strcmp(buf, "CWD") == 0) { /* @@ -1601,7 +1584,7 @@ meta_oodate(GNode *gn, Boolean oodate) if (!Lst_IsEmpty(missingFiles)) { if (DEBUG(META)) fprintf(debug_file, "%s: missing files: %s...\n", - fname, (char *)Lst_Datum(Lst_First(missingFiles))); + fname, (char *)LstNode_Datum(Lst_First(missingFiles))); oodate = TRUE; } if (!oodate && !have_filemon && filemonMissing) { @@ -1628,7 +1611,7 @@ meta_oodate(GNode *gn, Boolean oodate) } } - Lst_Destroy(missingFiles, (FreeProc *)free); + Lst_Destroy(missingFiles, free); if (oodate && needOODATE) { /* @@ -1638,12 +1621,12 @@ meta_oodate(GNode *gn, Boolean oodate) */ Var_Delete(OODATE, gn); Var_Set(OODATE, Var_Value(ALLSRC, gn, &cp), gn); - free(cp); + bmake_free(cp); } oodate_out: for (i--; i >= 0; i--) { - free(pa[i]); + bmake_free(pa[i]); } return oodate; } diff --git a/metachar.c b/metachar.c index 49603383e660..971962272a4a 100644 --- a/metachar.c +++ b/metachar.c @@ -1,4 +1,4 @@ -/* $NetBSD: metachar.c,v 1.5 2015/06/19 08:03:35 mlelstv Exp $ */ +/* $NetBSD: metachar.c,v 1.6 2020/08/03 20:43:41 rillig Exp $ */ /*- * Copyright (c) 2015 The NetBSD Foundation, Inc. @@ -38,7 +38,7 @@ #endif #if defined(__RCSID) && !defined(lint) -__RCSID("$NetBSD: metachar.c,v 1.5 2015/06/19 08:03:35 mlelstv Exp $"); +__RCSID("$NetBSD: metachar.c,v 1.6 2020/08/03 20:43:41 rillig Exp $"); #endif #include "metachar.h" @@ -52,37 +52,37 @@ __RCSID("$NetBSD: metachar.c,v 1.5 2015/06/19 08:03:35 mlelstv Exp $"); */ unsigned char _metachar[128] = { -// nul soh stx etx eot enq ack bel +/* nul soh stx etx eot enq ack bel */ 1, 0, 0, 0, 0, 0, 0, 0, -// bs ht nl vt np cr so si +/* bs ht nl vt np cr so si */ 0, 0, 1, 0, 0, 0, 0, 0, -// dle dc1 dc2 dc3 dc4 nak syn etb +/* dle dc1 dc2 dc3 dc4 nak syn etb */ 0, 0, 0, 0, 0, 0, 0, 0, -// can em sub esc fs gs rs us +/* can em sub esc fs gs rs us */ 0, 0, 0, 0, 0, 0, 0, 0, -// sp ! " # $ % & ' +/* sp ! " # $ % & ' */ 0, 1, 1, 1, 1, 0, 1, 1, -// ( ) * + , - . / +/* ( ) * + , - . / */ 1, 1, 1, 0, 0, 0, 0, 0, -// 0 1 2 3 4 5 6 7 +/* 0 1 2 3 4 5 6 7 */ 0, 0, 0, 0, 0, 0, 0, 0, -// 8 9 : ; < = > ? +/* 8 9 : ; < = > ? */ 0, 0, 0, 1, 1, 0, 1, 1, -// @ A B C D E F G +/* @ A B C D E F G */ 0, 0, 0, 0, 0, 0, 0, 0, -// H I J K L M N O +/* H I J K L M N O */ 0, 0, 0, 0, 0, 0, 0, 0, -// P Q R S T U V W +/* P Q R S T U V W */ 0, 0, 0, 0, 0, 0, 0, 0, -// X Y Z [ \ ] ^ _ +/* X Y Z [ \ ] ^ _ */ 0, 0, 0, 1, 1, 1, 1, 0, -// ` a b c d e f g +/* ` a b c d e f g */ 1, 0, 0, 0, 0, 0, 0, 0, -// h i j k l m n o +/* h i j k l m n o */ 0, 0, 0, 0, 0, 0, 0, 0, -// p q r s t u v w +/* p q r s t u v w */ 0, 0, 0, 0, 0, 0, 0, 0, -// x y z { | } ~ del +/* x y z { | } ~ del */ 0, 0, 0, 1, 1, 1, 1, 0, }; diff --git a/metachar.h b/metachar.h index db88d671067e..98408ab3fb58 100644 --- a/metachar.h +++ b/metachar.h @@ -1,4 +1,4 @@ -/* $NetBSD: metachar.h,v 1.4 2015/06/21 20:26:02 christos Exp $ */ +/* $NetBSD: metachar.h,v 1.7 2020/08/25 17:37:09 rillig Exp $ */ /*- * Copyright (c) 2015 The NetBSD Foundation, Inc. @@ -28,25 +28,16 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _METACHAR_H -#define _METACHAR_H +#ifndef MAKE_METACHAR_H +#define MAKE_METACHAR_H -#include <ctype.h> +#include "make.h" extern unsigned char _metachar[]; #define ismeta(c) _metachar[(c) & 0x7f] -static inline int -hasmeta(const char *cmd) -{ - while (!ismeta(*cmd)) - cmd++; - - return *cmd != '\0'; -} - -static inline int +static inline int MAKE_ATTR_UNUSED needshell(const char *cmd, int white) { while (!ismeta(*cmd) && *cmd != ':' && *cmd != '=') { @@ -58,4 +49,4 @@ needshell(const char *cmd, int white) return *cmd != '\0'; } -#endif /* _METACHAR_H */ +#endif /* MAKE_METACHAR_H */ diff --git a/mk/ChangeLog b/mk/ChangeLog index ca42bc2839c5..9ea9d42a7776 100644 --- a/mk/ChangeLog +++ b/mk/ChangeLog @@ -1,3 +1,106 @@ +2020-08-26 Simon J Gerraty <sjg@beast.crufty.net> + + * dirdeps.mk: ensure we cannot confuse a static cache for dynamic + (even more rare that use of static cache is playing clever tricks + with it) + +2020-08-16 Simon J Gerraty <sjg@beast.crufty.net> + + * dirdeps-cache-update.mk: allow + MK_STATIC_DIRDEPS_CACHE_UPDATE_IMMEDIATE to control when we + actually update STATIC_DIRDEPS_CACHE. + + * stage-install.sh: create dest directory if needed + before running install(1) + +2020-08-10 Simon J Gerraty <sjg@beast.crufty.net> + + * dirdeps-targets.mk: include Makefile.dirdeps.options + + * dirdeps.mk: use _TARGETS if defined for DIRDEPS_CACHE + +2020-08-09 Simon J Gerraty <sjg@beast.crufty.net> + + * dirdeps.mk: default BUILD_DIRDEPS_MAKEFILE to empty + + * dirdeps-cache-update.mk: building parallel cache update + under the context of dirdeps-cached would be ideal, but + is problematic, so it runs as a sibling. + Use cache-built target to ensure we wait for it to complete if + necessary. + +2020-08-06 Simon J Gerraty <sjg@beast.crufty.net> + + * install-mk (MK_VERSION): 20200806 + + * dirdeps-options: allow TARGET_SPEC to affect option values. + Use DIRDEPS_OPTIONS_QUALIFIER_LIST before using bare MK_* + + * dirdeps-targets.mk: check for MK_STATIC_DIRDEPS_CACHE defined + before looking for STATIC_DIRDEPS_CACHE + +2020-08-05 Simon J Gerraty <sjg@beast.crufty.net> + + * host-target.mk: Darwin use MACHINE for HOST_ARCH too + + * dirdeps-options.mk: improve debug output + +2020-07-22 Simon J Gerraty <sjg@beast.crufty.net> + + * dirdeps.mk: set and export DYNAMIC_DIRDEPS_CACHE + for use by dirdeps-cache-update.mk + + * dirdeps-targets.mk: set and export STATIC_DIRDEPS_CACHE + for use by dirdeps-cache-update.mk even if we don't use it. + + * dirdeps-cache-update.mk: we only need worry about the background + update case, with the above, the update from DIRDEPS_CACHE is + simple. + + * meta2deps.py: R 1234 . is not interesting + +2020-07-20 Simon J Gerraty <sjg@beast.crufty.net> + + * sys.mk: default MK_STATIC_DIRDEPS_CACHE from MK_DIRDEPS_CACHE + + * dirdeps-options.mk: do not :tu DIRDEPS_OPTIONS + allows use of lower case for pseudo options. + + * dirdeps-cache-update.mk: magic to deal with STATIC_DIRDEPS_CACHE + +2020-07-18 Simon J Gerraty <sjg@beast.crufty.net> + + * dirdeps-targets.mk: Look for Makefile.dirdeps.cache + which allows us to have a static cache for expensive targets. + Use -DWITHOUT_STATIC_DIRDEPS_CACHE -DWITH_DIRDEPS_CACHE + to regenerate the dirdeps.cache it is a copy of. + +2020-07-17 Simon J Gerraty <sjg@beast.crufty.net> + + * Get rid of BUILD_AT_LEVEL0, MK_DIRDEPS_BUILD makes more sense. + +2020-07-16 Simon J Gerraty <sjg@beast.crufty.net> + + * dirdeps.mk (DIRDEP_LOADAVG_REPORT): make it easy to record + load averages at intervals during build. + +2020-07-15 Simon J Gerraty <sjg@beast.crufty.net> + + * install-mk (MK_VERSION): 20200715 + + * dirdeps.mk: tweak Checking line to make matching Finished + lines for post-build analysis easier. + + * meta.autodep.mk: use !defined(WITHOUT_META_STATS) + + * progs.mk: avoid prog.mk outputting multiple Finished lines + +2020-07-11 Simon J Gerraty <sjg@beast.crufty.net> + + * dirdeps.mk: further optimize dirdeps.cache + generate a DIRDEPS.${.TARGET} list for other purposes + and improve the layout. + 2020-07-10 Simon J Gerraty <sjg@beast.crufty.net> * dirdeps.mk: optimize content of dirdeps.cache @@ -124,7 +227,7 @@ * install-mk (MK_VERSION): 20180919 * dirdeps-options.mk: .undef cannot handle var that expands to - more than one var. + more than one var. 2018-07-08 Simon J Gerraty <sjg@beast.crufty.net> @@ -309,8 +412,8 @@ 2016-12-12 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20161212 - - * meta2deps.py: set pid_cwd[pid] when we process 'C'hdir, + + * meta2deps.py: set pid_cwd[pid] when we process 'C'hdir, rather than when we detect pid change. 2016-12-07 Simon J. Gerraty <sjg@bad.crufty.net> @@ -322,7 +425,7 @@ to use foo.tgz to reference the latest staged version - so we make foo.tgz a symlink to it. Using a target to do both operations ensures we stay in sync. - + 2016-11-26 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20161126 @@ -374,18 +477,18 @@ 2016-08-13 Simon J. Gerraty <sjg@bad.crufty.net> - * meta.sys.mk (.MAKE.META.IGNORE_PATHS): + * meta.sys.mk (.MAKE.META.IGNORE_PATHS): in meta mode we can ignore the mtime of makefiles 2016-08-02 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20160802 - + * lib.mk (libinstall): depends on beforinstall * prog.mk (proginstall): depends on beforinstall patch from Lauri Tirkkonen - + * dirdeps.mk (bootstrap): When bootstrapping; creat .MAKE.DEPENDFILE_DEFAULT and allow additional filtering via .MAKE.DEPENDFILE_BOOTSTRAP_SED @@ -408,7 +511,7 @@ * install-mk (MK_VERSION): 20160530 * meta.stage.mk: we assume ${CLEANFILES} gets .NOPATH make it so. - + 2016-05-12 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20160512 @@ -418,7 +521,7 @@ skip INCLUDES_* for staged libs unless SRC_* defined. * own.mk: add INCLUDEDIR - + 2016-04-18 Simon J. Gerraty <sjg@bad.crufty.net> * dirdeps.mk: when doing -f dirdeps.mk if target suppies no @@ -428,9 +531,9 @@ 2016-04-07 Simon J. Gerraty <sjg@bad.crufty.net> * meta.autodep.mk: issue a warning if UPDATE_DEPENDFILE=NO due to - NO_FILEMON_COOKIE + NO_FILEMON_COOKIE - * dirdeps.mk: move the logic that allows for + * dirdeps.mk: move the logic that allows for make -f dirdeps.mk some/dir.${TARGET_SPEC} inside the check for !target(_DIRDEP_USE) @@ -438,15 +541,15 @@ * Use <> when including local*.mk and others which may exist elsewhere so that user can better control what they get. - - * meta.autodep.mk (NO_FILEMON_COOKIE): + + * meta.autodep.mk (NO_FILEMON_COOKIE): create a cookie if we ever build dir with nofilemon so that UPDATE_DEPENDFILE will be forced to NO until cleaned. 2016-04-01 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20160401 - + * meta2deps.py: fix old print statement when debugging. * gendirdeps.mk: META2DEPS_CMD append M2D_EXCLUDES with -X @@ -455,12 +558,12 @@ 2016-03-22 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20160317 (St. Pats) - + * warnings.mk: g++ does not like -Wimplicit - + * sys.mk sys/*.mk lib.mk prog.mk: use CXX_SUFFIXES to handle the pelthora of common suffixes for C++ - + * lib.mk: use .So for shared objects 2016-03-15 Simon J. Gerraty <sjg@bad.crufty.net> @@ -469,9 +572,9 @@ * meta.stage.mk (LN_CP_SCRIPT): do not ln(1) if we have to chmod(1) normally only applies to scripts. - + * dirdeps.mk: NO_DIRDEPS_BELOW to supress DIRDEPS below RELDIR as - well as outside it. + well as outside it. 2016-03-10 Simon J. Gerraty <sjg@bad.crufty.net> @@ -509,17 +612,17 @@ we cannot use the '$$' trick, but .export-literal does the job we need. * auto.dep.mk: make use .dinclude if we can. - + 2016-02-05 Simon J. Gerraty <sjg@bad.crufty.net> - * dirdeps.mk: + * dirdeps.mk: Add _build_all_dirs such that local.dirdeps.mk can add fully qualified dirs to it. - These will be built normally but the current + These will be built normally but the current DEP_RELDIR will not depend on then (to avoid cycles). This makes it easy to hook things like unit-tests into build. - + 2016-01-21 Simon J. Gerraty <sjg@bad.crufty.net> @@ -541,7 +644,7 @@ to set MK_AUTO_OBJ and MK_DIRDEPS_BUILD include local.sys.env.mk early include local.sys.mk later - + * own.mk (OPTIONS_DEFAULT_NO): AUTO_OBJ etc moved to sys.mk 2015-11-13 Simon J. Gerraty <sjg@bad.crufty.net> @@ -560,8 +663,8 @@ 2015-10-20 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20151020 - - * dirdeps.mk: Add logic for + + * dirdeps.mk: Add logic for make -f dirdeps.mk some/dir.${TARGET_SPEC} 2015-10-14 Simon J. Gerraty <sjg@bad.crufty.net> @@ -587,16 +690,16 @@ 2015-06-15 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20150615 - + * auto.obj.mk: allow use of MAKEOBJDIRPREFIX too. Follow make's normal precedence rules. - + * gendirdeps.mk: allow customization of the header. - eg. for FreeBSD: + eg. for FreeBSD: GENDIRDEPS_HEADER= echo '\# ${FreeBSD:L:@v@$$$v$$ @:M*F*}'; * meta.autodep.mk: ignore dirdeps.cache* - + * meta.stage.mk: when bootstrapping options it can be handy to throw warnings rather than errors for staging conflicts. @@ -605,11 +708,11 @@ 2015-06-06 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20150606 - + * dirdeps.mk: don't rely on manually maintained Makefile.depend to set DEP_RELDIR and reset DIRDEPS. By setting DEP_RELDIR ourselves we can skip :tA - + * gendirdeps.mk: skip setting DEP_RELDIR. 2015-05-24 Simon J. Gerraty <sjg@bad.crufty.net> @@ -626,19 +729,19 @@ * meta.stage.mk: for STAGE_AS_* basename of file may not be unique so first use absolute path as key. - Also skip staging at level 0. + Also skip staging at level 0. 2015-04-30 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20150430 - + * dirdeps.mk: fix _count_dirdeps for non-cache case. 2015-04-16 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20150411 bump version - + * own.mk: put AUTO_OBJ in OPTIONS_DEFAULT_NO rather than YES. it is here mainly for documentation purposes, since if using auto.obj.mk it is better done via sys.mk @@ -646,21 +749,21 @@ 2015-04-01 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20150401 - + * meta2deps.sh: support @list - + * meta2deps.py: updates from Juniper - o add EXCLUDES + o add EXCLUDES o skip bogus input files. o treat 'M' and 'L' as both an 'R' and a 'W' 2015-03-03 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20150303 - + * dirdeps.mk: if MK_DIRDEPS_CACHE is yes, use dirdeps-cache which is built via sub-make so we have a .meta file to tell if - it is out-of-date. + it is out-of-date. The dirdeps-cache contains the same dependency rules that we normaly construct on the fly. This adds a few seconds overhead when the cache is out of date, @@ -669,15 +772,15 @@ 2014-11-18 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20141118 - + * meta.stage.mk: add stale_staged - + * dirdeps.mk (_DIRDEP_USE_LEVEL): allow this to be tweaked only useful under very rare conditions such as FreeBSD's make universe. * auto.obj.mk: Allow MK_AUTO_OBJ to set MKOBJDIRS=auto - + 2014-11-11 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20141111 @@ -695,24 +798,24 @@ for GENDIRDEPS_FILTER to avoid surprises. 2014-10-10 Simon J. Gerraty <sjg@bad.crufty.net> - + * dirdeps.mk (NSkipHostDir): this needs SRCTOP prepended since by the time it is applied to __depdirs they have. - + * dirdeps.mk fix filtering of _machines since M_dep_qual_fixes expects patterns like *.${MACHINE} - + * cython.mk (pyprefix?): use pyprefix to find python bits since prefix might be something else (where we install our stuff) - + 2014-09-11 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20140911 - + * dirdeps.mk: add bootstrap target to simplify adding support for new MACHINE. - + 2014-09-01 Simon J. Gerraty <sjg@bad.crufty.net> * gendirdeps.mk: Add handling of GENDIRDEPS_FILTER_DIR_VARS and @@ -722,7 +825,7 @@ 2014-08-28 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20140828 - + * cython.mk: capture logic for building python extension modules with Cython. @@ -735,8 +838,8 @@ * install-mk (MK_VERSION): 20140801 * dep.mk: use explicit MKDEP_MK rather than overload MKDEP to - identify the autodep.mk variant. - + identify the autodep.mk variant. + * sys.dependfile.mk: delete .MAKE.DEPENDFILE if its initial value does not match .MAKE.DEPENDFILE_PREFIX @@ -774,7 +877,7 @@ build). * dirdeps.mk (__depdirs): ensure // don't sneak in - + * gendirdeps.mk (DIRDEPS): ensure // don't sneak in @@ -794,7 +897,7 @@ 2014-02-09 Simon J. Gerraty <sjg@bad.crufty.net> - * options.mk: cleanup and simplify semanitcs + * options.mk: cleanup and simplify semanitcs NO_* dominates all, if both WITH_* and WITHOUT_* are defined then result is DOMINATE_* which defaults to "no". Ie. WITHOUT_ normally wins. @@ -815,7 +918,7 @@ as _build_dirs. Also fix the filtering of Makefile.depend files - for reporting what we are looking for (M_dep_qual_fixes can get confused by - Makefile.depend) + Makefile.depend) Add some more debug info. 2013-09-04 Simon J. Gerraty <sjg@bad.crufty.net> @@ -828,7 +931,7 @@ * install-mk (MK_VERSION): 20130801 * libs.mk: update to match progs.mk - + 2013-07-26 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20130726 @@ -837,13 +940,13 @@ errors also allow @file to provide huge list of .meta files. * meta2deps.py: add try_parse() to cleanup the above. - + 2013-07-16 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20130716 * own.mk: add GPROG as an option * prog.mk: honor MK_GPROF==yes - + 2013-05-10 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): 20130505 @@ -878,7 +981,7 @@ * meta.stage.mk (LN_CP_SCRIPT): Add LnCp to do the ln||cp dance consistently. * dirdeps.mk: better describe the dance in sys.mk for TARGET_SPEC. - + 2013-03-18 Simon J. Gerraty <sjg@bad.crufty.net> * gendirdeps.mk: revert the dance around .MAKE.DEPENDFILE_DEFAULT @@ -893,7 +996,7 @@ * gendirdeps.mk: ensure _objroot has trailing / if it needs it. * meta2deps.py: if machine is "host", then also trim self.host_target from any OBJROOTS. - + 2013-03-11 Simon J. Gerraty <sjg@bad.crufty.net> @@ -909,7 +1012,7 @@ 2013-03-07 Simon J. Gerraty <sjg@bad.crufty.net> - * sys.dependfile.mk (.MAKE.DEPENDFILE_DEFAULT): + * sys.dependfile.mk (.MAKE.DEPENDFILE_DEFAULT): use a separate variable for the default .MAKE.DEPENDFILE value so that it can be controlled independently of .MAKE.DEPENDFILE_PREFERENCE @@ -926,12 +1029,12 @@ 2013-02-10 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): bump version to 20130210 - * import latest dirdeps.mk, gendirdeps.mk and meta2deps.py - from Juniper. + * import latest dirdeps.mk, gendirdeps.mk and meta2deps.py + from Juniper. o dirdeps.mk now fully supports TARGET_SPEC consisting of more than just MACHINE. o no longer use DEP_MACHINE from Makefile.depend* so remove it. - + 2013-01-23 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): bump version to 20130123 @@ -976,13 +1079,13 @@ * progs.mk: add MAN and CXXFLAGS to PROG_VARS also add PROGS_TARGETS and pass on PROG_CXX if it seems appropriate. - + 2012-11-04 Simon J. Gerraty <sjg@bad.crufty.net> * meta.stage.mk: update CLEANFILES remove redundant cp of .dirdep from STAGE_AS_SCRIPT. * progs.mk: Add LDADD to PROG_VARS - + 2012-10-12 Simon J. Gerraty <sjg@bad.crufty.net> * meta.stage.mk (STAGE_DIR_FILTER): track dirs we stage to in @@ -1004,11 +1107,11 @@ * install-mk (MK_VERSION): bump version to 20120711 * dep.mk: add explicit dependencies on SRCS after applying - SRCS_DEP_FILTER + SRCS_DEP_FILTER * meta.autodep.mk: add explicit dependencies on SRCS after applying SRCS_DEP_FILTER * meta.autodep.mk: ensure GENDIRDEPS_FILTER is exported if needed. - + 2012-06-26 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): bump version to 20120626 @@ -1020,14 +1123,14 @@ * gendirdeps.mk: only produce unqualified deps if no .MAKE.DEPENDFILE_PREFERENCE ends in .${MACHINE} * meta.subdir.mk: apply SUBDIRDEPS_FILTER - + 2012-04-20 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): bump version to 20120420 * add sys.dependfile.mk so we can experiment with - .MAKE.DEPENDFILE_PREFERENCE + .MAKE.DEPENDFILE_PREFERENCE * meta.autodep.mk: _DEPENDFILE is precious! - + 2012-03-15 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): bump version to 20120315 @@ -1068,37 +1171,37 @@ o meta2deps.py add a clear 'ERROR:' token if an exception is raised. o gendirdeps.mk if ERROR: from meta2deps.py do not update anything. - + 2011-10-30 Simon J. Gerraty <sjg@bad.crufty.net> * install-new.mk separate the cmp and copy logic to its own function. - + 2011-10-28 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): bump version to 20111028 * sys.mk: include auto.obj.mk if MKOBJDIRS is set to auto * subdir.mk: ensure _SUBDIRUSE is provided - * meta.autodep.mk: remove dependency of gendirdeps.mk on auto.obj.mk + * meta.autodep.mk: remove dependency of gendirdeps.mk on auto.obj.mk * meta.subdir.mk: always allow for Makefile.depend - + 2011-10-10 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): bump version to 20111010 o minor tweak to *dirdeps.mk from Juniper sjg@ - + 2011-10-01 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): bump version to 20111001 o add meta2deps.py from Juniper sjg@ o tweak gendirdeps.mk to work with meta2deps.py when not - cross-building - * autoconf.mk: add autoconf-input as a hook for regenerating + cross-building + * autoconf.mk: add autoconf-input as a hook for regenerating AUTOCONF_INPUTS (configure). 2011-08-24 Simon J. Gerraty <sjg@bad.crufty.net> * meta.autodep.mk: if we do not have OBJS, .depend isn't a useful - trigger for updating Makefile.depend* + trigger for updating Makefile.depend* 2011-08-08 Simon J. Gerraty <sjg@bad.crufty.net> @@ -1155,7 +1258,7 @@ 2011-04-03 Simon J. Gerraty <sjg@bad.crufty.net> * rst2htm.mk: convert rst to s5 (slides) or plain html depending - on target name. + on target name. 2011-03-30 Simon J. Gerraty <sjg@bad.crufty.net> @@ -1167,16 +1270,16 @@ can be used to debug level 0 only and DEBUG_MAKE_FLAGS for the rest. * sys.mk: re-define M_whence in terms of M_type. M_type is useful for checking if something is a builtin. - + 2011-03-16 Simon J. Gerraty <sjg@bad.crufty.net> * meta.stage.mk: add stage_symlinks and leverage StageLinks for - stage_libs + stage_libs 2011-03-10 Simon J. Gerraty <sjg@bad.crufty.net> * dirdeps.mk: correct value for _depdir_files depends on - .MAKE.DEPENDFILE + .MAKE.DEPENDFILE Add our copyright - just to make it clear we have frobbed this quite a bit. DEP_MACHINE needs to be set to MACHINE each time, if using only @@ -1216,7 +1319,7 @@ 2010-09-24 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): bump version to 20100919 - include dirdeps.mk et al from Juniper Networks, + include dirdeps.mk et al from Juniper Networks, for meta mode - requires filemon(9). * sys.mk, subdir.mk: Add hooks for meta mode. we do this as meta.sys.mk, meta.autodep.mk and meta.subdir.mk @@ -1282,7 +1385,7 @@ * install-mk (MK_VERSION): bump version to 20100420 * sys/NetBSD.mk: add MACHINE_CPU to keep netbsd makefiles happy * autoconf.mk allow AUTO_AUTOCONF - + 2010-04-19 Simon J. Gerraty <sjg@bad.crufty.net> * obj.mk: add objwarn to keep freebsd makefiles happy @@ -1294,7 +1397,7 @@ * auto.dep.mk: add some explanation of how/what we do. * autodep.mk: skip the .OPTIONAL frobbing of .depend bmake's FROM_DEPEND flag makes it redundant. - + 2010-04-13 Simon J. Gerraty <sjg@bad.crufty.net> * install-mk (MK_VERSION): bump version to 20100404 @@ -1335,7 +1438,7 @@ * sys.mk,libnames.mk add .-include <local.*> this allows local customization without the need to edit the - distributed files. + distributed files. 2009-12-14 Simon J. Gerraty <sjg@void.crufty.net> @@ -1361,7 +1464,7 @@ 2009-11-17 Simon J. Gerraty <sjg@void.crufty.net> * install-mk (MK_VERSION): bump version - * host-target.mk: only export the expensive stuff + * host-target.mk: only export the expensive stuff * Generic.sys.mk (sys_mk): for SunOS we need to look for ${HOST_OS}.${HOST_OSMAJOR} too! @@ -1411,7 +1514,7 @@ * install-mk (MK_VERSION): bump version * general cleanup - * dpadd.mk introduce DPMAGIC_LIBS_* + * dpadd.mk introduce DPMAGIC_LIBS_* 2007-04-30 Simon J. Gerraty <sjg@void.crufty.net> @@ -1453,10 +1556,10 @@ * install-mk (MK_VERSION): bump version to 20061126 * warnings.mk: detect invalid WARNINGS_SET - + * warnings.mk: use ${.TARGET:T:R}.o when looking for target - specific warnings. - + specific warnings. + * For .cc sources, turn off warnings that g++ vomits on. 2006-11-08 Simon J. Gerraty <sjg@void.crufty.net> @@ -1483,11 +1586,11 @@ 2006-03-01 Simon J. Gerraty <sjg@void.crufty.net> * install-mk (MK_VERSION): bump version to 20060301 - * autodep.mk (.depend): + * autodep.mk (.depend): if MAKE_VERSION is newer than 20050530 we can make .END depend on .depend and make .depend depend on __depsrcs that exist. * dpadd.mk: add SRC_PATHADD - + 2005-11-04 Simon J. Gerraty <sjg@void.crufty.net> * install-mk (MK_VERSION): bump version to 20051104 @@ -1512,7 +1615,7 @@ 2004-02-15 Simon J. Gerraty <sjg@void.crufty.net> * own.mk: don't use NetBSD's _SRC_TOP_ it can - cause confusion. Also don't take just 'mk' as a + cause confusion. Also don't take just 'mk' as a srctop indicator. 2004-02-14 Simon J. Gerraty <sjg@void.crufty.net> @@ -1538,7 +1641,7 @@ * set OS and ROOT_GROUP for those that we know the value. for others (eg. Generic.sys.mk) wrap the != in an .ifndef so we don't do it again for each sub-make. - + 2003-09-28 Simon J. Gerraty <sjg@void.crufty.net> * install-mk (MK_VERSION): 20030928 @@ -1554,8 +1657,8 @@ 2003-07-31 Simon J. Gerraty <sjg@void.crufty.net> - * install-mk: add ability to use cp -f when updating - destination .mk files. Also now possible to play games with + * install-mk: add ability to use cp -f when updating + destination .mk files. Also now possible to play games with FORCE_SYS_MK=ln etc on *BSD machines to link /usr/share/mk/sys.mk into dest - not recommended unless you seriously want to. @@ -1575,4 +1678,4 @@ * install-mk: Allow FORCE_SYS_MK to come from env - + @@ -61,6 +61,7 @@ warnings.mk whats.mk yacc.mk dirdeps.mk +dirdeps-cache-update.mk dirdeps-options.mk dirdeps-targets.mk gendirdeps.mk diff --git a/mk/README b/mk/README index 3d79b6a2de7d..161426cfcd0c 100644 --- a/mk/README +++ b/mk/README @@ -1,10 +1,10 @@ -# $Id: README,v 1.1 1997/03/11 07:27:15 sjg Exp $ +# $Id: README,v 1.2 2020/08/19 17:51:53 sjg Exp $ This directory contains some macro's derrived from the NetBSD bsd.*.mk macros. They have the same names but without the bsd., separate macro files are needed to ensure we can make them do what we want for builing things outside of /usr/src. Nearly all the comments below -apply. +apply. # $NetBSD: bsd.README,v 1.18 1997/01/13 00:54:23 mark Exp $ # @(#)bsd.README 5.1 (Berkeley) 5/11/90 @@ -347,9 +347,9 @@ If foo has multiple source files, add the line: =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= The include file <bsd.subdir.mk> contains the default targets for building -subdirectories. It has the same eight targets as <bsd.prog.mk>: all, +subdirectories. It has the same eight targets as <bsd.prog.mk>: all, clean, cleandir, depend, includes, install, lint, and tags. For all of -the directories listed in the variable SUBDIRS, the specified directory +the directories listed in the variable SUBDIRS, the specified directory will be visited and the target made. There is also a default target which allows the command "make subdir" where subdir is any directory listed in the variable SUBDIRS. diff --git a/mk/auto.dep.mk b/mk/auto.dep.mk index 55b2971be6e0..d905649ab206 100644 --- a/mk/auto.dep.mk +++ b/mk/auto.dep.mk @@ -1,16 +1,16 @@ # # RCSid: -# $Id: auto.dep.mk,v 1.5 2016/04/05 15:58:37 sjg Exp $ +# $Id: auto.dep.mk,v 1.6 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2010, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # @@ -20,11 +20,11 @@ # set MKDEP_MK=auto.dep.mk and dep.mk will include us -# This version differs from autodep.mk, in that +# This version differs from autodep.mk, in that # we use ${.TARGET:T}.d rather than ${.TARGET:T:R}.d # this makes it simpler to get the args to -MF and -MT right # and ensure we can simply include all the .d files. -# +# # However suffix rules do not work with something like .o.d so we # don't even try to handle 'make depend' gracefully. # dep.mk will handle that itself. @@ -37,7 +37,7 @@ __${.PARSEFILE}__: # set this to -MMD to ignore /usr/include # actually it ignores <> so may not be a great idea -CFLAGS_MD ?= -MD +CFLAGS_MD ?= -MD # -MF etc not available on all gcc versions. CFLAGS_MF ?= -MF ${.TARGET:T}.d -MT ${.TARGET:T} CFLAGS += ${CFLAGS_MD} ${CFLAGS_MF} diff --git a/mk/auto.obj.mk b/mk/auto.obj.mk index a12ea7a81318..0405b5e6441d 100644 --- a/mk/auto.obj.mk +++ b/mk/auto.obj.mk @@ -1,14 +1,14 @@ -# $Id: auto.obj.mk,v 1.15 2017/11/04 21:05:04 sjg Exp $ +# $Id: auto.obj.mk,v 1.16 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2004, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # @@ -16,7 +16,7 @@ ECHO_TRACE ?= echo .ifndef Mkdirs -# A race condition in some versions of mkdir, means that it can bail +# A race condition in some versions of mkdir, means that it can bail # if another process made a dir that mkdir expected to. # We repeat the mkdir -p a number of times to try and work around this. # We stop looping as soon as the dir exists. @@ -53,7 +53,7 @@ __objdir:= ${__objdir} # We need to chdir, make the directory if needed .if !exists(${__objdir}/) && \ (${.TARGETS} == "" || ${.TARGETS:Nclean*:N*clean:Ndestroy*} != "") -# This will actually make it... +# This will actually make it... __objdir_made != echo ${__objdir}/; umask ${OBJDIR_UMASK:U002}; \ ${ECHO_TRACE} "[Creating objdir ${__objdir}...]" >&2; \ ${Mkdirs}; Mkdirs ${__objdir} diff --git a/mk/autoconf.mk b/mk/autoconf.mk index 38f4ece48ad6..61e6978043a8 100644 --- a/mk/autoconf.mk +++ b/mk/autoconf.mk @@ -1,14 +1,14 @@ -# $Id: autoconf.mk,v 1.9 2017/08/13 20:03:13 sjg Exp $ +# $Id: autoconf.mk,v 1.10 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 1996-2009, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # @@ -49,7 +49,7 @@ CLEANFILES+= config.recheck config.gen config.status *.meta AUTOCONF ?= autoconf AUTOHEADER ?= autoheader -# expand it to a full path +# expand it to a full path AUTOCONF := ${AUTOCONF:${M_whence}} .if exists(${AUTOCONF}) diff --git a/mk/autodep.mk b/mk/autodep.mk index 7b5029f15728..a7bb942278c9 100644 --- a/mk/autodep.mk +++ b/mk/autodep.mk @@ -1,16 +1,16 @@ # # RCSid: -# $Id: autodep.mk,v 1.37 2020/04/17 21:08:17 sjg Exp $ +# $Id: autodep.mk,v 1.38 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 1999-2010, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net @@ -51,7 +51,7 @@ ${s:T:R}.d: $s .endfor __depsrcs:=${__depsrcs:T:R:S/$/.d/g} -# we also need to handle makefiles where the .d's from __depsrcs +# we also need to handle makefiles where the .d's from __depsrcs # don't match those from OBJS # we avoid using := here, since the modifier applied to OBJS # can cause trouble if there are any undefined vars in OBJS. @@ -64,7 +64,7 @@ __dependsrcs= ${__dependsrcsx:O:u} # set this to -MMD to ignore /usr/include # actually it ignores <> so may not be a great idea -CFLAGS_MD?=-MD +CFLAGS_MD?=-MD # -MF etc not available on all gcc versions. # we "fix" the .o later CFLAGS_MF?=-MF ${.TARGET:T:R}.d -MT ${.TARGET:T:R}.o @@ -73,8 +73,8 @@ RM?= rm MAKE_SHELL?= sh # watch out for people who don't use CPPFLAGS -CPPFLAGS_MD=${CFLAGS:M-[IUD]*} ${CPPFLAGS} -CXXFLAGS_MD=${CXXFLAGS:M-[IUD]*} ${CPPFLAGS} +CPPFLAGS_MD=${CFLAGS:M-[IUD]*} ${CPPFLAGS} +CXXFLAGS_MD=${CXXFLAGS:M-[IUD]*} ${CPPFLAGS} # just in case these need to be different CC_MD?=${CC} diff --git a/mk/compiler.mk b/mk/compiler.mk index 40a57c081ab4..b20ecaa047e3 100644 --- a/mk/compiler.mk +++ b/mk/compiler.mk @@ -1,14 +1,14 @@ -# $Id: compiler.mk,v 1.6 2019/09/28 17:12:00 sjg Exp $ +# $Id: compiler.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2019, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # diff --git a/mk/cython.mk b/mk/cython.mk index c1318b2174e9..d3c229c7269a 100644 --- a/mk/cython.mk +++ b/mk/cython.mk @@ -1,15 +1,15 @@ # RCSid: -# $Id: cython.mk,v 1.7 2018/03/25 18:46:11 sjg Exp $ +# $Id: cython.mk,v 1.8 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2014, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # diff --git a/mk/dirdeps-cache-update.mk b/mk/dirdeps-cache-update.mk new file mode 100644 index 000000000000..eb992e936eb8 --- /dev/null +++ b/mk/dirdeps-cache-update.mk @@ -0,0 +1,179 @@ +# $Id: dirdeps-cache-update.mk,v 1.21 2020/08/19 17:51:53 sjg Exp $ +# +# @(#) Copyright (c) 2020, Simon J. Gerraty +# +# This file is provided in the hope that it will +# be of use. There is absolutely NO WARRANTY. +# Permission to copy, redistribute or otherwise +# use this file is hereby granted provided that +# the above copyright notice and this notice are +# left intact. +# +# Please send copies of changes and bug-fixes to: +# sjg@crufty.net +# + +## +# +# This makefile deals with the updating of STATIC_DIRDEPS_CACHE. +# Some targets are so huge that computing dirdeps takes a significant +# amount of time. For such targets a STATIC_DIRDEPS_CACHE can make +# sense. +# +# If the target is represented by targets/pseudo/production +# it's normal DIRDEPS would be in +# targets/pseudo/production/Makefile.depend +# and STATIC_DIRDEPS_CACHE would be +# targets/pseudo/production/Makefile.dirdeps.cache +# which is simply initialized by copying dirdeps.cache.production +# from $OBJTOP +# +# When dirdeps-targets.mk is initializing DIRDEPS it will look for +# Makefile.dirdeps.cache and unless told not to +# (MK_STATIC_DIRDEPS_CACHE=no) will use it as DIRDEPS_CACHE. +# +# If MK_STATIC_DIRDEPS_CACHE_UPDATE is "yes", then this makefile +# comes into play. +# +# We usually get included from local.dirdeps.mk +# as well as Makefile.depend of RELDIR with a static Makefile.dirdeps.cache +# +# If we see that STATIC_DIRDEPS_CACHE is in use, we need to hook a +# cache-update target into the build to regenerate dirdeps.cache +# in parallel with the rest of the build. +# If MK_STATIC_DIRDEPS_CACHE_UPDATE_IMMEDIATE is "yes" we update +# STATIC_DIRDEPS_CACHE as soon as the update is ready, +# otherwise it will be done at the end of the build. +# +# If STATIC_DIRDEPS_CACHE is not in use, but a DIRDEPS_CACHE is, +# then we need do nothing except export STATIC_DIRDEPS_CACHE and +# DYNAMIC_DIRDEPS_CACHE for use when we are include during the visit +# to the ultimate target (targets/pseudo/production). +# +# Regardless of which happens, when included at .MAKE.LEVEL > 0 +# for a target other than cache-update we simply copy +# DYNAMIC_DIRDEPS_CACHE to STATIC_DIRDEPS_CACHE with some optional +# filtering. +# +# If we are included for the target cache-update we take care of +# running dirdeps.mk again to generate the DYNAMIC_DIRDEPS_CACHE. +# + +.if !target(_${.PARSEFILE}_) +_${.PARSEFILE}_: .NOTMAIN + +STATIC_CACHE_SED += \ + -e '/Autogenerated/s,-.*,- edit with care!,' \ + -e '/cache-update/d' + +STATIC_DIRDEPS_CACHE_UPDATE_SCRIPT ?= \ + { echo Saving ${DYNAMIC_DIRDEPS_CACHE} as ${STATIC_DIRDEPS_CACHE}; \ + sed ${STATIC_CACHE_SED} ${DYNAMIC_DIRDEPS_CACHE} > ${STATIC_DIRDEPS_CACHE}; } +.endif + +.if ${MK_DIRDEPS_CACHE:Uno} == "yes" +.if ${MK_STATIC_DIRDEPS_CACHE_UPDATE:Uno} == "yes" +.if ${_debug_reldir:U0} || ${DEBUG_DIRDEPS:U:Mcache*} != "" +_debug_cache = 1 +.else +_debug_cache = 0 +.endif + +.if ${.MAKE.LEVEL} == 0 && !make(cache-update) + +.if ${_debug_cache} +.info ${MK_STATIC_DIRDEPS_CACHE_UPDATE MK_STATIC_DIRDEPS_CACHE MK_DIRDEPS_CACHE DIRDEPS_CACHE STATIC_DIRDEPS_CACHE:L:@v@$v=${$v}@} +.endif + +.if ${MK_STATIC_DIRDEPS_CACHE} == "yes" && defined(STATIC_DIRDEPS_CACHE) && exists(${STATIC_DIRDEPS_CACHE}) +.if !make(dirdeps) +# We are using static cache and this is the only look we will get. +# We want to generate an updated cache while we build +# so need to hook cache-update to dirdeps now. +# Note: we are running as a sibling to dirdeps-cached, +# attempting to do this in that context is problematic. + +# One of these should exist - to actually kick off the cache generation +.for d in ${STATIC_DIRDEPS_CACHE:H}/cache-update ${STATIC_DIRDEPS_CACHE:H:H}/cache-update ${STATIC_DIRDEPS_CACHE:H:H:H}/cache-update +.if exists($d) +cache_update_dirdep ?= $d.${TARGET_SPEC} +.endif +.endfor +.if !target(${cache_update_dirdep}) +dirdeps: ${cache_update_dirdep} +${cache_update_dirdep}: _DIRDEP_USE +DYNAMIC_DIRDEPS_CACHE := ${OBJTOP}/dirdeps.cache.${STATIC_DIRDEPS_CACHE:H:T}-update +.export DYNAMIC_DIRDEPS_CACHE STATIC_DIRDEPS_CACHE +.endif +.endif # make(dirdeps) +.endif # MK_* + +.endif # .MAKE.LEVEL 0 + +.if ${.MAKE.LEVEL} > 0 && ${.CURDIR:T} == "cache-update" +# we are the background update shim + +.if ${_debug_cache} +.info level ${.MAKE.LEVEL}: ${MK_DIRDEPS_CACHE DYNAMIC_DIRDEPS_CACHE STATIC_DIRDEPS_CACHE:L:@v@$v=${$v}@} +.endif + +all: cache-build +cache-build: .META + @set -x; MAKELEVEL=0 \ + ${.MAKE} -C ${SRCTOP} -f ${RELDIR}/Makefile cache-update \ + -DWITHOUT_STATIC_DIRDEPS_CACHE_UPDATE + +.endif # cache-update + +.elif ${.MAKE.LEVEL} == 0 && make(cache-update) && !target(cache-update) +# we were invoked above +# we just leverage dirdeps.mk +BUILD_DIRDEPS_TARGETS := ${STATIC_DIRDEPS_CACHE:H:T} +DIRDEPS := ${STATIC_DIRDEPS_CACHE:H:S,^${SRCTOP}/,,}.${TARGET_SPEC} +DIRDEPS_CACHE := ${DYNAMIC_DIRDEPS_CACHE} + +.if ${DEBUG_DIRDEPS:U:Mcache*} != "" +.info level 0: ${MK_DIRDEPS_CACHE DIRDEPS_CACHE DIRDEPS:L:@v@$v=${$v}@} +.endif + +# so cache-built below can check on us +x!= echo; echo ${.MAKE.PID} > ${DIRDEPS_CACHE}.new.pid + +cache-update: ${DIRDEPS_CACHE} + @rm -f ${DIRDEPS_CACHE}.new.pid +.if ${MK_STATIC_DIRDEPS_CACHE_UPDATE_IMMEDIATE:Uno} == "yes" + ${STATIC_DIRDEPS_CACHE_UPDATE_SCRIPT} +.endif + +all: + +.include <dirdeps.mk> + +.endif # MK_STATIC_DIRDEPS_CACHE_UPDATE +.endif # MK_DIRDEPS_CACHE + +.if ${.MAKE.LEVEL} > 0 && ${MK_STATIC_DIRDEPS_CACHE_UPDATE:Uno} == "yes" && \ + ${STATIC_DIRDEPS_CACHE:Uno:H} == "${SRCTOP}/${RELDIR}" +.if !defined(DYNAMIC_DIRDEPS_CACHE) +all: +.else +# This is the easy bit, time to save the cache + +all: cache-update + +# ensure the cache update is completed +cache-built: + @test -s ${DYNAMIC_DIRDEPS_CACHE}.new || exit 0; \ + pid=`cat ${DYNAMIC_DIRDEPS_CACHE}.new.pid 2> /dev/null`; \ + test $${pid:-0} -gt 1 || exit 0; \ + echo "Waiting for $$pid to finish ${DYNAMIC_DIRDEPS_CACHE} ..."; \ + while 'kill' -0 $$pid; do sleep 30; done > /dev/null 2>&1 + +cache-update: cache-built +.if ${MK_STATIC_DIRDEPS_CACHE_UPDATE_IMMEDIATE:Uno} == "no" + @test ! -s ${DYNAMIC_DIRDEPS_CACHE} || \ + ${STATIC_DIRDEPS_CACHE_UPDATE_SCRIPT} +.endif + +.endif +.endif diff --git a/mk/dirdeps-options.mk b/mk/dirdeps-options.mk index 4f74c02e1b8c..74f54a4cf665 100644 --- a/mk/dirdeps-options.mk +++ b/mk/dirdeps-options.mk @@ -1,6 +1,6 @@ -# $Id: dirdeps-options.mk,v 1.9 2018/09/20 00:07:19 sjg Exp $ +# $Id: dirdeps-options.mk,v 1.17 2020/08/07 01:57:38 sjg Exp $ # -# @(#) Copyright (c) 2018, Simon J. Gerraty +# @(#) Copyright (c) 2018-2020, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. @@ -37,6 +37,11 @@ # to whatever applies for that dir, or it can rely on globals # set in local.dirdeps-options.mk # Either way, we will .undef DIRDEPS.* when done. +# +# In some cases the value of MK_FOO might depend on TARGET_SPEC +# so we qualify MK_FOO with .${TARGET_SPEC} and each component +# TARGET_SPEC_VAR (in reverse order) before using MK_FOO. +# # This should have been set by Makefile.depend.options # before including us @@ -47,21 +52,43 @@ DIRDEPS_OPTIONS ?= .if ${.MAKE.LEVEL} == 0 # :U below avoids potential errors when we := -.for o in ${DIRDEPS_OPTIONS:tu} -DIRDEPS += ${DIRDEPS.$o.${MK_$o:U}:U} +# some options can depend on TARGET_SPEC! +DIRDEPS_OPTIONS_QUALIFIER_LIST ?= \ + ${DEP_TARGET_SPEC:U${TARGET_SPEC}} \ + ${TARGET_SPEC_VARSr:U${TARGET_SPEC_VARS}:@v@${DEP_$v:U${$v}}@} +# note that we need to include $o in the variable _o$o +# to ensure correct evaluation. +.for o in ${DIRDEPS_OPTIONS} +.undef _o$o _v$o +.for x in ${DIRDEPS_OPTIONS_QUALIFIER_LIST} +.if defined(MK_$o.$x) +_o$o ?= MK_$o.$x +_v$o ?= ${MK_$o.$x} +.endif +.endfor +_v$o ?= ${MK_$o} +.if ${_debug_reldir:U0} +.info ${DEP_RELDIR:U${RELDIR}}.${DEP_TARGET_SPEC:U${TARGET_SPEC}}: o=$o ${_o$o:UMK_$o}=${_v$o:U} DIRDEPS += ${DIRDEPS.$o.${_v$o:U}:U} +.endif +DIRDEPS += ${DIRDEPS.$o.${_v$o:U}:U} .endfor DIRDEPS := ${DIRDEPS:O:u} +.if ${_debug_reldir:U0} +.info ${DEP_RELDIR:U${RELDIR}}: DIRDEPS=${DIRDEPS} +.endif # avoid cross contamination -.for o in ${DIRDEPS_OPTIONS:tu} +.for o in ${DIRDEPS_OPTIONS} .undef DIRDEPS.$o.yes .undef DIRDEPS.$o.no +.undef _o$o +.undef _v$o .endfor .else # whether options are enabled or not, # we want to filter out the relevant DIRDEPS.* # we should only be included by meta.autodep.mk # if dependencies are to be updated -.for o in ${DIRDEPS_OPTIONS:tu} +.for o in ${DIRDEPS_OPTIONS} .for d in ${DIRDEPS.$o.yes} ${DIRDEPS.$o.no} .if exists(${SRCTOP}/$d) GENDIRDEPS_FILTER += N$d* diff --git a/mk/dirdeps-targets.mk b/mk/dirdeps-targets.mk index 50a1970d6211..73dcf3639d3b 100644 --- a/mk/dirdeps-targets.mk +++ b/mk/dirdeps-targets.mk @@ -1,15 +1,15 @@ # RCSid: -# $Id: dirdeps-targets.mk,v 1.10 2020/06/06 22:41:02 sjg Exp $ +# $Id: dirdeps-targets.mk,v 1.22 2020/08/15 18:00:11 sjg Exp $ # # @(#) Copyright (c) 2019-2020 Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # @@ -25,7 +25,16 @@ # We then search those dirs for any Makefile.depend* # Finally we select any that match conditions like REQUESTED_MACHINE # or TARGET_SPEC and initialize DIRDEPS accordingly. -# +# +# We will check each of the initial DIRDEPS for Makefile.dirdeps.options +# and include any found. +# This makes it feasible to tweak options like MK_DIRDEPS_CACHE +# for a specific target. +# +# If MK_STATIC_DIRDEPS_CACHE is defined we will check if the +# initial DIRDEPS has a static cache (Makefile.dirdeps.cache). +# This only makes sense for seriously expensive targets. +# .if ${.MAKE.LEVEL} == 0 # pickup customizations @@ -125,12 +134,38 @@ DIRDEPS := ${DIRDEPS:O:u} .endif # if we got DIRDEPS get to work .if !empty(DIRDEPS) +DIRDEPS.dirs := ${DIRDEPS:S,^,${SRCTOP}/,:@d@${exists($d):?$d:${d:R}}@} +# some targets what to tweak options we might want to process now +.for m in ${DIRDEPS.dirs:S,$,/Makefile.dirdeps.options,} +.-include <$m> +.endfor +.if defined(MK_STATIC_DIRDEPS_CACHE) +# some targets are very expensive to compute dirdeps for +# so we may have a static cache +.for c in ${DIRDEPS.dirs:S,$,/Makefile.dirdeps.cache,} +.if exists($c) +STATIC_DIRDEPS_CACHE ?= $c +.if ${MK_STATIC_DIRDEPS_CACHE} == "yes" +DIRDEPS_CACHE ?= $c +MK_DIRDEPS_CACHE = yes +.endif +.endif +.endfor +.if defined(STATIC_DIRDEPS_CACHE) +.export STATIC_DIRDEPS_CACHE +.endif +.endif + +# allow a top-level makefile to do other stuff +# before including dirdeps.mk +.if ${MK_DIRDEPS_TARGETS_INCLUDE_DIRDEPS:Uyes} == "yes" .include <dirdeps.mk> +.endif DIRDEPS_TARGETS_SKIP += all clean* destroy* .for t in ${.TARGETS:${DIRDEPS_TARGETS_SKIP:${M_ListToSkip}}} $t: dirdeps -.endfor +.endfor .endif .endif diff --git a/mk/dirdeps.mk b/mk/dirdeps.mk index 337692479898..16673a04c07b 100644 --- a/mk/dirdeps.mk +++ b/mk/dirdeps.mk @@ -1,4 +1,4 @@ -# $Id: dirdeps.mk,v 1.106 2020/07/11 16:25:17 sjg Exp $ +# $Id: dirdeps.mk,v 1.125 2020/08/26 21:49:45 sjg Exp $ # Copyright (c) 2010-2020, Simon J. Gerraty # Copyright (c) 2010-2018, Juniper Networks, Inc. @@ -41,7 +41,7 @@ # or .<target_spec> suffix (see TARGET_SPEC_VARS below), # for example to force building something for the pseudo # machines "host" or "common" regardless of current ${MACHINE}. -# +# # All unqualified entries end up being qualified with .${TARGET_SPEC} # and partially qualified (if TARGET_SPEC_VARS has multiple # entries) are also expanded to a full .<target_spec>. @@ -50,29 +50,22 @@ # # The fully qualified directory entries are used to construct a # dependency graph that will drive the build later. -# +# # Also, for each fully qualified directory target, we will search # using ${.MAKE.DEPENDFILE_PREFERENCE} to find additional # dependencies. We use Makefile.depend (default value for # .MAKE.DEPENDFILE_PREFIX) to refer to these makefiles to # distinguish them from others. -# +# # Before each Makefile.depend file is read, we set # DEP_RELDIR to be the RELDIR (path relative to SRCTOP) for # its directory, and DEP_MACHINE etc according to the .<target_spec> # represented by the suffix of the corresponding target. -# +# # Since each Makefile.depend file includes dirdeps.mk, this # processing is recursive and results in .MAKE.LEVEL 0 learning the # dependencies of the tree wrt the initial directory (_DEP_RELDIR). # -# BUILD_AT_LEVEL0 -# Indicates whether .MAKE.LEVEL 0 builds anything: -# if "no" sub-makes are used to build everything, -# if "yes" sub-makes are only used to build for other machines. -# It is best to use "no", but this can require fixing some -# makefiles to not do anything at .MAKE.LEVEL 0. -# # TARGET_SPEC_VARS # The default value is just MACHINE, and for most environments # this is sufficient. The _DIRDEP_USE target actually sets @@ -113,12 +106,12 @@ # # make sure we know what TARGET_SPEC is # # as we may need it to find Makefile.depend* # TARGET_SPEC = ${TARGET_SPEC_VARS:@v@${$v:U}@:ts,} -# +# # The following variables can influence the initial DIRDEPS # computation with regard to the TARGET_SPECs that will be # built. # Most should also be considered by init.mk -# +# # ONLY_TARGET_SPEC_LIST # Defines a list of TARGET_SPECs for which the current # directory can be built. @@ -137,6 +130,19 @@ # A list of MACHINEs the current directory should not be # built for. # +# _build_xtra_dirs +# local.dirdeps.mk can add targets to this variable. +# They will be hooked into the build, but independent of +# any other DIRDEP. +# +# This allows for adding TESTS to the build, such that the build +# if any test fails, but without the risk of introducing +# circular dependencies. + +now_utc ?= ${%s:L:gmtime} +.if !defined(start_utc) +start_utc := ${now_utc} +.endif .if !target(bootstrap) && (make(bootstrap) || \ make(bootstrap-this) || \ @@ -157,11 +163,6 @@ _DIRDEP_USE_LEVEL?= 0 _CURDIR ?= ${.CURDIR} _OBJDIR ?= ${.OBJDIR} -now_utc = ${%s:L:gmtime} -.if !defined(start_utc) -start_utc := ${now_utc} -.endif - .if ${MAKEFILE:T} == ${.PARSEFILE} && empty(DIRDEPS) && ${.TARGETS:Uall:M*/*} != "" # This little trick let's us do # @@ -223,6 +224,7 @@ _tspec_m$i := ${TARGET_SPEC_VARS:[2..$i]:@w@[^,]+@:ts,} _tspec_a$i := ,${TARGET_SPEC_VARS:[$i..-1]:@v@$$$${DEP_$v}@:ts,} M_dep_qual_fixes += C;(\.${_tspec_m$i})$$;\1${_tspec_a$i}; .endfor +TARGET_SPEC_VARSr := ${TARGET_SPEC_VARS:[-1..1]} .else # A harmless? default. M_dep_qual_fixes = U @@ -307,6 +309,7 @@ DEP_MACHINE := ${_DEP_TARGET_SPEC} # reset each time through _build_all_dirs = +_build_xtra_dirs = # the first time we are included the _DIRDEP_USE target will not be defined # we can use this as a clue to do initialization and other one time things. @@ -337,7 +340,7 @@ BUILD_DIRDEPS ?= yes .if ${MK_DIRDEPS_CACHE} == "yes" # this is where we will cache all our work -DIRDEPS_CACHE ?= ${_OBJDIR:tA}/dirdeps.cache${.TARGETS:Nall:O:u:ts-:S,/,_,g:S,^,.,:N.} +DIRDEPS_CACHE ?= ${_OBJDIR:tA}/dirdeps.cache${_TARGETS:U${.TARGETS}:Nall:O:u:ts-:S,/,_,g:S,^,.,:N.} .endif .if ${DEBUG_DIRDEPS:@x@${DEP_RELDIR:M$x}${${DEP_RELDIR}.${DEP_MACHINE}:L:M$x}@} != "" @@ -393,6 +396,18 @@ DIRDEPS_FILTER += M${_DEP_RELDIR} DIRDEP_MAKE ?= ${.MAKE} DIRDEP_DIR ?= ${.TARGET:R} +# if you want us to report load averages during build +# DIRDEP_USE_PRELUDE += ${DIRDEP_LOADAVG_REPORT}; + +DIRDEP_LOADAVG_CMD ?= ${UPTIME:Uuptime} | sed 's,.*\(load\),\1,' +DIRDEP_LOADAVG_LAST = 0 +# yes the expression here is a bit complicated, +# the trick is to only eval ${DIRDEP_LOADAVG_LAST::=${now_utc}} +# when we want to report. +DIRDEP_LOADAVG_REPORT = \ + test -z "${"${expr ${now_utc} - ${DIRDEP_LOADAVG_INTEVAL:U60} - ${DIRDEP_LOADAVG_LAST}:L:sh:N-*}":?yes${DIRDEP_LOADAVG_LAST::=${now_utc}}:}" || \ + echo "${TRACER}`${DIRDEP_LOADAVG_CMD}`" + # we suppress SUBDIR when visiting the leaves # we assume sys.mk will set MACHINE_ARCH # you can add extras to DIRDEP_USE_ENV @@ -400,7 +415,7 @@ DIRDEP_DIR ?= ${.TARGET:R} _DIRDEP_USE: .USE .MAKE @for m in ${.MAKE.MAKEFILE_PREFERENCE}; do \ test -s ${.TARGET:R}/$$m || continue; \ - echo "${TRACER}Checking ${.TARGET:R} for ${.TARGET:E} ..."; \ + echo "${TRACER}Checking ${.TARGET:S,${SRCTOP}/,,} for ${.TARGET:E} ..."; \ ${DIRDEP_USE_PRELUDE} \ MACHINE_ARCH= NO_SUBDIR=1 ${DIRDEP_USE_ENV} \ TARGET_SPEC=${.TARGET:E} \ @@ -480,9 +495,13 @@ dirdeps-cached: ${DIRDEPS_CACHE} .MAKE dirdeps MK_DIRDEPS_CACHE=no BUILD_DIRDEPS=no # these should generally do -BUILD_DIRDEPS_MAKEFILE ?= ${MAKEFILE} +BUILD_DIRDEPS_MAKEFILE ?= BUILD_DIRDEPS_TARGETS ?= ${.TARGETS} +.if ${DIRDEPS_CACHE} != ${STATIC_DIRDEPS_CACHE:Uno} && ${DIRDEPS_CACHE:M${SRCTOP}/*} == "" +# export this for dirdeps-cache-update.mk +DYNAMIC_DIRDEPS_CACHE := ${DIRDEPS_CACHE} +.export DYNAMIC_DIRDEPS_CACHE # we need the .meta file to ensure we update if # any of the Makefile.depend* changed. # We do not want to compare the command line though. @@ -494,26 +513,28 @@ ${DIRDEPS_CACHE}: .META .NOMETA_CMP +@MAKELEVEL=${.MAKE.LEVEL} DIRDEPS_CACHE=${DIRDEPS_CACHE} \ DIRDEPS="${DIRDEPS}" \ TARGET_SPEC=${TARGET_SPEC} \ - MAKEFLAGS= ${.MAKE} -C ${_CURDIR} -f ${BUILD_DIRDEPS_MAKEFILE} \ + MAKEFLAGS= ${DIRDEP_CACHE_MAKE:U${.MAKE}} -C ${_CURDIR} \ + ${BUILD_DIRDEPS_MAKEFILE} \ ${BUILD_DIRDEPS_TARGETS} BUILD_DIRDEPS_CACHE=yes \ .MAKE.DEPENDFILE=.none \ ${.MAKEFLAGS:tW:S,-D ,-D,g:tw:M*WITH*} \ ${.MAKEFLAGS:tW:S,-d ,-d,g:tw:M-d*} \ - 3>&1 1>&2 | sed 's,${SRCTOP},$${SRCTOP},g' >> ${.TARGET}.new && \ + 3>&1 1>&2 | sed 's,${SRCTOP},$${SRCTOP},g;s,_{,$${,g' >> ${.TARGET}.new && \ mv ${.TARGET}.new ${.TARGET} .endif +.endif .elif !target(_count_dirdeps) # we want to capture the dirdeps count in the cache .END: _count_dirdeps _count_dirdeps: .NOMETA - @{ echo; echo '.info $${.newline}$${TRACER}Makefiles read: total=${.MAKE.MAKEFILES:[#]} depend=${.MAKE.MAKEFILES:M*depend*:[#]} dirdeps=${.ALLTARGETS:M${SRCTOP}*:O:u:[#]}'; } >&3 + @{ echo; echo '.info $${.newline}$${TRACER}Makefiles read: total=${.MAKE.MAKEFILES:[#]} depend=${.MAKE.MAKEFILES:M*depend*:[#]} dirdeps=${.ALLTARGETS:M${SRCTOP}*:O:u:[#]} ${DIRDEP_INFO_XTRAS}'; } >&3 .endif .elif !make(dirdeps) && !target(_count_dirdeps) beforedirdeps: _count_dirdeps _count_dirdeps: .NOMETA - @echo "${TRACER}Makefiles read: total=${.MAKE.MAKEFILES:[#]} depend=${.MAKE.MAKEFILES:M*depend*:[#]} dirdeps=${.ALLTARGETS:M${SRCTOP}*:O:u:[#]} seconds=`expr ${now_utc} - ${start_utc}`" + @echo "${TRACER}Makefiles read: total=${.MAKE.MAKEFILES:[#]} depend=${.MAKE.MAKEFILES:M*depend*:[#]} dirdeps=${.ALLTARGETS:M${SRCTOP}*:O:u:[#]} ${DIRDEP_INFO_XTRAS} seconds=`expr ${now_utc} - ${start_utc}`" .endif .endif @@ -570,19 +591,7 @@ _build_dirs = .if ${DEP_RELDIR} == ${_DEP_RELDIR} # pickup other machines for this dir if necessary -.if ${BUILD_AT_LEVEL0:Uyes} == "no" _build_dirs += ${_machines:@m@${_CURDIR}.$m@} -.else -_build_dirs += ${_machines:N${DEP_TARGET_SPEC}:@m@${_CURDIR}.$m@} -.if ${DEP_TARGET_SPEC} == ${TARGET_SPEC} -# pickup local dependencies now -.if ${MAKE_VERSION} < 20160220 -.-include <.depend> -.else -.dinclude <.depend> -.endif -.endif -.endif .endif .if ${_debug_reldir} @@ -635,7 +644,7 @@ _build_dirs := ${_build_dirs:${M_dep_qual_fixes:ts:}:O:u} .endif # empty DIRDEPS -_build_all_dirs += ${_build_dirs} +_build_all_dirs += ${_build_dirs} ${_build_xtra_dirs} _build_all_dirs := ${_build_all_dirs:O:u} # Normally if doing make -V something, @@ -647,12 +656,7 @@ _build_all_dirs := ${_build_all_dirs:O:u} x!= echo; { echo; echo '\# ${DEP_RELDIR}.${DEP_TARGET_SPEC}'; } >&3 # guard against _new_dirdeps being too big for a single command line _new_dirdeps := ${_build_all_dirs:@x@${target($x):?:$x}@} -.if !empty(_new_dirdeps) -.export _new_dirdeps -x!= echo; { echo; echo "dirdeps: \\"; \ - for x in $$_new_dirdeps; do echo " $$x \\"; done; echo; \ - for x in $$_new_dirdeps; do echo "$$x: _DIRDEP_USE"; done; } >&3 -.endif +.export _build_xtra_dirs _new_dirdeps .if !empty(DEP_EXPORT_VARS) # Discouraged, but there are always exceptions. # Handle it here rather than explain how. @@ -676,7 +680,8 @@ DEP_EXPORT_VARS= # this builds the dependency graph .for m in ${_machines} .if ${BUILD_DIRDEPS_CACHE} == "yes" && !empty(_build_dirs) -x!= echo; { echo; echo "${_this_dir}.$m: \\"; } >&3 +x!= echo; { echo; echo 'DIRDEPS.${_this_dir}.$m = \'; } >&3 +_cache_deps = .endif # it would be nice to do :N${.TARGET} .if !empty(__qual_depdirs) @@ -685,11 +690,7 @@ x!= echo; { echo; echo "${_this_dir}.$m: \\"; } >&3 .info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$q} .endif .if ${BUILD_DIRDEPS_CACHE} == "yes" -_cache_deps := ${_build_dirs:M*.$q} -.if !empty(_cache_deps) -.export _cache_deps -x!= echo; for x in $$_cache_deps; do echo " $$x \\"; done >&3 -.endif +_cache_deps += ${_build_dirs:M*.$q} .else ${_this_dir}.$m: ${_build_dirs:M*.$q} .endif @@ -699,11 +700,17 @@ ${_this_dir}.$m: ${_build_dirs:M*.$q} .info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$m:N${_this_dir}.$m} .endif .if ${BUILD_DIRDEPS_CACHE} == "yes" -_cache_deps := ${_build_dirs:M*.$m:N${_this_dir}.$m} +.if !empty(_build_dirs) +_cache_deps += ${_build_dirs:M*.$m:N${_this_dir}.$m} .if !empty(_cache_deps) .export _cache_deps x!= echo; for x in $$_cache_deps; do echo " $$x \\"; done >&3 .endif +x!= echo; { echo; echo '${_this_dir}.$m: $${DIRDEPS.${_this_dir}.$m}'; \ + echo; echo 'dirdeps: ${_this_dir}.$m \'; \ + for x in $$_build_xtra_dirs; do echo " $$x \\"; done; \ + echo; for x in $$_new_dirdeps; do echo "$$x: _DIRDEP_USE"; done; } >&3 +.endif .else ${_this_dir}.$m: ${_build_dirs:M*.$m:N${_this_dir}.$m} .endif @@ -773,6 +780,27 @@ _DEP_RELDIR := ${RELDIR} # This is a final opportunity to add/hook global rules. .-include <local.dirdeps-build.mk> +# skip _reldir_{finish,failed} if not included from Makefile.depend* +# or not in meta mode +.if !defined(WITHOUT_META_STATS) && ${.INCLUDEDFROMFILE:U:M${.MAKE.DEPENDFILE_PREFIX}*} != "" && ${.MAKE.MODE:Mmeta} != "" + +meta_stats= meta=${empty(.MAKE.META.FILES):?0:${.MAKE.META.FILES:[#]}} \ + created=${empty(.MAKE.META.CREATED):?0:${.MAKE.META.CREATED:[#]}} + +.if !target(_reldir_finish) +.END: _reldir_finish +_reldir_finish: .NOMETA + @echo "${TRACER}Finished ${RELDIR}.${TARGET_SPEC} seconds=$$(( ${now_utc} - ${start_utc} )) ${meta_stats}" +.endif + +.if !target(_reldir_failed) +.ERROR: _reldir_failed +_reldir_failed: .NOMETA + @echo "${TRACER}Failed ${RELDIR}.${TARGET_SPEC} seconds=$$(( ${now_utc} - ${start_utc} )) ${meta_stats}" +.endif + +.endif + # pickup local dependencies .if ${MAKE_VERSION} < 20160220 .-include <.depend> diff --git a/mk/dpadd.mk b/mk/dpadd.mk index 02fa7f386921..45585ce76d9b 100644 --- a/mk/dpadd.mk +++ b/mk/dpadd.mk @@ -1,14 +1,14 @@ -# $Id: dpadd.mk,v 1.27 2019/05/17 13:58:53 sjg Exp $ +# $Id: dpadd.mk,v 1.28 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2004, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # @@ -75,7 +75,7 @@ # and -L${STAGE_OBJTOP}/usr/lib are sufficient, and we should # have no need of anything else. # - + .if !target(__${.PARSEFILE}__) __${.PARSEFILE}__: @@ -98,7 +98,7 @@ RELOBJTOP?= ${OBJTOP} RELSRCTOP?= ${SRCTOP} # we get included just about everywhere so this is handy... -# C*DEBUG_XTRA are for defining on cmd line etc +# C*DEBUG_XTRA are for defining on cmd line etc # so do not use in makefiles. .ifdef CFLAGS_DEBUG_XTRA CFLAGS_LAST += ${CFLAGS_DEBUG_XTRA} @@ -180,14 +180,14 @@ SRC_LIBS+= ${_OBJDIR}/lib${LIB}.a .endif .endif -# +# # This little bit of magic, assumes that SRC_libfoo will be # set if it cannot be correctly derrived from ${LIBFOO} # Note that SRC_libfoo and INCLUDES_libfoo should be named for the # actual library name not the variable name that might refer to it. # 99% of the time the two are the same, but the DPADD logic # only has the library name available, so stick to that. -# +# SRC_LIBS?= # magic_libs includes those we want to link with @@ -198,7 +198,7 @@ DPMAGIC_LIBS += ${__dpadd_magic_libs} \ # we skip this for staged libs .for __lib in ${DPMAGIC_LIBS:O:u:N${STAGE_OBJTOP:Unot}*/lib/*} -# +# # if SRC_libfoo is not set, then we assume that the srcdir corresponding # to where we found the library is correct. # @@ -215,8 +215,8 @@ INCLUDES_${__lib:T:R}?= -I${exists(${SRC_${__lib:T:R}}/h):?${SRC_${__lib:T:R}}/h .endfor -# even for staged libs we sometimes -# need to allow direct -I to avoid cicular dependencies +# even for staged libs we sometimes +# need to allow direct -I to avoid cicular dependencies .for __lib in ${DPMAGIC_LIBS:O:u:T:R} .if !empty(SRC_${__lib}) && empty(INCLUDES_${__lib}) # must be a staged lib @@ -267,7 +267,7 @@ __dpadd_last_incs += ${__dpadd_magic_libs:O:u:@s@${SRC_LIBS_${s:T:R}:U}@:@x@${IN __dpadd_last_incs := \ ${__dpadd_last_incs:N-I/usr/*} \ ${__dpadd_incs:M-I/usr/*} \ - ${__dpadd_last_incs:M-I/usr/*} + ${__dpadd_last_incs:M-I/usr/*} __dpadd_incs := ${__dpadd_incs:N-I/usr/*} .endif @@ -310,12 +310,12 @@ dpadd: .NOTMAIN .endif .ifdef SRC_PATHADD -# We don't want to assume that we need to .PATH every element of +# We don't want to assume that we need to .PATH every element of # SRC_LIBS, but the Makefile cannot do # .PATH: ${SRC_libfoo} # since the value of SRC_libfoo must be available at the time .PATH: -# is read - and we only just worked it out. -# Further, they can't wait until after include of {lib,prog}.mk as +# is read - and we only just worked it out. +# Further, they can't wait until after include of {lib,prog}.mk as # the .PATH is needed before then. # So we let the Makefile do # SRC_PATHADD+= ${SRC_libfoo} diff --git a/mk/files.mk b/mk/files.mk index fa16b118fb6c..513ab1fd819e 100644 --- a/mk/files.mk +++ b/mk/files.mk @@ -1,14 +1,14 @@ -# $Id: files.mk,v 1.6 2017/05/07 02:21:02 sjg Exp $ +# $Id: files.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2017, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # diff --git a/mk/gendirdeps.mk b/mk/gendirdeps.mk index 82618f2225a3..b977f3c48d99 100644 --- a/mk/gendirdeps.mk +++ b/mk/gendirdeps.mk @@ -1,18 +1,18 @@ -# $Id: gendirdeps.mk,v 1.44 2020/06/23 04:21:51 sjg Exp $ +# $Id: gendirdeps.mk,v 1.46 2020/08/19 17:51:53 sjg Exp $ # Copyright (c) 2011-2020, Simon J. Gerraty # Copyright (c) 2010-2018, Juniper Networks, Inc. # All rights reserved. -# +# # Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: +# modification, are permitted provided that the following conditions +# are met: # 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. +# notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# +# documentation and/or other materials provided with the distribution. +# # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -23,7 +23,7 @@ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # This makefile [re]generates ${.MAKE.DEPENDFILE} @@ -104,7 +104,7 @@ GENDIRDEPS_FILTER += ${GENDIRDEPS_FILTER_DIR_VARS:@v@S,${$v},_{${v}},@} GENDIRDEPS_FILTER += ${GENDIRDEPS_FILTER_VARS:@v@S,/${$v}/,/_{${v}}/,@:NS,//,*:u} .endif -# this (*should* be set in meta.sys.mk) +# this (*should* be set in meta.sys.mk) # is the script that extracts what we want. META2DEPS ?= ${.PARSEDIR}/meta2deps.sh META2DEPS := ${META2DEPS} @@ -122,7 +122,7 @@ _py_d = .if ${META2DEPS:E} == "py" # we can afford to do this all the time. DPDEPS ?= no -META2DEPS_CMD = ${_time} ${PYTHON} ${META2DEPS} ${_py_d} +META2DEPS_CMD = ${_time} ${PYTHON} ${META2DEPS} ${_py_d} .if ${DPDEPS:tl} != "no" META2DEPS_CMD += -D ${DPDEPS} .endif @@ -155,8 +155,8 @@ M2D_OBJROOTS += ${STAGE_ROOT} # and tell it not to add machine qualifiers META2DEPS_ARGS += MACHINE=none .endif -.if defined(SB_BACKING_SB) -META2DEPS_CMD += -S ${SB_BACKING_SB}/src +.if defined(SB_BACKING_SB) +META2DEPS_CMD += -S ${SB_BACKING_SB}/src M2D_OBJROOTS += ${SB_BACKING_SB}/${SB_OBJPREFIX} .endif @@ -177,7 +177,7 @@ _meta_files := ${META_FILES:N\*.meta:O:u} # assume a big list _meta_files_arg= @meta.list .if empty(_meta_files) && ${META_FILES:M\*.meta} != "" -# XXX this should be considered a bad idea, +# XXX this should be considered a bad idea, # since we cannot ignore stale .meta x != cd ${_OBJDIR} && find . -name '*.meta' -print -o \( -type d ! -name . -prune \) | sed 's,^./,,' > meta.list; echo .elif ${_meta_files:[#]} > 500 @@ -234,10 +234,10 @@ dir_list += ${ddeps} # DIRDEPS represent things that had to have been built first # so they should all be undir OBJTOP. -# Note that ${_OBJTOP}/bsd/include/machine will get reported +# Note that ${_OBJTOP}/bsd/include/machine will get reported # to us as $SRCTOP/bsd/sys/$MACHINE_ARCH/include meaning we # will want to visit bsd/include -# so we add +# so we add # ${"${dir_list:M*bsd/sys/${MACHINE_ARCH}/include}":?bsd/include:} # to GENDIRDEPS_DIR_LIST_XTRAS _objtops = ${OBJTOP} ${_OBJTOP} ${_objtop} @@ -272,11 +272,11 @@ DIRDEPS = \ # We only consider things below $RELDIR/ if they have a makefile. # This is the same test that _DIRDEP_USE applies. -# We have do a double test with dirdep_list as it _may_ contain +# We have do a double test with dirdep_list as it _may_ contain # qualified dirs - if we got anything from a stage dir. # qualdir_list we know are all qualified. # It would be nice do peform this check for all of DIRDEPS, -# but we cannot assume that all of the tree is present, +# but we cannot assume that all of the tree is present, # in fact we can only assume that RELDIR is. DIRDEPS += \ ${dirdep_list:M${RELDIR}/*:@d@${.MAKE.MAKEFILE_PREFERENCE:@m@${exists(${SRCTOP}/$d/$m):?$d:${exists(${SRCTOP}/${d:R}/$m):?$d:}}@}@} \ @@ -309,7 +309,7 @@ SRC_DIRDEPS = \ SRC_DIRDEPS := ${SRC_DIRDEPS:${GENDIRDEPS_SRC_FILTER:UN/*:ts:}:C,//+,/,g:O:u} # if you want to capture SRC_DIRDEPS in .MAKE.DEPENDFILE put -# SRC_DIRDEPS_FILE = ${_DEPENDFILE} +# SRC_DIRDEPS_FILE = ${_DEPENDFILE} # in local.gendirdeps.mk .if ${SRC_DIRDEPS_FILE:Uno:tl} != "no" ECHO_SRC_DIRDEPS = echo 'SRC_DIRDEPS = \'; echo '${SRC_DIRDEPS:@d@ $d \\${.newline}@}'; echo; @@ -324,7 +324,7 @@ ${SRC_DIRDEPS_FILE}: ${META_FILES} ${_this} ${META2DEPS} .endif .endif .endif -_include_src_dirdeps ?= +_include_src_dirdeps ?= all: ${_DEPENDFILE} @@ -339,11 +339,7 @@ CAT_DEPEND ?= .depend .PHONY: ${_DEPENDFILE} .endif -.if ${BUILD_AT_LEVEL0:Uno:tl} == "no" LOCAL_DEPENDS_GUARD ?= _{.MAKE.LEVEL} > 0 -.else -LOCAL_DEPENDS_GUARD ?= _{DEP_RELDIR} == _{_DEP_RELDIR} -.endif # 'cat .depend' should suffice, but if we are mixing build modes # .depend may contain things we don't want. diff --git a/mk/host-target.mk b/mk/host-target.mk index a83642c9698c..3e6094f16730 100644 --- a/mk/host-target.mk +++ b/mk/host-target.mk @@ -1,5 +1,5 @@ # RCSid: -# $Id: host-target.mk,v 1.12 2020/07/08 23:35:29 sjg Exp $ +# $Id: host-target.mk,v 1.13 2020/08/05 23:32:08 sjg Exp $ # Host platform information; may be overridden .if !defined(_HOST_OSNAME) @@ -16,7 +16,7 @@ _HOST_MACHINE != uname -m .endif .if !defined(_HOST_ARCH) # for NetBSD prefer $MACHINE (amd64 rather than x86_64) -.if ${_HOST_OSNAME:NNetBSD} == "" +.if ${_HOST_OSNAME:NDarwin:NNetBSD} == "" _HOST_ARCH := ${_HOST_MACHINE} .else _HOST_ARCH != uname -p 2> /dev/null || uname -m diff --git a/mk/host.libnames.mk b/mk/host.libnames.mk index c0a13d756d16..3afa73f9d2a3 100644 --- a/mk/host.libnames.mk +++ b/mk/host.libnames.mk @@ -1,14 +1,14 @@ -# $Id: host.libnames.mk,v 1.4 2010/01/11 23:01:31 sjg Exp $ +# $Id: host.libnames.mk,v 1.5 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2007-2009, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # diff --git a/mk/inc.mk b/mk/inc.mk index 74626d34646f..5fc14b32d88d 100644 --- a/mk/inc.mk +++ b/mk/inc.mk @@ -1,14 +1,14 @@ -# $Id: inc.mk,v 1.7 2017/05/06 17:29:45 sjg Exp $ +# $Id: inc.mk,v 1.8 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2008, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # diff --git a/mk/init.mk b/mk/init.mk index d4ae45372d9c..0dae997beb54 100644 --- a/mk/init.mk +++ b/mk/init.mk @@ -1,14 +1,14 @@ -# $Id: init.mk,v 1.17 2020/05/25 20:15:07 sjg Exp $ +# $Id: init.mk,v 1.21 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2002, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # @@ -65,7 +65,11 @@ CC_PIC?= -DPIC CXX_PIC?= ${CC_PIC} PROFFLAGS?= -DGPROF -DPROF -.if ${.MAKE.LEVEL:U1} == 0 && ${BUILD_AT_LEVEL0:Uyes:tl} == "no" +# targets that are ok at level 0 +LEVEL0_TARGETS += clean* destory* +M_ListToSkip= O:u:S,^,N,:ts: + +.if ${.MAKE.LEVEL:U1} == 0 && ${MK_DIRDEPS_BUILD:Uno} == "yes" && ${.TARGETS:Uall:${LEVEL0_TARGETS:${M_ListToSkip}}} != "" # this tells lib.mk and prog.mk to not actually build anything _SKIP_BUILD = not building at level 0 .endif diff --git a/mk/install-mk b/mk/install-mk index 486bcd25d05f..66185e42cc6e 100644 --- a/mk/install-mk +++ b/mk/install-mk @@ -33,7 +33,7 @@ # # All our *.mk files are copied to "dest" with appropriate # ownership and permissions. -# +# # By default if a sys.mk can be found in a standard location # (that bmake will find) then no sys.mk will be put in "dest". # @@ -55,22 +55,22 @@ # Simon J. Gerraty <sjg@crufty.net> # RCSid: -# $Id: install-mk,v 1.174 2020/07/10 21:50:14 sjg Exp $ +# $Id: install-mk,v 1.179 2020/08/26 21:49:45 sjg Exp $ # # @(#) Copyright (c) 1994 Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # -MK_VERSION=20200710 +MK_VERSION=20200826 OWNER= GROUP= MODE=444 @@ -137,7 +137,7 @@ if [ -s $SYS_MK -a -d $dest ]; then sys_mk_dir=`realpath $SYS_MK_DIR` if [ $dest = $sys_mk_dir ]; then case "$os" in - *BSD*) SKIP_SYS_MK=: + *BSD*) SKIP_SYS_MK=: SKIP_BSD_MK=: ;; *) # could be fake? diff --git a/mk/install-new.mk b/mk/install-new.mk index ddfff20e3b85..d312bdc26d46 100644 --- a/mk/install-new.mk +++ b/mk/install-new.mk @@ -1,14 +1,14 @@ -# $Id: install-new.mk,v 1.3 2012/03/24 18:25:49 sjg Exp $ +# $Id: install-new.mk,v 1.4 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2009, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # diff --git a/mk/java.mk b/mk/java.mk index ef4a5ea0ba10..e2149e7089a7 100644 --- a/mk/java.mk +++ b/mk/java.mk @@ -1,16 +1,16 @@ # # RCSid: -# $Id: java.mk,v 1.14 2007/11/22 08:16:25 sjg Exp $ +# $Id: java.mk,v 1.15 2020/08/19 17:51:53 sjg Exp $ # @(#) Copyright (c) 1998-2001, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # @@ -53,7 +53,7 @@ JAVAC_FLAGS+= ${JAVAC_DBG} .if defined(MAKE_VERSION) && !defined(NO_CLASSES_COOKIE) # java works best by compiling a bunch of classes at once. -# this lot does that but needs a recent netbsd make or +# this lot does that but needs a recent netbsd make or # or its portable cousin bmake. .for __s in ${SRCS} __c:= ${__classdest}${__s:.java=.class} diff --git a/mk/lib.mk b/mk/lib.mk index 03d24e27cbe6..c3979414ec49 100644 --- a/mk/lib.mk +++ b/mk/lib.mk @@ -1,4 +1,4 @@ -# $Id: lib.mk,v 1.70 2020/05/02 02:10:20 sjg Exp $ +# $Id: lib.mk,v 1.71 2020/08/19 17:51:53 sjg Exp $ .if !target(__${.PARSEFILE}__) __${.PARSEFILE}__: @@ -67,7 +67,7 @@ META_NOECHO?= echo # Alpha-specific shared library flags FPICFLAGS ?= -fPIC CPICFLAGS ?= -fPIC -DPIC -CPPPICFLAGS?= -DPIC +CPPPICFLAGS?= -DPIC CAPICFLAGS?= ${CPPPICFLAGS} ${CPICFLAGS} APICFLAGS ?= .elif ${MACHINE_ARCH} == "mipsel" || ${MACHINE_ARCH} == "mipseb" @@ -87,7 +87,7 @@ MKPICLIB= no .elif (${MACHINE_ARCH} == "sparc" || ${MACHINE_ARCH} == "sparc64") && \ ${OBJECT_FMT} == "ELF" -# If you use -fPIC you need to define BIGPIC to turn on 32-bit +# If you use -fPIC you need to define BIGPIC to turn on 32-bit # relocations in asm code FPICFLAGS ?= -fPIC CPICFLAGS ?= -fPIC -DPIC @@ -102,7 +102,7 @@ SHLIB_SOVERSION=${SHLIB_FULLVERSION} SHLIB_SHFLAGS= FPICFLAGS ?= -fPIC CPICFLAGS?= -fPIC -DPIC -CPPPICFLAGS?= -DPIC +CPPPICFLAGS?= -DPIC CAPICFLAGS?= ${CPPPICFLAGS} ${CPICFLAGS} APICFLAGS?= -k @@ -246,7 +246,7 @@ DLLIB ?= -ldl .endif # some libs have lots of objects, and scanning all .o, .po and ${PICO} meta files -# is a waste of time, this tells meta.autodep.mk to just pick one +# is a waste of time, this tells meta.autodep.mk to just pick one # (typically ${PICO}) # yes, 42 is a random number. .if ${MK_DIRDEPS_BUILD} == "yes" && ${SRCS:Uno:[\#]} > 42 @@ -272,7 +272,7 @@ ${CXX_SUFFIXES:%=%.o}: ${COMPILE.cc} ${.IMPSRC} .S.o .s.o: - ${COMPILE.S} ${CFLAGS:M-[ID]*} ${AINC} ${.IMPSRC} + ${COMPILE.S} ${CFLAGS:M-[ID]*} ${AINC} ${.IMPSRC} .if (${LD_X} == "") .c.po: @@ -382,7 +382,7 @@ _LIBS+= ${libLDORDER_INC} .endif .if !defined(_SKIP_BUILD) -realbuild: ${_LIBS} +realbuild: ${_LIBS} .endif all: _SUBDIRUSE diff --git a/mk/libnames.mk b/mk/libnames.mk index b0eabed5ea53..8140360714df 100644 --- a/mk/libnames.mk +++ b/mk/libnames.mk @@ -1,14 +1,14 @@ -# $Id: libnames.mk,v 1.8 2016/04/05 15:58:37 sjg Exp $ +# $Id: libnames.mk,v 1.9 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2007-2009, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # diff --git a/mk/libs.mk b/mk/libs.mk index 7f974bfa86b9..9f0079d6e511 100644 --- a/mk/libs.mk +++ b/mk/libs.mk @@ -1,14 +1,14 @@ -# $Id: libs.mk,v 1.3 2013/08/02 18:28:48 sjg Exp $ +# $Id: libs.mk,v 1.6 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2006, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # @@ -82,12 +82,18 @@ UPDATE_DEPENDFILE = NO LIBS_TARGETS+= cleandepend cleandir cleanobj depend install .for b in ${LIBS:R:T:S,^lib,,} -lib$b.a: ${SRCS} ${DPADD} ${SRCS_lib$b} ${DPADD_lib$b} - (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} LIB=$b) +lib$b.a: ${SRCS} ${DPADD} ${SRCS_lib$b} ${DPADD_lib$b} + (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} LIB=$b -DWITHOUT_META_STATS) .for t in ${LIBS_TARGETS:O:u} $b.$t: .PHONY .MAKE - (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} LIB=$b ${@:E}) + (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} LIB=$b ${@:E} -DWITHOUT_META_STATS) .endfor .endfor + +.if !defined(WITHOUT_META_STATS) && ${.MAKE.LEVEL} > 0 +.END: _reldir_finish +.ERROR: _reldir_failed +.endif + .endif diff --git a/mk/links.mk b/mk/links.mk index aac3914fdd00..6bf0db080c23 100644 --- a/mk/links.mk +++ b/mk/links.mk @@ -1,14 +1,14 @@ -# $Id: links.mk,v 1.6 2014/09/29 17:14:40 sjg Exp $ +# $Id: links.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2005, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # diff --git a/mk/manifest.mk b/mk/manifest.mk index 797038d19391..1e2f728f094e 100644 --- a/mk/manifest.mk +++ b/mk/manifest.mk @@ -1,14 +1,14 @@ -# $Id: manifest.mk,v 1.2 2014/10/31 18:06:17 sjg Exp $ +# $Id: manifest.mk,v 1.3 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2014, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # @@ -21,12 +21,12 @@ # ${MANIFEST}.DIRS += bin sbin usr/bin ... # for each dir we have a ${MANIFEST}.SRCS.$dir # that provides the absolute path to the contents -# ${MANIFEST}.SRCS.bin += ${OBJTOP}/bin/sh/sh +# ${MANIFEST}.SRCS.bin += ${OBJTOP}/bin/sh/sh # ${MANIFEST}.SYMLINKS is a list of src target pairs # for each file/dir there are a number of attributes # UID GID MODE FLAGS # which can be set per dir, per file or we use defaults -# eg. +# eg. # MODE.sbin = 550 # MODE.usr/sbin = 550 # MODE.dirs = 555 @@ -37,7 +37,7 @@ # means passwd gets 4555 other files in usr/bin get 555 and # files in usr/sbin get 500 # STORE defaults to basename of src and target directory -# but we can use +# but we can use # ${MANIFEST}.SRCS.sbin += ${OBJTOP}/bin/sh-static/sh-static # STORE.sbin/sh-static = sbin/sh # @@ -47,7 +47,7 @@ UID.dirs ?= 0 GID.dirs ?= 0 MODE.dirs ?= 775 -FLAGS.dirs ?= +FLAGS.dirs ?= UID.files ?= 0 GID.files ?= 0 diff --git a/mk/meta.autodep.mk b/mk/meta.autodep.mk index 9cb3f14ea0c6..5e18c35fa560 100644 --- a/mk/meta.autodep.mk +++ b/mk/meta.autodep.mk @@ -1,4 +1,4 @@ -# $Id: meta.autodep.mk,v 1.50 2018/06/08 01:25:31 sjg Exp $ +# $Id: meta.autodep.mk,v 1.52 2020/07/18 05:57:57 sjg Exp $ # # @(#) Copyright (c) 2010, Simon J. Gerraty @@ -57,7 +57,7 @@ _OBJTOP ?= ${OBJTOP} _OBJROOT ?= ${OBJROOT:U${_OBJTOP}} _DEPENDFILE := ${_CURDIR}/${.MAKE.DEPENDFILE:T} -.if ${.MAKE.LEVEL} > 0 || ${BUILD_AT_LEVEL0:Uyes:tl} == "yes" +.if ${.MAKE.LEVEL} > 0 # do not allow auto update if we ever built this dir without filemon NO_FILEMON_COOKIE = .nofilemon CLEANFILES += ${NO_FILEMON_COOKIE} @@ -73,10 +73,8 @@ UPDATE_DEPENDFILE = NO .endif .if ${.MAKE.LEVEL} == 0 -.if ${BUILD_AT_LEVEL0:Uyes:tl} == "no" UPDATE_DEPENDFILE = NO .endif -.endif .if !exists(${_DEPENDFILE}) _bootstrap_dirdeps = yes .endif @@ -283,9 +281,7 @@ ${_DEPENDFILE}: ${_depend} ${.PARSEDIR}/gendirdeps.mk ${META2DEPS} $${.MAKE.MET .endif .if ${_bootstrap_dirdeps} == "yes" -.if ${BUILD_AT_LEVEL0:Uno} == "no" DIRDEPS+= ${RELDIR}.${TARGET_SPEC:U${MACHINE}} -.endif # make sure this is included at least once .include <dirdeps.mk> .else @@ -312,7 +308,7 @@ _reldir_finish: .NOMETA _reldir_failed: .NOMETA @echo "${TIME_STAMP} Failed ${RELDIR}.${TARGET_SPEC} seconds=$$(( ${now_utc} - ${start_utc} )) ${meta_stats}" -.if defined(WITH_META_STATS) && ${.MAKE.LEVEL} > 0 +.if !defined(WITHOUT_META_STATS) && ${.MAKE.LEVEL} > 0 .END: _reldir_finish .ERROR: _reldir_failed .endif diff --git a/mk/meta.stage.mk b/mk/meta.stage.mk index 3b4624fc4599..c78685760c11 100644 --- a/mk/meta.stage.mk +++ b/mk/meta.stage.mk @@ -1,14 +1,14 @@ -# $Id: meta.stage.mk,v 1.59 2020/04/25 18:18:27 sjg Exp $ +# $Id: meta.stage.mk,v 1.60 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2011-2017, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # diff --git a/mk/meta.subdir.mk b/mk/meta.subdir.mk index d27de1079f87..39cf875d6b77 100644 --- a/mk/meta.subdir.mk +++ b/mk/meta.subdir.mk @@ -1,4 +1,4 @@ -# $Id: meta.subdir.mk,v 1.11 2015/11/24 22:26:51 sjg Exp $ +# $Id: meta.subdir.mk,v 1.12 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2010, Simon J. Gerraty @@ -6,10 +6,10 @@ # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # @@ -32,7 +32,7 @@ DIRDEPS = ${SUBDIR:N.WAIT:O:u:@d@${RELDIR}/$d@} .include <meta.autodep.mk> .else # this is the cunning bit -# actually it is probably a bit risky +# actually it is probably a bit risky # since we may pickup subdirs which are not relevant # the alternative is a walk through the tree though # which is difficult without a sub-make. diff --git a/mk/meta.sys.mk b/mk/meta.sys.mk index a561e04534f8..77b4893a8785 100644 --- a/mk/meta.sys.mk +++ b/mk/meta.sys.mk @@ -1,4 +1,4 @@ -# $Id: meta.sys.mk,v 1.36 2020/05/16 23:21:48 sjg Exp $ +# $Id: meta.sys.mk,v 1.38 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2010-2020, Simon J. Gerraty @@ -6,10 +6,10 @@ # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # @@ -155,12 +155,6 @@ dirdeps: # the first .MAIN: is what counts # by default dirdeps is all we want at level0 .MAIN: dirdeps -# tell dirdeps.mk what we want -BUILD_AT_LEVEL0 = no -.endif -.if ${.TARGETS:Nall} == "" -# it works best if we do everything via sub-makes -BUILD_AT_LEVEL0 ?= no .endif .endif diff --git a/mk/meta2deps.py b/mk/meta2deps.py index 253287a87d1b..9231003b70df 100755 --- a/mk/meta2deps.py +++ b/mk/meta2deps.py @@ -25,7 +25,7 @@ We only pay attention to a subset of the information in the 'W' files opened for write or read-write, for filemon V3 and earlier. - + 'E' files executed. 'L' files linked @@ -37,20 +37,20 @@ We only pay attention to a subset of the information in the """ RCSid: - $Id: meta2deps.py,v 1.30 2020/06/08 23:05:00 sjg Exp $ + $Id: meta2deps.py,v 1.33 2020/08/19 17:51:53 sjg Exp $ - Copyright (c) 2011-2019, Simon J. Gerraty + Copyright (c) 2011-2020, Simon J. Gerraty Copyright (c) 2011-2017, Juniper Networks, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: + modification, are permitted provided that the following conditions + are met: 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. + notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. + documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -62,8 +62,8 @@ RCSid: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + """ import os, re, sys @@ -164,7 +164,7 @@ def sort_unique(list, cmp=None, key=None, reverse=False): def add_trims(x): return ['/' + x + '/', - '/' + x, + '/' + x, x + '/', x] @@ -181,7 +181,7 @@ class MetaFile: obj_deps = [] src_deps = [] file_deps = [] - + def __init__(self, name, conf={}): """if name is set we will parse it now. conf can have the follwing keys: @@ -198,7 +198,7 @@ class MetaFile: TARGET_SPEC Sometimes MACHINE isn't enough. - + HOST_TARGET when we build for the pseudo machine 'host' the object tree uses HOST_TARGET rather than MACHINE. @@ -222,7 +222,7 @@ class MetaFile: debug_out open file to send debug output to (sys.stderr) """ - + self.name = name self.debug = getv(conf, 'debug', 0) self.debug_out = getv(conf, 'debug_out', sys.stderr) @@ -310,11 +310,11 @@ class MetaFile: self.obj_deps = [] self.src_deps = [] self.file_deps = [] - + def dirdeps(self, sep='\n'): """return DIRDEPS""" return sep.strip() + sep.join(self.obj_deps) - + def src_dirdeps(self, sep='\n'): """return SRC_DIRDEPS""" return sep.strip() + sep.join(self.src_deps) @@ -333,7 +333,7 @@ class MetaFile: def seenit(self, dir): """rememer that we have seen dir.""" self.seen[dir] = 1 - + def add(self, list, data, clue=''): """add data to list if it isn't already there.""" if data not in list: @@ -392,10 +392,10 @@ class MetaFile: # give a useful clue print('{}:{}: '.format(self.name, self.line), end=' ', file=sys.stderr) raise - + def parse(self, name=None, file=None): """A meta file looks like: - + # Meta data file "path" CMD "command-line" CWD "cwd" @@ -506,6 +506,8 @@ class MetaFile: continue elif w[0] in 'ERWS': path = w[2] + if path == '.': + continue self.parse_path(path, cwd, w[0], w) if not file: @@ -601,13 +603,13 @@ class MetaFile: self.seenit(w[2]) self.seenit(dir) - + def main(argv, klass=MetaFile, xopts='', xoptf=None): """Simple driver for class MetaFile. Usage: script [options] [key=value ...] "meta" ... - + Options and key=value pairs contribute to the dictionary passed to MetaFile. @@ -615,7 +617,7 @@ def main(argv, klass=MetaFile, xopts='', xoptf=None): add "SRCTOP" to the "SRCTOPS" list. -C "CURDIR" - + -O "OBJROOT" add "OBJROOT" to the "OBJROOTS" list. @@ -626,7 +628,7 @@ def main(argv, klass=MetaFile, xopts='', xoptf=None): -H "HOST_TARGET" -D "DPDEPS" - + -d bumps debug level """ @@ -666,7 +668,7 @@ def main(argv, klass=MetaFile, xopts='', xoptf=None): debug = 0 output = True - + opts, args = getopt.getopt(argv[1:], 'a:dS:C:O:R:m:D:H:qT:X:' + xopts) for o, a in opts: if o == '-a': diff --git a/mk/meta2deps.sh b/mk/meta2deps.sh index 59ccf7bd6216..e56e52a89675 100755 --- a/mk/meta2deps.sh +++ b/mk/meta2deps.sh @@ -5,11 +5,11 @@ # # SYNOPSIS: # meta2deps.sh SB="SB" "meta" ... -# +# # DESCRIPTION: # This script looks each "meta" file and extracts the # information needed to deduce build and src dependencies. -# +# # To do this, we extract the 'CWD' record as well as all the # syscall traces which describe 'R'ead, 'C'hdir and 'E'xec # syscalls. @@ -77,20 +77,20 @@ # RCSid: -# $Id: meta2deps.sh,v 1.12 2016/12/13 20:44:16 sjg Exp $ +# $Id: meta2deps.sh,v 1.13 2020/08/19 17:51:53 sjg Exp $ # Copyright (c) 2010-2013, Juniper Networks, Inc. # All rights reserved. -# +# # Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: +# modification, are permitted provided that the following conditions +# are met: # 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. +# notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# +# documentation and/or other materials provided with the distribution. +# # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -101,14 +101,14 @@ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. meta2src() { cat /dev/null "$@" | sed -n '/^R .*\.[chyl]$/s,^..[0-9]* ,,p' | sort -u } - + meta2dirs() { cat /dev/null "$@" | sed -n '/^R .*\/.*\.[a-z0-9][^\/]*$/s,^..[0-9]* \(.*\)/[^/]*$,\1,p' | @@ -169,7 +169,7 @@ meta2deps() { case "$MACHINE" in host) _ht=$HOST_TARGET;; esac - + for o in $OBJROOTS do case "$MACHINE,/$o/" in @@ -203,7 +203,7 @@ meta2deps() { obj_re= add_list '|' -s '/*' src_re $SRCTOPS add_list '|' -s '*' obj_re $OBJROOTS - + [ -z "$RELDIR" ] && unset DPDEPS tf=/tmp/m2d$$-$USER rm -f $tf.* @@ -274,10 +274,10 @@ meta2deps() { eval cwd_$pid=$cwd continue ;; - F,*) # $path is new pid + F,*) # $path is new pid eval cwd_$path=$cwd ldir_$path=$ldir continue - ;; + ;; *) dir=${path%/*} case "$path" in $src_re|$obj_re) ;; diff --git a/mk/mk-files.txt b/mk/mk-files.txt index 4e69dfec0808..282f9b87a63c 100644 --- a/mk/mk-files.txt +++ b/mk/mk-files.txt @@ -14,17 +14,17 @@ Many years ago, when building large software projects, I used GNU make to simplify developing complex build trees. Since the early 90's my main development machines, run BSD -(NetBSD_ to be precise), and the BSD source tree is good example of a -large software project. It quickly became clear that -``/usr/share/mk/*.mk`` were a great model, but were quite tightly -linked to building the BSD tree. +(NetBSD_ to be precise, and more recently FreeBSD), and the BSD source +tree is good example of a large software project. +It quickly became clear that ``/usr/share/mk/*.mk`` were a great +model, but at the time were quite tightly linked to building the BSD tree. Much as I liked using NetBSD, my customers were more likely to be using SunOS, HP-UX etc, so I started on bmake_ and a portable collection of mk-files (mk.tar.gz_). NetBSD provided much of the original structure. Since then I've added a lot of features to NetBSD's make and hence to -bmake which is kept closely in sync. The mk-files however have +bmake which is kept closely in sync. The mk-files however have diverged quite a bit, though ideas are still picked up from NetBSD and FreeBSD. @@ -59,7 +59,7 @@ in such cases even the ``SRCS`` line is unnecessary as ``prog.mk`` will default it to ``${PROG}.c``. It is the sensible use of defaults and the plethora of macro modifiers -provided by bmake_ that allow simple makefiles such as the above +provided by bmake_ that allow simple makefiles such as the above to *just work* on many different systems. @@ -67,7 +67,7 @@ mk-files ======== This section provides a brief description of some of the ``*.mk`` -files. +files. sys.mk ------ @@ -76,7 +76,7 @@ When bmake starts, it looks for ``sys.mk`` and reads it before doing anything else. Thus, this is the place to setup the environment for everyone else. -In this distribution, sys.mk avoids doing anything platform dependent. +In this distribution, ``sys.mk`` avoids doing anything platform dependent. It is quite short, and includes a number of other files (which may or may not exists) @@ -97,7 +97,7 @@ examples/sys.clean-env.mk PATH HOME USER LOGNAME \ SRCTOP OBJTOP OBJROOT \ ${_env_vars} - + _env_vars != env | egrep '^(${MAKE_SAVE_ENV_PREFIX:ts|})' | sed 's,=.*,,'; echo _export_list = .for v in ${MAKE_SAVE_ENV_VARS:O:u} @@ -169,7 +169,7 @@ lib${LIB}.a An archive lib of ``.o`` files, this is the default lib${LIB}_p.a - A profiled lib of ``.po`` files. + A profiled lib of ``.po`` files. Still an archive lib, but all the objects are built with profiling in mind - hence the different extension. It is skipped if ``MKPROFILE`` is "no". @@ -194,7 +194,7 @@ lib${LIB}.${LD_so} There is a lot of platform specific tweaking in ``lib.mk``, largely the result of the original distributions trying to avoid interfering with -the system's ``sys.mk``. +the system's ``sys.mk``. libnames.mk ----------- @@ -204,22 +204,19 @@ include ``*.libnames.mk`` of which: local.libnames.mk does not exist unless you create it. It is a handy way for you - to customize without touching the distributed files. + to customize without touching the distributed files. For example, on a test machine I needed to build openssl but - not install it, so put the following in ``local.libnames.mk``:: + not install it, so put the following in ``local.libnames.mk``:: .if ${host_os} == "sunos" LIBCRYPTO = ${OBJTOP}/openssl/lib/crypto/libcrypto${DLIBEXT} LIBSSL = ${OBJTOP}/openssl/lib/ssl/libssl${DLIBEXT} INCLUDES_libcrypto = -I${OBJ_libcrypto} .endif - + The makefile created an openssl dir in ``${OBJ_libcrypto}`` to gather all the headers. dpadd.mk_ did the rest. -sjg.libnames.mk - not part of the mk-files distribution. - host.libnames.mk contains logic to find any libs named in ``HOST_LIBS`` in ``HOST_LIBDIRS``. @@ -248,7 +245,7 @@ else in various ways:: # it also has the same effect as SRC_LIBS DPADD += ${LIBSSLFD} - # indicate that not only must libsslfd be built, + # indicate that not only must libsslfd be built, # but that we need to link with it. # this is almost exactly equivalent to # DPADD += ${LIBSSLFD} @@ -324,7 +321,7 @@ If ``SUBDIR_MUST_EXIST`` is set, missing directories cause an error, otherwise a warning is issued. If you don't even want the warning, set ``MISSING_DIR=continue``. -Traditionally, ``subdir.mk`` prints clue as it visits each subdir:: +Traditionally, ``subdir.mk`` prints clues as it visits each subdir:: ===> ssl ===> ssl/lib @@ -342,7 +339,7 @@ links.mk Provides rules for processing lists of ``LINKS`` and ``SYMLINKS``. Each is expected to be a list of ``link`` and ``target`` pairs -(``link`` -> ``target``). +(``link`` -> ``target``). The logic is generally in a ``_*_SCRIPT`` which is referenced in a ``_*_USE`` (``.USE``) target. @@ -353,7 +350,7 @@ For example:: SYMLINKS += ${.CURDIR}/${MACHINE_ARCH}/include machine header_links: _BUILD_SYMLINKS_USE - + md.o: header_links would create a symlink called ``machine`` in ``${.OBJDIR}`` pointing to @@ -394,10 +391,10 @@ post process the ``.d`` files into ``.depend``. auto.dep.mk ----------- -A much simpler implementation than autodep.mk_ it uses +A much simpler implementation than autodep.mk_ it uses ``-MF ${.TARGET:T}.d`` -to avoid possible conflicts during parallel builds. -This precludes the use of suffix rules to drive ``make depend``, so +to avoid possible conflicts during parallel builds. +This precludes the use of suffix rules to drive ``make depend``, so dep.mk_ handles that if specifically requested. options.mk @@ -452,7 +449,7 @@ on sparc, ``-Wno-unused`` would replace ``-Wunused``. You should never need to edit ``warnings.mk``, it will include ``warnings-sets.mk`` if it exists and you use that to make any local -customizations. +customizations. rst2htm.mk ---------- @@ -470,7 +467,7 @@ Meta mode ========= The 20110505 and later versions of ``mk-files`` include a number of -makefiles contributed by Juniper Networks, Inc. +makefiles contributed by Juniper Networks, Inc. These allow the latest version of bmake_ to run in `meta mode`_ see `dirdeps.mk`_ @@ -499,5 +496,5 @@ where you unpacked the tar file, you can:: .. _mk.tar.gz: http://www.crufty.net/ftp/pub/sjg/mk.tar.gz :Author: sjg@crufty.net -:Revision: $Id: mk-files.txt,v 1.18 2018/12/08 07:27:15 sjg Exp $ +:Revision: $Id: mk-files.txt,v 1.20 2020/08/19 17:51:53 sjg Exp $ :Copyright: Crufty.NET diff --git a/mk/mkopt.sh b/mk/mkopt.sh index 929a5aa83a66..4a42c0ddf122 100755 --- a/mk/mkopt.sh +++ b/mk/mkopt.sh @@ -1,16 +1,16 @@ #!/bin/sh -# $Id: mkopt.sh,v 1.12 2020/06/23 04:16:35 sjg Exp $ +# $Id: mkopt.sh,v 1.13 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2014, 2020, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # diff --git a/mk/obj.mk b/mk/obj.mk index b936379d4f6b..487e25a55b6c 100644 --- a/mk/obj.mk +++ b/mk/obj.mk @@ -1,14 +1,14 @@ -# $Id: obj.mk,v 1.15 2012/11/11 22:37:02 sjg Exp $ +# $Id: obj.mk,v 1.16 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 1999-2010, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # diff --git a/mk/options.mk b/mk/options.mk index a03c6cde34dc..eb5253a6b7e8 100644 --- a/mk/options.mk +++ b/mk/options.mk @@ -1,14 +1,14 @@ -# $Id: options.mk,v 1.11 2020/05/02 21:23:52 sjg Exp $ +# $Id: options.mk,v 1.13 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2012, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # @@ -77,4 +77,6 @@ ${OPTION_PREFIX}${o:H} ?= no ${OPTION_PREFIX}${o:H} ?= ${${OPTION_PREFIX}${o:T}} .endif .endfor -.undef OPTIONS_DEFAULT_VALUES OPTIONS_DEFAULT_NO OPTIONS_DEFAULT_YES +.undef OPTIONS_DEFAULT_VALUES +.undef OPTIONS_DEFAULT_NO +.undef OPTIONS_DEFAULT_YES diff --git a/mk/own.mk b/mk/own.mk index 4c8425b0ee7c..b20b9e5e2c35 100644 --- a/mk/own.mk +++ b/mk/own.mk @@ -1,4 +1,4 @@ -# $Id: own.mk,v 1.40 2018/04/23 04:53:57 sjg Exp $ +# $Id: own.mk,v 1.41 2020/08/19 17:51:53 sjg Exp $ .if !target(__${.PARSEFILE}__) __${.PARSEFILE}__: @@ -61,7 +61,7 @@ YACC.y?= ${YACC} ${YFLAGS} # for suffix rules IMPFLAGS?= ${COPTS.${.IMPSRC:T}} ${CPUFLAGS.${.IMPSRC:T}} ${CPPFLAGS.${.IMPSRC:T}} -.for s in .c .cc +.for s in .c .cc COMPILE.$s += ${IMPFLAGS} LINK.$s += ${IMPFLAGS} .endfor @@ -211,8 +211,8 @@ CFLAGS+= ${CPPFLAGS} # allow for per target flags # apply the :T:R first, so the more specific :T can override if needed -CPPFLAGS += ${CPPFLAGS_${.TARGET:T:R}} ${CPPFLAGS_${.TARGET:T}} -CFLAGS += ${CFLAGS_${.TARGET:T:R}} ${CFLAGS_${.TARGET:T}} +CPPFLAGS += ${CPPFLAGS_${.TARGET:T:R}} ${CPPFLAGS_${.TARGET:T}} +CFLAGS += ${CFLAGS_${.TARGET:T:R}} ${CFLAGS_${.TARGET:T}} # Define SYS_INCLUDE to indicate whether you want symbolic links to the system # source (``symlinks''), or a separate copy (``copies''); (latter useful diff --git a/mk/prlist.mk b/mk/prlist.mk index 09d7dfdacc97..aca1fde25555 100644 --- a/mk/prlist.mk +++ b/mk/prlist.mk @@ -1,14 +1,14 @@ -# $Id: prlist.mk,v 1.3 2008/07/17 16:24:57 sjg Exp $ +# $Id: prlist.mk,v 1.4 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2006, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # diff --git a/mk/prog.mk b/mk/prog.mk index 4bc6260d208b..ea48837d5544 100644 --- a/mk/prog.mk +++ b/mk/prog.mk @@ -1,4 +1,4 @@ -# $Id: prog.mk,v 1.35 2018/01/26 20:04:07 sjg Exp $ +# $Id: prog.mk,v 1.36 2020/08/19 17:51:53 sjg Exp $ .if !target(__${.PARSEFILE}__) __${.PARSEFILE}__: @@ -99,7 +99,7 @@ _PROGLDOPTS+= -Wl,-dynamic-linker=${_SHLINKER} _PROGLDOPTS+= -Wl,-rpath-link,${DESTDIR}${SHLIBDIR}:${DESTDIR}/usr/lib \ -L${DESTDIR}${SHLIBDIR} .endif -_PROGLDOPTS+= -Wl,-rpath,${SHLIBDIR}:/usr/lib +_PROGLDOPTS+= -Wl,-rpath,${SHLIBDIR}:/usr/lib .if defined(PROG_CXX) _CCLINK= ${CXX} diff --git a/mk/progs.mk b/mk/progs.mk index 7ccebbffb44c..16c381a50bf9 100644 --- a/mk/progs.mk +++ b/mk/progs.mk @@ -1,14 +1,14 @@ -# $Id: progs.mk,v 1.13 2013/08/02 18:28:48 sjg Exp $ +# $Id: progs.mk,v 1.16 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2006, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # @@ -87,11 +87,11 @@ x.$p= PROG_CXX=$p .endif $p ${p}_p: .PHONY .MAKE - (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} PROG=$p ${x.$p}) + (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} PROG=$p ${x.$p} -DWITHOUT_META_STATS) .for t in ${PROGS_TARGETS:O:u} $p.$t: .PHONY .MAKE - (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} PROG=$p ${x.$p} ${@:E}) + (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} PROG=$p ${x.$p} ${@:E} -DWITHOUT_META_STATS) .endfor .endfor @@ -99,4 +99,9 @@ $p.$t: .PHONY .MAKE $t: ${PROGS:%=%.$t} .endfor +.if !defined(WITHOUT_META_STATS) && ${.MAKE.LEVEL} > 0 +.END: _reldir_finish +.ERROR: _reldir_failed +.endif + .endif diff --git a/mk/rst2htm.mk b/mk/rst2htm.mk index 296b73c68574..1db9792f4127 100644 --- a/mk/rst2htm.mk +++ b/mk/rst2htm.mk @@ -1,14 +1,14 @@ -# $Id: rst2htm.mk,v 1.10 2015/09/08 22:17:46 sjg Exp $ +# $Id: rst2htm.mk,v 1.11 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2009, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # diff --git a/mk/scripts.mk b/mk/scripts.mk index 9b6d82f49253..5ea2474e65a3 100644 --- a/mk/scripts.mk +++ b/mk/scripts.mk @@ -1,14 +1,14 @@ -# $Id: scripts.mk,v 1.3 2017/05/06 17:29:45 sjg Exp $ +# $Id: scripts.mk,v 1.4 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2006, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # diff --git a/mk/srctop.mk b/mk/srctop.mk index fab090c80d39..91594c7a98e4 100644 --- a/mk/srctop.mk +++ b/mk/srctop.mk @@ -1,14 +1,14 @@ -# $Id: srctop.mk,v 1.3 2012/11/11 23:20:18 sjg Exp $ +# $Id: srctop.mk,v 1.4 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2012, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # @@ -46,7 +46,7 @@ SRCTOP!= cd ${.CURDIR}; while :; do \ here=`pwd`; \ ${_SRCTOP_TEST_} && { echo $$here; break; }; \ case $$here in /*/*/*) cd ..;; *) echo ""; break;; esac; \ - done + done .endif .if defined(SRCTOP) && exists(${SRCTOP}/.) .export SRCTOP diff --git a/mk/stage-install.sh b/mk/stage-install.sh index 64d044fa048c..674652d1d482 100755 --- a/mk/stage-install.sh +++ b/mk/stage-install.sh @@ -28,22 +28,26 @@ # "file".dirdep placed in "dest" or "dest".dirdep if it happed # to be a file rather than a directory. # +# Before we run install(1), we check if "dest" needs to be a +# directory (more than one file in "args") and create it +# if necessary. +# # SEE ALSO: # meta.stage.mk -# +# # RCSid: -# $Id: stage-install.sh,v 1.5 2013/04/19 16:32:24 sjg Exp $ +# $Id: stage-install.sh,v 1.9 2020/08/28 01:04:13 sjg Exp $ # -# @(#) Copyright (c) 2013, Simon J. Gerraty +# @(#) Copyright (c) 2013-2020, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # @@ -59,6 +63,45 @@ do esac done +# get last entry from "$@" without side effects +last_entry() { + while [ $# -gt 8 ] + do + shift 8 + done + eval last=\$$# + echo $last +} + +# mkdir $dest if needed (more than one file) +mkdir_if_needed() { + ( + lf= + while [ $# -gt 8 ] + do + shift 4 + done + for f in "$@" + do + [ -f $f ] || continue + [ $f = $dest ] && continue + if [ -n "$lf" ]; then + # dest must be a directory + mkdir -p $dest + break + fi + lf=$f + done + ) +} + +args="$@" +dest=`last_entry "$@"` +case " $args " in +*" -d "*) ;; +*) [ -e $dest ] || mkdir_if_needed "$@";; +esac + # if .dirdep doesn't exist, just run install and be done _DIRDEP=${_DIRDEP:-$OBJDIR/.dirdep} [ -s $_DIRDEP ] && EXEC= || EXEC=exec @@ -80,12 +123,6 @@ StageDirdep() { LnCp $_DIRDEP $t.dirdep || exit 1 } -args="$@" -while [ $# -gt 8 ] -do - shift 8 -done -eval dest=\$$# if [ -f $dest ]; then # a file, there can be only one .dirdep needed StageDirdep $dest diff --git a/mk/sys.clean-env.mk b/mk/sys.clean-env.mk index 5ac74bba9342..88d32cb3c6e9 100644 --- a/mk/sys.clean-env.mk +++ b/mk/sys.clean-env.mk @@ -1,14 +1,14 @@ -# $Id: sys.clean-env.mk,v 1.22 2017/10/25 23:44:20 sjg Exp $ +# $Id: sys.clean-env.mk,v 1.23 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2009, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # @@ -55,7 +55,7 @@ MAKE_ENV_SAVE_PREFIX_LIST += \ MAKE_ENV_SAVE_EXCLUDE_LIST ?= _ # This is the actual list that we will save -# HOME is probably something worth clobbering eg. +# HOME is probably something worth clobbering eg. # HOME=/var/empty MAKE_ENV_SAVE_VAR_LIST += \ HOME \ diff --git a/mk/sys.debug.mk b/mk/sys.debug.mk index 7debfc6cf051..7fde27c24fdb 100644 --- a/mk/sys.debug.mk +++ b/mk/sys.debug.mk @@ -1,14 +1,14 @@ -# $Id: sys.debug.mk,v 1.1 2016/10/01 19:11:55 sjg Exp $ +# $Id: sys.debug.mk,v 1.2 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2009, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # diff --git a/mk/sys.dependfile.mk b/mk/sys.dependfile.mk index 5389c24fd1bb..7c1fd94d3eb8 100644 --- a/mk/sys.dependfile.mk +++ b/mk/sys.dependfile.mk @@ -1,14 +1,14 @@ -# $Id: sys.dependfile.mk,v 1.8 2016/03/11 01:34:13 sjg Exp $ +# $Id: sys.dependfile.mk,v 1.9 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2012, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # diff --git a/mk/sys.mk b/mk/sys.mk index 37e37390eb27..b21dfa8b4ebf 100644 --- a/mk/sys.mk +++ b/mk/sys.mk @@ -1,14 +1,14 @@ -# $Id: sys.mk,v 1.47 2020/04/17 21:08:17 sjg Exp $ +# $Id: sys.mk,v 1.51 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2003-2009, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # @@ -77,6 +77,7 @@ OPTIONS_DEFAULT_DEPENDENT += \ AUTO_OBJ/DIRDEPS_BUILD \ META_MODE/DIRDEPS_BUILD \ STAGING/DIRDEPS_BUILD \ + STATIC_DIRDEPS_CACHE/DIRDEPS_CACHE \ .-include <options.mk> @@ -116,7 +117,7 @@ ROOT_GROUP != sed -n /:0:/s/:.*//p /etc/group unix ?= We run ${_HOST_OSNAME}. -# We need a Bourne/POSIX shell +# We need a Bourne/POSIX shell MAKE_SHELL ?= sh SHELL ?= ${MAKE_SHELL} diff --git a/mk/sys.vars.mk b/mk/sys.vars.mk index 2f2e66700266..24e0ed26a15f 100644 --- a/mk/sys.vars.mk +++ b/mk/sys.vars.mk @@ -1,20 +1,20 @@ -# $Id: sys.vars.mk,v 1.4 2019/05/27 20:22:52 sjg Exp $ +# $Id: sys.vars.mk,v 1.5 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2003-2009, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # We use the following paradigm for preventing multiple inclusion. -# It relies on the fact that conditionals and dependencies are resolved +# It relies on the fact that conditionals and dependencies are resolved # at the time they are read. # # _this ?= ${.PARSEFILE} diff --git a/mk/sys/AIX.mk b/mk/sys/AIX.mk index b848d99fb4e8..d591385be603 100644 --- a/mk/sys/AIX.mk +++ b/mk/sys/AIX.mk @@ -28,9 +28,9 @@ CC ?= gcc DBG ?= -O -g STATIC ?= -static .else -CC ?= cc +CC ?= cc DBG ?= -g -STATIC ?= +STATIC ?= .endif CFLAGS ?= ${DBG} COMPILE.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} -c @@ -138,7 +138,7 @@ ${CXX_SUFFIXES:%=%.a}: .s: ${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .s.o: - ${COMPILE.s} -o ${.TARGET} ${.IMPSRC} + ${COMPILE.s} -o ${.TARGET} ${.IMPSRC} .s.a: ${COMPILE.s} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o @@ -162,7 +162,7 @@ ${CXX_SUFFIXES:%=%.a}: mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc diff --git a/mk/sys/Darwin.mk b/mk/sys/Darwin.mk index ea04b416ff3a..06918a11a4ad 100644 --- a/mk/sys/Darwin.mk +++ b/mk/sys/Darwin.mk @@ -176,7 +176,7 @@ ${CXX_SUFFIXES:%=%.a}: .s: ${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .s.o: - ${COMPILE.s} -o ${.TARGET} ${.IMPSRC} + ${COMPILE.s} -o ${.TARGET} ${.IMPSRC} .s.a: ${COMPILE.s} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o @@ -200,7 +200,7 @@ ${CXX_SUFFIXES:%=%.a}: mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc diff --git a/mk/sys/Generic.mk b/mk/sys/Generic.mk index 9a3d3bffc183..51c72990f2ea 100644 --- a/mk/sys/Generic.mk +++ b/mk/sys/Generic.mk @@ -1,4 +1,4 @@ -# $Id: Generic.mk,v 1.16 2020/06/29 14:34:42 sjg Exp $ +# $Id: Generic.mk,v 1.17 2020/08/19 17:51:53 sjg Exp $ # # some reasonable defaults @@ -31,7 +31,7 @@ ARFLAGS ?= rl AS ?= as AFLAGS ?= -.if ${MACHINE_ARCH} == "sparc64" +.if ${MACHINE_ARCH} == "sparc64" AFLAGS+= -Wa,-Av9a .endif COMPILE.s ?= ${CC} ${AFLAGS} -c @@ -56,7 +56,7 @@ COMPILE.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} -c LINK.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} ${LDFLAGS} CPP ?= cpp -CPPFLAGS ?= +CPPFLAGS ?= FC ?= f77 FFLAGS ?= -O @@ -181,7 +181,7 @@ ${CXX_SUFFIXES:%=%.a}: mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc diff --git a/mk/sys/HP-UX.mk b/mk/sys/HP-UX.mk index 34dd2881d83d..f1c23148c186 100644 --- a/mk/sys/HP-UX.mk +++ b/mk/sys/HP-UX.mk @@ -1,4 +1,4 @@ -# $Id: HP-UX.mk,v 1.14 2020/06/29 14:34:42 sjg Exp $ +# $Id: HP-UX.mk,v 1.15 2020/08/19 17:51:53 sjg Exp $ # $NetBSD: sys.mk,v 1.19.2.1 1994/07/26 19:58:31 cgd Exp $ # @(#)sys.mk 5.11 (Berkeley) 3/13/91 @@ -8,7 +8,7 @@ unix ?= We run ${OS}. # HP-UX's cc does not provide any clues as to wether this is 9.x or 10.x # nor does sys/param.h, so we'll use the existence of /hp-ux -.if exists("/hp-ux") +.if exists("/hp-ux") OSMAJOR ?=9 .endif OSMAJOR ?=10 @@ -60,7 +60,7 @@ PICFLAG ?= +z LD_x= DBG ?=-g -O .endif -DBG ?= +DBG ?= STATIC ?= -Wl,-a,archive .endif .if (${__HPUX_VERSION} == "10") @@ -180,7 +180,7 @@ ${CXX_SUFFIXES:%=%.a}: .s: ${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .s.o: - ${COMPILE.s} -o ${.TARGET} ${.IMPSRC} + ${COMPILE.s} -o ${.TARGET} ${.IMPSRC} .s.a: ${COMPILE.s} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o @@ -204,7 +204,7 @@ ${CXX_SUFFIXES:%=%.a}: mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc diff --git a/mk/sys/IRIX.mk b/mk/sys/IRIX.mk index a194c1e78a11..00af15027f6e 100644 --- a/mk/sys/IRIX.mk +++ b/mk/sys/IRIX.mk @@ -44,7 +44,7 @@ COMPILE.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} -c LINK.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} ${LDFLAGS} CPP ?= CC -CPPFLAGS ?= +CPPFLAGS ?= FC ?= f77 FFLAGS ?= -O @@ -173,7 +173,7 @@ ${CXX_SUFFIXES:%=%.a}: mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc diff --git a/mk/sys/Linux.mk b/mk/sys/Linux.mk index bf80bb3addce..3cdc4dbe1a62 100644 --- a/mk/sys/Linux.mk +++ b/mk/sys/Linux.mk @@ -1,4 +1,4 @@ -# $Id: Linux.mk,v 1.12 2020/06/29 14:34:42 sjg Exp $ +# $Id: Linux.mk,v 1.13 2020/08/19 17:51:53 sjg Exp $ # $NetBSD: sys.mk,v 1.19.2.1 1994/07/26 19:58:31 cgd Exp $ # @(#)sys.mk 5.11 (Berkeley) 3/13/91 @@ -141,7 +141,7 @@ ${CXX_SUFFIXES:%=%.a}: .s: ${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .s.o: - ${COMPILE.s} -o ${.TARGET} ${.IMPSRC} + ${COMPILE.s} -o ${.TARGET} ${.IMPSRC} .s.a: ${COMPILE.s} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o @@ -165,7 +165,7 @@ ${CXX_SUFFIXES:%=%.a}: mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc diff --git a/mk/sys/NetBSD.mk b/mk/sys/NetBSD.mk index 2e72cc742e4f..6629a4445a2e 100644 --- a/mk/sys/NetBSD.mk +++ b/mk/sys/NetBSD.mk @@ -79,7 +79,7 @@ COMPILE.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} -c LINK.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} ${LDFLAGS} CPP ?= cpp -CPPFLAGS ?= +CPPFLAGS ?= FC ?= f77 FFLAGS ?= -O @@ -208,7 +208,7 @@ ${CXX_SUFFIXES:%=%.a}: mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc diff --git a/mk/sys/OSF1.mk b/mk/sys/OSF1.mk index 4c1a09f6f5f7..88e0ea28b930 100644 --- a/mk/sys/OSF1.mk +++ b/mk/sys/OSF1.mk @@ -1,4 +1,4 @@ -# $Id: OSF1.mk,v 1.11 2020/06/29 14:34:42 sjg Exp $ +# $Id: OSF1.mk,v 1.12 2020/08/19 17:51:53 sjg Exp $ # $NetBSD: sys.mk,v 1.19.2.1 1994/07/26 19:58:31 cgd Exp $ # @(#)sys.mk 5.11 (Berkeley) 3/13/91 @@ -31,7 +31,7 @@ LINK.s ?= ${CC} ${AFLAGS} ${LDFLAGS} COMPILE.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} -c LINK.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS} .if exists(/opt/gnu/bin/gcc) || exists(/usr/local/bin/gcc) -CC ?= gcc +CC ?= gcc .else CC ?= cc -std .endif @@ -176,7 +176,7 @@ ${CXX_SUFFIXES:%=%.a}: mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc diff --git a/mk/sys/OpenBSD.mk b/mk/sys/OpenBSD.mk index d73bb0d82c9a..7440a231e3bf 100644 --- a/mk/sys/OpenBSD.mk +++ b/mk/sys/OpenBSD.mk @@ -21,7 +21,7 @@ RANLIB ?= ranlib AS ?= as AFLAGS ?= -.if ${MACHINE_ARCH} == "sparc64" +.if ${MACHINE_ARCH} == "sparc64" AFLAGS+= -Wa,-Av9a .endif COMPILE.s ?= ${CC} ${AFLAGS} -c @@ -54,7 +54,7 @@ COMPILE.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} -c LINK.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} ${LDFLAGS} CPP ?= cpp -CPPFLAGS ?= +CPPFLAGS ?= FC ?= f77 FFLAGS ?= -O @@ -183,7 +183,7 @@ ${CXX_SUFFIXES:%=%.a}: mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc diff --git a/mk/sys/SunOS.mk b/mk/sys/SunOS.mk index e79cd3a7dbd4..4369c8d43b93 100644 --- a/mk/sys/SunOS.mk +++ b/mk/sys/SunOS.mk @@ -1,4 +1,4 @@ -# $Id: SunOS.mk,v 1.11 2020/06/29 14:34:42 sjg Exp $ +# $Id: SunOS.mk,v 1.12 2020/08/19 17:51:53 sjg Exp $ .if ${.PARSEFILE} == "sys.mk" .include <host-target.mk> @@ -197,7 +197,7 @@ ${CXX_SUFFIXES:%=%.a}: mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc diff --git a/mk/sys/UnixWare.mk b/mk/sys/UnixWare.mk index 888f0d90c0b4..272d3e65c2d8 100644 --- a/mk/sys/UnixWare.mk +++ b/mk/sys/UnixWare.mk @@ -1,4 +1,4 @@ -# $Id: UnixWare.mk,v 1.6 2020/06/29 14:34:42 sjg Exp $ +# $Id: UnixWare.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $ # based on "Id: SunOS.5.sys.mk,v 1.6 2003/09/30 16:42:23 sjg Exp " # $NetBSD: sys.mk,v 1.19.2.1 1994/07/26 19:58:31 cgd Exp $ # @(#)sys.mk 5.11 (Berkeley) 3/13/91 @@ -219,7 +219,7 @@ ${CXX_SUFFIXES:%=%.a}: mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc diff --git a/mk/target-flags.mk b/mk/target-flags.mk index 4525dbd08d7b..789f09b23115 100644 --- a/mk/target-flags.mk +++ b/mk/target-flags.mk @@ -27,27 +27,27 @@ # variables. The original version of this macro file did # elaborate things with CFLAGS. The current, simpler # implementation is ultimately more flexible. -# +# # It is important that target-flags.mk is included after other # macro files and that target specific flags that may reference # _$v are set after that. -# +# # Only works with a make(1) that does nested evaluation correctly. # RCSid: -# $Id: target-flags.mk,v 1.9 2014/04/05 22:56:54 sjg Exp $ +# $Id: target-flags.mk,v 1.10 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 1998-2002, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # diff --git a/mk/warnings.mk b/mk/warnings.mk index 7fb3ebd4a8f8..77635fbc8a29 100644 --- a/mk/warnings.mk +++ b/mk/warnings.mk @@ -1,15 +1,15 @@ # RCSid: -# $Id: warnings.mk,v 1.14 2016/04/05 15:58:37 sjg Exp $ +# $Id: warnings.mk,v 1.15 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2002, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # @@ -28,10 +28,10 @@ MIN_WARNINGS?= -Wall \ -Wformat \ -Wimplicit \ -Wunused \ - -Wuninitialized + -Wuninitialized LOW_WARNINGS?= ${MIN_WARNINGS} -W -Wstrict-prototypes -Wmissing-prototypes - + MEDIUM_WARNINGS?= ${LOW_WARNINGS} -Werror HIGH_WARNINGS?= ${MEDIUM_WARNINGS} \ @@ -84,7 +84,7 @@ W_uninitialized= # .for loops have the [dis]advantage of being evaluated when read, -# so adding to WARNINGS_SET[_${MACHINE_ARCH}] after this file is +# so adding to WARNINGS_SET[_${MACHINE_ARCH}] after this file is # read has no effect. # Replacing the above .for loops with the WARNINGS+= below solves that # but tiggers a double free bug in bmake-20040118 and earlier. @@ -95,11 +95,11 @@ W_uninitialized= # In the second :@ "loop", the ::?= noise sets W_foo?=-Wfoo etc # which makes it easy to turn off override individual flags # (see W_uninitialized above). -# +# # The last bit expands to ${W_foo_${.TARGET:T}:U${W_foo}} # which is the bit we ultimately want. It allows W_* to be set on a # per target basis. -# +# # NOTE: that we force the target extension to be .o # @@ -110,7 +110,7 @@ M_warnings_list = @s@$${$$s_WARNINGS}@:O:u:@w@$${$${w:C/-(.)/\1_/}::?=$$w} $${$$ _warnings = ${WARNINGS_SET_${MACHINE_ARCH}:U${WARNINGS_SET}:${M_warnings_list}} # now a list of all -Wno-* overrides not just those defined by WARNINGS_SET # since things like -Wall imply lots of others. -# this should be a super-set of the -Wno-* in _warnings, but +# this should be a super-set of the -Wno-* in _warnings, but # just in case... _no_warnings = ${_warnings:M-Wno-*} ${ALL_WARNINGS_SETS:${M_warnings_list}:M-Wno-*} # -Wno-* must follow any others diff --git a/mk/whats.mk b/mk/whats.mk index 1dacb4c14365..e10964463d4a 100644 --- a/mk/whats.mk +++ b/mk/whats.mk @@ -1,20 +1,20 @@ -# $Id: whats.mk,v 1.9 2020/05/09 19:48:53 sjg Exp $ +# $Id: whats.mk,v 1.10 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2014-2020, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .if ${MK_WHATSTRING:Uno} == "yes" -# it can be useful to embed a what(1) string in binaries +# it can be useful to embed a what(1) string in binaries # so that the build location can be seen from a core file. .if defined(KMOD) what_thing ?= ${KMOD} diff --git a/mk/yacc.mk b/mk/yacc.mk index 910b004431df..7f7e99578d70 100644 --- a/mk/yacc.mk +++ b/mk/yacc.mk @@ -1,4 +1,4 @@ -# $Id: yacc.mk,v 1.6 2011/06/10 22:45:46 sjg Exp $ +# $Id: yacc.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 1999-2011, Simon J. Gerraty @@ -6,10 +6,10 @@ # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # diff --git a/nonints.h b/nonints.h index 3126650c9b59..6bcbd7690adb 100644 --- a/nonints.h +++ b/nonints.h @@ -1,4 +1,4 @@ -/* $NetBSD: nonints.h,v 1.78 2020/07/03 07:40:13 rillig Exp $ */ +/* $NetBSD: nonints.h,v 1.102 2020/08/30 19:56:02 rillig Exp $ */ /*- * Copyright (c) 1988, 1989, 1990, 1993 @@ -73,7 +73,7 @@ */ /* arch.c */ -ReturnStatus Arch_ParseArchive(char **, Lst, GNode *); +Boolean Arch_ParseArchive(char **, Lst, GNode *); void Arch_Touch(GNode *); void Arch_TouchLib(GNode *); time_t Arch_MTime(GNode *); @@ -82,7 +82,7 @@ void Arch_FindLib(GNode *, Lst); Boolean Arch_LibOODate(GNode *); void Arch_Init(void); void Arch_End(void); -int Arch_IsLib(GNode *); +Boolean Arch_IsLib(GNode *); /* compat.c */ int CompatRunCommand(void *, void *); @@ -91,8 +91,8 @@ int Compat_Make(void *, void *); /* cond.c */ struct If; -int Cond_EvalExpression(const struct If *, char *, Boolean *, int, Boolean); -int Cond_Eval(char *); +CondEvalResult Cond_EvalExpression(const struct If *, char *, Boolean *, int, Boolean); +CondEvalResult Cond_Eval(char *); void Cond_restore_depth(unsigned int); unsigned int Cond_save_depth(void); @@ -125,7 +125,6 @@ char *cached_realpath(const char *, char *); /* parse.c */ void Parse_Error(int, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3); -Boolean Parse_AnyExport(void); Boolean Parse_IsVar(char *); void Parse_DoVar(char *, GNode *); void Parse_AddIncludeDir(char *); @@ -136,12 +135,24 @@ void Parse_SetInput(const char *, int, int, char *(*)(void *, size_t *), void *) Lst Parse_MainName(void); /* str.c */ -char *str_concat(const char *, const char *, int); -char **brk_string(const char *, int *, Boolean, char **); +typedef struct { + char **words; + size_t len; + void *freeIt; +} Words; + +Words Str_Words(const char *, Boolean); +static inline void MAKE_ATTR_UNUSED +Words_Free(Words w) { + free(w.words); + free(w.freeIt); +} + +char *str_concat2(const char *, const char *); +char *str_concat3(const char *, const char *, const char *); +char *str_concat4(const char *, const char *, const char *, const char *); char *Str_FindSubstring(const char *, const char *); Boolean Str_Match(const char *, const char *); -char *Str_SYSVMatch(const char *, const char *, size_t *, Boolean *); -void Str_SYSVSubst(Buffer *, char *, char *, size_t, Boolean); #ifndef HAVE_STRLCPY /* strlcpy.c */ @@ -153,11 +164,11 @@ void Suff_ClearSuffixes(void); Boolean Suff_IsTransform(char *); GNode *Suff_AddTransform(char *); int Suff_EndTransform(void *, void *); -void Suff_AddSuffix(char *, GNode **); +void Suff_AddSuffix(const char *, GNode **); Lst Suff_GetPath(char *); void Suff_DoPaths(void); void Suff_AddInclude(char *); -void Suff_AddLib(char *); +void Suff_AddLib(const char *); void Suff_FindDeps(GNode *); Lst Suff_FindPath(GNode *); void Suff_SetNull(char *); @@ -168,6 +179,7 @@ void Suff_PrintAll(void); /* targ.c */ void Targ_Init(void); void Targ_End(void); +void Targ_Stats(void); Lst Targ_List(void); GNode *Targ_NewGN(const char *); GNode *Targ_FindNode(const char *, int); @@ -182,31 +194,40 @@ char *Targ_FmtTime(time_t); void Targ_PrintType(int); void Targ_PrintGraph(int); void Targ_Propagate(void); -void Targ_Propagate_Wait(void); /* var.c */ typedef enum { - VARF_UNDEFERR = 1, - VARF_WANTRES = 2, - VARF_ASSIGN = 4 -} Varf_Flags; + /* Treat undefined variables as errors. */ + VARE_UNDEFERR = 0x01, + /* Expand and evaluate variables during parsing. */ + VARE_WANTRES = 0x02, + VARE_ASSIGN = 0x04 +} VarEvalFlags; + +typedef enum { + VAR_NO_EXPORT = 0x01, /* do not export */ + /* Make the variable read-only. No further modification is possible, + * except for another call to Var_Set with the same flag. */ + VAR_SET_READONLY = 0x02 +} VarSet_Flags; + void Var_Delete(const char *, GNode *); void Var_Set(const char *, const char *, GNode *); +void Var_Set_with_flags(const char *, const char *, GNode *, VarSet_Flags); void Var_Append(const char *, const char *, GNode *); Boolean Var_Exists(const char *, GNode *); -char *Var_Value(const char *, GNode *, char **); -char *Var_Parse(const char *, GNode *, Varf_Flags, int *, void **); -char *Var_Subst(const char *, const char *, GNode *, Varf_Flags); -char *Var_GetTail(const char *); -char *Var_GetHead(const char *); +const char *Var_Value(const char *, GNode *, char **); +const char *Var_Parse(const char *, GNode *, VarEvalFlags, int *, void **); +char *Var_Subst(const char *, GNode *, VarEvalFlags); void Var_Init(void); void Var_End(void); +void Var_Stats(void); void Var_Dump(GNode *); void Var_ExportVars(void); -void Var_Export(char *, int); -void Var_UnExport(char *); +void Var_Export(const char *, Boolean); +void Var_UnExport(const char *); /* util.c */ void (*bmake_signal(int, void (*)(int)))(int); @@ -17,7 +17,7 @@ # Simon J. Gerraty <sjg@crufty.net> # RCSid: -# $Id: os.sh,v 1.55 2017/12/11 20:31:41 sjg Exp $ +# $Id: os.sh,v 1.56 2020/08/05 23:25:22 sjg Exp $ # # @(#) Copyright (c) 1994 Simon J. Gerraty # @@ -86,6 +86,9 @@ AIX) # everyone loves to be different... PS_AXC=-e SHARE_ARCH=$OS/$OSMAJOR.X ;; +Darwin) # a bit like BSD + HOST_ARCH=$MACHINE + ;; SunOS) CHOWN=`Which chown /usr/etc:/usr/bin` export CHOWN @@ -208,12 +211,12 @@ esac TMP_DIRS=${TMP_DIRS:-"/tmp /var/tmp"} MACHINE_ARCH=${MACHINE_ARCH:-$MACHINE} -case "$MACHINE_ARCH" in +HOST_ARCH=${HOST_ARCH:-$MACHINE_ARCH} +case "$HOST_ARCH" in x86*64|amd64) MACHINE32_ARCH=i386;; *64) MACHINE32_ARCH=`echo $MACHINE_ARCH | sed 's,64,32,'`;; *) MACHINE32_ARCH=$MACHINE_ARCH;; esac -HOST_ARCH=${HOST_ARCH:-$MACHINE_ARCH} HOST_ARCH32=${HOST_ARCH32:-$MACHINE32_ARCH} # we mount server:/share/arch/$SHARE_ARCH as /usr/local SHARE_ARCH_DEFAULT=$OS/$OSMAJOR.X/$HOST_ARCH @@ -1,4 +1,4 @@ -/* $NetBSD: parse.c,v 1.236 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: parse.c,v 1.275 2020/09/01 17:38:26 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -69,14 +69,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: parse.c,v 1.236 2020/07/03 08:13:23 rillig Exp $"; +static char rcsid[] = "$NetBSD: parse.c,v 1.275 2020/09/01 17:38:26 rillig Exp $"; #else #include <sys/cdefs.h> #ifndef lint #if 0 static char sccsid[] = "@(#)parse.c 8.3 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: parse.c,v 1.236 2020/07/03 08:13:23 rillig Exp $"); +__RCSID("$NetBSD: parse.c,v 1.275 2020/09/01 17:38:26 rillig Exp $"); #endif #endif /* not lint */ #endif @@ -125,17 +125,13 @@ __RCSID("$NetBSD: parse.c,v 1.236 2020/07/03 08:13:23 rillig Exp $"); #include <sys/types.h> #include <sys/stat.h> -#include <assert.h> -#include <ctype.h> #include <errno.h> #include <stdarg.h> #include <stdio.h> #include "make.h" -#include "hash.h" #include "dir.h" #include "job.h" -#include "buf.h" #include "pathnames.h" #ifdef HAVE_STDINT_H @@ -153,8 +149,7 @@ __RCSID("$NetBSD: parse.c,v 1.236 2020/07/03 08:13:23 rillig Exp $"); #endif #endif -//////////////////////////////////////////////////////////// -// types and constants +/* types and constants */ /* * Structure for a file being read ("included file") @@ -231,8 +226,7 @@ typedef enum { #define RPAREN ')' -//////////////////////////////////////////////////////////// -// result data +/* result data */ /* * The main target to create. This is the first target on the first @@ -240,8 +234,7 @@ typedef enum { */ static GNode *mainNode; -//////////////////////////////////////////////////////////// -// eval state +/* eval state */ /* targets we're working on */ static Lst targets; @@ -265,8 +258,7 @@ static ParseSpecial specType; */ static GNode *predecessor; -//////////////////////////////////////////////////////////// -// parser state +/* parser state */ /* true if currently in a dependency line or its commands */ static Boolean inLine; @@ -289,8 +281,7 @@ Lst parseIncPath; /* dirs for "..." includes */ Lst sysIncPath; /* dirs for <...> includes */ Lst defIncPath; /* default for sysIncPath */ -//////////////////////////////////////////////////////////// -// parser tables +/* parser tables */ /* * The parseKeywords table is searched using binary search when deciding @@ -350,8 +341,7 @@ static const struct { { ".WAIT", Wait, 0 }, }; -//////////////////////////////////////////////////////////// -// local functions +/* local functions */ static int ParseIsEscaped(const char *, const char *); static void ParseErrorInternal(const char *, size_t, int, const char *, ...) @@ -379,8 +369,7 @@ static char *ParseReadLine(void); static void ParseFinishLine(void); static void ParseMark(GNode *); -//////////////////////////////////////////////////////////// -// file loader +/* file loader */ struct loadedfile { const char *path; /* name, for error reports */ @@ -399,7 +388,7 @@ loadedfile_create(const char *path) struct loadedfile *lf; lf = bmake_malloc(sizeof(*lf)); - lf->path = (path == NULL ? "(stdin)" : path); + lf->path = path == NULL ? "(stdin)" : path; lf->buf = NULL; lf->len = 0; lf->maplen = 0; @@ -442,17 +431,17 @@ loadedfile_nextbuf(void *x, size_t *len) /* * Try to get the size of a file. */ -static ReturnStatus +static Boolean load_getsize(int fd, size_t *ret) { struct stat st; if (fstat(fd, &st) < 0) { - return FAILURE; + return FALSE; } if (!S_ISREG(st.st_mode)) { - return FAILURE; + return FALSE; } /* @@ -465,11 +454,11 @@ load_getsize(int fd, size_t *ret) * While we're at it reject negative sizes too, just in case. */ if (st.st_size < 0 || st.st_size > 0x7fffffff) { - return FAILURE; + return FALSE; } *ret = (size_t) st.st_size; - return SUCCESS; + return TRUE; } /* @@ -509,7 +498,7 @@ loadfile(const char *path, int fd) } #ifdef HAVE_MMAP - if (load_getsize(fd, &lf->len) == SUCCESS) { + if (load_getsize(fd, &lf->len)) { /* found a size, try mmap */ #ifdef _SC_PAGESIZE if (pagesize == 0) @@ -600,8 +589,7 @@ done: return lf; } -//////////////////////////////////////////////////////////// -// old code +/* old code */ /*- *---------------------------------------------------------------------- @@ -714,8 +702,8 @@ ParseVErrorInternal(FILE *f, const char *cfname, size_t clineno, int type, fname = cfname; } (void)fprintf(f, "%s/%s", dir, fname); - free(cp2); - free(cp); + bmake_free(cp2); + bmake_free(cp); } else (void)fprintf(f, "%s", cfname); @@ -840,7 +828,7 @@ ParseMessage(char *line) while (isspace((unsigned char)*line)) line++; - line = Var_Subst(NULL, line, VAR_CMD, VARF_WANTRES); + line = Var_Subst(line, VAR_CMD, VARE_WANTRES); Parse_Error(mtype, "%s", line); free(line); @@ -877,11 +865,11 @@ ParseLinkSrc(void *pgnp, void *cgnp) GNode *pgn = (GNode *)pgnp; GNode *cgn = (GNode *)cgnp; - if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (pgn->cohorts)) - pgn = (GNode *)Lst_Datum(Lst_Last(pgn->cohorts)); - (void)Lst_AtEnd(pgn->children, cgn); + if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(pgn->cohorts)) + pgn = LstNode_Datum(Lst_Last(pgn->cohorts)); + Lst_Append(pgn->children, cgn); if (specType == Not) - (void)Lst_AtEnd(cgn->parents, pgn); + Lst_Append(cgn->parents, pgn); pgn->unmade += 1; if (DEBUG(PARSE)) { fprintf(debug_file, "# %s: added child %s - %s\n", __func__, @@ -929,7 +917,7 @@ ParseDoOp(void *gnp, void *opp) return 1; } - if ((op == OP_DOUBLEDEP) && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) { + if (op == OP_DOUBLEDEP && (gn->type & OP_OPMASK) == OP_DOUBLEDEP) { /* * If the node was the object of a :: operator, we need to create a * new instance of it for the children and commands on this dependency @@ -957,7 +945,7 @@ ParseDoOp(void *gnp, void *opp) * traversals will no longer see this node anyway. -mycroft) */ cohort->type = op | OP_INVISIBLE; - (void)Lst_AtEnd(gn->cohorts, cohort); + Lst_Append(gn->cohorts, cohort); cohort->centurion = gn; gn->unmade_cohorts += 1; snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d", @@ -1006,7 +994,8 @@ ParseDoSrc(int tOp, const char *src) if (keywd != -1) { int op = parseKeywords[keywd].op; if (op != 0) { - Lst_ForEach(targets, ParseDoOp, &op); + if (targets != NULL) + Lst_ForEach(targets, ParseDoOp, &op); return; } if (parseKeywords[keywd].spec == Wait) { @@ -1024,7 +1013,8 @@ ParseDoSrc(int tOp, const char *src) if (doing_depend) ParseMark(gn); gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN; - Lst_ForEach(targets, ParseLinkSrc, gn); + if (targets != NULL) + Lst_ForEach(targets, ParseLinkSrc, gn); return; } } @@ -1040,7 +1030,7 @@ ParseDoSrc(int tOp, const char *src) * invoked if the user didn't specify a target on the command * line. This is to allow #ifmake's to succeed, or something... */ - (void)Lst_AtEnd(create, bmake_strdup(src)); + Lst_Append(create, bmake_strdup(src)); /* * Add the name to the .TARGETS variable as well, so the user can * employ that, if desired. @@ -1057,8 +1047,8 @@ ParseDoSrc(int tOp, const char *src) if (doing_depend) ParseMark(gn); if (predecessor != NULL) { - (void)Lst_AtEnd(predecessor->order_succ, gn); - (void)Lst_AtEnd(gn->order_pred, predecessor); + Lst_Append(predecessor->order_succ, gn); + Lst_Append(gn->order_pred, predecessor); if (DEBUG(PARSE)) { fprintf(debug_file, "# %s: added Order dependency %s - %s\n", __func__, predecessor->name, gn->name); @@ -1092,7 +1082,8 @@ ParseDoSrc(int tOp, const char *src) if (tOp) { gn->type |= tOp; } else { - Lst_ForEach(targets, ParseLinkSrc, gn); + if (targets != NULL) + Lst_ForEach(targets, ParseLinkSrc, gn); } break; } @@ -1120,7 +1111,7 @@ static int ParseFindMain(void *gnp, void *dummy MAKE_ATTR_UNUSED) { GNode *gn = (GNode *)gnp; - if ((gn->type & OP_NOTARGET) == 0) { + if (!(gn->type & OP_NOTARGET)) { mainNode = gn; Targ_SetMain(gn); return 1; @@ -1229,7 +1220,7 @@ ParseDoDependency(char *line) specType = Not; paths = NULL; - curTargs = Lst_Init(FALSE); + curTargs = Lst_Init(); /* * First, grind through the targets. @@ -1257,10 +1248,10 @@ ParseDoDependency(char *line) int length; void *freeIt; - (void)Var_Parse(cp, VAR_CMD, VARF_UNDEFERR|VARF_WANTRES, + (void)Var_Parse(cp, VAR_CMD, VARE_UNDEFERR|VARE_WANTRES, &length, &freeIt); free(freeIt); - cp += length-1; + cp += length - 1; } } @@ -1275,11 +1266,11 @@ ParseDoDependency(char *line) * things like "archive(file1.o file2.o file3.o)" are permissible. * Arch_ParseArchive will set 'line' to be the first non-blank * after the archive-spec. It creates/finds nodes for the members - * and places them on the given list, returning SUCCESS if all - * went well and FAILURE if there was an error in the + * and places them on the given list, returning TRUE if all + * went well and FALSE if there was an error in the * specification. On error, line should remain untouched. */ - if (Arch_ParseArchive(&line, targets, VAR_CMD) != SUCCESS) { + if (!Arch_ParseArchive(&line, targets, VAR_CMD)) { Parse_Error(PARSE_FATAL, "Error in archive specification: \"%s\"", line); goto out; @@ -1304,9 +1295,18 @@ ParseDoDependency(char *line) (strncmp(line, ">>>>>>", 6) == 0)) Parse_Error(PARSE_FATAL, "Makefile appears to contain unresolved cvs/rcs/??? merge conflicts"); - else - Parse_Error(PARSE_FATAL, lstart[0] == '.' ? "Unknown directive" - : "Need an operator"); + else if (lstart[0] == '.') { + const char *dirstart = lstart + 1; + const char *dirend; + while (isspace((unsigned char)*dirstart)) + dirstart++; + dirend = dirstart; + while (isalnum((unsigned char)*dirend) || *dirend == '-') + dirend++; + Parse_Error(PARSE_FATAL, "Unknown directive \"%.*s\"", + (int)(dirend - dirstart), dirstart); + } else + Parse_Error(PARSE_FATAL, "Need an operator"); goto out; } @@ -1368,9 +1368,9 @@ ParseDoDependency(char *line) switch (specType) { case ExPath: if (paths == NULL) { - paths = Lst_Init(FALSE); + paths = Lst_Init(); } - (void)Lst_AtEnd(paths, dirSearchPath); + Lst_Append(paths, dirSearchPath); break; case Main: if (!Lst_IsEmpty(create)) { @@ -1386,12 +1386,12 @@ ParseDoDependency(char *line) if (doing_depend) ParseMark(gn); gn->type |= OP_NOTMAIN|OP_SPECIAL; - (void)Lst_AtEnd(targets, gn); + Lst_Append(targets, gn); break; case Default: gn = Targ_NewGN(".DEFAULT"); gn->type |= (OP_NOTMAIN|OP_TRANSFORM); - (void)Lst_AtEnd(targets, gn); + Lst_Append(targets, gn); DEFAULT = gn; break; case DeleteOnError: @@ -1426,9 +1426,9 @@ ParseDoDependency(char *line) goto out; } else { if (paths == NULL) { - paths = Lst_Init(FALSE); + paths = Lst_Init(); } - (void)Lst_AtEnd(paths, path); + Lst_Append(paths, path); } } } @@ -1437,7 +1437,7 @@ ParseDoDependency(char *line) * Have word in line. Get or create its node and stick it at * the end of the targets list */ - if ((specType == Not) && (*line != '\0')) { + if (specType == Not && *line != '\0') { if (Dir_HasWildcards(line)) { /* * Targets are to be sought only in the current directory, @@ -1445,7 +1445,7 @@ ParseDoDependency(char *line) * use Dir_Destroy in the destruction of the path as the * Dir module could have added a directory to the path... */ - Lst emptyPath = Lst_Init(FALSE); + Lst emptyPath = Lst_Init(); Dir_Expand(line, emptyPath, curTargs); @@ -1455,13 +1455,13 @@ ParseDoDependency(char *line) * No wildcards, but we want to avoid code duplication, * so create a list with the word on it. */ - (void)Lst_AtEnd(curTargs, line); + Lst_Append(curTargs, line); } /* Apply the targets. */ while(!Lst_IsEmpty(curTargs)) { - char *targName = (char *)Lst_DeQueue(curTargs); + char *targName = Lst_Dequeue(curTargs); if (!Suff_IsTransform (targName)) { gn = Targ_FindNode(targName, TARG_CREATE); @@ -1471,7 +1471,7 @@ ParseDoDependency(char *line) if (doing_depend) ParseMark(gn); - (void)Lst_AtEnd(targets, gn); + Lst_Append(targets, gn); } } else if (specType == ExPath && *line != '.' && *line != '\0') { Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line); @@ -1488,7 +1488,7 @@ ParseDoDependency(char *line) Boolean warning = FALSE; while (*cp && (ParseIsEscaped(lstart, cp) || - ((*cp != '!') && (*cp != ':')))) { + (*cp != '!' && *cp != ':'))) { if (ParseIsEscaped(lstart, cp) || (*cp != ' ' && *cp != '\t')) { warning = TRUE; @@ -1505,15 +1505,15 @@ ParseDoDependency(char *line) } line = cp; } while (*line && (ParseIsEscaped(lstart, line) || - ((*line != '!') && (*line != ':')))); + (*line != '!' && *line != ':'))); /* * Don't need the list of target names anymore... */ - Lst_Destroy(curTargs, NULL); + Lst_Free(curTargs); curTargs = NULL; - if (!Lst_IsEmpty(targets)) { + if (targets != NULL && !Lst_IsEmpty(targets)) { switch(specType) { default: Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored"); @@ -1563,7 +1563,8 @@ ParseDoDependency(char *line) * operator a target was defined with. It fails if the operator * used isn't consistent across all references. */ - Lst_ForEach(targets, ParseDoOp, &op); + if (targets != NULL) + Lst_ForEach(targets, ParseDoOp, &op); /* * Onward to the sources. @@ -1600,13 +1601,14 @@ ParseDoDependency(char *line) beSilent = TRUE; break; case ExPath: - Lst_ForEach(paths, ParseClearPath, NULL); + if (paths != NULL) + Lst_ForEach(paths, ParseClearPath, NULL); Dir_SetPATH(); break; #ifdef POSIX - case Posix: - Var_Set("%POSIX", "1003.2", VAR_GLOBAL); - break; + case Posix: + Var_Set("%POSIX", "1003.2", VAR_GLOBAL); + break; #endif default: break; @@ -1620,22 +1622,22 @@ ParseDoDependency(char *line) Main_ParseArgLine(line); *line = '\0'; } else if (specType == ExShell) { - if (Job_ParseShell(line) != SUCCESS) { + if (!Job_ParseShell(line)) { Parse_Error(PARSE_FATAL, "improper shell specification"); goto out; } *line = '\0'; - } else if ((specType == NotParallel) || (specType == SingleShell) || - (specType == DeleteOnError)) { + } else if (specType == NotParallel || specType == SingleShell || + specType == DeleteOnError) { *line = '\0'; } /* * NOW GO FOR THE SOURCES */ - if ((specType == Suffixes) || (specType == ExPath) || - (specType == Includes) || (specType == Libs) || - (specType == Null) || (specType == ExObjdir)) + if (specType == Suffixes || specType == ExPath || + specType == Includes || specType == Libs || + specType == Null || specType == ExObjdir) { while (*line) { /* @@ -1675,7 +1677,8 @@ ParseDoDependency(char *line) Suff_AddSuffix(line, &mainNode); break; case ExPath: - Lst_ForEach(paths, ParseAddDir, line); + if (paths != NULL) + Lst_ForEach(paths, ParseAddDir, line); break; case Includes: Suff_AddInclude(line); @@ -1702,7 +1705,7 @@ ParseDoDependency(char *line) line = cp; } if (paths) { - Lst_Destroy(paths, NULL); + Lst_Free(paths); paths = NULL; } if (specType == ExPath) @@ -1716,7 +1719,7 @@ ParseDoDependency(char *line) * and handle them accordingly. */ for (; *cp && !isspace ((unsigned char)*cp); cp++) { - if ((*cp == LPAREN) && (cp > line) && (cp[-1] != '$')) { + if (*cp == LPAREN && cp > line && cp[-1] != '$') { /* * Only stop for a left parenthesis if it isn't at the * start of a word (that'll be for variable changes @@ -1728,18 +1731,18 @@ ParseDoDependency(char *line) } if (*cp == LPAREN) { - sources = Lst_Init(FALSE); - if (Arch_ParseArchive(&line, sources, VAR_CMD) != SUCCESS) { + sources = Lst_Init(); + if (!Arch_ParseArchive(&line, sources, VAR_CMD)) { Parse_Error(PARSE_FATAL, "Error in source archive spec \"%s\"", line); goto out; } - while (!Lst_IsEmpty (sources)) { - gn = (GNode *)Lst_DeQueue(sources); + while (!Lst_IsEmpty(sources)) { + gn = Lst_Dequeue(sources); ParseDoSrc(tOp, gn->name); } - Lst_Destroy(sources, NULL); + Lst_Free(sources); cp = line; } else { if (*cp) { @@ -1756,7 +1759,7 @@ ParseDoDependency(char *line) } } - if (mainNode == NULL) { + if (mainNode == NULL && targets != NULL) { /* * If we have yet to decide on a main target to make, in the * absence of any user input, we want the first target on @@ -1767,10 +1770,10 @@ ParseDoDependency(char *line) } out: - if (paths) - Lst_Destroy(paths, NULL); - if (curTargs) - Lst_Destroy(curTargs, NULL); + if (paths != NULL) + Lst_Free(paths); + if (curTargs != NULL) + Lst_Free(curTargs); } /*- @@ -1804,8 +1807,8 @@ Parse_IsVar(char *line) /* * Skip to variable name */ - for (;(*line == ' ') || (*line == '\t'); line++) - continue; + while (*line == ' ' || *line == '\t') + line++; /* Scan for one of the assignment operators outside a variable expansion */ while ((ch = *line++) != 0) { @@ -1882,16 +1885,15 @@ Parse_DoVar(char *line, GNode *ctxt) /* * Skip to variable name */ - while ((*line == ' ') || (*line == '\t')) { + while (*line == ' ' || *line == '\t') line++; - } /* * Skip to operator character, nulling out whitespace as we go * XXX Rather than counting () and {} we should look for $ and * then expand the variable. */ - for (depth = 0, cp = line + 1; depth > 0 || *cp != '='; cp++) { + for (depth = 0, cp = line; depth > 0 || *cp != '='; cp++) { if (*cp == '(' || *cp == '{') { depth++; continue; @@ -1953,8 +1955,17 @@ Parse_DoVar(char *line, GNode *ctxt) break; } - while (isspace ((unsigned char)*cp)) { + while (isspace((unsigned char)*cp)) cp++; + + if (DEBUG(LINT)) { + if (type != VAR_SUBST && strchr(cp, '$') != NULL) { + /* sanity check now */ + char *cp2; + + cp2 = Var_Subst(cp, ctxt, VARE_ASSIGN); + free(cp2); + } } if (type == VAR_APPEND) { @@ -1982,7 +1993,7 @@ Parse_DoVar(char *line, GNode *ctxt) if (!Var_Exists(line, ctxt)) Var_Set(line, "", ctxt); - cp = Var_Subst(NULL, cp, ctxt, VARF_WANTRES|VARF_ASSIGN); + cp = Var_Subst(cp, ctxt, VARE_WANTRES|VARE_ASSIGN); oldVars = oldOldVars; freeCp = TRUE; @@ -1995,9 +2006,9 @@ Parse_DoVar(char *line, GNode *ctxt) /* * There's a dollar sign in the command, so perform variable * expansion on the whole thing. The resulting string will need - * freeing when we're done, so set freeCmd to TRUE. + * freeing when we're done. */ - cp = Var_Subst(NULL, cp, VAR_CMD, VARF_UNDEFERR|VARF_WANTRES); + cp = Var_Subst(cp, VAR_CMD, VARE_UNDEFERR|VARE_WANTRES); freeCp = TRUE; } @@ -2026,7 +2037,7 @@ Parse_DoVar(char *line, GNode *ctxt) } else if (strcmp(line, MAKE_JOB_PREFIX) == 0) { Job_SetPrefix(); } else if (strcmp(line, MAKE_EXPORTED) == 0) { - Var_Export(cp, 0); + Var_Export(cp, FALSE); } if (freeCp) free(cp); @@ -2089,19 +2100,19 @@ ParseAddCmd(void *gnp, void *cmd) GNode *gn = (GNode *)gnp; /* Add to last (ie current) cohort for :: targets */ - if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (gn->cohorts)) - gn = (GNode *)Lst_Datum(Lst_Last(gn->cohorts)); + if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(gn->cohorts)) + gn = LstNode_Datum(Lst_Last(gn->cohorts)); /* if target already supplied, ignore commands */ if (!(gn->type & OP_HAS_COMMANDS)) { - (void)Lst_AtEnd(gn->commands, cmd); + Lst_Append(gn->commands, cmd); if (ParseMaybeSubMake(cmd)) gn->type |= OP_SUBMAKE; ParseMark(gn); } else { #ifdef notyet /* XXX: We cannot do this until we fix the tree */ - (void)Lst_AtEnd(gn->commands, cmd); + Lst_Append(gn->commands, cmd); Parse_Error(PARSE_WARNING, "overriding commands for target \"%s\"; " "previous commands defined at %s: %d ignored", @@ -2225,7 +2236,7 @@ Parse_include_file(char *file, Boolean isSystem, Boolean depinc, int silent) break; *prefEnd = '\0'; } - newName = str_concat(incdir, file + i, STR_ADDSLASH); + newName = str_concat3(incdir, "/", file + i); fullname = Dir_FindFile(newName, parseIncPath); if (fullname == NULL) fullname = Dir_FindFile(newName, dirSearchPath); @@ -2235,7 +2246,7 @@ Parse_include_file(char *file, Boolean isSystem, Boolean depinc, int silent) if (fullname == NULL) { /* - * Makefile wasn't found in same directory as included makefile. + * Makefile wasn't found in same directory as included makefile. * Search for it first on the -I search path, * then on the .PATH search path, if not found in a -I directory. * If we have a suffix specific path we should use that. @@ -2298,7 +2309,7 @@ ParseDoInclude(char *line) { char endc; /* the character which ends the file spec */ char *cp; /* current position in file spec */ - int silent = (*line != 'i') ? 1 : 0; + int silent = *line != 'i'; char *file = &line[7 + silent]; /* Skip to delimiter character so we know where to look */ @@ -2338,9 +2349,9 @@ ParseDoInclude(char *line) * Substitute for any variables in the file name before trying to * find the thing. */ - file = Var_Subst(NULL, file, VAR_CMD, VARF_WANTRES); + file = Var_Subst(file, VAR_CMD, VARE_WANTRES); - Parse_include_file(file, endc == '>', (*line == 'd'), silent); + Parse_include_file(file, endc == '>', *line == 'd', silent); free(file); } @@ -2363,20 +2374,20 @@ ParseDoInclude(char *line) static void ParseSetIncludedFile(void) { - char *pf, *fp = NULL; - char *pd, *dp = NULL; + const char *pf, *pd; + char *pf_freeIt, *pd_freeIt; - pf = Var_Value(".PARSEFILE", VAR_GLOBAL, &fp); + pf = Var_Value(".PARSEFILE", VAR_GLOBAL, &pf_freeIt); Var_Set(".INCLUDEDFROMFILE", pf, VAR_GLOBAL); - pd = Var_Value(".PARSEDIR", VAR_GLOBAL, &dp); + pd = Var_Value(".PARSEDIR", VAR_GLOBAL, &pd_freeIt); Var_Set(".INCLUDEDFROMDIR", pd, VAR_GLOBAL); if (DEBUG(PARSE)) fprintf(debug_file, "%s: ${.INCLUDEDFROMDIR} = `%s' " "${.INCLUDEDFROMFILE} = `%s'\n", __func__, pd, pf); - free(fp); - free(dp); + bmake_free(pf_freeIt); + bmake_free(pd_freeIt); } /*- *--------------------------------------------------------------------- @@ -2397,18 +2408,14 @@ ParseSetParseFile(const char *filename) { char *slash, *dirname; const char *pd, *pf; - int len; slash = strrchr(filename, '/'); if (slash == NULL) { Var_Set(".PARSEDIR", pd = curdir, VAR_GLOBAL); Var_Set(".PARSEFILE", pf = filename, VAR_GLOBAL); - dirname= NULL; + dirname = NULL; } else { - len = slash - filename; - dirname = bmake_malloc(len + 1); - memcpy(dirname, filename, len); - dirname[len] = '\0'; + dirname = bmake_strsedup(filename, slash); Var_Set(".PARSEDIR", pd = dirname, VAR_GLOBAL); Var_Set(".PARSEFILE", pf = slash + 1, VAR_GLOBAL); } @@ -2427,14 +2434,12 @@ ParseSetParseFile(const char *filename) static void ParseTrackInput(const char *name) { - char *old; - char *ep; char *fp = NULL; - size_t name_len = strlen(name); - old = Var_Value(MAKE_MAKEFILES, VAR_GLOBAL, &fp); + const char *old = Var_Value(MAKE_MAKEFILES, VAR_GLOBAL, &fp); if (old) { - ep = old + strlen(old) - name_len; + size_t name_len = strlen(name); + const char *ep = old + strlen(old) - name_len; /* does it contain name? */ for (; old != NULL; old = strchr(old, ' ')) { if (*old == ' ') @@ -2448,9 +2453,7 @@ ParseTrackInput(const char *name) } Var_Append (MAKE_MAKEFILES, name, VAR_GLOBAL); cleanup: - if (fp) { - free(fp); - } + bmake_free(fp); } @@ -2489,7 +2492,7 @@ Parse_SetInput(const char *name, int line, int fd, if (curFile != NULL) /* Save exiting file info */ - Lst_AtFront(includes, curFile); + Lst_Prepend(includes, curFile); /* Allocate and fill in new structure */ curFile = bmake_malloc(sizeof *curFile); @@ -2513,7 +2516,7 @@ Parse_SetInput(const char *name, int line, int fd, /* Get first block of input data */ buf = curFile->nextbuf(curFile->nextbuf_arg, &len); if (buf == NULL) { - /* Was all a waste of time ... */ + /* Was all a waste of time ... */ if (curFile->fname) free(curFile->fname); free(curFile); @@ -2546,13 +2549,13 @@ IsInclude(const char *line, Boolean sysv) static const char inc[] = "include"; static const size_t inclen = sizeof(inc) - 1; - // 'd' is not valid for sysv - int o = strchr(&("ds-"[sysv]), *line) != NULL; + /* 'd' is not valid for sysv */ + int o = strchr(sysv ? "s-" : "ds-", *line) != NULL; if (strncmp(line + o, inc, inclen) != 0) return FALSE; - // Space is not mandatory for BSD .include + /* Space is not mandatory for BSD .include */ return !sysv || isspace((unsigned char)line[inclen + o]); } @@ -2614,7 +2617,7 @@ ParseTraditionalInclude(char *line) { char *cp; /* current position in file spec */ int done = 0; - int silent = (line[0] != 'i') ? 1 : 0; + int silent = line[0] != 'i'; char *file = &line[silent + 7]; char *all_files; @@ -2632,7 +2635,7 @@ ParseTraditionalInclude(char *line) * Substitute for any variables in the file name before trying to * find the thing. */ - all_files = Var_Subst(NULL, file, VAR_CMD, VARF_WANTRES); + all_files = Var_Subst(file, VAR_CMD, VARE_WANTRES); if (*file == '\0') { Parse_Error(PARSE_FATAL, @@ -2701,7 +2704,7 @@ ParseGmakeExport(char *line) /* * Expand the value before putting it in the environment. */ - value = Var_Subst(NULL, value, VAR_CMD, VARF_WANTRES); + value = Var_Subst(value, VAR_CMD, VARE_WANTRES); setenv(variable, value, 1); free(value); } @@ -2755,9 +2758,8 @@ ParseEOF(void) free(curFile->P_str); free(curFile); - curFile = Lst_DeQueue(includes); - - if (curFile == NULL) { + if (Lst_IsEmpty(includes)) { + curFile = NULL; /* We've run out of input */ Var_Delete(".PARSEDIR", VAR_GLOBAL); Var_Delete(".PARSEFILE", VAR_GLOBAL); @@ -2766,6 +2768,7 @@ ParseEOF(void) return DONE; } + curFile = Lst_Dequeue(includes); if (DEBUG(PARSE)) fprintf(debug_file, "ParseEOF: returning to file %s, line %d\n", curFile->fname, curFile->lineno); @@ -3030,8 +3033,10 @@ static void ParseFinishLine(void) { if (inLine) { - Lst_ForEach(targets, Suff_EndTransform, NULL); - Lst_Destroy(targets, ParseHasCommands); + if (targets != NULL) { + Lst_ForEach(targets, Suff_EndTransform, NULL); + Lst_Destroy(targets, ParseHasCommands); + } targets = NULL; inLine = FALSE; } @@ -3070,9 +3075,8 @@ Parse_File(const char *name, int fd) inLine = FALSE; fatals = 0; - if (name == NULL) { - name = "(stdin)"; - } + if (name == NULL) + name = "(stdin)"; Parse_SetInput(name, 0, -1, loadedfile_nextbuf, lf); curFile->lf = lf; @@ -3101,7 +3105,7 @@ Parse_File(const char *name, int fd) for (cp += 5; isspace((unsigned char) *cp); cp++) continue; for (cp2 = cp; !isspace((unsigned char) *cp2) && - (*cp2 != '\0'); cp2++) + *cp2 != '\0'; cp2++) continue; *cp2 = '\0'; Var_Delete(cp, VAR_GLOBAL); @@ -3109,7 +3113,7 @@ Parse_File(const char *name, int fd) } else if (strncmp(cp, "export", 6) == 0) { for (cp += 6; isspace((unsigned char) *cp); cp++) continue; - Var_Export(cp, 1); + Var_Export(cp, TRUE); continue; } else if (strncmp(cp, "unexport", 8) == 0) { Var_UnExport(cp); @@ -3146,7 +3150,7 @@ Parse_File(const char *name, int fd) cp = bmake_strdup(cp); Lst_ForEach(targets, ParseAddCmd, cp); #ifdef CLEANUP - Lst_AtEnd(targCmds, cp); + Lst_Append(targCmds, cp); #endif } } @@ -3189,10 +3193,10 @@ Parse_File(const char *name, int fd) */ cp = line; if (isspace((unsigned char) line[0])) { - while ((*cp != '\0') && isspace((unsigned char) *cp)) + while (isspace((unsigned char) *cp)) cp++; while (*cp && (ParseIsEscaped(line, cp) || - (*cp != ':') && (*cp != '!'))) { + *cp != ':' && *cp != '!')) { cp++; } if (*cp == '\0') { @@ -3245,15 +3249,15 @@ Parse_File(const char *name, int fd) * variables expanded before being parsed. Tell the variable * module to complain if some variable is undefined... */ - line = Var_Subst(NULL, line, VAR_CMD, VARF_UNDEFERR|VARF_WANTRES); + line = Var_Subst(line, VAR_CMD, VARE_UNDEFERR|VARE_WANTRES); /* * Need a non-circular list for the target nodes */ - if (targets) - Lst_Destroy(targets, NULL); + if (targets != NULL) + Lst_Free(targets); - targets = Lst_Init(FALSE); + targets = Lst_Init(); inLine = TRUE; ParseDoDependency(line); @@ -3295,12 +3299,12 @@ void Parse_Init(void) { mainNode = NULL; - parseIncPath = Lst_Init(FALSE); - sysIncPath = Lst_Init(FALSE); - defIncPath = Lst_Init(FALSE); - includes = Lst_Init(FALSE); + parseIncPath = Lst_Init(); + sysIncPath = Lst_Init(); + defIncPath = Lst_Init(); + includes = Lst_Init(); #ifdef CLEANUP - targCmds = Lst_Init(FALSE); + targCmds = Lst_Init(); #endif } @@ -3308,13 +3312,13 @@ void Parse_End(void) { #ifdef CLEANUP - Lst_Destroy(targCmds, (FreeProc *)free); + Lst_Destroy(targCmds, free); if (targets) - Lst_Destroy(targets, NULL); + Lst_Free(targets); Lst_Destroy(defIncPath, Dir_Destroy); Lst_Destroy(sysIncPath, Dir_Destroy); Lst_Destroy(parseIncPath, Dir_Destroy); - Lst_Destroy(includes, NULL); /* Should be empty now */ + Lst_Free(includes); /* Should be empty now */ #endif } @@ -3338,17 +3342,17 @@ Parse_MainName(void) { Lst mainList; /* result list */ - mainList = Lst_Init(FALSE); + mainList = Lst_Init(); if (mainNode == NULL) { Punt("no target to make."); - /*NOTREACHED*/ + /*NOTREACHED*/ } else if (mainNode->type & OP_DOUBLEDEP) { - (void)Lst_AtEnd(mainList, mainNode); - Lst_Concat(mainList, mainNode->cohorts, LST_CONCNEW); + Lst_Append(mainList, mainNode); + Lst_AppendAll(mainList, mainNode->cohorts); } else - (void)Lst_AtEnd(mainList, mainNode); + Lst_Append(mainList, mainNode); Var_Append(".TARGETS", mainNode->name, VAR_GLOBAL); return mainList; } diff --git a/sprite.h b/sprite.h deleted file mode 100644 index cdcffd99abed..000000000000 --- a/sprite.h +++ /dev/null @@ -1,116 +0,0 @@ -/* $NetBSD: sprite.h,v 1.14 2017/05/31 22:02:06 maya Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * from: @(#)sprite.h 8.1 (Berkeley) 6/6/93 - */ - -/* - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * from: @(#)sprite.h 8.1 (Berkeley) 6/6/93 - */ - -/* - * sprite.h -- - * - * Common constants and type declarations for Sprite. - */ - -#ifndef MAKE_SPRITE_H -#define MAKE_SPRITE_H - - -/* - * A boolean type is defined as an integer, not an enum. This allows a - * boolean argument to be an expression that isn't strictly 0 or 1 valued. - */ - -typedef int Boolean; -#ifndef TRUE -#define TRUE 1 -#endif /* TRUE */ -#ifndef FALSE -#define FALSE 0 -#endif /* FALSE */ - -/* - * Functions that must return a status can return a ReturnStatus to - * indicate success or type of failure. - */ - -typedef int ReturnStatus; - -/* - * The following statuses overlap with the first 2 generic statuses - * defined in status.h: - * - * SUCCESS There was no error. - * FAILURE There was a general error. - */ - -#define SUCCESS 0x00000000 -#define FAILURE 0x00000001 - -#endif /* MAKE_SPRITE_H */ @@ -1,4 +1,4 @@ -/* $NetBSD: str.c,v 1.51 2020/07/03 07:40:13 rillig Exp $ */ +/* $NetBSD: str.c,v 1.64 2020/08/30 19:56:02 rillig Exp $ */ /*- * Copyright (c) 1988, 1989, 1990, 1993 @@ -69,85 +69,85 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: str.c,v 1.51 2020/07/03 07:40:13 rillig Exp $"; +static char rcsid[] = "$NetBSD: str.c,v 1.64 2020/08/30 19:56:02 rillig Exp $"; #else #include <sys/cdefs.h> #ifndef lint #if 0 static char sccsid[] = "@(#)str.c 5.8 (Berkeley) 6/1/90"; #else -__RCSID("$NetBSD: str.c,v 1.51 2020/07/03 07:40:13 rillig Exp $"); +__RCSID("$NetBSD: str.c,v 1.64 2020/08/30 19:56:02 rillig Exp $"); #endif #endif /* not lint */ #endif #include "make.h" -/*- - * str_concat -- - * concatenate the two strings, inserting a space or slash between them, - * freeing them if requested. - * - * returns -- - * the resulting string in allocated space. - */ +/* Return the concatenation of s1 and s2, freshly allocated. */ char * -str_concat(const char *s1, const char *s2, int flags) +str_concat2(const char *s1, const char *s2) { - int len1, len2; - char *result; - - /* get the length of both strings */ - len1 = strlen(s1); - len2 = strlen(s2); - - /* allocate length plus separator plus EOS */ - result = bmake_malloc((unsigned int)(len1 + len2 + 2)); - - /* copy first string into place */ + size_t len1 = strlen(s1); + size_t len2 = strlen(s2); + char *result = bmake_malloc(len1 + len2 + 1); memcpy(result, s1, len1); - - /* add separator character */ - if (flags & STR_ADDSPACE) { - result[len1] = ' '; - ++len1; - } else if (flags & STR_ADDSLASH) { - result[len1] = '/'; - ++len1; - } - - /* copy second string plus EOS into place */ memcpy(result + len1, s2, len2 + 1); + return result; +} +/* Return the concatenation of s1, s2 and s3, freshly allocated. */ +char * +str_concat3(const char *s1, const char *s2, const char *s3) +{ + size_t len1 = strlen(s1); + size_t len2 = strlen(s2); + size_t len3 = strlen(s3); + char *result = bmake_malloc(len1 + len2 + len3 + 1); + memcpy(result, s1, len1); + memcpy(result + len1, s2, len2); + memcpy(result + len1 + len2, s3, len3 + 1); return result; } -/*- - * brk_string -- - * Fracture a string into an array of words (as delineated by tabs or - * spaces) taking quotation marks into account. Leading tabs/spaces - * are ignored. +/* Return the concatenation of s1, s2, s3 and s4, freshly allocated. */ +char * +str_concat4(const char *s1, const char *s2, const char *s3, const char *s4) +{ + size_t len1 = strlen(s1); + size_t len2 = strlen(s2); + size_t len3 = strlen(s3); + size_t len4 = strlen(s4); + char *result = bmake_malloc(len1 + len2 + len3 + len4 + 1); + memcpy(result, s1, len1); + memcpy(result + len1, s2, len2); + memcpy(result + len1 + len2, s3, len3); + memcpy(result + len1 + len2 + len3, s4, len4 + 1); + return result; +} + +/* Fracture a string into an array of words (as delineated by tabs or spaces) + * taking quotation marks into account. Leading tabs/spaces are ignored. * - * If expand is TRUE, quotes are removed and escape sequences - * such as \r, \t, etc... are expanded. + * If expand is TRUE, quotes are removed and escape sequences such as \r, \t, + * etc... are expanded. In this case, the return value is NULL on parse + * errors. * - * returns -- - * Pointer to the array of pointers to the words. - * Memory containing the actual words in *store_words_buf. - * Both of these must be free'd by the caller. - * Number of words in *store_words_len. + * Returns the fractured words, which must be freed later using Words_Free. + * If expand was TRUE and there was a parse error, words is NULL, and in that + * case, nothing needs to be freed. */ -char ** -brk_string(const char *str, int *store_words_len, Boolean expand, - char **store_words_buf) +Words +Str_Words(const char *str, Boolean expand) { + size_t str_len; + char *words_buf; + size_t words_cap; + char **words; + size_t words_len; char inquote; + char *word_start; + char *word_end; const char *str_p; - size_t str_len; - char **words; - int words_len; - int words_cap = 50; - char *words_buf, *word_start, *word_end; /* skip leading space chars. */ for (; *str == ' ' || *str == '\t'; ++str) @@ -166,10 +166,11 @@ brk_string(const char *str, int *store_words_len, Boolean expand, */ words_len = 0; inquote = '\0'; - word_start = word_end = words_buf; + word_start = words_buf; + word_end = words_buf; for (str_p = str;; ++str_p) { char ch = *str_p; - switch(ch) { + switch (ch) { case '"': case '\'': if (inquote) { @@ -177,9 +178,8 @@ brk_string(const char *str, int *store_words_len, Boolean expand, inquote = '\0'; else break; - } - else { - inquote = (char) ch; + } else { + inquote = (char)ch; /* Don't miss "" or '' */ if (word_start == NULL && str_p[1] == inquote) { if (!expand) { @@ -212,13 +212,14 @@ brk_string(const char *str, int *store_words_len, Boolean expand, * space and save off a pointer. */ if (word_start == NULL) - goto done; + goto done; *word_end++ = '\0'; if (words_len == words_cap) { + size_t new_size; words_cap *= 2; /* ramp up fast */ - words = (char **)bmake_realloc(words, - (words_cap + 1) * sizeof(char *)); + new_size = (words_cap + 1) * sizeof(char *); + words = bmake_realloc(words, new_size); } words[words_len++] = word_start; word_start = NULL; @@ -226,8 +227,7 @@ brk_string(const char *str, int *store_words_len, Boolean expand, if (expand && inquote) { free(words); free(words_buf); - *store_words_buf = NULL; - return NULL; + return (Words){ NULL, 0, NULL }; } goto done; } @@ -273,10 +273,9 @@ brk_string(const char *str, int *store_words_len, Boolean expand, word_start = word_end; *word_end++ = ch; } -done: words[words_len] = NULL; - *store_words_len = words_len; - *store_words_buf = words_buf; - return words; +done: + words[words_len] = NULL; + return (Words){ words, words_len, words_buf }; } /* @@ -375,7 +374,7 @@ Str_Match(const char *str, const char *pat) */ if (*pat == '[') { Boolean neg = pat[1] == '^'; - pat += 1 + neg; + pat += neg ? 2 : 1; for (;;) { if (*pat == ']' || *pat == 0) { @@ -423,107 +422,3 @@ Str_Match(const char *str, const char *pat) str++; } } - -/*- - *----------------------------------------------------------------------- - * Str_SYSVMatch -- - * Check word against pattern for a match (% is wild), - * - * Input: - * word Word to examine - * pattern Pattern to examine against - * len Number of characters to substitute - * - * Results: - * Returns the beginning position of a match or null. The number - * of characters matched is returned in len. - * - * Side Effects: - * None - * - *----------------------------------------------------------------------- - */ -char * -Str_SYSVMatch(const char *word, const char *pattern, size_t *len, - Boolean *hasPercent) -{ - const char *p = pattern; - const char *w = word; - const char *m; - - *hasPercent = FALSE; - if (*p == '\0') { - /* Null pattern is the whole string */ - *len = strlen(w); - return UNCONST(w); - } - - if ((m = strchr(p, '%')) != NULL) { - *hasPercent = TRUE; - if (*w == '\0') { - /* empty word does not match pattern */ - return NULL; - } - /* check that the prefix matches */ - for (; p != m && *w && *w == *p; w++, p++) - continue; - - if (p != m) - return NULL; /* No match */ - - if (*++p == '\0') { - /* No more pattern, return the rest of the string */ - *len = strlen(w); - return UNCONST(w); - } - } - - m = w; - - /* Find a matching tail */ - do - if (strcmp(p, w) == 0) { - *len = w - m; - return UNCONST(m); - } - while (*w++ != '\0'); - - return NULL; -} - - -/*- - *----------------------------------------------------------------------- - * Str_SYSVSubst -- - * Substitute '%' on the pattern with len characters from src. - * If the pattern does not contain a '%' prepend len characters - * from src. - * - * Results: - * None - * - * Side Effects: - * Places result on buf - * - *----------------------------------------------------------------------- - */ -void -Str_SYSVSubst(Buffer *buf, char *pat, char *src, size_t len, - Boolean lhsHasPercent) -{ - char *m; - - if ((m = strchr(pat, '%')) != NULL && lhsHasPercent) { - /* Copy the prefix */ - Buf_AddBytes(buf, m - pat, pat); - /* skip the % */ - pat = m + 1; - } - if (m != NULL || !lhsHasPercent) { - /* Copy the pattern */ - Buf_AddBytes(buf, len, src); - } - - /* append the rest */ - Buf_AddBytes(buf, strlen(pat), pat); -} diff --git a/strlist.c b/strlist.c index 3fb2f7dbb6c8..905e78fd8caa 100644 --- a/strlist.c +++ b/strlist.c @@ -1,4 +1,4 @@ -/* $NetBSD: strlist.c,v 1.4 2009/01/24 11:59:39 dsl Exp $ */ +/* $NetBSD: strlist.c,v 1.6 2020/08/25 17:37:09 rillig Exp $ */ /*- * Copyright (c) 2008 - 2009 The NetBSD Foundation, Inc. @@ -33,18 +33,16 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: strlist.c,v 1.4 2009/01/24 11:59:39 dsl Exp $"; +static char rcsid[] = "$NetBSD: strlist.c,v 1.6 2020/08/25 17:37:09 rillig Exp $"; #else #include <sys/cdefs.h> #ifndef lint -__RCSID("$NetBSD: strlist.c,v 1.4 2009/01/24 11:59:39 dsl Exp $"); +__RCSID("$NetBSD: strlist.c,v 1.6 2020/08/25 17:37:09 rillig Exp $"); #endif /* not lint */ #endif -#include <stddef.h> -#include <stdlib.h> +#include "make.h" #include "strlist.h" -#include "make_malloc.h" void strlist_init(strlist_t *sl) @@ -76,18 +74,18 @@ strlist_add_str(strlist_t *sl, char *str, unsigned int info) strlist_item_t *items; if (str == NULL) - return; + return; n = sl->sl_num + 1; sl->sl_num = n; items = sl->sl_items; if (n >= sl->sl_max) { - items = bmake_realloc(items, (n + 7) * sizeof *sl->sl_items); - sl->sl_items = items; - sl->sl_max = n + 6; + items = bmake_realloc(items, (n + 7) * sizeof *sl->sl_items); + sl->sl_items = items; + sl->sl_max = n + 6; } items += n - 1; items->si_str = str; items->si_info = info; - items[1].si_str = NULL; /* STRLIST_FOREACH() terminator */ + items[1].si_str = NULL; /* STRLIST_FOREACH() terminator */ } diff --git a/strlist.h b/strlist.h index 2fc049e8643a..551317c090f3 100644 --- a/strlist.h +++ b/strlist.h @@ -1,4 +1,4 @@ -/* $NetBSD: strlist.h,v 1.3 2009/01/16 21:15:34 dsl Exp $ */ +/* $NetBSD: strlist.h,v 1.4 2020/08/13 03:54:57 rillig Exp $ */ /*- * Copyright (c) 2008 - 2009 The NetBSD Foundation, Inc. @@ -32,8 +32,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _STRLIST_H -#define _STRLIST_H +#ifndef MAKE_STRLIST_H +#define MAKE_STRLIST_H typedef struct { char *si_str; @@ -59,4 +59,4 @@ void strlist_add_str(strlist_t *, char *, unsigned int); if ((sl)->sl_items != NULL) \ for (index = 0; (v = strlist_str(sl, index)) != NULL; index++) -#endif /* _STRLIST_H */ +#endif /* MAKE_STRLIST_H */ @@ -1,4 +1,4 @@ -/* $NetBSD: suff.c,v 1.88 2020/07/03 08:02:55 rillig Exp $ */ +/* $NetBSD: suff.c,v 1.142 2020/08/31 16:44:25 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -69,14 +69,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: suff.c,v 1.88 2020/07/03 08:02:55 rillig Exp $"; +static char rcsid[] = "$NetBSD: suff.c,v 1.142 2020/08/31 16:44:25 rillig Exp $"; #else #include <sys/cdefs.h> #ifndef lint #if 0 static char sccsid[] = "@(#)suff.c 8.4 (Berkeley) 3/21/94"; #else -__RCSID("$NetBSD: suff.c,v 1.88 2020/07/03 08:02:55 rillig Exp $"); +__RCSID("$NetBSD: suff.c,v 1.142 2020/08/31 16:44:25 rillig Exp $"); #endif #endif /* not lint */ #endif @@ -135,12 +135,21 @@ __RCSID("$NetBSD: suff.c,v 1.88 2020/07/03 08:02:55 rillig Exp $"); * order to find the node. */ -#include <assert.h> -#include <stdio.h> #include "make.h" -#include "hash.h" #include "dir.h" +#define SUFF_DEBUG0(fmt) \ + if (!DEBUG(SUFF)) (void) 0; else fprintf(debug_file, fmt) + +#define SUFF_DEBUG1(fmt, arg1) \ + if (!DEBUG(SUFF)) (void) 0; else fprintf(debug_file, fmt, arg1) + +#define SUFF_DEBUG2(fmt, arg1, arg2) \ + if (!DEBUG(SUFF)) (void) 0; else fprintf(debug_file, fmt, arg1, arg2) + +#define SUFF_DEBUG3(fmt, arg1, arg2, arg3) \ + if (!DEBUG(SUFF)) (void) 0; else fprintf(debug_file, fmt, arg1, arg2, arg3) + static Lst sufflist; /* Lst of suffixes */ #ifdef CLEANUP static Lst suffClean; /* Lst of suffixes to be cleaned */ @@ -150,16 +159,23 @@ static Lst transforms; /* Lst of transformation rules */ static int sNum = 0; /* Counter for assigning suffix numbers */ +typedef enum { + SUFF_INCLUDE = 0x01, /* One which is #include'd */ + SUFF_LIBRARY = 0x02, /* One which contains a library */ + SUFF_NULL = 0x04 /* The empty suffix */ + /* XXX: Why is SUFF_NULL needed? Wouldn't nameLen == 0 mean the same? */ +} SuffFlags; + +ENUM_FLAGS_RTTI_3(SuffFlags, + SUFF_INCLUDE, SUFF_LIBRARY, SUFF_NULL); + /* * Structure describing an individual suffix. */ -typedef struct _Suff { - char *name; /* The suffix itself */ - int nameLen; /* Length of the suffix */ - short flags; /* Type of suffix */ -#define SUFF_INCLUDE 0x01 /* One which is #include'd */ -#define SUFF_LIBRARY 0x02 /* One which contains a library */ -#define SUFF_NULL 0x04 /* The empty suffix */ +typedef struct Suff { + char *name; /* The suffix itself, such as ".c" */ + int nameLen; /* Length of the name, to avoid strlen calls */ + SuffFlags flags; /* Type of suffix */ Lst searchPath; /* The path along which files of this suffix * may be found */ int sNum; /* The suffix number */ @@ -170,14 +186,6 @@ typedef struct _Suff { } Suff; /* - * for SuffSuffIsSuffix - */ -typedef struct { - char *ename; /* The end of the name */ - int len; /* Length of the name */ -} SuffixCmpData; - -/* * Structure used in the search for implied sources. */ typedef struct _Src { @@ -213,12 +221,6 @@ static Suff *emptySuff; /* The empty suffix required for POSIX * single-suffix transformation rules */ -static const char *SuffStrIsPrefix(const char *, const char *); -static char *SuffSuffIsSuffix(const Suff *, const SuffixCmpData *); -static int SuffSuffIsSuffixP(const void *, const void *); -static int SuffSuffHasNameP(const void *, const void *); -static int SuffSuffIsPrefix(const void *, const void *); -static int SuffGNHasNameP(const void *, const void *); static void SuffUnRef(void *, void *); static void SuffFree(void *); static void SuffInsert(Lst, Suff *); @@ -227,10 +229,7 @@ static Boolean SuffParseTransform(char *, Suff **, Suff **); static int SuffRebuildGraph(void *, void *); static int SuffScanTargets(void *, void *); static int SuffAddSrc(void *, void *); -static int SuffRemoveSrc(Lst); static void SuffAddLevel(Lst, Src *); -static Src *SuffFindThem(Lst, Lst); -static Src *SuffFindCmds(Src *, Lst); static void SuffExpandChildren(LstNode, GNode *); static void SuffExpandWildcards(LstNode, GNode *); static Boolean SuffApplyTransform(GNode *, GNode *, Suff *, Suff *); @@ -269,35 +268,33 @@ SuffStrIsPrefix(const char *pref, const char *str) return *pref ? NULL : str; } -/*- - *----------------------------------------------------------------------- - * SuffSuffIsSuffix -- - * See if suff is a suffix of str. sd->ename should point to THE END - * of the string to check. (THE END == the null byte) +typedef struct { + char *ename; /* The end of the name */ + int len; /* Length of the name */ +} SuffSuffGetSuffixArgs; + +/* See if suff is a suffix of str. str->ename should point to THE END + * of the string to check. (THE END == the null byte) * * Input: * s possible suffix - * sd string to examine + * str string to examine * * Results: * NULL if it ain't, pointer to character in str before suffix if * it is. - * - * Side Effects: - * None - *----------------------------------------------------------------------- */ static char * -SuffSuffIsSuffix(const Suff *s, const SuffixCmpData *sd) +SuffSuffGetSuffix(const Suff *s, const SuffSuffGetSuffixArgs *str) { char *p1; /* Pointer into suffix name */ char *p2; /* Pointer into string being examined */ - if (sd->len < s->nameLen) + if (str->len < s->nameLen) return NULL; /* this string is shorter than the suffix */ p1 = s->name + s->nameLen; - p2 = sd->ename; + p2 = str->ename; while (p1 >= s->name && *p1 == *p2) { p1--; @@ -307,126 +304,55 @@ SuffSuffIsSuffix(const Suff *s, const SuffixCmpData *sd) return p1 == s->name - 1 ? p2 : NULL; } -/*- - *----------------------------------------------------------------------- - * SuffSuffIsSuffixP -- - * Predicate form of SuffSuffIsSuffix. Passed as the callback function - * to Lst_Find. - * - * Results: - * 0 if the suffix is the one desired, non-zero if not. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -static int -SuffSuffIsSuffixP(const void *s, const void *sd) +/* Predicate form of SuffSuffGetSuffix, for Lst_Find. */ +static Boolean +SuffSuffIsSuffix(const void *s, const void *sd) { - return !SuffSuffIsSuffix(s, sd); + return SuffSuffGetSuffix(s, sd) != NULL; } -/*- - *----------------------------------------------------------------------- - * SuffSuffHasNameP -- - * Callback procedure for finding a suffix based on its name. Used by - * Suff_GetPath. - * - * Input: - * s Suffix to check - * sd Desired name - * - * Results: - * 0 if the suffix is of the given name. non-zero otherwise. - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -static int -SuffSuffHasNameP(const void *s, const void *sname) +/* See if the suffix has the desired name. */ +static Boolean +SuffSuffHasName(const void *s, const void *desiredName) { - return strcmp(sname, ((const Suff *)s)->name); + return strcmp(((const Suff *)s)->name, desiredName) == 0; } -/*- - *----------------------------------------------------------------------- - * SuffSuffIsPrefix -- - * See if the suffix described by s is a prefix of the string. Care - * must be taken when using this to search for transformations and - * what-not, since there could well be two suffixes, one of which - * is a prefix of the other... - * - * Input: - * s suffix to compare - * str string to examine - * - * Results: - * 0 if s is a prefix of str. non-zero otherwise - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -static int +/* See if the suffix name is a prefix of the string. Care must be taken when + * using this to search for transformations and what-not, since there could + * well be two suffixes, one of which is a prefix of the other... */ +static Boolean SuffSuffIsPrefix(const void *s, const void *str) { - return SuffStrIsPrefix(((const Suff *)s)->name, str) == NULL; + return SuffStrIsPrefix(((const Suff *)s)->name, str) != NULL; } -/*- - *----------------------------------------------------------------------- - * SuffGNHasNameP -- - * See if the graph node has the desired name - * - * Input: - * gn current node we're looking at - * name name we're looking for - * - * Results: - * 0 if it does. non-zero if it doesn't - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -static int -SuffGNHasNameP(const void *gn, const void *name) +/* See if the graph node has the desired name. */ +static Boolean +SuffGNHasName(const void *gn, const void *desiredName) { - return strcmp(name, ((const GNode *)gn)->name); + return strcmp(((const GNode *)gn)->name, desiredName) == 0; } - /*********** Maintenance Functions ************/ + /*********** Maintenance Functions ************/ static void SuffUnRef(void *lp, void *sp) { Lst l = (Lst) lp; - LstNode ln = Lst_Member(l, sp); + LstNode ln = Lst_FindDatum(l, sp); if (ln != NULL) { Lst_Remove(l, ln); ((Suff *)sp)->refCount--; } } -/*- - *----------------------------------------------------------------------- - * SuffFree -- - * Free up all memory associated with the given suffix structure. - * - * Results: - * none - * - * Side Effects: - * the suffix entry is detroyed - *----------------------------------------------------------------------- - */ +/* Free up all memory associated with the given suffix structure. */ static void SuffFree(void *sp) { - Suff *s = (Suff *)sp; + Suff *s = (Suff *)sp; if (s == suffNull) suffNull = NULL; @@ -441,28 +367,16 @@ SuffFree(void *sp) s->refCount); #endif - Lst_Destroy(s->ref, NULL); - Lst_Destroy(s->children, NULL); - Lst_Destroy(s->parents, NULL); + Lst_Free(s->ref); + Lst_Free(s->children); + Lst_Free(s->parents); Lst_Destroy(s->searchPath, Dir_Destroy); free(s->name); free(s); } -/*- - *----------------------------------------------------------------------- - * SuffRemove -- - * Remove the suffix into the list - * - * Results: - * None - * - * Side Effects: - * The reference count for the suffix is decremented and the - * suffix is possibly freed - *----------------------------------------------------------------------- - */ +/* Remove the suffix from the list, and free if it is otherwise unused. */ static void SuffRemove(Lst l, Suff *s) { @@ -472,23 +386,12 @@ SuffRemove(Lst l, Suff *s) SuffFree(s); } } - -/*- - *----------------------------------------------------------------------- - * SuffInsert -- - * Insert the suffix into the list keeping the list ordered by suffix - * numbers. + +/* Insert the suffix into the list keeping the list ordered by suffix numbers. * * Input: * l the list where in s should be inserted * s the suffix to insert - * - * Results: - * None - * - * Side Effects: - * The reference count of the suffix is incremented - *----------------------------------------------------------------------- */ static void SuffInsert(Lst l, Suff *s) @@ -496,100 +399,83 @@ SuffInsert(Lst l, Suff *s) LstNode ln; /* current element in l we're examining */ Suff *s2 = NULL; /* the suffix descriptor in this element */ - if (Lst_Open(l) == FAILURE) { - return; - } + Lst_Open(l); while ((ln = Lst_Next(l)) != NULL) { - s2 = (Suff *)Lst_Datum(ln); + s2 = LstNode_Datum(ln); if (s2->sNum >= s->sNum) { break; } } - Lst_Close(l); - if (DEBUG(SUFF)) { - fprintf(debug_file, "inserting %s(%d)...", s->name, s->sNum); - } + + SUFF_DEBUG2("inserting %s(%d)...", s->name, s->sNum); + if (ln == NULL) { - if (DEBUG(SUFF)) { - fprintf(debug_file, "at end of list\n"); - } - (void)Lst_AtEnd(l, s); + SUFF_DEBUG0("at end of list\n"); + Lst_Append(l, s); s->refCount++; - (void)Lst_AtEnd(s->ref, l); + Lst_Append(s->ref, l); } else if (s2->sNum != s->sNum) { - if (DEBUG(SUFF)) { - fprintf(debug_file, "before %s(%d)\n", s2->name, s2->sNum); - } - (void)Lst_InsertBefore(l, ln, s); + SUFF_DEBUG2("before %s(%d)\n", s2->name, s2->sNum); + Lst_InsertBefore(l, ln, s); s->refCount++; - (void)Lst_AtEnd(s->ref, l); - } else if (DEBUG(SUFF)) { - fprintf(debug_file, "already there\n"); + Lst_Append(s->ref, l); + } else { + SUFF_DEBUG0("already there\n"); } } -/*- - *----------------------------------------------------------------------- - * Suff_ClearSuffixes -- - * This is gross. Nuke the list of suffixes but keep all transformation - * rules around. The transformation graph is destroyed in this process, - * but we leave the list of rules so when a new graph is formed the rules - * will remain. - * This function is called from the parse module when a - * .SUFFIXES:\n line is encountered. - * - * Results: - * none - * - * Side Effects: - * the sufflist and its graph nodes are destroyed - *----------------------------------------------------------------------- - */ +static Suff * +SuffNew(const char *name) +{ + Suff *s = bmake_malloc(sizeof(Suff)); + + s->name = bmake_strdup(name); + s->nameLen = strlen(s->name); + s->searchPath = Lst_Init(); + s->children = Lst_Init(); + s->parents = Lst_Init(); + s->ref = Lst_Init(); + s->sNum = sNum++; + s->flags = 0; + s->refCount = 1; + + return s; +} + +/* This is gross. Nuke the list of suffixes but keep all transformation + * rules around. The transformation graph is destroyed in this process, but + * we leave the list of rules so when a new graph is formed the rules will + * remain. This function is called from the parse module when a .SUFFIXES:\n + * line is encountered. */ void Suff_ClearSuffixes(void) { #ifdef CLEANUP - Lst_Concat(suffClean, sufflist, LST_CONCLINK); + Lst_MoveAll(suffClean, sufflist); #endif - sufflist = Lst_Init(FALSE); + sufflist = Lst_Init(); sNum = 0; if (suffNull) SuffFree(suffNull); - emptySuff = suffNull = bmake_malloc(sizeof(Suff)); + emptySuff = suffNull = SuffNew(""); - suffNull->name = bmake_strdup(""); - suffNull->nameLen = 0; - suffNull->searchPath = Lst_Init(FALSE); Dir_Concat(suffNull->searchPath, dirSearchPath); - suffNull->children = Lst_Init(FALSE); - suffNull->parents = Lst_Init(FALSE); - suffNull->ref = Lst_Init(FALSE); - suffNull->sNum = sNum++; suffNull->flags = SUFF_NULL; - suffNull->refCount = 1; } -/*- - *----------------------------------------------------------------------- - * SuffParseTransform -- - * Parse a transformation string to find its two component suffixes. +/* Parse a transformation string to find its two component suffixes. * * Input: * str String being parsed - * srcPtr Place to store source of trans. - * targPtr Place to store target of trans. + * out_src Place to store source of trans. + * out_targ Place to store target of trans. * * Results: - * TRUE if the string is a valid transformation and FALSE otherwise. - * - * Side Effects: - * The passed pointers are overwritten. - * - *----------------------------------------------------------------------- + * TRUE if the string is a valid transformation, FALSE otherwise. */ static Boolean -SuffParseTransform(char *str, Suff **srcPtr, Suff **targPtr) +SuffParseTransform(char *str, Suff **out_src, Suff **out_targ) { LstNode srcLn; /* element in suffix list of trans source*/ Suff *src; /* Source of transformation */ @@ -611,10 +497,10 @@ SuffParseTransform(char *str, Suff **srcPtr, Suff **targPtr) */ for (;;) { if (srcLn == NULL) { - srcLn = Lst_Find(sufflist, str, SuffSuffIsPrefix); + srcLn = Lst_Find(sufflist, SuffSuffIsPrefix, str); } else { - srcLn = Lst_FindFrom(sufflist, Lst_Succ(srcLn), str, - SuffSuffIsPrefix); + srcLn = Lst_FindFrom(sufflist, LstNode_Next(srcLn), + SuffSuffIsPrefix, str); } if (srcLn == NULL) { /* @@ -630,45 +516,30 @@ SuffParseTransform(char *str, Suff **srcPtr, Suff **targPtr) * * XXX: Use emptySuff over suffNull? */ - *srcPtr = single; - *targPtr = suffNull; + *out_src = single; + *out_targ = suffNull; return TRUE; } return FALSE; } - src = (Suff *)Lst_Datum(srcLn); + src = LstNode_Datum(srcLn); str2 = str + src->nameLen; if (*str2 == '\0') { single = src; singleLn = srcLn; } else { - targLn = Lst_Find(sufflist, str2, SuffSuffHasNameP); + targLn = Lst_Find(sufflist, SuffSuffHasName, str2); if (targLn != NULL) { - *srcPtr = src; - *targPtr = (Suff *)Lst_Datum(targLn); + *out_src = src; + *out_targ = LstNode_Datum(targLn); return TRUE; } } } } -/*- - *----------------------------------------------------------------------- - * Suff_IsTransform -- - * Return TRUE if the given string is a transformation rule - * - * - * Input: - * str string to check - * - * Results: - * TRUE if the string is a concatenation of two known suffixes. - * FALSE otherwise - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ +/* Return TRUE if the given string is a transformation rule, that is, a + * concatenation of two known suffixes. */ Boolean Suff_IsTransform(char *str) { @@ -677,39 +548,35 @@ Suff_IsTransform(char *str) return SuffParseTransform(str, &src, &targ); } -/*- - *----------------------------------------------------------------------- - * Suff_AddTransform -- - * Add the transformation rule described by the line to the - * list of rules and place the transformation itself in the graph +/* Add the transformation rule described by the line to the list of rules + * and place the transformation itself in the graph. * + * The node is placed on the end of the transforms Lst and links are made + * between the two suffixes mentioned in the target name. + * Input: * line name of transformation to add * * Results: * The node created for the transformation in the transforms list - * - * Side Effects: - * The node is placed on the end of the transforms Lst and links are - * made between the two suffixes mentioned in the target name - *----------------------------------------------------------------------- */ GNode * Suff_AddTransform(char *line) { GNode *gn; /* GNode of transformation rule */ Suff *s, /* source suffix */ - *t; /* target suffix */ + *t; /* target suffix */ LstNode ln; /* Node for existing transformation */ + Boolean ok; - ln = Lst_Find(transforms, line, SuffGNHasNameP); + ln = Lst_Find(transforms, SuffGNHasName, line); if (ln == NULL) { /* * Make a new graph node for the transformation. It will be filled in * by the Parse module. */ gn = Targ_NewGN(line); - (void)Lst_AtEnd(transforms, gn); + Lst_Append(transforms, gn); } else { /* * New specification for transformation rule. Just nuke the old list @@ -717,58 +584,50 @@ Suff_AddTransform(char *line) * free the commands themselves, because a given command can be * attached to several different transformations. */ - gn = (GNode *)Lst_Datum(ln); - Lst_Destroy(gn->commands, NULL); - Lst_Destroy(gn->children, NULL); - gn->commands = Lst_Init(FALSE); - gn->children = Lst_Init(FALSE); + gn = LstNode_Datum(ln); + Lst_Free(gn->commands); + Lst_Free(gn->children); + gn->commands = Lst_Init(); + gn->children = Lst_Init(); } gn->type = OP_TRANSFORM; - (void)SuffParseTransform(line, &s, &t); + ok = SuffParseTransform(line, &s, &t); + assert(ok); + (void)ok; /* * link the two together in the proper relationship and order */ - if (DEBUG(SUFF)) { - fprintf(debug_file, "defining transformation from `%s' to `%s'\n", + SUFF_DEBUG2("defining transformation from `%s' to `%s'\n", s->name, t->name); - } SuffInsert(t->children, s); SuffInsert(s->parents, t); return gn; } -/*- - *----------------------------------------------------------------------- - * Suff_EndTransform -- - * Handle the finish of a transformation definition, removing the - * transformation from the graph if it has neither commands nor - * sources. This is a callback procedure for the Parse module via - * Lst_ForEach +/* Handle the finish of a transformation definition, removing the + * transformation from the graph if it has neither commands nor sources. + * This is a callback procedure for the Parse module via Lst_ForEach. + * + * If the node has no commands or children, the children and parents lists + * of the affected suffixes are altered. * * Input: * gnp Node for transformation - * dummy Node for transformation * * Results: - * === 0 - * - * Side Effects: - * If the node has no commands or children, the children and parents - * lists of the affected suffixes are altered. - * - *----------------------------------------------------------------------- + * 0, so that Lst_ForEach continues */ int Suff_EndTransform(void *gnp, void *dummy MAKE_ATTR_UNUSED) { GNode *gn = (GNode *)gnp; - if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (gn->cohorts)) - gn = (GNode *)Lst_Datum(Lst_Last(gn->cohorts)); + if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(gn->cohorts)) + gn = LstNode_Datum(Lst_Last(gn->cohorts)); if ((gn->type & OP_TRANSFORM) && Lst_IsEmpty(gn->commands) && Lst_IsEmpty(gn->children)) { @@ -781,10 +640,8 @@ Suff_EndTransform(void *gnp, void *dummy MAKE_ATTR_UNUSED) if (SuffParseTransform(gn->name, &s, &t)) { Lst p; - if (DEBUG(SUFF)) { - fprintf(debug_file, "deleting transformation from `%s' to `%s'\n", - s->name, t->name); - } + SUFF_DEBUG2("deleting transformation from `%s' to `%s'\n", + s->name, t->name); /* * Store s->parents because s could be deleted in SuffRemove @@ -806,35 +663,28 @@ Suff_EndTransform(void *gnp, void *dummy MAKE_ATTR_UNUSED) */ SuffRemove(p, t); } - } else if ((gn->type & OP_TRANSFORM) && DEBUG(SUFF)) { - fprintf(debug_file, "transformation %s complete\n", gn->name); + } else if (gn->type & OP_TRANSFORM) { + SUFF_DEBUG1("transformation %s complete\n", gn->name); } return 0; } -/*- - *----------------------------------------------------------------------- - * SuffRebuildGraph -- - * Called from Suff_AddSuffix via Lst_ForEach to search through the - * list of existing transformation rules and rebuild the transformation - * graph when it has been destroyed by Suff_ClearSuffixes. If the - * given rule is a transformation involving this suffix and another, - * existing suffix, the proper relationship is established between - * the two. +/* Called from Suff_AddSuffix via Lst_ForEach to search through the list of + * existing transformation rules and rebuild the transformation graph when + * it has been destroyed by Suff_ClearSuffixes. If the given rule is a + * transformation involving this suffix and another, existing suffix, the + * proper relationship is established between the two. + * + * The appropriate links will be made between this suffix and others if + * transformation rules exist for it. * * Input: * transformp Transformation to test * sp Suffix to rebuild * * Results: - * Always 0. - * - * Side Effects: - * The appropriate links will be made between this suffix and - * others if transformation rules exist for it. - * - *----------------------------------------------------------------------- + * 0, so that Lst_ForEach continues */ static int SuffRebuildGraph(void *transformp, void *sp) @@ -844,20 +694,20 @@ SuffRebuildGraph(void *transformp, void *sp) char *cp; LstNode ln; Suff *s2; - SuffixCmpData sd; + SuffSuffGetSuffixArgs sd; /* * First see if it is a transformation from this suffix. */ cp = UNCONST(SuffStrIsPrefix(s->name, transform->name)); if (cp != NULL) { - ln = Lst_Find(sufflist, cp, SuffSuffHasNameP); + ln = Lst_Find(sufflist, SuffSuffHasName, cp); if (ln != NULL) { /* * Found target. Link in and return, since it can't be anything * else. */ - s2 = (Suff *)Lst_Datum(ln); + s2 = LstNode_Datum(ln); SuffInsert(s2->children, s); SuffInsert(s->parents, s2); return 0; @@ -869,13 +719,13 @@ SuffRebuildGraph(void *transformp, void *sp) */ sd.len = strlen(transform->name); sd.ename = transform->name + sd.len; - cp = SuffSuffIsSuffix(s, &sd); + cp = SuffSuffGetSuffix(s, &sd); if (cp != NULL) { /* * Null-terminate the source suffix in order to find it. */ cp[1] = '\0'; - ln = Lst_Find(sufflist, transform->name, SuffSuffHasNameP); + ln = Lst_Find(sufflist, SuffSuffHasName, transform->name); /* * Replace the start of the target suffix */ @@ -884,7 +734,7 @@ SuffRebuildGraph(void *transformp, void *sp) /* * Found it -- establish the proper relationship */ - s2 = (Suff *)Lst_Datum(ln); + s2 = LstNode_Datum(ln); SuffInsert(s->children, s2); SuffInsert(s2->parents, s); } @@ -892,22 +742,16 @@ SuffRebuildGraph(void *transformp, void *sp) return 0; } -/*- - *----------------------------------------------------------------------- - * SuffScanTargets -- - * Called from Suff_AddSuffix via Lst_ForEach to search through the - * list of existing targets and find if any of the existing targets - * can be turned into a transformation rule. +/* Called from Suff_AddSuffix via Lst_ForEach to search through the list of + * existing targets and find if any of the existing targets can be turned + * into a transformation rule. + * + * If such a target is found and the target is the current main target, the + * main target is set to NULL and the next target examined (if that exists) + * becomes the main target. * * Results: * 1 if a new main target has been selected, 0 otherwise. - * - * Side Effects: - * If such a target is found and the target is the current main - * target, the main target is set to NULL and the next target - * examined (if that exists) becomes the main target. - * - *----------------------------------------------------------------------- */ static int SuffScanTargets(void *targetp, void *gsp) @@ -923,7 +767,7 @@ SuffScanTargets(void *targetp, void *gsp) return 1; } - if ((unsigned int)target->type == OP_TRANSFORM) + if (target->type == OP_TRANSFORM) return 0; if ((ptr = strstr(target->name, gs->s->name)) == NULL || @@ -936,63 +780,43 @@ SuffScanTargets(void *targetp, void *gsp) *gs->gn = NULL; Targ_SetMain(NULL); } - Lst_Destroy(target->children, NULL); - target->children = Lst_Init(FALSE); + Lst_Free(target->children); + target->children = Lst_Init(); target->type = OP_TRANSFORM; /* * link the two together in the proper relationship and order */ - if (DEBUG(SUFF)) { - fprintf(debug_file, "defining transformation from `%s' to `%s'\n", - s->name, t->name); - } + SUFF_DEBUG2("defining transformation from `%s' to `%s'\n", + s->name, t->name); SuffInsert(t->children, s); SuffInsert(s->parents, t); } return 0; } -/*- - *----------------------------------------------------------------------- - * Suff_AddSuffix -- - * Add the suffix in string to the end of the list of known suffixes. - * Should we restructure the suffix graph? Make doesn't... +/* Add the suffix to the end of the list of known suffixes. + * Should we restructure the suffix graph? Make doesn't... * - * Input: - * str the name of the suffix to add + * A GNode is created for the suffix and a Suff structure is created and + * added to the suffixes list unless the suffix was already known. + * The mainNode passed can be modified if a target mutated into a + * transform and that target happened to be the main target. * - * Results: - * None - * - * Side Effects: - * A GNode is created for the suffix and a Suff structure is created and - * added to the suffixes list unless the suffix was already known. - * The mainNode passed can be modified if a target mutated into a - * transform and that target happened to be the main target. - *----------------------------------------------------------------------- + * Input: + * name the name of the suffix to add */ void -Suff_AddSuffix(char *str, GNode **gn) +Suff_AddSuffix(const char *name, GNode **gn) { Suff *s; /* new suffix descriptor */ LstNode ln; GNodeSuff gs; - ln = Lst_Find(sufflist, str, SuffSuffHasNameP); + ln = Lst_Find(sufflist, SuffSuffHasName, name); if (ln == NULL) { - s = bmake_malloc(sizeof(Suff)); - - s->name = bmake_strdup(str); - s->nameLen = strlen(s->name); - s->searchPath = Lst_Init(FALSE); - s->children = Lst_Init(FALSE); - s->parents = Lst_Init(FALSE); - s->ref = Lst_Init(FALSE); - s->sNum = sNum++; - s->flags = 0; - s->refCount = 1; - - (void)Lst_AtEnd(sufflist, s); + s = SuffNew(name); + + Lst_Append(sufflist, s); /* * We also look at our existing targets list to see if adding * this suffix will make one of our current targets mutate into @@ -1011,51 +835,30 @@ Suff_AddSuffix(char *str, GNode **gn) } } -/*- - *----------------------------------------------------------------------- - * Suff_GetPath -- - * Return the search path for the given suffix, if it's defined. - * - * Results: - * The searchPath for the desired suffix or NULL if the suffix isn't - * defined. - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ +/* Return the search path for the given suffix, or NULL. */ Lst Suff_GetPath(char *sname) { LstNode ln; Suff *s; - ln = Lst_Find(sufflist, sname, SuffSuffHasNameP); + ln = Lst_Find(sufflist, SuffSuffHasName, sname); if (ln == NULL) { return NULL; } else { - s = (Suff *)Lst_Datum(ln); + s = LstNode_Datum(ln); return s->searchPath; } } -/*- - *----------------------------------------------------------------------- - * Suff_DoPaths -- - * Extend the search paths for all suffixes to include the default - * search path. +/* Extend the search paths for all suffixes to include the default search + * path. * - * Results: - * None. - * - * Side Effects: - * The searchPath field of all the suffixes is extended by the - * directories in dirSearchPath. If paths were specified for the - * ".h" suffix, the directories are stuffed into a global variable - * called ".INCLUDES" with each directory preceded by a -I. The same - * is done for the ".a" suffix, except the variable is called - * ".LIBS" and the flag is -L. - *----------------------------------------------------------------------- + * The searchPath field of all the suffixes is extended by the directories + * in dirSearchPath. If paths were specified for the ".h" suffix, the + * directories are stuffed into a global variable called ".INCLUDES" with + * each directory preceded by a -I. The same is done for the ".a" suffix, + * except the variable is called ".LIBS" and the flag is -L. */ void Suff_DoPaths(void) @@ -1066,16 +869,14 @@ Suff_DoPaths(void) Lst inIncludes; /* Cumulative .INCLUDES path */ Lst inLibs; /* Cumulative .LIBS path */ - if (Lst_Open(sufflist) == FAILURE) { - return; - } - inIncludes = Lst_Init(FALSE); - inLibs = Lst_Init(FALSE); + inIncludes = Lst_Init(); + inLibs = Lst_Init(); + Lst_Open(sufflist); while ((ln = Lst_Next(sufflist)) != NULL) { - s = (Suff *)Lst_Datum(ln); - if (!Lst_IsEmpty (s->searchPath)) { + s = LstNode_Datum(ln); + if (!Lst_IsEmpty(s->searchPath)) { #ifdef INCLUDES if (s->flags & SUFF_INCLUDE) { Dir_Concat(inIncludes, s->searchPath); @@ -1089,9 +890,10 @@ Suff_DoPaths(void) Dir_Concat(s->searchPath, dirSearchPath); } else { Lst_Destroy(s->searchPath, Dir_Destroy); - s->searchPath = Lst_Duplicate(dirSearchPath, Dir_CopyDir); + s->searchPath = Lst_Copy(dirSearchPath, Dir_CopyDir); } } + Lst_Close(sufflist); Var_Set(".INCLUDES", ptr = Dir_MakeFlags("-I", inIncludes), VAR_GLOBAL); free(ptr); @@ -1100,27 +902,15 @@ Suff_DoPaths(void) Lst_Destroy(inIncludes, Dir_Destroy); Lst_Destroy(inLibs, Dir_Destroy); - - Lst_Close(sufflist); } -/*- - *----------------------------------------------------------------------- - * Suff_AddInclude -- - * Add the given suffix as a type of file which gets included. - * Called from the parse module when a .INCLUDES line is parsed. - * The suffix must have already been defined. +/* Add the given suffix as a type of file which gets included. + * Called from the parse module when a .INCLUDES line is parsed. + * The suffix must have already been defined. + * The SUFF_INCLUDE bit is set in the suffix's flags field. * * Input: * sname Name of the suffix to mark - * - * Results: - * None. - * - * Side Effects: - * The SUFF_INCLUDE bit is set in the suffix's flags field - * - *----------------------------------------------------------------------- */ void Suff_AddInclude(char *sname) @@ -1128,64 +918,46 @@ Suff_AddInclude(char *sname) LstNode ln; Suff *s; - ln = Lst_Find(sufflist, sname, SuffSuffHasNameP); + ln = Lst_Find(sufflist, SuffSuffHasName, sname); if (ln != NULL) { - s = (Suff *)Lst_Datum(ln); + s = LstNode_Datum(ln); s->flags |= SUFF_INCLUDE; } } -/*- - *----------------------------------------------------------------------- - * Suff_AddLib -- - * Add the given suffix as a type of file which is a library. - * Called from the parse module when parsing a .LIBS line. The - * suffix must have been defined via .SUFFIXES before this is - * called. +/* Add the given suffix as a type of file which is a library. + * Called from the parse module when parsing a .LIBS line. + * The suffix must have been defined via .SUFFIXES before this is called. + * The SUFF_LIBRARY bit is set in the suffix's flags field. * * Input: * sname Name of the suffix to mark - * - * Results: - * None. - * - * Side Effects: - * The SUFF_LIBRARY bit is set in the suffix's flags field - * - *----------------------------------------------------------------------- */ void -Suff_AddLib(char *sname) +Suff_AddLib(const char *sname) { LstNode ln; Suff *s; - ln = Lst_Find(sufflist, sname, SuffSuffHasNameP); + ln = Lst_Find(sufflist, SuffSuffHasName, sname); if (ln != NULL) { - s = (Suff *)Lst_Datum(ln); + s = LstNode_Datum(ln); s->flags |= SUFF_LIBRARY; } } - /********** Implicit Source Search Functions *********/ + /********** Implicit Source Search Functions *********/ -/*- - *----------------------------------------------------------------------- - * SuffAddSrc -- - * Add a suffix as a Src structure to the given list with its parent - * being the given Src structure. If the suffix is the null suffix, - * the prefix is used unaltered as the file name in the Src structure. +/* Add a suffix as a Src structure to the given list with its parent + * being the given Src structure. If the suffix is the null suffix, + * the prefix is used unaltered as the file name in the Src structure. * * Input: * sp suffix for which to create a Src structure * lsp list and parent for the new Src * * Results: - * always returns 0 - * - * Side Effects: - * A Src structure is created and tacked onto the end of the list - *----------------------------------------------------------------------- + * 0, so that Lst_ForEach continues */ static int SuffAddSrc(void *sp, void *lsp) @@ -1212,17 +984,17 @@ SuffAddSrc(void *sp, void *lsp) s->refCount++; s2->children = 0; targ->children += 1; - (void)Lst_AtEnd(ls->l, s2); + Lst_Append(ls->l, s2); #ifdef DEBUG_SRC - s2->cp = Lst_Init(FALSE); - Lst_AtEnd(targ->cp, s2); + s2->cp = Lst_Init(); + Lst_Append(targ->cp, s2); fprintf(debug_file, "1 add %p %p to %p:", targ, s2, ls->l); Lst_ForEach(ls->l, PrintAddr, NULL); fprintf(debug_file, "\n"); #endif } s2 = bmake_malloc(sizeof(Src)); - s2->file = str_concat(targ->pref, s->name, 0); + s2->file = str_concat2(targ->pref, s->name); s2->pref = targ->pref; s2->parent = targ; s2->node = NULL; @@ -1230,10 +1002,10 @@ SuffAddSrc(void *sp, void *lsp) s->refCount++; s2->children = 0; targ->children += 1; - (void)Lst_AtEnd(ls->l, s2); + Lst_Append(ls->l, s2); #ifdef DEBUG_SRC - s2->cp = Lst_Init(FALSE); - Lst_AtEnd(targ->cp, s2); + s2->cp = Lst_Init(); + Lst_Append(targ->cp, s2); fprintf(debug_file, "2 add %p %p to %p:", targ, s2, ls->l); Lst_ForEach(ls->l, PrintAddr, NULL); fprintf(debug_file, "\n"); @@ -1242,21 +1014,11 @@ SuffAddSrc(void *sp, void *lsp) return 0; } -/*- - *----------------------------------------------------------------------- - * SuffAddLevel -- - * Add all the children of targ as Src structures to the given list +/* Add all the children of targ as Src structures to the given list. * * Input: * l list to which to add the new level * targ Src structure to use as the parent - * - * Results: - * None - * - * Side Effects: - * Lots of structures are created and added to the list - *----------------------------------------------------------------------- */ static void SuffAddLevel(Lst l, Src *targ) @@ -1269,44 +1031,31 @@ SuffAddLevel(Lst l, Src *targ) Lst_ForEach(targ->suff->children, SuffAddSrc, &ls); } -/*- - *---------------------------------------------------------------------- - * SuffRemoveSrc -- - * Free all src structures in list that don't have a reference count - * - * Results: - * Ture if an src was removed - * - * Side Effects: - * The memory is free'd. - *---------------------------------------------------------------------- - */ -static int +/* Free the first Src in the list that doesn't have a reference count. + * Return whether a Src was removed. */ +static Boolean SuffRemoveSrc(Lst l) { LstNode ln; Src *s; - int t = 0; - if (Lst_Open(l) == FAILURE) { - return 0; - } + Lst_Open(l); + #ifdef DEBUG_SRC fprintf(debug_file, "cleaning %lx: ", (unsigned long) l); Lst_ForEach(l, PrintAddr, NULL); fprintf(debug_file, "\n"); #endif - while ((ln = Lst_Next(l)) != NULL) { - s = (Src *)Lst_Datum(ln); + s = LstNode_Datum(ln); if (s->children == 0) { free(s->file); if (!s->parent) free(s->pref); else { #ifdef DEBUG_SRC - LstNode ln2 = Lst_Member(s->parent->cp, s); + LstNode ln2 = Lst_FindDatum(s->parent->cp, s); if (ln2 != NULL) Lst_Remove(s->parent->cp, ln2); #endif @@ -1314,11 +1063,10 @@ SuffRemoveSrc(Lst l) } #ifdef DEBUG_SRC fprintf(debug_file, "free: [l=%p] p=%p %d\n", l, s, s->children); - Lst_Destroy(s->cp, NULL); + Lst_Free(s->cp); #endif Lst_Remove(l, ln); free(s); - t |= 1; Lst_Close(l); return TRUE; } @@ -1333,23 +1081,16 @@ SuffRemoveSrc(Lst l) Lst_Close(l); - return t; + return FALSE; } -/*- - *----------------------------------------------------------------------- - * SuffFindThem -- - * Find the first existing file/target in the list srcs +/* Find the first existing file/target in the list srcs. * * Input: * srcs list of Src structures to search through * * Results: - * The lowest structure in the chain of transformations - * - * Side Effects: - * None - *----------------------------------------------------------------------- + * The lowest structure in the chain of transformations, or NULL. */ static Src * SuffFindThem(Lst srcs, Lst slst) @@ -1360,12 +1101,10 @@ SuffFindThem(Lst srcs, Lst slst) rs = NULL; - while (!Lst_IsEmpty (srcs)) { - s = (Src *)Lst_DeQueue(srcs); + while (!Lst_IsEmpty(srcs)) { + s = Lst_Dequeue(srcs); - if (DEBUG(SUFF)) { - fprintf(debug_file, "\ttrying %s...", s->file); - } + SUFF_DEBUG1("\ttrying %s...", s->file); /* * A file is considered to exist if either a node exists in the @@ -1388,51 +1127,41 @@ SuffFindThem(Lst srcs, Lst slst) break; } - if (DEBUG(SUFF)) { - fprintf(debug_file, "not there\n"); - } + SUFF_DEBUG0("not there\n"); SuffAddLevel(srcs, s); - Lst_AtEnd(slst, s); + Lst_Append(slst, s); } - if (DEBUG(SUFF) && rs) { - fprintf(debug_file, "got it\n"); + if (rs) { + SUFF_DEBUG0("got it\n"); } return rs; } -/*- - *----------------------------------------------------------------------- - * SuffFindCmds -- - * See if any of the children of the target in the Src structure is - * one from which the target can be transformed. If there is one, - * a Src structure is put together for it and returned. +/* See if any of the children of the target in the Src structure is one from + * which the target can be transformed. If there is one, a Src structure is + * put together for it and returned. * * Input: - * targ Src structure to play with + * targ Src to play with * * Results: - * The Src structure of the "winning" child, or NULL if no such beast. - * - * Side Effects: - * A Src structure may be allocated. - * - *----------------------------------------------------------------------- + * The Src of the "winning" child, or NULL. */ static Src * SuffFindCmds(Src *targ, Lst slst) { LstNode ln; /* General-purpose list node */ GNode *t, /* Target GNode */ - *s; /* Source GNode */ + *s; /* Source GNode */ int prefLen;/* The length of the defined prefix */ Suff *suff; /* Suffix on matching beastie */ Src *ret; /* Return value */ char *cp; t = targ->node; - (void)Lst_Open(t->children); + Lst_Open(t->children); prefLen = strlen(targ->pref); for (;;) { @@ -1441,7 +1170,7 @@ SuffFindCmds(Src *targ, Lst slst) Lst_Close(t->children); return NULL; } - s = (GNode *)Lst_Datum(ln); + s = LstNode_Datum(ln); if (s->type & OP_OPTIONAL && Lst_IsEmpty(t->commands)) { /* @@ -1466,7 +1195,7 @@ SuffFindCmds(Src *targ, Lst slst) * The node matches the prefix ok, see if it has a known * suffix. */ - ln = Lst_Find(sufflist, &cp[prefLen], SuffSuffHasNameP); + ln = Lst_Find(sufflist, SuffSuffHasName, &cp[prefLen]); if (ln == NULL) continue; /* @@ -1475,9 +1204,11 @@ SuffFindCmds(Src *targ, Lst slst) * * XXX: Handle multi-stage transformations here, too. */ - suff = (Suff *)Lst_Datum(ln); + suff = LstNode_Datum(ln); - if (Lst_Member(suff->parents, targ->suff) != NULL) + /* XXX: Can targ->suff be NULL here? */ + if (targ->suff != NULL && + Lst_FindDatum(suff->parents, targ->suff) != NULL) break; } @@ -1497,41 +1228,30 @@ SuffFindCmds(Src *targ, Lst slst) ret->children = 0; targ->children += 1; #ifdef DEBUG_SRC - ret->cp = Lst_Init(FALSE); + ret->cp = Lst_Init(); fprintf(debug_file, "3 add %p %p\n", targ, ret); - Lst_AtEnd(targ->cp, ret); + Lst_Append(targ->cp, ret); #endif - Lst_AtEnd(slst, ret); - if (DEBUG(SUFF)) { - fprintf(debug_file, "\tusing existing source %s\n", s->name); - } + Lst_Append(slst, ret); + SUFF_DEBUG1("\tusing existing source %s\n", s->name); + Lst_Close(t->children); return ret; } -/*- - *----------------------------------------------------------------------- - * SuffExpandChildren -- - * Expand the names of any children of a given node that contain - * variable invocations or file wildcards into actual targets. +/* Expand the names of any children of a given node that contain variable + * invocations or file wildcards into actual targets. + * + * The expanded node is removed from the parent's list of children, and the + * parent's unmade counter is decremented, but other nodes may be added. * * Input: * cln Child to examine * pgn Parent node being processed - * - * Results: - * === 0 (continue) - * - * Side Effects: - * The expanded node is removed from the parent's list of children, - * and the parent's unmade counter is decremented, but other nodes - * may be added. - * - *----------------------------------------------------------------------- */ static void SuffExpandChildren(LstNode cln, GNode *pgn) { - GNode *cgn = (GNode *)Lst_Datum(cln); + GNode *cgn = LstNode_Datum(cln); GNode *gn; /* New source 8) */ char *cp; /* Expanded value */ @@ -1554,13 +1274,11 @@ SuffExpandChildren(LstNode cln, GNode *pgn) return; } - if (DEBUG(SUFF)) { - fprintf(debug_file, "Expanding \"%s\"...", cgn->name); - } - cp = Var_Subst(NULL, cgn->name, pgn, VARF_UNDEFERR|VARF_WANTRES); + SUFF_DEBUG1("Expanding \"%s\"...", cgn->name); + cp = Var_Subst(cgn->name, pgn, VARE_UNDEFERR|VARE_WANTRES); - if (cp != NULL) { - Lst members = Lst_Init(FALSE); + { + Lst members = Lst_Init(); if (cgn->type & OP_ARCHV) { /* @@ -1592,7 +1310,7 @@ SuffExpandChildren(LstNode cln, GNode *pgn) */ *cp++ = '\0'; gn = Targ_FindNode(start, TARG_CREATE); - (void)Lst_AtEnd(members, gn); + Lst_Append(members, gn); while (*cp == ' ' || *cp == '\t') { cp++; } @@ -1606,12 +1324,12 @@ SuffExpandChildren(LstNode cln, GNode *pgn) * Start of a variable spec -- contact variable module * to find the end so we can skip over it. */ - char *junk; + const char *junk; int len; void *freeIt; - junk = Var_Parse(cp, pgn, VARF_UNDEFERR|VARF_WANTRES, - &len, &freeIt); + junk = Var_Parse(cp, pgn, VARE_UNDEFERR|VARE_WANTRES, + &len, &freeIt); if (junk != var_Error) { cp += len - 1; } @@ -1630,7 +1348,7 @@ SuffExpandChildren(LstNode cln, GNode *pgn) * Stuff left over -- add it to the list too */ gn = Targ_FindNode(start, TARG_CREATE); - (void)Lst_AtEnd(members, gn); + Lst_Append(members, gn); } /* * Point cp back at the beginning again so the variable value @@ -1643,28 +1361,25 @@ SuffExpandChildren(LstNode cln, GNode *pgn) * Add all elements of the members list to the parent node. */ while(!Lst_IsEmpty(members)) { - gn = (GNode *)Lst_DeQueue(members); + gn = Lst_Dequeue(members); - if (DEBUG(SUFF)) { - fprintf(debug_file, "%s...", gn->name); - } + SUFF_DEBUG1("%s...", gn->name); /* Add gn to the parents child list before the original child */ - (void)Lst_InsertBefore(pgn->children, cln, gn); - (void)Lst_AtEnd(gn->parents, pgn); + Lst_InsertBefore(pgn->children, cln, gn); + Lst_Append(gn->parents, pgn); pgn->unmade++; /* Expand wildcards on new node */ - SuffExpandWildcards(Lst_Prev(cln), pgn); + SuffExpandWildcards(LstNode_Prev(cln), pgn); } - Lst_Destroy(members, NULL); + Lst_Free(members); /* * Free the result */ free(cp); } - if (DEBUG(SUFF)) { - fprintf(debug_file, "\n"); - } + + SUFF_DEBUG0("\n"); /* * Now the source is expanded, remove it from the list of children to @@ -1672,13 +1387,13 @@ SuffExpandChildren(LstNode cln, GNode *pgn) */ pgn->unmade--; Lst_Remove(pgn->children, cln); - Lst_Remove(cgn->parents, Lst_Member(cgn->parents, pgn)); + Lst_Remove(cgn->parents, Lst_FindDatum(cgn->parents, pgn)); } static void SuffExpandWildcards(LstNode cln, GNode *pgn) { - GNode *cgn = (GNode *)Lst_Datum(cln); + GNode *cgn = LstNode_Datum(cln); GNode *gn; /* New source 8) */ char *cp; /* Expanded value */ Lst explist; /* List of expansions */ @@ -1689,34 +1404,27 @@ SuffExpandWildcards(LstNode cln, GNode *pgn) /* * Expand the word along the chosen path */ - explist = Lst_Init(FALSE); + explist = Lst_Init(); Dir_Expand(cgn->name, Suff_FindPath(cgn), explist); while (!Lst_IsEmpty(explist)) { /* * Fetch next expansion off the list and find its GNode */ - cp = (char *)Lst_DeQueue(explist); + cp = Lst_Dequeue(explist); - if (DEBUG(SUFF)) { - fprintf(debug_file, "%s...", cp); - } + SUFF_DEBUG1("%s...", cp); gn = Targ_FindNode(cp, TARG_CREATE); /* Add gn to the parents child list before the original child */ - (void)Lst_InsertBefore(pgn->children, cln, gn); - (void)Lst_AtEnd(gn->parents, pgn); + Lst_InsertBefore(pgn->children, cln, gn); + Lst_Append(gn->parents, pgn); pgn->unmade++; } - /* - * Nuke what's left of the list - */ - Lst_Destroy(explist, NULL); + Lst_Free(explist); - if (DEBUG(SUFF)) { - fprintf(debug_file, "\n"); - } + SUFF_DEBUG0("\n"); /* * Now the source is expanded, remove it from the list of children to @@ -1724,28 +1432,19 @@ SuffExpandWildcards(LstNode cln, GNode *pgn) */ pgn->unmade--; Lst_Remove(pgn->children, cln); - Lst_Remove(cgn->parents, Lst_Member(cgn->parents, pgn)); + Lst_Remove(cgn->parents, Lst_FindDatum(cgn->parents, pgn)); } -/*- - *----------------------------------------------------------------------- - * Suff_FindPath -- - * Find a path along which to expand the node. +/* Find a path along which to expand the node. * - * If the word has a known suffix, use that path. - * If it has no known suffix, use the default system search path. + * If the word has a known suffix, use that path. + * If it has no known suffix, use the default system search path. * * Input: * gn Node being examined * * Results: * The appropriate path to search for the GNode. - * - * Side Effects: - * XXX: We could set the suffix here so that we don't have to scan - * again. - * - *----------------------------------------------------------------------- */ Lst Suff_FindPath(GNode* gn) @@ -1753,24 +1452,20 @@ Suff_FindPath(GNode* gn) Suff *suff = gn->suffix; if (suff == NULL) { - SuffixCmpData sd; /* Search string data */ + SuffSuffGetSuffixArgs sd; /* Search string data */ LstNode ln; sd.len = strlen(gn->name); sd.ename = gn->name + sd.len; - ln = Lst_Find(sufflist, &sd, SuffSuffIsSuffixP); + ln = Lst_Find(sufflist, SuffSuffIsSuffix, &sd); - if (DEBUG(SUFF)) { - fprintf(debug_file, "Wildcard expanding \"%s\"...", gn->name); - } + SUFF_DEBUG1("Wildcard expanding \"%s\"...", gn->name); if (ln != NULL) - suff = (Suff *)Lst_Datum(ln); + suff = LstNode_Datum(ln); /* XXX: Here we can save the suffix so we don't have to do this again */ } if (suff != NULL) { - if (DEBUG(SUFF)) { - fprintf(debug_file, "suffix is \"%s\"...", suff->name); - } + SUFF_DEBUG1("suffix is \"%s\"...", suff->name); return suff->searchPath; } else { /* @@ -1780,11 +1475,8 @@ Suff_FindPath(GNode* gn) } } -/*- - *----------------------------------------------------------------------- - * SuffApplyTransform -- - * Apply a transformation rule, given the source and target nodes - * and suffixes. +/* Apply a transformation rule, given the source and target nodes and + * suffixes. * * Input: * tGn Target node @@ -1801,8 +1493,6 @@ Suff_FindPath(GNode* gn) * All attributes but OP_DEPMASK and OP_TRANSFORM are applied * to the target. The target also inherits all the sources for * the transformation rule. - * - *----------------------------------------------------------------------- */ static Boolean SuffApplyTransform(GNode *tGn, GNode *sGn, Suff *t, Suff *s) @@ -1814,15 +1504,15 @@ SuffApplyTransform(GNode *tGn, GNode *sGn, Suff *t, Suff *s) /* * Form the proper links between the target and source. */ - (void)Lst_AtEnd(tGn->children, sGn); - (void)Lst_AtEnd(sGn->parents, tGn); + Lst_Append(tGn->children, sGn); + Lst_Append(sGn->parents, tGn); tGn->unmade += 1; /* * Locate the transformation rule itself */ - tname = str_concat(s->name, t->name, 0); - ln = Lst_Find(transforms, tname, SuffGNHasNameP); + tname = str_concat2(s->name, t->name); + ln = Lst_Find(transforms, SuffGNHasName, tname); free(tname); if (ln == NULL) { @@ -1834,11 +1524,9 @@ SuffApplyTransform(GNode *tGn, GNode *sGn, Suff *t, Suff *s) return FALSE; } - gn = (GNode *)Lst_Datum(ln); + gn = LstNode_Datum(ln); - if (DEBUG(SUFF)) { - fprintf(debug_file, "\tapplying %s -> %s to \"%s\"\n", s->name, t->name, tGn->name); - } + SUFF_DEBUG3("\tapplying %s -> %s to \"%s\"\n", s->name, t->name, tGn->name); /* * Record last child for expansion purposes @@ -1853,8 +1541,8 @@ SuffApplyTransform(GNode *tGn, GNode *sGn, Suff *t, Suff *s) /* * Deal with wildcards and variables in any acquired sources */ - for (ln = Lst_Succ(ln); ln != NULL; ln = nln) { - nln = Lst_Succ(ln); + for (ln = ln != NULL ? LstNode_Next(ln) : NULL; ln != NULL; ln = nln) { + nln = LstNode_Next(ln); SuffExpandChildren(ln, tGn); } @@ -1862,27 +1550,19 @@ SuffApplyTransform(GNode *tGn, GNode *sGn, Suff *t, Suff *s) * Keep track of another parent to which this beast is transformed so * the .IMPSRC variable can be set correctly for the parent. */ - (void)Lst_AtEnd(sGn->iParents, tGn); + Lst_Append(sGn->implicitParents, tGn); return TRUE; } -/*- - *----------------------------------------------------------------------- - * SuffFindArchiveDeps -- - * Locate dependencies for an OP_ARCHV node. +/* Locate dependencies for an OP_ARCHV node. * * Input: * gn Node for which to locate dependencies * - * Results: - * None - * * Side Effects: * Same as Suff_FindDeps - * - *----------------------------------------------------------------------- */ static void SuffFindArchiveDeps(GNode *gn, Lst slst) @@ -1890,13 +1570,7 @@ SuffFindArchiveDeps(GNode *gn, Lst slst) char *eoarch; /* End of archive portion */ char *eoname; /* End of member portion */ GNode *mem; /* Node for member */ - static const char *copy[] = { - /* Variables to be copied from the member node */ - TARGET, /* Must be first */ - PREFIX, /* Must be second */ - }; LstNode ln, nln; /* Next suffix node to check */ - int i; /* Index into copy and vals */ Suff *ms; /* Suffix descriptor for member */ char *name; /* Start of member's name */ @@ -1932,18 +1606,19 @@ SuffFindArchiveDeps(GNode *gn, Lst slst) /* * Create the link between the two nodes right off */ - (void)Lst_AtEnd(gn->children, mem); - (void)Lst_AtEnd(mem->parents, gn); + Lst_Append(gn->children, mem); + Lst_Append(mem->parents, gn); gn->unmade += 1; /* * Copy in the variables from the member node to this one. */ - for (i = (sizeof(copy)/sizeof(copy[0]))-1; i >= 0; i--) { - char *p1; - Var_Set(copy[i], Var_Value(copy[i], mem, &p1), gn); - free(p1); - + { + char *freeIt; + Var_Set(PREFIX, Var_Value(PREFIX, mem, &freeIt), gn); + bmake_free(freeIt); + Var_Set(TARGET, Var_Value(TARGET, mem, &freeIt), gn); + bmake_free(freeIt); } ms = mem->suffix; @@ -1951,9 +1626,7 @@ SuffFindArchiveDeps(GNode *gn, Lst slst) /* * Didn't know what it was -- use .NULL suffix if not in make mode */ - if (DEBUG(SUFF)) { - fprintf(debug_file, "using null suffix\n"); - } + SUFF_DEBUG0("using null suffix\n"); ms = suffNull; } @@ -1974,7 +1647,7 @@ SuffFindArchiveDeps(GNode *gn, Lst slst) * that still contain variables or wildcards in their names. */ for (ln = Lst_First(gn->children); ln != NULL; ln = nln) { - nln = Lst_Succ(ln); + nln = LstNode_Next(ln); SuffExpandChildren(ln, gn); } @@ -1985,24 +1658,23 @@ SuffFindArchiveDeps(GNode *gn, Lst slst) * through the entire list, we just look at suffixes to which the * member's suffix may be transformed... */ - SuffixCmpData sd; /* Search string data */ + SuffSuffGetSuffixArgs sd; /* Search string data */ /* * Use first matching suffix... */ sd.len = eoarch - gn->name; sd.ename = eoarch; - ln = Lst_Find(ms->parents, &sd, SuffSuffIsSuffixP); + ln = Lst_Find(ms->parents, SuffSuffIsSuffix, &sd); if (ln != NULL) { /* * Got one -- apply it */ - if (!SuffApplyTransform(gn, mem, (Suff *)Lst_Datum(ln), ms) && - DEBUG(SUFF)) - { - fprintf(debug_file, "\tNo transformation from %s -> %s\n", - ms->name, ((Suff *)Lst_Datum(ln))->name); + Suff *suff = LstNode_Datum(ln); + if (!SuffApplyTransform(gn, mem, suff, ms)) { + SUFF_DEBUG2("\tNo transformation from %s -> %s\n", + ms->name, suff->name); } } } @@ -2030,21 +1702,13 @@ SuffFindArchiveDeps(GNode *gn, Lst slst) mem->type |= OP_MEMBER | OP_JOIN | OP_MADE; } -/*- - *----------------------------------------------------------------------- - * SuffFindNormalDeps -- - * Locate implicit dependencies for regular targets. +/* Locate implicit dependencies for regular targets. * * Input: * gn Node for which to find sources * - * Results: - * None. - * * Side Effects: - * Same as Suff_FindDeps... - * - *----------------------------------------------------------------------- + * Same as Suff_FindDeps */ static void SuffFindNormalDeps(GNode *gn, Lst slst) @@ -2060,7 +1724,7 @@ SuffFindNormalDeps(GNode *gn, Lst slst) Src *src; /* General Src pointer */ char *pref; /* Prefix to use */ Src *targ; /* General Src target pointer */ - SuffixCmpData sd; /* Search string data */ + SuffSuffGetSuffixArgs sd; /* Search string data */ sd.len = strlen(gn->name); @@ -2072,8 +1736,8 @@ SuffFindNormalDeps(GNode *gn, Lst slst) * Begin at the beginning... */ ln = Lst_First(sufflist); - srcs = Lst_Init(FALSE); - targs = Lst_Init(FALSE); + srcs = Lst_Init(); + targs = Lst_Init(); /* * We're caught in a catch-22 here. On the one hand, we want to use any @@ -2102,34 +1766,27 @@ SuffFindNormalDeps(GNode *gn, Lst slst) /* * Look for next possible suffix... */ - ln = Lst_FindFrom(sufflist, ln, &sd, SuffSuffIsSuffixP); + ln = Lst_FindFrom(sufflist, ln, SuffSuffIsSuffix, &sd); if (ln != NULL) { - int prefLen; /* Length of the prefix */ + const char *eopref; /* * Allocate a Src structure to which things can be transformed */ targ = bmake_malloc(sizeof(Src)); targ->file = bmake_strdup(gn->name); - targ->suff = (Suff *)Lst_Datum(ln); + targ->suff = LstNode_Datum(ln); targ->suff->refCount++; targ->node = gn; targ->parent = NULL; targ->children = 0; #ifdef DEBUG_SRC - targ->cp = Lst_Init(FALSE); + targ->cp = Lst_Init(); #endif - /* - * Allocate room for the prefix, whose end is found by - * subtracting the length of the suffix from - * the end of the name. - */ - prefLen = (eoname - targ->suff->nameLen) - sopref; - targ->pref = bmake_malloc(prefLen + 1); - memcpy(targ->pref, sopref, prefLen); - targ->pref[prefLen] = '\0'; + eopref = eoname - targ->suff->nameLen; + targ->pref = bmake_strsedup(sopref, eopref); /* * Add nodes from which the target can be made @@ -2139,12 +1796,12 @@ SuffFindNormalDeps(GNode *gn, Lst slst) /* * Record the target so we can nuke it */ - (void)Lst_AtEnd(targs, targ); + Lst_Append(targs, targ); /* * Search from this suffix's successor... */ - ln = Lst_Succ(ln); + ln = LstNode_Next(ln); } } @@ -2152,9 +1809,8 @@ SuffFindNormalDeps(GNode *gn, Lst slst) * Handle target of unknown suffix... */ if (Lst_IsEmpty(targs) && suffNull != NULL) { - if (DEBUG(SUFF)) { - fprintf(debug_file, "\tNo known suffix on %s. Using .NULL suffix\n", gn->name); - } + SUFF_DEBUG1("\tNo known suffix on %s. Using .NULL suffix\n", + gn->name); targ = bmake_malloc(sizeof(Src)); targ->file = bmake_strdup(gn->name); @@ -2165,7 +1821,7 @@ SuffFindNormalDeps(GNode *gn, Lst slst) targ->children = 0; targ->pref = bmake_strdup(sopref); #ifdef DEBUG_SRC - targ->cp = Lst_Init(FALSE); + targ->cp = Lst_Init(); #endif /* @@ -2177,14 +1833,12 @@ SuffFindNormalDeps(GNode *gn, Lst slst) if (Lst_IsEmpty(gn->commands)) SuffAddLevel(srcs, targ); else { - if (DEBUG(SUFF)) - fprintf(debug_file, "not "); + SUFF_DEBUG0("not "); } - if (DEBUG(SUFF)) - fprintf(debug_file, "adding suffix rules\n"); + SUFF_DEBUG0("adding suffix rules\n"); - (void)Lst_AtEnd(targs, targ); + Lst_Append(targs, targ); } /* @@ -2199,7 +1853,7 @@ SuffFindNormalDeps(GNode *gn, Lst slst) * for setting the local variables. */ if (!Lst_IsEmpty(targs)) { - targ = (Src *)Lst_Datum(Lst_First(targs)); + targ = LstNode_Datum(Lst_First(targs)); } else { targ = NULL; } @@ -2223,14 +1877,12 @@ SuffFindNormalDeps(GNode *gn, Lst slst) * that still contain variables or wildcards in their names. */ for (ln = Lst_First(gn->children); ln != NULL; ln = nln) { - nln = Lst_Succ(ln); + nln = LstNode_Next(ln); SuffExpandChildren(ln, gn); } if (targ == NULL) { - if (DEBUG(SUFF)) { - fprintf(debug_file, "\tNo valid suffix on %s\n", gn->name); - } + SUFF_DEBUG1("\tNo valid suffix on %s\n", gn->name); sfnd_abort: /* @@ -2314,8 +1966,8 @@ sfnd_abort: * up to, but not including, the parent node. */ while (bottom && bottom->parent != NULL) { - if (Lst_Member(slst, bottom) == NULL) { - Lst_AtEnd(slst, bottom); + if (Lst_FindDatum(slst, bottom) == NULL) { + Lst_Append(slst, bottom); } bottom = bottom->parent; } @@ -2390,45 +2042,31 @@ sfnd_abort: */ sfnd_return: if (bottom) - if (Lst_Member(slst, bottom) == NULL) - Lst_AtEnd(slst, bottom); + if (Lst_FindDatum(slst, bottom) == NULL) + Lst_Append(slst, bottom); while (SuffRemoveSrc(srcs) || SuffRemoveSrc(targs)) continue; - Lst_Concat(slst, srcs, LST_CONCLINK); - Lst_Concat(slst, targs, LST_CONCLINK); + Lst_MoveAll(slst, srcs); + Lst_MoveAll(slst, targs); } -/*- - *----------------------------------------------------------------------- - * Suff_FindDeps -- - * Find implicit sources for the target described by the graph node - * gn - * - * Results: - * Nothing. +/* Find implicit sources for the target described by the graph node. * - * Side Effects: - * Nodes are added to the graph below the passed-in node. The nodes - * are marked to have their IMPSRC variable filled in. The - * PREFIX variable is set for the given node and all its - * implied children. - * - * Notes: - * The path found by this target is the shortest path in the - * transformation graph, which may pass through non-existent targets, - * to an existing target. The search continues on all paths from the - * root suffix until a file is found. I.e. if there's a path - * .o -> .c -> .l -> .l,v from the root and the .l,v file exists but - * the .c and .l files don't, the search will branch out in - * all directions from .o and again from all the nodes on the - * next level until the .l,v node is encountered. + * Nodes are added to the graph below the passed-in node. The nodes are + * marked to have their IMPSRC variable filled in. The PREFIX variable is set + * for the given node and all its implied children. * - *----------------------------------------------------------------------- + * The path found by this target is the shortest path in the transformation + * graph, which may pass through non-existent targets, to an existing target. + * The search continues on all paths from the root suffix until a file is + * found. I.e. if there's a path .o -> .c -> .l -> .l,v from the root and the + * .l,v file exists but the .c and .l files don't, the search will branch out + * in all directions from .o and again from all the nodes on the next level + * until the .l,v node is encountered. */ - void Suff_FindDeps(GNode *gn) { @@ -2438,32 +2076,20 @@ Suff_FindDeps(GNode *gn) continue; } - -/* - * Input: - * gn node we're dealing with - * - */ static void SuffFindDeps(GNode *gn, Lst slst) { - if (gn->type & OP_DEPS_FOUND) { - /* - * If dependencies already found, no need to do it again... - */ + if (gn->type & OP_DEPS_FOUND) return; - } else { - gn->type |= OP_DEPS_FOUND; - } + gn->type |= OP_DEPS_FOUND; + /* * Make sure we have these set, may get revised below. */ Var_Set(TARGET, gn->path ? gn->path : gn->name, gn); Var_Set(PREFIX, gn->name, gn); - if (DEBUG(SUFF)) { - fprintf(debug_file, "SuffFindDeps (%s)\n", gn->name); - } + SUFF_DEBUG1("SuffFindDeps (%s)\n", gn->name); if (gn->type & OP_ARCHV) { SuffFindArchiveDeps(gn, slst); @@ -2479,11 +2105,11 @@ SuffFindDeps(GNode *gn, Lst slst) LstNode ln; Suff *s; - ln = Lst_Find(sufflist, LIBSUFF, SuffSuffHasNameP); + ln = Lst_Find(sufflist, SuffSuffHasName, LIBSUFF); if (gn->suffix) gn->suffix->refCount--; if (ln != NULL) { - gn->suffix = s = (Suff *)Lst_Datum(ln); + gn->suffix = s = LstNode_Datum(ln); gn->suffix->refCount++; Arch_FindLib(gn, s->searchPath); } else { @@ -2501,25 +2127,13 @@ SuffFindDeps(GNode *gn, Lst slst) } } -/*- - *----------------------------------------------------------------------- - * Suff_SetNull -- - * Define which suffix is the null suffix. +/* Define which suffix is the null suffix. + * + * Need to handle the changing of the null suffix gracefully so the old + * transformation rules don't just go away. * * Input: * name Name of null suffix - * - * Results: - * None. - * - * Side Effects: - * 'suffNull' is altered. - * - * Notes: - * Need to handle the changing of the null suffix gracefully so the - * old transformation rules don't just go away. - * - *----------------------------------------------------------------------- */ void Suff_SetNull(char *name) @@ -2527,9 +2141,9 @@ Suff_SetNull(char *name) Suff *s; LstNode ln; - ln = Lst_Find(sufflist, name, SuffSuffHasNameP); + ln = Lst_Find(sufflist, SuffSuffHasName, name); if (ln != NULL) { - s = (Suff *)Lst_Datum(ln); + s = LstNode_Datum(ln); if (suffNull != NULL) { suffNull->flags &= ~SUFF_NULL; } @@ -2544,26 +2158,16 @@ Suff_SetNull(char *name) } } -/*- - *----------------------------------------------------------------------- - * Suff_Init -- - * Initialize suffixes module - * - * Results: - * None - * - * Side Effects: - * Many - *----------------------------------------------------------------------- - */ +/* Initialize the suffixes module. */ void Suff_Init(void) { #ifdef CLEANUP - suffClean = Lst_Init(FALSE); + suffClean = Lst_Init(); + sufflist = Lst_Init(); #endif - srclist = Lst_Init(FALSE); - transforms = Lst_Init(FALSE); + srclist = Lst_Init(); + transforms = Lst_Init(); /* * Create null suffix for single-suffix rules (POSIX). The thing doesn't @@ -2574,19 +2178,7 @@ Suff_Init(void) } -/*- - *---------------------------------------------------------------------- - * Suff_End -- - * Cleanup the this module - * - * Results: - * None - * - * Side Effects: - * The memory is free'd. - *---------------------------------------------------------------------- - */ - +/* Clean up the suffixes module. */ void Suff_End(void) { @@ -2595,8 +2187,8 @@ Suff_End(void) Lst_Destroy(suffClean, SuffFree); if (suffNull) SuffFree(suffNull); - Lst_Destroy(srclist, NULL); - Lst_Destroy(transforms, NULL); + Lst_Free(srclist); + Lst_Free(transforms); #endif } @@ -2614,30 +2206,15 @@ static int SuffPrintSuff(void *sp, void *dummy MAKE_ATTR_UNUSED) { Suff *s = (Suff *)sp; - int flags; - int flag; fprintf(debug_file, "# `%s' [%d] ", s->name, s->refCount); - flags = s->flags; - if (flags) { - fputs(" (", debug_file); - while (flags) { - flag = 1 << (ffs(flags) - 1); - flags &= ~flag; - switch (flag) { - case SUFF_NULL: - fprintf(debug_file, "NULL"); - break; - case SUFF_INCLUDE: - fprintf(debug_file, "INCLUDE"); - break; - case SUFF_LIBRARY: - fprintf(debug_file, "LIBRARY"); - break; - } - fputc(flags ? '|' : ')', debug_file); - } + if (s->flags != 0) { + char flags_buf[SuffFlags_ToStringSize]; + + fprintf(debug_file, " (%s)", + Enum_FlagsToString(flags_buf, sizeof flags_buf, + s->flags, SuffFlags_ToStringSpecs)); } fputc('\n', debug_file); fprintf(debug_file, "#\tTo: "); @@ -1,4 +1,4 @@ -/* $NetBSD: targ.c,v 1.63 2020/07/03 08:02:55 rillig Exp $ */ +/* $NetBSD: targ.c,v 1.81 2020/09/01 20:54:00 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -69,14 +69,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: targ.c,v 1.63 2020/07/03 08:02:55 rillig Exp $"; +static char rcsid[] = "$NetBSD: targ.c,v 1.81 2020/09/01 20:54:00 rillig Exp $"; #else #include <sys/cdefs.h> #ifndef lint #if 0 static char sccsid[] = "@(#)targ.c 8.2 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: targ.c,v 1.63 2020/07/03 08:02:55 rillig Exp $"); +__RCSID("$NetBSD: targ.c,v 1.81 2020/09/01 20:54:00 rillig Exp $"); #endif #endif /* not lint */ #endif @@ -84,8 +84,7 @@ __RCSID("$NetBSD: targ.c,v 1.63 2020/07/03 08:02:55 rillig Exp $"); /*- * targ.c -- * Functions for maintaining the Lst allTargets. Target nodes are - * kept in two structures: a Lst, maintained by the list library, and a - * hash table, maintained by the hash library. + * kept in two structures: a Lst and a hash table. * * Interface: * Targ_Init Initialization procedure. @@ -133,7 +132,6 @@ __RCSID("$NetBSD: targ.c,v 1.63 2020/07/03 08:02:55 rillig Exp $"); #include <time.h> #include "make.h" -#include "hash.h" #include "dir.h" static Lst allTargets; /* the list of all targets found so far */ @@ -142,91 +140,49 @@ static Lst allGNs; /* List of all the GNodes */ #endif static Hash_Table targets; /* a hash table of same */ -#define HTSIZE 191 /* initial size of hash table */ - static int TargPrintOnlySrc(void *, void *); static int TargPrintName(void *, void *); #ifdef CLEANUP static void TargFreeGN(void *); #endif -static int TargPropagateCohort(void *, void *); -static int TargPropagateNode(void *, void *); -/*- - *----------------------------------------------------------------------- - * Targ_Init -- - * Initialize this module - * - * Results: - * None - * - * Side Effects: - * The allTargets list and the targets hash table are initialized - *----------------------------------------------------------------------- - */ void Targ_Init(void) { - allTargets = Lst_Init(FALSE); - Hash_InitTable(&targets, HTSIZE); + allTargets = Lst_Init(); + Hash_InitTable(&targets, 191); } -/*- - *----------------------------------------------------------------------- - * Targ_End -- - * Finalize this module - * - * Results: - * None - * - * Side Effects: - * All lists and gnodes are cleared - *----------------------------------------------------------------------- - */ void Targ_End(void) { + Targ_Stats(); #ifdef CLEANUP - Lst_Destroy(allTargets, NULL); - if (allGNs) + Lst_Free(allTargets); + if (allGNs != NULL) Lst_Destroy(allGNs, TargFreeGN); Hash_DeleteTable(&targets); #endif } -/*- - *----------------------------------------------------------------------- - * Targ_List -- - * Return the list of all targets - * - * Results: - * The list of all targets. - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ +void +Targ_Stats(void) +{ + Hash_DebugStats(&targets, "targets"); +} + +/* Return the list of all targets. */ Lst Targ_List(void) { return allTargets; } -/*- - *----------------------------------------------------------------------- - * Targ_NewGN -- - * Create and initialize a new graph node +/* Create and initialize a new graph node. The gnode is added to the list of + * all gnodes. * * Input: - * name the name to stick in the new node - * - * Results: - * An initialized graph node with the name field filled with a copy - * of the passed name - * - * Side Effects: - * The gnode is added to the list of all gnodes. - *----------------------------------------------------------------------- + * name the name of the node, such as "clean", "src.c" */ GNode * Targ_NewGN(const char *name) @@ -237,11 +193,7 @@ Targ_NewGN(const char *name) gn->name = bmake_strdup(name); gn->uname = NULL; gn->path = NULL; - if (name[0] == '-' && name[1] == 'l') { - gn->type = OP_LIB; - } else { - gn->type = 0; - } + gn->type = name[0] == '-' && name[1] == 'l' ? OP_LIB : 0; gn->unmade = 0; gn->unmade_cohorts = 0; gn->cohort_num[0] = 0; @@ -251,82 +203,65 @@ Targ_NewGN(const char *name) gn->checked = 0; gn->mtime = 0; gn->cmgn = NULL; - gn->iParents = Lst_Init(FALSE); - gn->cohorts = Lst_Init(FALSE); - gn->parents = Lst_Init(FALSE); - gn->children = Lst_Init(FALSE); - gn->order_pred = Lst_Init(FALSE); - gn->order_succ = Lst_Init(FALSE); + gn->implicitParents = Lst_Init(); + gn->cohorts = Lst_Init(); + gn->parents = Lst_Init(); + gn->children = Lst_Init(); + gn->order_pred = Lst_Init(); + gn->order_succ = Lst_Init(); Hash_InitTable(&gn->context, 0); - gn->commands = Lst_Init(FALSE); + gn->commands = Lst_Init(); gn->suffix = NULL; - gn->lineno = 0; gn->fname = NULL; + gn->lineno = 0; #ifdef CLEANUP if (allGNs == NULL) - allGNs = Lst_Init(FALSE); - Lst_AtEnd(allGNs, gn); + allGNs = Lst_Init(); + Lst_Append(allGNs, gn); #endif return gn; } #ifdef CLEANUP -/*- - *----------------------------------------------------------------------- - * TargFreeGN -- - * Destroy a GNode - * - * Results: - * None. - * - * Side Effects: - * None. - *----------------------------------------------------------------------- - */ static void TargFreeGN(void *gnp) { GNode *gn = (GNode *)gnp; - free(gn->name); free(gn->uname); free(gn->path); - /* gn->fname points to name allocated when file was opened, don't free */ - Lst_Destroy(gn->iParents, NULL); - Lst_Destroy(gn->cohorts, NULL); - Lst_Destroy(gn->parents, NULL); - Lst_Destroy(gn->children, NULL); - Lst_Destroy(gn->order_succ, NULL); - Lst_Destroy(gn->order_pred, NULL); + Lst_Free(gn->implicitParents); + Lst_Free(gn->cohorts); + Lst_Free(gn->parents); + Lst_Free(gn->children); + Lst_Free(gn->order_succ); + Lst_Free(gn->order_pred); Hash_DeleteTable(&gn->context); - Lst_Destroy(gn->commands, NULL); + Lst_Free(gn->commands); + + /* XXX: does gn->suffix need to be freed? It is reference-counted. */ + /* gn->fname points to name allocated when file was opened, don't free */ + free(gn); } #endif -/*- - *----------------------------------------------------------------------- - * Targ_FindNode -- - * Find a node in the list using the given name for matching +/* Find a node in the list using the given name for matching. + * If the node is created, it is added to the .ALLTARGETS list. * * Input: * name the name to find - * flags flags governing events when target not - * found + * flags flags governing events when target not found * * Results: - * The node in the list if it was. If it wasn't, return NULL of + * The node in the list if it was. If it wasn't, return NULL if * flags was TARG_NOCREATE or the newly created and initialized node * if it was TARG_CREATE - * - * Side Effects: - * Sometimes a node is created and added to the list - *----------------------------------------------------------------------- */ GNode * Targ_FindNode(const char *name, int flags) @@ -353,16 +288,16 @@ Targ_FindNode(const char *name, int flags) if (!(flags & TARG_NOHASH)) Hash_SetValue(he, gn); Var_Append(".ALLTARGETS", name, VAR_GLOBAL); - (void)Lst_AtEnd(allTargets, gn); + Lst_Append(allTargets, gn); if (doing_depend) gn->flags |= FROM_DEPEND; return gn; } -/*- - *----------------------------------------------------------------------- - * Targ_FindList -- - * Make a complete list of GNodes from the given list of names +/* Make a complete list of GNodes from the given list of names. + * If flags is TARG_CREATE, nodes will be created for all names in + * names which do not yet have graph nodes. If flags is TARG_NOCREATE, + * an error message will be printed for each name which can't be found. * * Input: * name list of names to find @@ -371,12 +306,6 @@ Targ_FindNode(const char *name, int flags) * Results: * A complete list of graph nodes corresponding to all instances of all * the names in names. - * - * Side Effects: - * If flags is TARG_CREATE, nodes will be created for all names in - * names which do not yet have graph nodes. If flags is TARG_NOCREATE, - * an error message will be printed for each name which can't be found. - * ----------------------------------------------------------------------- */ Lst Targ_FindList(Lst names, int flags) @@ -386,21 +315,19 @@ Targ_FindList(Lst names, int flags) GNode *gn; /* node in tLn */ char *name; - nodes = Lst_Init(FALSE); + nodes = Lst_Init(); - if (Lst_Open(names) == FAILURE) { - return nodes; - } + Lst_Open(names); while ((ln = Lst_Next(names)) != NULL) { - name = (char *)Lst_Datum(ln); + name = LstNode_Datum(ln); gn = Targ_FindNode(name, flags); if (gn != NULL) { /* - * Note: Lst_AtEnd must come before the Lst_Concat so the nodes + * Note: Lst_Append must come before the Lst_Concat so the nodes * are added to the list in the order in which they were * encountered in the makefile. */ - (void)Lst_AtEnd(nodes, gn); + Lst_Append(nodes, gn); } else if (flags == TARG_NOCREATE) { Error("\"%s\" -- target unknown.", name); } @@ -409,100 +336,33 @@ Targ_FindList(Lst names, int flags) return nodes; } -/*- - *----------------------------------------------------------------------- - * Targ_Ignore -- - * Return true if should ignore errors when creating gn - * - * Input: - * gn node to check for - * - * Results: - * TRUE if should ignore errors - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ +/* Return true if should ignore errors when creating gn. */ Boolean Targ_Ignore(GNode *gn) { - if (ignoreErrors || gn->type & OP_IGNORE) { - return TRUE; - } else { - return FALSE; - } + return ignoreErrors || gn->type & OP_IGNORE; } -/*- - *----------------------------------------------------------------------- - * Targ_Silent -- - * Return true if be silent when creating gn - * - * Input: - * gn node to check for - * - * Results: - * TRUE if should be silent - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ +/* Return true if be silent when creating gn. */ Boolean Targ_Silent(GNode *gn) { - if (beSilent || gn->type & OP_SILENT) { - return TRUE; - } else { - return FALSE; - } + return beSilent || gn->type & OP_SILENT; } -/*- - *----------------------------------------------------------------------- - * Targ_Precious -- - * See if the given target is precious - * - * Input: - * gn the node to check - * - * Results: - * TRUE if it is precious. FALSE otherwise - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ +/* See if the given target is precious. */ Boolean Targ_Precious(GNode *gn) { - if (allPrecious || (gn->type & (OP_PRECIOUS|OP_DOUBLEDEP))) { - return TRUE; - } else { - return FALSE; - } + return allPrecious || gn->type & (OP_PRECIOUS | OP_DOUBLEDEP); } /******************* DEBUG INFO PRINTING ****************/ static GNode *mainTarg; /* the main target, as set by Targ_SetMain */ -/*- - *----------------------------------------------------------------------- - * Targ_SetMain -- - * Set our idea of the main target we'll be creating. Used for - * debugging output. - * - * Input: - * gn The main target we'll create - * - * Results: - * None. - * - * Side Effects: - * "mainTarg" is set to the main target's node. - *----------------------------------------------------------------------- - */ + +/* Set our idea of the main target we'll be creating. Used for debugging + * output. */ void Targ_SetMain(GNode *gn) { @@ -527,20 +387,8 @@ Targ_PrintCmd(void *cmd, void *dummy MAKE_ATTR_UNUSED) return 0; } -/*- - *----------------------------------------------------------------------- - * Targ_FmtTime -- - * Format a modification time in some reasonable way and return it. - * - * Results: - * The time reformatted. - * - * Side Effects: - * The time is placed in a static area, so it is overwritten - * with each call. - * - *----------------------------------------------------------------------- - */ +/* Format a modification time in some reasonable way and return it. + * The time is placed in a static area, so it is overwritten with each call. */ char * Targ_FmtTime(time_t tm) { @@ -552,18 +400,7 @@ Targ_FmtTime(time_t tm) return buf; } -/*- - *----------------------------------------------------------------------- - * Targ_PrintType -- - * Print out a type field giving only those attributes the user can - * set. - * - * Results: - * - * Side Effects: - * - *----------------------------------------------------------------------- - */ +/* Print out a type field giving only those attributes the user can set. */ void Targ_PrintType(int type) { @@ -600,7 +437,7 @@ Targ_PrintType(int type) } static const char * -made_name(enum enum_made made) +made_name(GNodeMade made) { switch (made) { case UNMADE: return "unmade"; @@ -615,20 +452,15 @@ made_name(enum enum_made made) } } -/*- - *----------------------------------------------------------------------- - * TargPrintNode -- - * print the contents of a node - *----------------------------------------------------------------------- - */ +/* Print the contents of a node. */ int Targ_PrintNode(void *gnp, void *passp) { GNode *gn = (GNode *)gnp; int pass = passp ? *(int *)passp : 0; - fprintf(debug_file, "# %s%s, flags %x, type %x, made %d\n", - gn->name, gn->cohort_num, gn->flags, gn->type, gn->made); + fprintf(debug_file, "# %s%s", gn->name, gn->cohort_num); + GNode_FprintDetails(debug_file, ", ", gn, "\n"); if (gn->flags == 0) return 0; @@ -655,26 +487,26 @@ Targ_PrintNode(void *gnp, void *passp) fprintf(debug_file, "# unmade\n"); } } - if (!Lst_IsEmpty (gn->iParents)) { + if (!Lst_IsEmpty(gn->implicitParents)) { fprintf(debug_file, "# implicit parents: "); - Lst_ForEach(gn->iParents, TargPrintName, NULL); + Lst_ForEach(gn->implicitParents, TargPrintName, NULL); fprintf(debug_file, "\n"); } } else { if (gn->unmade) fprintf(debug_file, "# %d unmade children\n", gn->unmade); } - if (!Lst_IsEmpty (gn->parents)) { + if (!Lst_IsEmpty(gn->parents)) { fprintf(debug_file, "# parents: "); Lst_ForEach(gn->parents, TargPrintName, NULL); fprintf(debug_file, "\n"); } - if (!Lst_IsEmpty (gn->order_pred)) { + if (!Lst_IsEmpty(gn->order_pred)) { fprintf(debug_file, "# order_pred: "); Lst_ForEach(gn->order_pred, TargPrintName, NULL); fprintf(debug_file, "\n"); } - if (!Lst_IsEmpty (gn->order_succ)) { + if (!Lst_IsEmpty(gn->order_succ)) { fprintf(debug_file, "# order_succ: "); Lst_ForEach(gn->order_succ, TargPrintName, NULL); fprintf(debug_file, "\n"); @@ -701,19 +533,8 @@ Targ_PrintNode(void *gnp, void *passp) return 0; } -/*- - *----------------------------------------------------------------------- - * TargPrintOnlySrc -- - * Print only those targets that are just a source. - * - * Results: - * 0. - * - * Side Effects: - * The name of each file is printed preceded by #\t - * - *----------------------------------------------------------------------- - */ +/* Print only those targets that are just a source. + * The name of each file is printed, preceded by #\t. */ static int TargPrintOnlySrc(void *gnp, void *dummy MAKE_ATTR_UNUSED) { @@ -729,21 +550,10 @@ TargPrintOnlySrc(void *gnp, void *dummy MAKE_ATTR_UNUSED) return 0; } -/*- - *----------------------------------------------------------------------- - * Targ_PrintGraph -- - * print the entire graph. heh heh - * - * Input: - * pass Which pass this is. 1 => no processing - * 2 => processing done - * - * Results: - * none - * - * Side Effects: - * lots o' output - *----------------------------------------------------------------------- +/* Input: + * pass 1 => before processing + * 2 => after processing + * 3 => after processing, an error occurred */ void Targ_PrintGraph(int pass) @@ -763,84 +573,26 @@ Targ_PrintGraph(int pass) Suff_PrintAll(); } -/*- - *----------------------------------------------------------------------- - * TargPropagateNode -- - * Propagate information from a single node to related nodes if - * appropriate. +/* Propagate some type information to cohort nodes (those from the :: + * dependency operator). * - * Input: - * gnp The node that we are processing. - * - * Results: - * Always returns 0, for the benefit of Lst_ForEach(). - * - * Side Effects: - * Information is propagated from this node to cohort or child - * nodes. - * - * If the node was defined with "::", then TargPropagateCohort() - * will be called for each cohort node. - * - * If the node has recursive predecessors, then - * TargPropagateRecpred() will be called for each recursive - * predecessor. - *----------------------------------------------------------------------- - */ -static int -TargPropagateNode(void *gnp, void *junk MAKE_ATTR_UNUSED) + * Should be called after the makefiles are parsed but before any action is + * taken. */ +void +Targ_Propagate(void) { - GNode *gn = (GNode *)gnp; + LstNode pn, cn; - if (gn->type & OP_DOUBLEDEP) - Lst_ForEach(gn->cohorts, TargPropagateCohort, gnp); - return 0; -} + for (pn = Lst_First(allTargets); pn != NULL; pn = LstNode_Next(pn)) { + GNode *pgn = LstNode_Datum(pn); -/*- - *----------------------------------------------------------------------- - * TargPropagateCohort -- - * Propagate some bits in the type mask from a node to - * a related cohort node. - * - * Input: - * cnp The node that we are processing. - * gnp Another node that has cnp as a cohort. - * - * Results: - * Always returns 0, for the benefit of Lst_ForEach(). - * - * Side Effects: - * cnp's type bitmask is modified to incorporate some of the - * bits from gnp's type bitmask. (XXX need a better explanation.) - *----------------------------------------------------------------------- - */ -static int -TargPropagateCohort(void *cgnp, void *pgnp) -{ - GNode *cgn = (GNode *)cgnp; - GNode *pgn = (GNode *)pgnp; + if (!(pgn->type & OP_DOUBLEDEP)) + continue; - cgn->type |= pgn->type & ~OP_OPMASK; - return 0; -} + for (cn = Lst_First(pgn->cohorts); cn != NULL; cn = LstNode_Next(cn)) { + GNode *cgn = LstNode_Datum(cn); -/*- - *----------------------------------------------------------------------- - * Targ_Propagate -- - * Propagate information between related nodes. Should be called - * after the makefiles are parsed but before any action is taken. - * - * Results: - * none - * - * Side Effects: - * Information is propagated between related nodes throughout the - * graph. - *----------------------------------------------------------------------- - */ -void -Targ_Propagate(void) -{ - Lst_ForEach(allTargets, TargPropagateNode, NULL); + cgn->type |= pgn->type & ~OP_OPMASK; + } + } } @@ -1,4 +1,4 @@ -/* $NetBSD: trace.c,v 1.12 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: trace.c,v 1.15 2020/08/03 20:26:09 rillig Exp $ */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. @@ -31,11 +31,11 @@ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: trace.c,v 1.12 2020/07/03 08:13:23 rillig Exp $"; +static char rcsid[] = "$NetBSD: trace.c,v 1.15 2020/08/03 20:26:09 rillig Exp $"; #else #include <sys/cdefs.h> #ifndef lint -__RCSID("$NetBSD: trace.c,v 1.12 2020/07/03 08:13:23 rillig Exp $"); +__RCSID("$NetBSD: trace.c,v 1.15 2020/08/03 20:26:09 rillig Exp $"); #endif /* not lint */ #endif @@ -63,7 +63,7 @@ __RCSID("$NetBSD: trace.c,v 1.12 2020/07/03 08:13:23 rillig Exp $"); static FILE *trfile; static pid_t trpid; -char *trwd; +const char *trwd; static const char *evname[] = { "BEG", @@ -77,10 +77,10 @@ static const char *evname[] = { void Trace_Init(const char *pathname) { - char *p1; if (pathname != NULL) { + char *dontFreeIt; trpid = getpid(); - trwd = Var_Value(".CURDIR", VAR_GLOBAL, &p1); + trwd = Var_Value(".CURDIR", VAR_GLOBAL, &dontFreeIt); trfile = fopen(pathname, "a"); } diff --git a/unit-tests/Makefile b/unit-tests/Makefile index c9cc13d769db..1566b177087a 100644 --- a/unit-tests/Makefile +++ b/unit-tests/Makefile @@ -1,6 +1,6 @@ -# $Id: Makefile,v 1.60 2020/07/10 00:48:32 sjg Exp $ +# $Id: Makefile,v 1.92 2020/09/02 18:39:29 sjg Exp $ # -# $NetBSD: Makefile,v 1.63 2020/07/09 22:40:14 sjg Exp $ +# $NetBSD: Makefile,v 1.130 2020/09/02 05:33:57 rillig Exp $ # # Unit tests for make(1) # @@ -25,68 +25,364 @@ # named makefile (*.mk), with its own set of expected results (*.exp), # and it should be added to the TESTS list. # -# Any added files must also be added to src/distrib/sets/lists/tests/mi. -# Makefiles that are not added to TESTS must be ignored in -# src/tests/usr.bin/make/t_make.sh (example: include-sub). +# A few *.mk files are helper files for other tests (such as include-sub.mk) +# and are thus not added to TESTS. Such files must be ignored in +# src/tests/usr.bin/make/t_make.sh. # # Each test is in a sub-makefile. # Keep the list sorted. +# Any test that is commented out must be ignored in +# src/tests/usr.bin/make/t_make.sh as well. +TESTS+= # archive # broken on FreeBSD, enabled in t_make.sh +TESTS+= archive-suffix +TESTS+= cmd-interrupt +TESTS+= cmdline TESTS+= comment +TESTS+= cond-cmp-numeric +TESTS+= cond-cmp-numeric-eq +TESTS+= cond-cmp-numeric-ge +TESTS+= cond-cmp-numeric-gt +TESTS+= cond-cmp-numeric-le +TESTS+= cond-cmp-numeric-lt +TESTS+= cond-cmp-numeric-ne +TESTS+= cond-cmp-string +TESTS+= cond-func +TESTS+= cond-func-commands +TESTS+= cond-func-defined +TESTS+= cond-func-empty +TESTS+= cond-func-exists +TESTS+= cond-func-make +TESTS+= cond-func-target TESTS+= cond-late +TESTS+= cond-op +TESTS+= cond-op-and +TESTS+= cond-op-not +TESTS+= cond-op-or +TESTS+= cond-op-parentheses TESTS+= cond-short +TESTS+= cond-token-number +TESTS+= cond-token-plain +TESTS+= cond-token-string +TESTS+= cond-token-var TESTS+= cond1 TESTS+= cond2 +TESTS+= counter +TESTS+= dep +TESTS+= dep-colon +TESTS+= dep-double-colon +TESTS+= dep-exclam +TESTS+= dep-none +TESTS+= dep-var +TESTS+= dep-wildcards +TESTS+= depsrc +TESTS+= depsrc-exec +TESTS+= depsrc-ignore +TESTS+= depsrc-made +TESTS+= depsrc-make +TESTS+= depsrc-meta +TESTS+= depsrc-nometa +TESTS+= depsrc-nometa_cmp +TESTS+= depsrc-nopath +TESTS+= depsrc-notmain +TESTS+= depsrc-optional +TESTS+= depsrc-phony +TESTS+= depsrc-precious +TESTS+= depsrc-recursive +TESTS+= depsrc-silent +TESTS+= depsrc-use +TESTS+= depsrc-usebefore +TESTS+= depsrc-usebefore-double-colon +TESTS+= depsrc-wait +TESTS+= deptgt +TESTS+= deptgt-begin +TESTS+= deptgt-default +TESTS+= deptgt-delete_on_error +TESTS+= deptgt-end +TESTS+= deptgt-error +TESTS+= deptgt-ignore +TESTS+= deptgt-interrupt +TESTS+= deptgt-main +TESTS+= deptgt-makeflags +TESTS+= deptgt-no_parallel +TESTS+= deptgt-nopath +TESTS+= deptgt-notparallel +TESTS+= deptgt-objdir +TESTS+= deptgt-order +TESTS+= deptgt-path +TESTS+= deptgt-path-suffix +TESTS+= deptgt-phony +TESTS+= deptgt-precious +TESTS+= deptgt-shell +TESTS+= deptgt-silent +TESTS+= deptgt-stale +TESTS+= deptgt-suffixes +TESTS+= dir +TESTS+= dir-expand-path +TESTS+= directive +TESTS+= directive-elif +TESTS+= directive-elifdef +TESTS+= directive-elifmake +TESTS+= directive-elifndef +TESTS+= directive-elifnmake +TESTS+= directive-else +TESTS+= directive-endif +TESTS+= directive-error +TESTS+= directive-export +TESTS+= directive-export-env +TESTS+= directive-export-literal +TESTS+= directive-for +TESTS+= directive-for-generating-endif +TESTS+= directive-if +TESTS+= directive-ifdef +TESTS+= directive-ifmake +TESTS+= directive-ifndef +TESTS+= directive-ifnmake +TESTS+= directive-info +TESTS+= directive-undef +TESTS+= directive-unexport +TESTS+= directive-unexport-env +TESTS+= directive-warning +TESTS+= directives TESTS+= dollar TESTS+= doterror TESTS+= dotwait +TESTS+= envfirst TESTS+= error TESTS+= # escape # broken by reverting POSIX changes TESTS+= export TESTS+= export-all TESTS+= export-env +TESTS+= export-variants TESTS+= forloop TESTS+= forsubst TESTS+= hash -TESTS+= # impsrc # broken by reverting POSIX changes +TESTS+= impsrc TESTS+= include-main +TESTS+= lint +TESTS+= make-exported TESTS+= misc TESTS+= moderrs TESTS+= modmatch TESTS+= modmisc -TESTS+= modorder TESTS+= modts TESTS+= modword +TESTS+= opt +TESTS+= opt-backwards +TESTS+= opt-chdir +TESTS+= opt-debug +TESTS+= opt-debug-g1 +TESTS+= opt-define +TESTS+= opt-env +TESTS+= opt-file +TESTS+= opt-ignore +TESTS+= opt-include-dir +TESTS+= opt-jobs +TESTS+= opt-jobs-internal +TESTS+= opt-keep-going +TESTS+= opt-m-include-dir +TESTS+= opt-no-action +TESTS+= opt-no-action-at-all +TESTS+= opt-query +TESTS+= opt-raw +TESTS+= opt-silent +TESTS+= opt-touch +TESTS+= opt-tracefile +TESTS+= opt-var-expanded +TESTS+= opt-var-literal +TESTS+= opt-warnings-as-errors +TESTS+= opt-where-am-i +TESTS+= opt-x-reduce-exported TESTS+= order -TESTS+= # phony-end # broken by reverting POSIX changes +TESTS+= phony-end TESTS+= posix TESTS+= # posix1 # broken by reverting POSIX changes TESTS+= qequals -TESTS+= # suffixes # broken by reverting POSIX changes +TESTS+= recursive +TESTS+= sh +TESTS+= sh-dots +TESTS+= sh-jobs +TESTS+= sh-jobs-error +TESTS+= sh-leading-at +TESTS+= sh-leading-hyphen +TESTS+= sh-leading-plus +TESTS+= sh-meta-chars +TESTS+= sh-multi-line +TESTS+= sh-single-line +TESTS+= # suffixes # runs into an endless loop (try -dA) TESTS+= sunshcmd TESTS+= sysv TESTS+= ternary TESTS+= unexport TESTS+= unexport-env +TESTS+= use-inference +TESTS+= var-class +TESTS+= var-class-cmdline +TESTS+= var-class-env +TESTS+= var-class-global +TESTS+= var-class-local +TESTS+= var-class-local-legacy +TESTS+= var-op +TESTS+= var-op-append +TESTS+= var-op-assign +TESTS+= var-op-default +TESTS+= var-op-expand +TESTS+= var-op-shell TESTS+= varcmd +TESTS+= vardebug +TESTS+= varfind TESTS+= varmisc +TESTS+= varmod +TESTS+= varmod-assign +TESTS+= varmod-defined TESTS+= varmod-edge +TESTS+= varmod-exclam-shell +TESTS+= varmod-extension +TESTS+= varmod-gmtime +TESTS+= varmod-hash +TESTS+= varmod-head +TESTS+= varmod-ifelse +TESTS+= varmod-l-name-to-value +TESTS+= varmod-localtime +TESTS+= varmod-loop +TESTS+= varmod-match +TESTS+= varmod-match-escape +TESTS+= varmod-no-match +TESTS+= varmod-order +TESTS+= varmod-order-reverse +TESTS+= varmod-order-shuffle +TESTS+= varmod-path +TESTS+= varmod-quote +TESTS+= varmod-quote-dollar +TESTS+= varmod-range +TESTS+= varmod-remember +TESTS+= varmod-root +TESTS+= varmod-select-words +TESTS+= varmod-shell +TESTS+= varmod-subst +TESTS+= varmod-subst-regex +TESTS+= varmod-sysv +TESTS+= varmod-tail +TESTS+= varmod-to-abs +TESTS+= varmod-to-lower +TESTS+= varmod-to-many-words +TESTS+= varmod-to-one-word +TESTS+= varmod-to-separator +TESTS+= varmod-to-upper +TESTS+= varmod-undefined +TESTS+= varmod-unique +TESTS+= varname +TESTS+= varname-dollar +TESTS+= varname-dot-alltargets +TESTS+= varname-dot-curdir +TESTS+= varname-dot-includes +TESTS+= varname-dot-includedfromdir +TESTS+= varname-dot-includedfromfile +TESTS+= varname-dot-libs +TESTS+= varname-dot-make-dependfile +TESTS+= varname-dot-make-expand_variables +TESTS+= varname-dot-make-exported +TESTS+= varname-dot-make-jobs +TESTS+= varname-dot-make-jobs-prefix +TESTS+= varname-dot-make-level +TESTS+= varname-dot-make-makefile_preference +TESTS+= varname-dot-make-makefiles +TESTS+= varname-dot-make-meta-bailiwick +TESTS+= varname-dot-make-meta-created +TESTS+= varname-dot-make-meta-files +TESTS+= varname-dot-make-meta-ignore_filter +TESTS+= varname-dot-make-meta-ignore_paths +TESTS+= varname-dot-make-meta-ignore_patterns +TESTS+= varname-dot-make-meta-prefix +TESTS+= varname-dot-make-mode +TESTS+= varname-dot-make-path_filemon +TESTS+= varname-dot-make-pid +TESTS+= varname-dot-make-ppid +TESTS+= varname-dot-make-save_dollars +TESTS+= varname-dot-makeoverrides +TESTS+= varname-dot-newline +TESTS+= varname-dot-objdir +TESTS+= varname-dot-parsedir +TESTS+= varname-dot-parsefile +TESTS+= varname-dot-path +TESTS+= varname-dot-shell +TESTS+= varname-dot-targets +TESTS+= varname-empty +TESTS+= varname-make +TESTS+= varname-make_print_var_on_error +TESTS+= varname-makeflags +TESTS+= varname-pwd +TESTS+= varname-vpath +TESTS+= varparse-dynamic TESTS+= varquote TESTS+= varshell -# Override make flags for certain tests; default is -k. +# Additional environment variables for some of the tests. +# The base environment is -i PATH="$PATH". +ENV.envfirst= FROM_ENV=value-from-env +ENV.varmisc= FROM_ENV=env +ENV.varmisc+= FROM_ENV_BEFORE=env +ENV.varmisc+= FROM_ENV_AFTER=env + +# Override make flags for some of the tests; default is -k. +# If possible, write ".MAKEFLAGS: -dv" in the test .mk file instead of +# settings FLAGS.test=-dv here, since that is closer to the test code. +FLAGS.archive= -dA +FLAGS.counter= -dv +FLAGS.directive-ifmake= first second FLAGS.doterror= # none +FLAGS.envfirst= -e +FLAGS.export= # none +FLAGS.lint= -dL -k +FLAGS.opt-debug-g1= -dg1 +FLAGS.opt-ignore= -i +FLAGS.opt-keep-going= -k +FLAGS.opt-no-action= -n +FLAGS.opt-query= -q +FLAGS.opt-var-expanded= -v VAR -v VALUE +FLAGS.opt-var-literal= -V VAR -V VALUE +FLAGS.opt-warnings-as-errors= -W FLAGS.order= -j1 +FLAGS.recursive= -dL +FLAGS.sh-leading-plus= -n +FLAGS.vardebug= -k -dv FROM_CMDLINE= +FLAGS.varmod-match-escape= -dv +FLAGS.varname-dot-shell= -dpv +FLAGS.varname-empty= -dv '$${:U}=cmdline-u' '=cmline-plain' # Some tests need extra post-processing. -SED_CMDS.modmisc+= -e 's,\(substitution error:\).*,\1 (details omitted),' -SED_CMDS.varshell+= -e 's,^[a-z]*sh: ,,' +SED_CMDS.opt-debug-g1= -e 's,${.CURDIR},CURDIR,' +SED_CMDS.opt-debug-g1+= -e '/Global Variables:/,/Suffixes:/d' +SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,<normalized: ...: not found>,' +SED_CMDS.varmod-subst-regex+= \ + -e 's,\(Regex compilation error:\).*,\1 (details omitted),' +SED_CMDS.varmod-edge+= -e 's, line [0-9]*:, line omitted:,' +SED_CMDS.varshell+= -e 's,^${.SHELL:T}: ,,' SED_CMDS.varshell+= -e '/command/s,No such.*,not found,' +SED_CMDS.varname-dot-shell= -e 's, = /.*, = (details omitted),' +SED_CMDS.varname-dot-shell+= -e 's,"/[^"]*","(details omitted)",' +SED_CMDS.varname-dot-shell+= -e 's,\[/[^]]*\],[(details omitted)],' + +# Some tests need an additional round of postprocessing. +POSTPROC.counter= ${TOOL_SED} -n -e '/:RELEVANT = yes/,/:RELEVANT = no/p' +POSTPROC.deptgt-suffixes= \ + ${TOOL_SED} -n -e '/^\#\*\*\* Suffixes/,/^\#\*/p' +POSTPROC.vardebug= ${TOOL_SED} -n -e '/:RELEVANT = yes/,/:RELEVANT = no/p' +POSTPROC.varmod-match-escape= ${TOOL_SED} -n -e '/^Pattern/p' +POSTPROC.varname-dot-shell= \ + awk '/\.SHELL/ || /^ParseReadLine/' +POSTPROC.varname-empty= ${TOOL_SED} -n -e '/^Var_Set/p' -e '/^out:/p' + +# Some tests reuse other tests, which makes them unnecessarily fragile. +export-all.rawout: export.mk +unexport.rawout: export.mk +unexport-env.rawout: export.mk # End of the configuration section. .MAIN: all +.-include "Makefile.inc" .-include "Makefile.config" UNIT_TESTS:= ${.PARSEDIR} @@ -96,16 +392,14 @@ OUTFILES= ${TESTS:=.out} all: ${OUTFILES} -CLEANFILES+= *.rawout *.out *.status *.tmp *.core *.tmp +CLEANFILES= *.rawout *.out *.status *.tmp *.core *.tmp CLEANFILES+= obj*.[och] lib*.a # posix1.mk CLEANFILES+= issue* .[ab]* # suffixes.mk -CLEANRECURSIVE+= dir dummy # posix1.mk +CLEANDIRS= dir dummy # posix1.mk clean: rm -f ${CLEANFILES} -.if !empty(CLEANRECURSIVE) - rm -rf ${CLEANRECURSIVE} -.endif + rm -rf ${CLEANDIRS} TEST_MAKE?= ${.MAKE} TOOL_SED?= sed @@ -120,13 +414,24 @@ LANG= C .export LANG LC_ALL .endif +.if ${.MAKE.MODE:Unormal:Mmeta} != "" +# we don't need the noise +_MKMSG_TEST= : +.endif + # the tests are actually done with sub-makes. .SUFFIXES: .mk .rawout .out .mk.rawout: - @echo ${TEST_MAKE} ${FLAGS.${.TARGET:R}:U-k} -f ${.IMPSRC} - -@cd ${.OBJDIR} && \ - { ${TEST_MAKE} ${FLAGS.${.TARGET:R}:U-k} -f ${.IMPSRC} \ - 2>&1 ; echo $$? >${.TARGET:R}.status ; } > ${.TARGET}.tmp + @${_MKMSG_TEST:Uecho '# test '} ${.PREFIX} + @set -eu; \ + cd ${.OBJDIR}; \ + env -i PATH="$$PATH" ${ENV.${.TARGET:R}} \ + ${TEST_MAKE} \ + -r -C ${.CURDIR} -f ${.IMPSRC} \ + ${FLAGS.${.TARGET:R}:U-k} \ + > ${.TARGET}.tmp 2>&1 \ + && status=$$? || status=$$?; \ + echo $$status > ${.TARGET:R}.status @mv ${.TARGET}.tmp ${.TARGET} # Post-process the test output so that the results can be compared. @@ -141,11 +446,12 @@ _SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g' _SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g' .rawout.out: - @echo postprocess ${.TARGET} @${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.TARGET:R}} \ - < ${.IMPSRC} > ${.TARGET}.tmp - @echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp - @mv ${.TARGET}.tmp ${.TARGET} + < ${.IMPSRC} > ${.TARGET}.tmp1 + @${POSTPROC.${.TARGET:R}:Ucat} < ${.TARGET}.tmp1 > ${.TARGET}.tmp2 + @rm ${.TARGET}.tmp1 + @echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp2 + @mv ${.TARGET}.tmp2 ${.TARGET} # Compare all output files test: ${OUTFILES} .PHONY @@ -168,7 +474,7 @@ accept: done .if exists(${TEST_MAKE}) -${TESTS:=.rawout}: ${TEST_MAKE} +${TESTS:=.rawout}: ${TEST_MAKE} ${.PARSEDIR}/Makefile .endif .-include <obj.mk> diff --git a/unit-tests/archive-suffix.exp b/unit-tests/archive-suffix.exp new file mode 100755 index 000000000000..05f2ac6624c4 --- /dev/null +++ b/unit-tests/archive-suffix.exp @@ -0,0 +1,2 @@ +`all' is up to date. +exit status 0 diff --git a/unit-tests/archive-suffix.mk b/unit-tests/archive-suffix.mk new file mode 100755 index 000000000000..9f7fa219c667 --- /dev/null +++ b/unit-tests/archive-suffix.mk @@ -0,0 +1,23 @@ +# $NetBSD: archive-suffix.mk,v 1.1 2020/08/29 14:47:26 rillig Exp $ +# +# Between 2020-08-23 and 2020-08-30, the below code produced an assertion +# failure in Var_Set_with_flags, triggered by Compat_Make, when setting the +# .IMPSRC of an archive node to its .TARGET. +# +# The code assumed that the .TARGET variable of every node would be set, but +# but that is not guaranteed. +# +# Between 2016-03-15 and 2016-03-16 the behavior of the below code changed. +# Until 2016-03-15, it remade the target, starting with 2016-03-16 it says +# "`all' is up to date". + +.SUFFIXES: +.SUFFIXES: .c .o + +all: lib.a(obj1.o) + +.c.o: + : making $@ + +obj1.c: + : $@ diff --git a/unit-tests/archive.exp b/unit-tests/archive.exp new file mode 100644 index 000000000000..a42b4f39e173 --- /dev/null +++ b/unit-tests/archive.exp @@ -0,0 +1,13 @@ +rm -f libprog.a +ar cru libprog.a archive.mk modmisc.mk varmisc.mk +ranlib libprog.a +ar t libprog.a +archive.mk +modmisc.mk +varmisc.mk +list-archive-wildcard: archive.mk +list-archive-wildcard: ternary.mk +depend-on-existing-member +`depend-on-nonexistent-member' is up to date. +rm -f libprog.a +exit status 0 diff --git a/unit-tests/archive.mk b/unit-tests/archive.mk new file mode 100644 index 000000000000..c3b7e919eab2 --- /dev/null +++ b/unit-tests/archive.mk @@ -0,0 +1,45 @@ +# $NetBSD: archive.mk,v 1.5 2020/08/23 17:51:24 rillig Exp $ +# +# Very basic demonstration of handling archives, based on the description +# in PSD.doc/tutorial.ms. + +ARCHIVE= libprog.${EXT.a} +FILES= archive.${EXT.mk} modmisc.${EXT.mk} varmisc.mk + +EXT.a= a +EXT.mk= mk + +MAKE_CMD= ${.MAKE} -f ${MAKEFILE} +RUN?= @set -eu; + +all: + ${RUN} ${MAKE_CMD} remove-archive + ${RUN} ${MAKE_CMD} create-archive + ${RUN} ${MAKE_CMD} list-archive + ${RUN} ${MAKE_CMD} list-archive-wildcard + ${RUN} ${MAKE_CMD} depend-on-existing-member + ${RUN} ${MAKE_CMD} depend-on-nonexistent-member + ${RUN} ${MAKE_CMD} remove-archive + +create-archive: ${ARCHIVE} +${ARCHIVE}: ${ARCHIVE}(${FILES}) + ar cru ${.TARGET} ${.OODATE} + ranlib ${.TARGET} + +list-archive: ${ARCHIVE} + ar t ${.ALLSRC} + +# XXX: I had expected that this dependency would select all *.mk files from +# the archive. Instead, the globbing is done in the current directory. +# To prevent an overly long file list, the pattern is restricted to [at]*.mk. +list-archive-wildcard: ${ARCHIVE}([at]*.mk) + ${RUN} printf '%s\n' ${.ALLSRC:O:@member@${.TARGET:Q}': '${member:Q}@} + +depend-on-existing-member: ${ARCHIVE}(archive.mk) + ${RUN} echo $@ + +depend-on-nonexistent-member: ${ARCHIVE}(nonexistent.mk) + ${RUN} echo $@ + +remove-archive: + rm -f ${ARCHIVE} diff --git a/unit-tests/cmd-interrupt.exp b/unit-tests/cmd-interrupt.exp new file mode 100755 index 000000000000..91f4439e7bea --- /dev/null +++ b/unit-tests/cmd-interrupt.exp @@ -0,0 +1,9 @@ +> cmd-interrupt-ordinary +make: *** cmd-interrupt-ordinary removed +interrupt-ordinary: ok +> cmd-interrupt-phony +make: *** cmd-interrupt-phony removed +interrupt-phony: ok +> cmd-interrupt-precious +interrupt-precious: ok +exit status 0 diff --git a/unit-tests/cmd-interrupt.mk b/unit-tests/cmd-interrupt.mk new file mode 100755 index 000000000000..033f3307bd2e --- /dev/null +++ b/unit-tests/cmd-interrupt.mk @@ -0,0 +1,50 @@ +# $NetBSD: cmd-interrupt.mk,v 1.2 2020/08/28 18:16:22 rillig Exp $ +# +# Tests for interrupting a command. +# +# If a command is interrupted (usually by the user, here by itself), the +# target is removed. This is to avoid having an unfinished target that +# would be newer than all of its sources and would therefore not be +# tried again in the next run. +# +# This happens for ordinary targets as well as for .PHONY targets, even +# though the .PHONY targets usually do not correspond to a file. +# +# To protect the target from being removed, the target has to be marked with +# the special source .PRECIOUS. These targets need to ensure for themselves +# that interrupting them does not leave an inconsistent state behind. +# +# See also: +# CompatDeleteTarget + +all: clean-before interrupt-ordinary interrupt-phony interrupt-precious clean-after + +clean-before clean-after: .PHONY + @rm -f cmd-interrupt-ordinary cmd-interrupt-phony cmd-interrupt-precious + +interrupt-ordinary: .PHONY + @${.MAKE} ${MAKEFLAGS} -f ${MAKEFILE} cmd-interrupt-ordinary || true + # The ././ is necessary to work around the file cache. + @echo ${.TARGET}: ${exists(././cmd-interrupt-ordinary) :? error : ok } + +interrupt-phony: .PHONY + @${.MAKE} ${MAKEFLAGS} -f ${MAKEFILE} cmd-interrupt-phony || true + # The ././ is necessary to work around the file cache. + @echo ${.TARGET}: ${exists(././cmd-interrupt-phony) :? error : ok } + +interrupt-precious: .PRECIOUS + @${.MAKE} ${MAKEFLAGS} -f ${MAKEFILE} cmd-interrupt-precious || true + # The ././ is necessary to work around the file cache. + @echo ${.TARGET}: ${exists(././cmd-interrupt-precious) :? ok : error } + +cmd-interrupt-ordinary: + > ${.TARGET} + @kill -INT ${.MAKE.PID} + +cmd-interrupt-phony: .PHONY + > ${.TARGET} + @kill -INT ${.MAKE.PID} + +cmd-interrupt-precious: .PRECIOUS + > ${.TARGET} + @kill -INT ${.MAKE.PID} diff --git a/unit-tests/cmdline.exp b/unit-tests/cmdline.exp new file mode 100644 index 000000000000..8e981ba5248c --- /dev/null +++ b/unit-tests/cmdline.exp @@ -0,0 +1,5 @@ +makeobjdir-direct: +show-objdir: /tmp/6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 +makeobjdir-indirect: +show-objdir: /tmp/a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45/ +exit status 0 diff --git a/unit-tests/cmdline.mk b/unit-tests/cmdline.mk new file mode 100644 index 000000000000..c12c31220cb5 --- /dev/null +++ b/unit-tests/cmdline.mk @@ -0,0 +1,37 @@ +# $NetBSD: cmdline.mk,v 1.1 2020/07/28 22:44:44 rillig Exp $ +# +# Tests for command line parsing and related special variables. + +RUN?= @set -eu; +TMPBASE?= /tmp +SUB1= a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45 # just a random UUID +SUB2= 6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 # just a random UUID +MAKE_CMD= env TMPBASE=${TMPBASE}/${SUB1} ${.MAKE} -f ${MAKEFILE} -r +DIR2= ${TMPBASE}/${SUB2} +DIR12= ${TMPBASE}/${SUB1}/${SUB2} + +all: prepare-dirs +all: makeobjdir-direct makeobjdir-indirect + +prepare-dirs: + ${RUN} rm -rf ${DIR2} ${DIR12} + ${RUN} mkdir -p ${DIR2} ${DIR12} + +# The .OBJDIR can be set via the MAKEOBJDIR command line variable. +# It must be a command line variable; an environment variable would not work. +makeobjdir-direct: + @echo $@: + ${RUN} ${MAKE_CMD} MAKEOBJDIR=${DIR2} show-objdir + +# The .OBJDIR can be set via the MAKEOBJDIR command line variable, +# and that variable could even contain the usual modifiers. +# Since the .OBJDIR=MAKEOBJDIR assignment happens very early, +# the SUB2 variable in the modifier is not defined yet and is therefore empty. +# The SUB1 in the resulting path comes from the environment variable TMPBASE, +# see MAKE_CMD. +makeobjdir-indirect: + @echo $@: + ${RUN} ${MAKE_CMD} MAKEOBJDIR='$${TMPBASE}/$${SUB2}' show-objdir + +show-objdir: + @echo $@: ${.OBJDIR:Q} diff --git a/unit-tests/cond-cmp-numeric-eq.exp b/unit-tests/cond-cmp-numeric-eq.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-cmp-numeric-eq.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-cmp-numeric-eq.mk b/unit-tests/cond-cmp-numeric-eq.mk new file mode 100755 index 000000000000..02b95ae593cb --- /dev/null +++ b/unit-tests/cond-cmp-numeric-eq.mk @@ -0,0 +1,53 @@ +# $NetBSD: cond-cmp-numeric-eq.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $ +# +# Tests for numeric comparisons with the == operator in .if conditions. + +# This comparison yields the same result, whether numeric or character-based. +.if 1 == 1 +.else +.error +.endif + +# This comparison yields the same result, whether numeric or character-based. +.if 1 == 2 +.error +.endif + +.if 2 == 1 +.error +.endif + +# Scientific notation is supported, as per strtod. +.if 2e7 == 2000e4 +.else +.error +.endif + +.if 2000e4 == 2e7 +.else +.error +.endif + +# Trailing zeroes after the decimal point are irrelevant for the numeric +# value. +.if 3.30000 == 3.3 +.else +.error +.endif + +.if 3.3 == 3.30000 +.else +.error +.endif + +# As of 2020-08-23, numeric comparison is implemented as parsing both sides +# as double, and then performing a normal comparison. The range of double is +# typically 16 or 17 significant digits, therefore these two numbers seem to +# be equal. +.if 1.000000000000000001 == 1.000000000000000002 +.else +.error +.endif + +all: + @:; diff --git a/unit-tests/cond-cmp-numeric-ge.exp b/unit-tests/cond-cmp-numeric-ge.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-cmp-numeric-ge.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-cmp-numeric-ge.mk b/unit-tests/cond-cmp-numeric-ge.mk new file mode 100755 index 000000000000..510b80e14942 --- /dev/null +++ b/unit-tests/cond-cmp-numeric-ge.mk @@ -0,0 +1,75 @@ +# $NetBSD: cond-cmp-numeric-ge.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $ +# +# Tests for numeric comparisons with the >= operator in .if conditions. + +# When both sides are equal, the >= operator always yields true. +.if 1 >= 1 +.else +.error +.endif + +# This comparison yields the same result, whether numeric or character-based. +.if 1 >= 2 +.error +.endif + +.if 2 >= 1 +.else +.error +.endif + +# If this comparison were character-based instead of numerical, the +# 5 would be >= 14 since its first digit is greater. +.if 5 >= 14 +.error +.endif + +.if 14 >= 5 +.else +.error +.endif + +# Scientific notation is supported, as per strtod. +.if 2e7 >= 1e8 +.error +.endif + +.if 1e8 >= 2e7 +.else +.error +.endif + +# Floating pointer numbers can be compared as well. +# This might be tempting to use for version numbers, but there are a few pitfalls. +.if 3.141 >= 111.222 +.error +.endif + +.if 111.222 >= 3.141 +.else +.error +.endif + +# When parsed as a version number, 3.30 is greater than 3.7. +# Since make parses numbers as plain numbers, that leads to wrong results. +# Numeric comparisons are not suited for comparing version number. +.if 3.30 >= 3.7 +.error +.endif + +.if 3.7 >= 3.30 +.else +.error +.endif + +# As of 2020-08-23, numeric comparison is implemented as parsing both sides +# as double, and then performing a normal comparison. The range of double is +# typically 16 or 17 significant digits, therefore these two numbers seem to +# be equal. +.if 1.000000000000000001 >= 1.000000000000000002 +.else +.error +.endif + +all: + @:; diff --git a/unit-tests/cond-cmp-numeric-gt.exp b/unit-tests/cond-cmp-numeric-gt.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-cmp-numeric-gt.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-cmp-numeric-gt.mk b/unit-tests/cond-cmp-numeric-gt.mk new file mode 100755 index 000000000000..24ac1eb8531b --- /dev/null +++ b/unit-tests/cond-cmp-numeric-gt.mk @@ -0,0 +1,73 @@ +# $NetBSD: cond-cmp-numeric-gt.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $ +# +# Tests for numeric comparisons with the > operator in .if conditions. + +# When both sides are equal, the > operator always yields false. +.if 1 > 1 +.error +.endif + +# This comparison yields the same result, whether numeric or character-based. +.if 1 > 2 +.error +.endif + +.if 2 > 1 +.else +.error +.endif + +# If this comparison were character-based instead of numerical, the +# 5 would be > 14 since its first digit is greater. +.if 5 > 14 +.error +.endif + +.if 14 > 5 +.else +.error +.endif + +# Scientific notation is supported, as per strtod. +.if 2e7 > 1e8 +.error +.endif + +.if 1e8 > 2e7 +.else +.error +.endif + +# Floating pointer numbers can be compared as well. +# This might be tempting to use for version numbers, but there are a few pitfalls. +.if 3.141 > 111.222 +.error +.endif + +.if 111.222 > 3.141 +.else +.error +.endif + +# When parsed as a version number, 3.30 is greater than 3.7. +# Since make parses numbers as plain numbers, that leads to wrong results. +# Numeric comparisons are not suited for comparing version number. +.if 3.30 > 3.7 +.error +.endif + +.if 3.7 > 3.30 +.else +.error +.endif + +# As of 2020-08-23, numeric comparison is implemented as parsing both sides +# as double, and then performing a normal comparison. The range of double is +# typically 16 or 17 significant digits, therefore these two numbers seem to +# be equal. +.if 1.000000000000000001 > 1.000000000000000002 +.error +.endif + +all: + @:; diff --git a/unit-tests/cond-cmp-numeric-le.exp b/unit-tests/cond-cmp-numeric-le.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-cmp-numeric-le.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-cmp-numeric-le.mk b/unit-tests/cond-cmp-numeric-le.mk new file mode 100755 index 000000000000..2e4f5e9e694b --- /dev/null +++ b/unit-tests/cond-cmp-numeric-le.mk @@ -0,0 +1,75 @@ +# $NetBSD: cond-cmp-numeric-le.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $ +# +# Tests for numeric comparisons with the <= operator in .if conditions. + +# When both sides are equal, the <= operator always yields true. +.if 1 <= 1 +.else +.error +.endif + +# This comparison yields the same result, whether numeric or character-based. +.if 1 <= 2 +.else +.error +.endif + +.if 2 <= 1 +.error +.endif + +# If this comparison were character-based instead of numerical, the +# 5 would be >= 14 since its first digit is greater. +.if 5 <= 14 +.else +.error +.endif + +.if 14 <= 5 +.error +.endif + +# Scientific notation is supported, as per strtod. +.if 2e7 <= 1e8 +.else +.error +.endif + +.if 1e8 <= 2e7 +.error +.endif + +# Floating pointer numbers can be compared as well. +# This might be tempting to use for version numbers, but there are a few pitfalls. +.if 3.141 <= 111.222 +.else +.error +.endif + +.if 111.222 <= 3.141 +.error +.endif + +# When parsed as a version number, 3.30 is greater than 3.7. +# Since make parses numbers as plain numbers, that leads to wrong results. +# Numeric comparisons are not suited for comparing version number. +.if 3.30 <= 3.7 +.else +.error +.endif + +.if 3.7 <= 3.30 +.error +.endif + +# As of 2020-08-23, numeric comparison is implemented as parsing both sides +# as double, and then performing a normal comparison. The range of double is +# typically 16 or 17 significant digits, therefore these two numbers seem to +# be equal. +.if 1.000000000000000001 <= 1.000000000000000002 +.else +.error +.endif + +all: + @:; diff --git a/unit-tests/cond-cmp-numeric-lt.exp b/unit-tests/cond-cmp-numeric-lt.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-cmp-numeric-lt.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-cmp-numeric-lt.mk b/unit-tests/cond-cmp-numeric-lt.mk new file mode 100755 index 000000000000..a5fcceddff4b --- /dev/null +++ b/unit-tests/cond-cmp-numeric-lt.mk @@ -0,0 +1,73 @@ +# $NetBSD: cond-cmp-numeric-lt.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $ +# +# Tests for numeric comparisons with the < operator in .if conditions. + +# When both sides are equal, the < operator always yields false. +.if 1 < 1 +.error +.endif + +# This comparison yields the same result, whether numeric or character-based. +.if 1 < 2 +.else +.error +.endif + +.if 2 < 1 +.error +.endif + +# If this comparison were character-based instead of numerical, the +# 5 would be > 14 since its first digit is greater. +.if 5 < 14 +.else +.error +.endif + +.if 14 < 5 +.error +.endif + +# Scientific notation is supported, as per strtod. +.if 2e7 < 1e8 +.else +.error +.endif + +.if 1e8 < 2e7 +.error +.endif + +# Floating pointer numbers can be compared as well. +# This might be tempting to use for version numbers, but there are a few pitfalls. +.if 3.141 < 111.222 +.else +.error +.endif + +.if 111.222 < 3.141 +.error +.endif + +# When parsed as a version number, 3.30 is greater than 3.7. +# Since make parses numbers as plain numbers, that leads to wrong results. +# Numeric comparisons are not suited for comparing version number. +.if 3.30 < 3.7 +.else +.error +.endif + +.if 3.7 < 3.30 +.error +.endif + +# As of 2020-08-23, numeric comparison is implemented as parsing both sides +# as double, and then performing a normal comparison. The range of double is +# typically 16 or 17 significant digits, therefore these two numbers seem to +# be equal. +.if 1.000000000000000001 < 1.000000000000000002 +.error +.endif + +all: + @:; diff --git a/unit-tests/cond-cmp-numeric-ne.exp b/unit-tests/cond-cmp-numeric-ne.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-cmp-numeric-ne.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-cmp-numeric-ne.mk b/unit-tests/cond-cmp-numeric-ne.mk new file mode 100755 index 000000000000..6f858584d139 --- /dev/null +++ b/unit-tests/cond-cmp-numeric-ne.mk @@ -0,0 +1,49 @@ +# $NetBSD: cond-cmp-numeric-ne.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $ +# +# Tests for numeric comparisons with the != operator in .if conditions. + +# When both sides are equal, the != operator always yields false. +.if 1 != 1 +.error +.endif + +# This comparison yields the same result, whether numeric or character-based. +.if 1 != 2 +.else +.error +.endif + +.if 2 != 1 +.else +.error +.endif + +# Scientific notation is supported, as per strtod. +.if 2e7 != 2000e4 +.error +.endif + +.if 2000e4 != 2e7 +.error +.endif + +# Trailing zeroes after the decimal point are irrelevant for the numeric +# value. +.if 3.30000 != 3.3 +.error +.endif + +.if 3.3 != 3.30000 +.error +.endif + +# As of 2020-08-23, numeric comparison is implemented as parsing both sides +# as double, and then performing a normal comparison. The range of double is +# typically 16 or 17 significant digits, therefore these two numbers seem to +# be equal. +.if 1.000000000000000001 != 1.000000000000000002 +.error +.endif + +all: + @:; diff --git a/unit-tests/cond-cmp-numeric.exp b/unit-tests/cond-cmp-numeric.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-cmp-numeric.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-cmp-numeric.mk b/unit-tests/cond-cmp-numeric.mk new file mode 100644 index 000000000000..409636c3c3ca --- /dev/null +++ b/unit-tests/cond-cmp-numeric.mk @@ -0,0 +1,8 @@ +# $NetBSD: cond-cmp-numeric.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for numeric comparisons in .if conditions. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/cond-cmp-string.exp b/unit-tests/cond-cmp-string.exp new file mode 100644 index 000000000000..03dcb0416898 --- /dev/null +++ b/unit-tests/cond-cmp-string.exp @@ -0,0 +1,5 @@ +make: "cond-cmp-string.mk" line 18: Malformed conditional (str != str) +make: "cond-cmp-string.mk" line 37: Malformed conditional ("string" != "str""ing") +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/cond-cmp-string.mk b/unit-tests/cond-cmp-string.mk new file mode 100644 index 000000000000..67d86b61e88d --- /dev/null +++ b/unit-tests/cond-cmp-string.mk @@ -0,0 +1,39 @@ +# $NetBSD: cond-cmp-string.mk,v 1.3 2020/08/20 18:43:19 rillig Exp $ +# +# Tests for string comparisons in .if conditions. + +# This is a simple comparison of string literals. +# Nothing surprising here. +.if "str" != "str" +.error +.endif + +# The right-hand side of the comparison may be written without quotes. +.if "str" != str +.error +.endif + +# The left-hand side of the comparison must be enclosed in quotes. +# This one is not enclosed in quotes and thus generates an error message. +.if str != str +.error +.endif + +# The left-hand side of the comparison requires a defined variable. +# The variable named "" is not defined, but applying the :U modifier to it +# makes it "kind of defined" (see VAR_KEEP). Therefore it is ok here. +.if ${:Ustr} != "str" +.error +.endif + +# Any character in a string literal may be escaped using a backslash. +# This means that "\n" does not mean a newline but a simple "n". +.if "string" != "\s\t\r\i\n\g" +.error +.endif + +# It is not possible to concatenate two string literals to form a single +# string. +.if "string" != "str""ing" +.error +.endif diff --git a/unit-tests/cond-func-commands.exp b/unit-tests/cond-func-commands.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-func-commands.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-func-commands.mk b/unit-tests/cond-func-commands.mk new file mode 100644 index 000000000000..4a098169048c --- /dev/null +++ b/unit-tests/cond-func-commands.mk @@ -0,0 +1,36 @@ +# $NetBSD: cond-func-commands.mk,v 1.3 2020/08/23 14:07:20 rillig Exp $ +# +# Tests for the commands() function in .if conditions. + +.MAIN: all + +# The target "target" does not exist yet, therefore it cannot have commands. +.if commands(target) +.error +.endif + +target: + +# Now the target exists, but it still has no commands. +.if commands(target) +.error +.endif + +target: + # not a command + +# Even after the comment, the target still has no commands. +.if commands(target) +.error +.endif + +target: + @:; + +# Finally the target has commands. +.if !commands(target) +.error +.endif + +all: + @:; diff --git a/unit-tests/cond-func-defined.exp b/unit-tests/cond-func-defined.exp new file mode 100644 index 000000000000..70c6342a02c3 --- /dev/null +++ b/unit-tests/cond-func-defined.exp @@ -0,0 +1,5 @@ +make: "cond-func-defined.mk" line 23: warning: Missing closing parenthesis for defined() +make: "cond-func-defined.mk" line 23: Malformed conditional (!defined(A B)) +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/cond-func-defined.mk b/unit-tests/cond-func-defined.mk new file mode 100644 index 000000000000..dce1399183aa --- /dev/null +++ b/unit-tests/cond-func-defined.mk @@ -0,0 +1,33 @@ +# $NetBSD: cond-func-defined.mk,v 1.3 2020/08/20 17:23:43 rillig Exp $ +# +# Tests for the defined() function in .if conditions. + +DEF= defined +${:UA B}= variable name with spaces + +.if !defined(DEF) +.error +.endif + +# Horizontal whitespace after the opening parenthesis is ignored. +.if !defined( DEF) +.error +.endif + +# Horizontal whitespace before the closing parenthesis is ignored. +.if !defined(DEF ) +.error +.endif + +# The argument of a function must not directly contain whitespace. +.if !defined(A B) +.error +.endif + +# If necessary, the whitespace can be generated by a variable expression. +.if !defined(${:UA B}) +.error +.endif + +all: + @:; diff --git a/unit-tests/cond-func-empty.exp b/unit-tests/cond-func-empty.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-func-empty.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-func-empty.mk b/unit-tests/cond-func-empty.mk new file mode 100644 index 000000000000..737403f94525 --- /dev/null +++ b/unit-tests/cond-func-empty.mk @@ -0,0 +1,8 @@ +# $NetBSD: cond-func-empty.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the empty() function in .if conditions. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/cond-func-exists.exp b/unit-tests/cond-func-exists.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-func-exists.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-func-exists.mk b/unit-tests/cond-func-exists.mk new file mode 100644 index 000000000000..6386f21fdc7b --- /dev/null +++ b/unit-tests/cond-func-exists.mk @@ -0,0 +1,42 @@ +# $NetBSD: cond-func-exists.mk,v 1.4 2020/08/28 12:59:36 rillig Exp $ +# +# Tests for the exists() function in .if conditions. + +.if !exists(.) +.error +.endif + +# The argument to the function must not be enclosed in quotes. +# Neither double quotes nor single quotes are allowed. +.if exists(".") +.error +.endif + +.if exists('.') +.error +.endif + +# The only way to escape characters that would otherwise influence the parser +# is to enclose them in a variable expression. For function arguments, +# neither the backslash nor the dollar sign act as escape character. +.if exists(\.) +.error +.endif + +.if !exists(${:U.}) +.error +.endif + +# The argument to the function can have several variable expressions. +# See cond-func.mk for the characters that cannot be used directly. +.if !exists(${.PARSEDIR}/${.PARSEFILE}) +.error +.endif + +# Whitespace is trimmed on both sides of the function argument. +.if !exists( . ) +.error +.endif + +all: + @:; diff --git a/unit-tests/cond-func-make.exp b/unit-tests/cond-func-make.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-func-make.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-func-make.mk b/unit-tests/cond-func-make.mk new file mode 100644 index 000000000000..baa0d37da726 --- /dev/null +++ b/unit-tests/cond-func-make.mk @@ -0,0 +1,8 @@ +# $NetBSD: cond-func-make.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the make() function in .if conditions. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/cond-func-target.exp b/unit-tests/cond-func-target.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-func-target.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-func-target.mk b/unit-tests/cond-func-target.mk new file mode 100644 index 000000000000..c36bcfe0a5f8 --- /dev/null +++ b/unit-tests/cond-func-target.mk @@ -0,0 +1,38 @@ +# $NetBSD: cond-func-target.mk,v 1.3 2020/08/23 14:07:20 rillig Exp $ +# +# Tests for the target() function in .if conditions. + +.MAIN: all + +# The target "target" does not exist yet. +.if target(target) +.error +.endif + +target: + +# The target exists, even though it does not have any commands. +.if !target(target) +.error +.endif + +target: + # not a command + +# Adding a comment to an existing target does not change whether the target +# is defined or not. +.if !target(target) +.error +.endif + +target: + @:; + +# Adding a command to an existing target does not change whether the target +# is defined or not. +.if !target(target) +.error +.endif + +all: + @:; diff --git a/unit-tests/cond-func.exp b/unit-tests/cond-func.exp new file mode 100644 index 000000000000..0069ed75726c --- /dev/null +++ b/unit-tests/cond-func.exp @@ -0,0 +1,9 @@ +make: "cond-func.mk" line 29: warning: Missing closing parenthesis for defined() +make: "cond-func.mk" line 29: Malformed conditional (!defined(A B)) +make: "cond-func.mk" line 44: warning: Missing closing parenthesis for defined() +make: "cond-func.mk" line 44: Malformed conditional (!defined(A&B)) +make: "cond-func.mk" line 47: warning: Missing closing parenthesis for defined() +make: "cond-func.mk" line 47: Malformed conditional (!defined(A|B)) +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/cond-func.mk b/unit-tests/cond-func.mk new file mode 100644 index 000000000000..304735241e7f --- /dev/null +++ b/unit-tests/cond-func.mk @@ -0,0 +1,63 @@ +# $NetBSD: cond-func.mk,v 1.1 2020/08/20 17:45:47 rillig Exp $ +# +# Tests for those parts of the functions in .if conditions that are common +# among several functions. +# +# The below test uses the function defined(...) since it has no side-effects, +# the other functions (except empty(...)) would work equally well. + +DEF= defined +${:UA B}= variable name with spaces +${:UVAR(value)}= variable name with parentheses +${:UVAR{value}}= variable name with braces + +.if !defined(DEF) +.error +.endif + +# Horizontal whitespace after the opening parenthesis is ignored. +.if !defined( DEF) +.error +.endif + +# Horizontal whitespace before the closing parenthesis is ignored. +.if !defined(DEF ) +.error +.endif + +# The argument of a function must not directly contain whitespace. +.if !defined(A B) +.error +.endif + +# If necessary, the whitespace can be generated by a variable expression. +.if !defined(${:UA B}) +.error +.endif + +# Characters that could be mistaken for operators must not appear directly +# in a function argument. As with whitespace, these can be generated +# indirectly. +# +# It's not entirely clear why these characters are forbidden. +# The most plausible reason seems to be typo detection. +.if !defined(A&B) +.error +.endif +.if !defined(A|B) +.error +.endif + +# Even parentheses may appear in variable names. +# They must be balanced though. +.if !defined(VAR(value)) +.error +.endif + +# Braces do not have any special meaning when parsing arguments. +.if !defined(VAR{value}) +.error +.endif + +all: + @:; diff --git a/unit-tests/cond-late.exp b/unit-tests/cond-late.exp index 3717b088c646..46c4aa2f4230 100644 --- a/unit-tests/cond-late.exp +++ b/unit-tests/cond-late.exp @@ -1,3 +1,4 @@ +make: Bad conditional expression ` != "no"' in != "no"?: yes no exit status 0 diff --git a/unit-tests/cond-late.mk b/unit-tests/cond-late.mk index 2eb02b42b920..397f5febd480 100644 --- a/unit-tests/cond-late.mk +++ b/unit-tests/cond-late.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-late.mk,v 1.1 2020/04/29 23:15:21 rillig Exp $ +# $NetBSD: cond-late.mk,v 1.2 2020/07/25 20:37:46 rillig Exp $ # # Using the :? modifier, variable expressions can contain conditional # expressions that are evaluated late. Any variables appearing in these @@ -15,9 +15,15 @@ # and then expand the variables, the output would change from the # current "yes no" to "yes yes", since both variables are non-empty. +all: cond-literal + COND.true= "yes" == "yes" COND.false= "yes" != "yes" -all: +cond-literal: @echo ${ ${COND.true} :?yes:no} @echo ${ ${COND.false} :?yes:no} + +VAR+= ${${UNDEF} != "no":?:} +.if empty(VAR:Mpattern) +.endif diff --git a/unit-tests/cond-op-and.exp b/unit-tests/cond-op-and.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-op-and.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-op-and.mk b/unit-tests/cond-op-and.mk new file mode 100644 index 000000000000..a204a227c4f0 --- /dev/null +++ b/unit-tests/cond-op-and.mk @@ -0,0 +1,27 @@ +# $NetBSD: cond-op-and.mk,v 1.3 2020/08/28 14:48:37 rillig Exp $ +# +# Tests for the && operator in .if conditions. + +.if 0 && 0 +.error +.endif + +.if 1 && 0 +.error +.endif + +.if 0 && 1 +.error +.endif + +.if !(1 && 1) +.error +.endif + +# The right-hand side is not evaluated since the left-hand side is already +# false. +.if 0 && ${UNDEF} +.endif + +all: + @:; diff --git a/unit-tests/cond-op-not.exp b/unit-tests/cond-op-not.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-op-not.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-op-not.mk b/unit-tests/cond-op-not.mk new file mode 100644 index 000000000000..ad0a0939eecf --- /dev/null +++ b/unit-tests/cond-op-not.mk @@ -0,0 +1,22 @@ +# $NetBSD: cond-op-not.mk,v 1.3 2020/08/28 14:48:37 rillig Exp $ +# +# Tests for the ! operator in .if conditions. + +# The exclamation mark negates its operand. +.if !1 +.error +.endif + +# Exclamation marks can be chained. +# This doesn't happen in practice though. +.if !!!1 +.error +.endif + +# The ! binds more tightly than the &&. +.if !!0 && 1 +.error +.endif + +all: + @:; diff --git a/unit-tests/cond-op-or.exp b/unit-tests/cond-op-or.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-op-or.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-op-or.mk b/unit-tests/cond-op-or.mk new file mode 100644 index 000000000000..696b9dd23062 --- /dev/null +++ b/unit-tests/cond-op-or.mk @@ -0,0 +1,27 @@ +# $NetBSD: cond-op-or.mk,v 1.3 2020/08/28 14:48:37 rillig Exp $ +# +# Tests for the || operator in .if conditions. + +.if 0 || 0 +.error +.endif + +.if !(1 || 0) +.error +.endif + +.if !(0 || 1) +.error +.endif + +.if !(1 || 1) +.error +.endif + +# The right-hand side is not evaluated since the left-hand side is already +# true. +.if 1 || ${UNDEF} +.endif + +all: + @:; diff --git a/unit-tests/cond-op-parentheses.exp b/unit-tests/cond-op-parentheses.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-op-parentheses.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-op-parentheses.mk b/unit-tests/cond-op-parentheses.mk new file mode 100644 index 000000000000..6c48d83dd2be --- /dev/null +++ b/unit-tests/cond-op-parentheses.mk @@ -0,0 +1,8 @@ +# $NetBSD: cond-op-parentheses.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for parentheses in .if conditions. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/cond-op.exp b/unit-tests/cond-op.exp new file mode 100644 index 000000000000..4caec4df659b --- /dev/null +++ b/unit-tests/cond-op.exp @@ -0,0 +1,5 @@ +make: "cond-op.mk" line 45: Malformed conditional ("!word" == !word) +make: "cond-op.mk" line 57: Parsing continues until here. +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/cond-op.mk b/unit-tests/cond-op.mk new file mode 100644 index 000000000000..a22e4181e8fe --- /dev/null +++ b/unit-tests/cond-op.mk @@ -0,0 +1,60 @@ +# $NetBSD: cond-op.mk,v 1.4 2020/08/28 14:07:51 rillig Exp $ +# +# Tests for operators like &&, ||, ! in .if conditions. +# +# See also: +# cond-op-and.mk +# cond-op-not.mk +# cond-op-or.mk +# cond-op-parentheses.mk + +# In make, && binds more tightly than ||, like in C. +# If make had the same precedence for both && and ||, the result would be +# different. +# If || were to bind more tightly than &&, the result would be different +# as well. +.if !(1 || 1 && 0) +.error +.endif + +# If make were to interpret the && and || operators like the shell, the +# implicit binding would be this: +.if (1 || 1) && 0 +.error +.endif + +# The precedence of the ! operator is different from C though. It has a +# lower precedence than the comparison operators. +.if !"word" == "word" +.error +.endif + +# This is how the above condition is actually interpreted. +.if !("word" == "word") +.error +.endif + +# TODO: Demonstrate that the precedence of the ! and == operators actually +# makes a difference. There is a simple example for sure, I just cannot +# wrap my head around it. + +# This condition is malformed because the '!' on the right-hand side must not +# appear unquoted. If any, it must be enclosed in quotes. +# In any case, it is not interpreted as a negation of an unquoted string. +# See CondGetString. +.if "!word" == !word +.error +.endif + +# Surprisingly, the ampersand and pipe are allowed in bare strings. +# That's another opportunity for writing confusing code. +# See CondGetString, which only has '!' in the list of stop characters. +.if "a&&b||c" != a&&b||c +.error +.endif + +# Just in case that parsing should ever stop on the first error. +.info Parsing continues until here. + +all: + @:; diff --git a/unit-tests/cond-short.mk b/unit-tests/cond-short.mk index 41d76768a404..ae441d80f7d9 100644 --- a/unit-tests/cond-short.mk +++ b/unit-tests/cond-short.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-short.mk,v 1.7 2020/07/09 22:34:09 sjg Exp $ +# $NetBSD: cond-short.mk,v 1.9 2020/08/19 22:47:09 rillig Exp $ # # Demonstrates that in conditions, the right-hand side of an && or || # is only evaluated if it can actually influence the result. @@ -66,6 +66,19 @@ VAR= # empty again, for the following tests .if 0 && !empty(:U${:!echo "unexpected exclam modifier" 1>&2 !}) .endif +# Irrelevant assignment modifiers are skipped as well. +.if 0 && ${1 2 3:L:@i@${FIRST::?=$i}@} +.endif +.if 0 && ${1 2 3:L:@i@${LAST::=$i}@} +.endif +.if 0 && ${1 2 3:L:@i@${APPENDED::+=$i}@} +.endif +.if 0 && ${echo.1 echo.2 echo.3:L:@i@${RAN::!=${i:C,.*,&; & 1>\&2,:S,., ,g}}@} +.endif +.if defined(FIRST) || defined(LAST) || defined(APPENDED) || defined(RAN) +.warning first=${FIRST} last=${LAST} appended=${APPENDED} ran=${RAN} +.endif + # The || operator. .if 1 || ${echo "unexpected or" 1>&2 :L:sh} @@ -110,6 +123,7 @@ x=Ok x=Fail .endif x!= echo 'defined(V42) && ${V42} > 0: $x' >&2; echo + # this one throws both String comparison operator and # Malformed conditional with cond.c 1.78 # indirect iV2 would expand to "" and treated as 0 @@ -119,6 +133,7 @@ x=Fail x=Ok .endif x!= echo 'defined(V66) && ( "${iV2}" < ${V42} ): $x' >&2; echo + # next two thow String comparison operator with cond.c 1.78 # indirect iV1 would expand to 42 .if 1 || ${iV1} < ${V42} @@ -127,12 +142,14 @@ x=Ok x=Fail .endif x!= echo '1 || ${iV1} < ${V42}: $x' >&2; echo + .if 1 || ${iV2:U2} < ${V42} x=Ok .else x=Fail .endif x!= echo '1 || ${iV2:U2} < ${V42}: $x' >&2; echo + # the same expressions are fine when the lhs is expanded # ${iV1} expands to 42 .if 0 || ${iV1} <= ${V42} @@ -141,6 +158,7 @@ x=Ok x=Fail .endif x!= echo '0 || ${iV1} <= ${V42}: $x' >&2; echo + # ${iV2:U2} expands to 2 .if 0 || ${iV2:U2} < ${V42} x=Ok diff --git a/unit-tests/cond-token-number.exp b/unit-tests/cond-token-number.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-token-number.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-token-number.mk b/unit-tests/cond-token-number.mk new file mode 100644 index 000000000000..30e7e84e81bb --- /dev/null +++ b/unit-tests/cond-token-number.mk @@ -0,0 +1,8 @@ +# $NetBSD: cond-token-number.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for number tokens in .if conditions. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/cond-token-plain.exp b/unit-tests/cond-token-plain.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-token-plain.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-token-plain.mk b/unit-tests/cond-token-plain.mk new file mode 100644 index 000000000000..fc13245382f1 --- /dev/null +++ b/unit-tests/cond-token-plain.mk @@ -0,0 +1,9 @@ +# $NetBSD: cond-token-plain.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for plain tokens (that is, string literals without quotes) +# in .if conditions. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/cond-token-string.exp b/unit-tests/cond-token-string.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cond-token-string.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cond-token-string.mk b/unit-tests/cond-token-string.mk new file mode 100644 index 000000000000..1a8019754824 --- /dev/null +++ b/unit-tests/cond-token-string.mk @@ -0,0 +1,8 @@ +# $NetBSD: cond-token-string.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for quoted and unquoted string literals in .if conditions. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/cond-token-var.exp b/unit-tests/cond-token-var.exp new file mode 100644 index 000000000000..eb71a43c55f3 --- /dev/null +++ b/unit-tests/cond-token-var.exp @@ -0,0 +1,7 @@ +make: "cond-token-var.mk" line 9: ok +make: "cond-token-var.mk" line 15: Malformed conditional (${UNDEF} == ${DEF}) +make: "cond-token-var.mk" line 20: Malformed conditional (${DEF} == ${UNDEF}) +make: "cond-token-var.mk" line 29: Malformed conditional (${UNDEF}) +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/cond-token-var.mk b/unit-tests/cond-token-var.mk new file mode 100644 index 000000000000..5d5bbf2c7fc4 --- /dev/null +++ b/unit-tests/cond-token-var.mk @@ -0,0 +1,34 @@ +# $NetBSD: cond-token-var.mk,v 1.3 2020/08/20 19:43:42 rillig Exp $ +# +# Tests for variables in .if conditions. + +DEF= defined + +# A defined variable may appear on either side of the comparison. +.if ${DEF} == ${DEF} +.info ok +.else +.error +.endif + +# A variable that appears on the left-hand side must be defined. +.if ${UNDEF} == ${DEF} +.error +.endif + +# A variable that appears on the right-hand side must be defined. +.if ${DEF} == ${UNDEF} +.error +.endif + +# A defined variable may appear as an expression of its own. +.if ${DEF} +.endif + +# An undefined variable generates a warning. +.if ${UNDEF} +.endif + +# The :U modifier turns an undefined variable into an ordinary expression. +.if ${UNDEF:U} +.endif diff --git a/unit-tests/counter.exp b/unit-tests/counter.exp new file mode 100644 index 000000000000..c00b3e57056d --- /dev/null +++ b/unit-tests/counter.exp @@ -0,0 +1,88 @@ +Global:RELEVANT = yes (load-time part) +Global:COUNTER = +Global:NEXT = ${COUNTER::=${COUNTER} a}${COUNTER:[#]} +Global:A = +Var_Parse: ${NEXT} with VARE_WANTRES|VARE_ASSIGN +Var_Parse: ${COUNTER::=${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN +Applying ${COUNTER::...} to "" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Var_Parse: ${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES +Modifier part: " a" +Global:COUNTER = a +Result of ${COUNTER::=${COUNTER} a} is "" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Var_Parse: ${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN +Var_Parse: ${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN +Applying ${COUNTER:[...} to " a" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Modifier part: "#" +Result of ${COUNTER:[#]} is "1" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Global:A = ${COUNTER::= a a}1 +Global:B = +Var_Parse: ${NEXT} with VARE_WANTRES|VARE_ASSIGN +Var_Parse: ${COUNTER::=${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN +Applying ${COUNTER::...} to " a" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Var_Parse: ${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES +Modifier part: " a a" +Global:COUNTER = a a +Result of ${COUNTER::=${COUNTER} a} is "" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Var_Parse: ${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN +Var_Parse: ${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN +Applying ${COUNTER:[...} to " a a" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Modifier part: "#" +Result of ${COUNTER:[#]} is "2" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Global:B = ${COUNTER::= a a a}2 +Global:C = +Var_Parse: ${NEXT} with VARE_WANTRES|VARE_ASSIGN +Var_Parse: ${COUNTER::=${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN +Applying ${COUNTER::...} to " a a" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Var_Parse: ${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES +Modifier part: " a a a" +Global:COUNTER = a a a +Result of ${COUNTER::=${COUNTER} a} is "" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Var_Parse: ${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN +Var_Parse: ${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN +Applying ${COUNTER:[...} to " a a a" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Modifier part: "#" +Result of ${COUNTER:[#]} is "3" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Global:C = ${COUNTER::= a a a a}3 +Global:RELEVANT = no +Global:RELEVANT = yes (run-time part) +Result of ${RELEVANT::=yes (run-time part)} is "" (eflags = VARE_WANTRES, vflags = none) +Var_Parse: ${A:Q} B=${B:Q} C=${C:Q} COUNTER=${COUNTER:[#]:Q} with VARE_WANTRES +Var_Parse: ${COUNTER::= a a}1 with VARE_WANTRES +Applying ${COUNTER::...} to " a a a" (eflags = VARE_WANTRES, vflags = none) +Modifier part: " a a" +Global:COUNTER = a a +Result of ${COUNTER::= a a} is "" (eflags = VARE_WANTRES, vflags = none) +Applying ${A:Q} to "1" (eflags = VARE_WANTRES, vflags = none) +QuoteMeta: [1] +Result of ${A:Q} is "1" (eflags = VARE_WANTRES, vflags = none) +Var_Parse: ${B:Q} C=${C:Q} COUNTER=${COUNTER:[#]:Q} with VARE_WANTRES +Var_Parse: ${COUNTER::= a a a}2 with VARE_WANTRES +Applying ${COUNTER::...} to " a a" (eflags = VARE_WANTRES, vflags = none) +Modifier part: " a a a" +Global:COUNTER = a a a +Result of ${COUNTER::= a a a} is "" (eflags = VARE_WANTRES, vflags = none) +Applying ${B:Q} to "2" (eflags = VARE_WANTRES, vflags = none) +QuoteMeta: [2] +Result of ${B:Q} is "2" (eflags = VARE_WANTRES, vflags = none) +Var_Parse: ${C:Q} COUNTER=${COUNTER:[#]:Q} with VARE_WANTRES +Var_Parse: ${COUNTER::= a a a a}3 with VARE_WANTRES +Applying ${COUNTER::...} to " a a a" (eflags = VARE_WANTRES, vflags = none) +Modifier part: " a a a a" +Global:COUNTER = a a a a +Result of ${COUNTER::= a a a a} is "" (eflags = VARE_WANTRES, vflags = none) +Applying ${C:Q} to "3" (eflags = VARE_WANTRES, vflags = none) +QuoteMeta: [3] +Result of ${C:Q} is "3" (eflags = VARE_WANTRES, vflags = none) +Var_Parse: ${COUNTER:[#]:Q} with VARE_WANTRES +Applying ${COUNTER:[...} to " a a a a" (eflags = VARE_WANTRES, vflags = none) +Modifier part: "#" +Result of ${COUNTER:[#]} is "4" (eflags = VARE_WANTRES, vflags = none) +Applying ${COUNTER:Q} to "4" (eflags = VARE_WANTRES, vflags = none) +QuoteMeta: [4] +Result of ${COUNTER:Q} is "4" (eflags = VARE_WANTRES, vflags = none) +A=1 B=2 C=3 COUNTER=4 +Var_Parse: ${RELEVANT::=no} with VARE_WANTRES +Applying ${RELEVANT::...} to "yes (run-time part)" (eflags = VARE_WANTRES, vflags = none) +Modifier part: "no" +Global:RELEVANT = no +exit status 0 diff --git a/unit-tests/counter.mk b/unit-tests/counter.mk new file mode 100644 index 000000000000..23e7dcc0e8d2 --- /dev/null +++ b/unit-tests/counter.mk @@ -0,0 +1,31 @@ +# $NetBSD: counter.mk,v 1.1 2020/08/02 14:53:02 rillig Exp $ +# +# Demonstrates that it is not easily possible to let make count +# the number of times a variable is actually accessed. +# +# As of 2020-08-02, the counter ends up at having 4 words, even +# though the NEXT variable is only accessed 3 times. This is +# surprising. +# +# A hint to this surprising behavior is that the variables don't +# get fully expanded. For example, A does not simply contain the +# value "1" but an additional unexpanded ${COUNTER:...} before it. + +RELEVANT= yes (load-time part) # just to filter the output + +COUNTER= # zero + +NEXT= ${COUNTER::=${COUNTER} a}${COUNTER:[#]} + +# This variable is first set to empty and then expanded. +# See parse.c, function Parse_DoVar, keyword "!Var_Exists". +A:= ${NEXT} +B:= ${NEXT} +C:= ${NEXT} + +RELEVANT= no + +all: + @: ${RELEVANT::=yes (run-time part)} + @echo A=${A:Q} B=${B:Q} C=${C:Q} COUNTER=${COUNTER:[#]:Q} + @: ${RELEVANT::=no} diff --git a/unit-tests/dep-colon.exp b/unit-tests/dep-colon.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/dep-colon.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/dep-colon.mk b/unit-tests/dep-colon.mk new file mode 100644 index 000000000000..7355c59ae557 --- /dev/null +++ b/unit-tests/dep-colon.mk @@ -0,0 +1,8 @@ +# $NetBSD: dep-colon.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the : operator in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/dep-double-colon.exp b/unit-tests/dep-double-colon.exp new file mode 100644 index 000000000000..5528abbab32d --- /dev/null +++ b/unit-tests/dep-double-colon.exp @@ -0,0 +1,5 @@ +command 1a +command 1b +command 2a +command 2b +exit status 0 diff --git a/unit-tests/dep-double-colon.mk b/unit-tests/dep-double-colon.mk new file mode 100644 index 000000000000..de4cd9bc9e33 --- /dev/null +++ b/unit-tests/dep-double-colon.mk @@ -0,0 +1,11 @@ +# $NetBSD: dep-double-colon.mk,v 1.3 2020/08/22 12:42:32 rillig Exp $ +# +# Tests for the :: operator in dependency declarations. + +all:: + @echo 'command 1a' + @echo 'command 1b' + +all:: + @echo 'command 2a' + @echo 'command 2b' diff --git a/unit-tests/dep-exclam.exp b/unit-tests/dep-exclam.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/dep-exclam.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/dep-exclam.mk b/unit-tests/dep-exclam.mk new file mode 100644 index 000000000000..2779a66ea6e3 --- /dev/null +++ b/unit-tests/dep-exclam.mk @@ -0,0 +1,8 @@ +# $NetBSD: dep-exclam.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the ! operator in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/dep-none.exp b/unit-tests/dep-none.exp new file mode 100755 index 000000000000..0d299fcca8d3 --- /dev/null +++ b/unit-tests/dep-none.exp @@ -0,0 +1,4 @@ +make: no target to make. + +make: stopped in unit-tests +exit status 2 diff --git a/unit-tests/dep-none.mk b/unit-tests/dep-none.mk new file mode 100755 index 000000000000..74b50aa29051 --- /dev/null +++ b/unit-tests/dep-none.mk @@ -0,0 +1,3 @@ +# $NetBSD: dep-none.mk,v 1.1 2020/08/22 12:51:11 rillig Exp $ +# +# Test for a Makefile that declares no target at all. diff --git a/unit-tests/dep-var.exp b/unit-tests/dep-var.exp new file mode 100755 index 000000000000..819a939ed70a --- /dev/null +++ b/unit-tests/dep-var.exp @@ -0,0 +1,2 @@ +def2 +exit status 0 diff --git a/unit-tests/dep-var.mk b/unit-tests/dep-var.mk new file mode 100755 index 000000000000..b0ce4b2c53ae --- /dev/null +++ b/unit-tests/dep-var.mk @@ -0,0 +1,33 @@ +# $NetBSD: dep-var.mk,v 1.1 2020/08/22 16:51:26 rillig Exp $ +# +# Tests for variable references in dependency declarations. +# +# Uh oh, this feels so strange that probably nobody uses it. But it seems to +# be the only way to reach the lower half of SuffExpandChildren. + +# XXX: The -dv log says: +# Var_Parse: ${UNDEF1} with VARE_UNDEFERR|VARE_WANTRES +# but no error message is generated for this line. +# The variable expression ${UNDEF1} simply expands to an empty string. +all: ${UNDEF1} + +# Using a double dollar in order to circumvent immediate variable expansion +# feels like unintended behavior. At least the manual page says nothing at +# all about defined or undefined variables in dependency lines. +# +# At the point where the expression ${DEF2} is expanded, the variable DEF2 +# is defined, so everything's fine. +all: $${DEF2} + +# This variable is not defined at all. +# XXX: The -dv log says: +# Var_Parse: ${UNDEF3} with VARE_UNDEFERR|VARE_WANTRES +# but no error message is generated for this line, just like for UNDEF1. +# The variable expression ${UNDEF3} simply expands to an empty string. +all: $${UNDEF3} + +UNDEF1= undef1 +DEF2= def2 + +undef1 def2: + @echo ${.TARGET} diff --git a/unit-tests/dep-wildcards.exp b/unit-tests/dep-wildcards.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/dep-wildcards.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/dep-wildcards.mk b/unit-tests/dep-wildcards.mk new file mode 100644 index 000000000000..c557efab3ebc --- /dev/null +++ b/unit-tests/dep-wildcards.mk @@ -0,0 +1,8 @@ +# $NetBSD: dep-wildcards.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for wildcards such as *.c in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/dep.exp b/unit-tests/dep.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/dep.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/dep.mk b/unit-tests/dep.mk new file mode 100644 index 000000000000..b2463dfc6458 --- /dev/null +++ b/unit-tests/dep.mk @@ -0,0 +1,8 @@ +# $NetBSD: dep.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for dependency declarations, such as "target: sources". + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/depsrc-exec.exp b/unit-tests/depsrc-exec.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/depsrc-exec.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/depsrc-exec.mk b/unit-tests/depsrc-exec.mk new file mode 100644 index 000000000000..a71284d33b3c --- /dev/null +++ b/unit-tests/depsrc-exec.mk @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-exec.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .EXEC in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/depsrc-ignore.exp b/unit-tests/depsrc-ignore.exp new file mode 100644 index 000000000000..162f10ddc17b --- /dev/null +++ b/unit-tests/depsrc-ignore.exp @@ -0,0 +1,11 @@ +ignore-errors begin +false ignore-errors +ignore-errors end +all begin +*** Error code 1 (ignored) +false all +*** Error code 1 (continuing) + +Stop. +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/depsrc-ignore.mk b/unit-tests/depsrc-ignore.mk new file mode 100644 index 000000000000..1be3eabe8806 --- /dev/null +++ b/unit-tests/depsrc-ignore.mk @@ -0,0 +1,67 @@ +# $NetBSD: depsrc-ignore.mk,v 1.4 2020/08/29 16:13:27 rillig Exp $ +# +# Tests for the special source .IGNORE in dependency declarations, +# which ignores any command failures for that target. +# +# Even though ignore-errors fails, the all target is still made. +# Since the all target is not marked with .IGNORE, it stops at the +# first failing command. +# +# XXX: The ordering of the messages in the output is confusing. +# The "ignored" comes much too late to be related to the "false +# ignore-errors". This is due to stdout being buffered. +# +# The "continuing" message comes from the -k option. If there had been +# other targets independent of "all", these would be built as well. +# +# Enabling the debugging option -de changes the order in which the messages +# appear. Now the "ignored" message is issued in the correct position. +# The explanation for the output reordering is that the output is buffered. +# As the manual page says, in debugging mode stdout is line buffered. +# In these tests the output is redirected to a file, therefore stdout is +# fully buffered. +# +# This is what actually happens, as of 2020-08-29. To verify it, set the +# following breakpoints in CompatRunCommand: +# +# * the "!silent" line, to see all commands. +# * the "fflush" line, to see stdout being flushed. +# * the "status = WEXITSTATUS" line +# * the "(continuing)" line +# * the "(ignored)" line +# +# The breakpoints are visited in the following order: +# +# "ignore-errors begin" +# Goes directly to STDOUT_FILENO since it is run in a child process. +# "false ignore-errors" +# Goes to the stdout buffer (CompatRunCommand, keyword "!silent") and +# the immediate call to fflush(stdout) copies it to STDOUT_FILENO. +# "*** Error code 1 (ignored)" +# Goes to the stdout buffer but is not flushed (CompatRunCommand, near +# the end). +# "ignore-errors end" +# Goes directly to STDOUT_FILENO. +# "all begin" +# Goes directly to STDOUT_FILENO. +# "false all" +# Goes to the stdout buffer, where the "*** Error code 1 (ignored)" is +# still waiting to be flushed. These two lines are flushed now. +# "*** Error code 1 (continuing)" +# Goes to the stdout buffer. +# "Stop." +# Goes to the stdout buffer. +# exit(1) +# Flushes the stdout buffer to STDOUT_FILENO. + +all: ignore-errors + +ignore-errors: .IGNORE + @echo $@ begin + false $@ + @echo $@ end + +all: + @echo $@ begin + false $@ + @echo $@ end diff --git a/unit-tests/depsrc-made.exp b/unit-tests/depsrc-made.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/depsrc-made.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/depsrc-made.mk b/unit-tests/depsrc-made.mk new file mode 100644 index 000000000000..9adeb2487496 --- /dev/null +++ b/unit-tests/depsrc-made.mk @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-made.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .MADE in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/depsrc-make.exp b/unit-tests/depsrc-make.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/depsrc-make.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/depsrc-make.mk b/unit-tests/depsrc-make.mk new file mode 100644 index 000000000000..9631f3e913d0 --- /dev/null +++ b/unit-tests/depsrc-make.mk @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-make.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .MAKE in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/depsrc-meta.exp b/unit-tests/depsrc-meta.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/depsrc-meta.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/depsrc-meta.mk b/unit-tests/depsrc-meta.mk new file mode 100644 index 000000000000..6adef4baa5de --- /dev/null +++ b/unit-tests/depsrc-meta.mk @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-meta.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .META in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/depsrc-nometa.exp b/unit-tests/depsrc-nometa.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/depsrc-nometa.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/depsrc-nometa.mk b/unit-tests/depsrc-nometa.mk new file mode 100644 index 000000000000..1942383856ab --- /dev/null +++ b/unit-tests/depsrc-nometa.mk @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-nometa.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .NOMETA in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/depsrc-nometa_cmp.exp b/unit-tests/depsrc-nometa_cmp.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/depsrc-nometa_cmp.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/depsrc-nometa_cmp.mk b/unit-tests/depsrc-nometa_cmp.mk new file mode 100644 index 000000000000..a0032d2c812a --- /dev/null +++ b/unit-tests/depsrc-nometa_cmp.mk @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-nometa_cmp.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .NOMETA_CMP in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/depsrc-nopath.exp b/unit-tests/depsrc-nopath.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/depsrc-nopath.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/depsrc-nopath.mk b/unit-tests/depsrc-nopath.mk new file mode 100644 index 000000000000..052c6f10db66 --- /dev/null +++ b/unit-tests/depsrc-nopath.mk @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-nopath.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .NOPATH in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/depsrc-notmain.exp b/unit-tests/depsrc-notmain.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/depsrc-notmain.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/depsrc-notmain.mk b/unit-tests/depsrc-notmain.mk new file mode 100644 index 000000000000..6e4a68c3b70d --- /dev/null +++ b/unit-tests/depsrc-notmain.mk @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-notmain.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .NOTMAIN in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/depsrc-optional.exp b/unit-tests/depsrc-optional.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/depsrc-optional.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/depsrc-optional.mk b/unit-tests/depsrc-optional.mk new file mode 100644 index 000000000000..074dd0b4ef8f --- /dev/null +++ b/unit-tests/depsrc-optional.mk @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-optional.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .OPTIONAL in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/depsrc-phony.exp b/unit-tests/depsrc-phony.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/depsrc-phony.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/depsrc-phony.mk b/unit-tests/depsrc-phony.mk new file mode 100644 index 000000000000..73186e81a5f9 --- /dev/null +++ b/unit-tests/depsrc-phony.mk @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-phony.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .PHONY in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/depsrc-precious.exp b/unit-tests/depsrc-precious.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/depsrc-precious.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/depsrc-precious.mk b/unit-tests/depsrc-precious.mk new file mode 100644 index 000000000000..699b83d767b1 --- /dev/null +++ b/unit-tests/depsrc-precious.mk @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-precious.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .PRECIOUS in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/depsrc-recursive.exp b/unit-tests/depsrc-recursive.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/depsrc-recursive.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/depsrc-recursive.mk b/unit-tests/depsrc-recursive.mk new file mode 100644 index 000000000000..5c629338bc8d --- /dev/null +++ b/unit-tests/depsrc-recursive.mk @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-recursive.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .RECURSIVE in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/depsrc-silent.exp b/unit-tests/depsrc-silent.exp new file mode 100644 index 000000000000..1ea493702c3b --- /dev/null +++ b/unit-tests/depsrc-silent.exp @@ -0,0 +1,4 @@ +one +two +three +exit status 0 diff --git a/unit-tests/depsrc-silent.mk b/unit-tests/depsrc-silent.mk new file mode 100644 index 000000000000..98da7b387906 --- /dev/null +++ b/unit-tests/depsrc-silent.mk @@ -0,0 +1,12 @@ +# $NetBSD: depsrc-silent.mk,v 1.3 2020/08/29 17:34:21 rillig Exp $ +# +# Tests for the special source .SILENT in dependency declarations, +# which hides the commands, no matter whether they are prefixed with +# '@' or not. + +# Without the .SILENT, the commands 'echo one' and 'echo two' would be +# written to stdout. +all: .SILENT + echo one + echo two + @echo three diff --git a/unit-tests/depsrc-use.exp b/unit-tests/depsrc-use.exp new file mode 100644 index 000000000000..c9810bda462d --- /dev/null +++ b/unit-tests/depsrc-use.exp @@ -0,0 +1,6 @@ +first 1 +first 2 +second 1 +second 2 +directly +exit status 0 diff --git a/unit-tests/depsrc-use.mk b/unit-tests/depsrc-use.mk new file mode 100644 index 000000000000..17836cd39e23 --- /dev/null +++ b/unit-tests/depsrc-use.mk @@ -0,0 +1,24 @@ +# $NetBSD: depsrc-use.mk,v 1.4 2020/08/22 12:30:57 rillig Exp $ +# +# Tests for the special source .USE in dependency declarations, +# which allows to append common commands to other targets. + +all: action directly + +first: .USE + @echo first 1 # Using ${.TARGET} here would expand to "action" + @echo first 2 + +second: .USE + @echo second 1 + @echo second 2 + +# It's possible but uncommon to have a .USE target with no commands. +# This may happen as the result of expanding a .for loop. +empty: .USE + +# It's possible but uncommon to directly make a .USEBEFORE target. +directly: .USE + @echo directly + +action: first second empty diff --git a/unit-tests/depsrc-usebefore-double-colon.exp b/unit-tests/depsrc-usebefore-double-colon.exp new file mode 100755 index 000000000000..8bbdef5ad326 --- /dev/null +++ b/unit-tests/depsrc-usebefore-double-colon.exp @@ -0,0 +1,2 @@ +double-colon early 1 +exit status 0 diff --git a/unit-tests/depsrc-usebefore-double-colon.mk b/unit-tests/depsrc-usebefore-double-colon.mk new file mode 100755 index 000000000000..448214112d32 --- /dev/null +++ b/unit-tests/depsrc-usebefore-double-colon.mk @@ -0,0 +1,30 @@ +# $NetBSD: depsrc-usebefore-double-colon.mk,v 1.1 2020/08/22 08:29:13 rillig Exp $ +# +# Tests for the special source .USEBEFORE in dependency declarations, +# combined with the double-colon dependency operator. + +all: action + +# The dependency operator :: allows commands to be added later to the same +# target. +double-colon:: .USEBEFORE + @echo double-colon early 1 + +# This command is ignored, which kind of makes sense since this dependency +# declaration has no .USEBEFORE source. +double-colon:: + @echo double-colon early 2 + +# XXX: This command is ignored even though it has a .USEBEFORE source. +# This is unexpected. +double-colon:: .USEBEFORE + @echo double-colon early 3 + +# At this point, the commands from the .USEBEFORE targets are copied to +# the "action" target. +action: double-colon + +# This command is not added to the "action" target since it comes too late. +# The commands had been copied in the previous line already. +double-colon:: + @echo double-colon late diff --git a/unit-tests/depsrc-usebefore.exp b/unit-tests/depsrc-usebefore.exp new file mode 100644 index 000000000000..c9810bda462d --- /dev/null +++ b/unit-tests/depsrc-usebefore.exp @@ -0,0 +1,6 @@ +first 1 +first 2 +second 1 +second 2 +directly +exit status 0 diff --git a/unit-tests/depsrc-usebefore.mk b/unit-tests/depsrc-usebefore.mk new file mode 100644 index 000000000000..c6be2bae0a9c --- /dev/null +++ b/unit-tests/depsrc-usebefore.mk @@ -0,0 +1,24 @@ +# $NetBSD: depsrc-usebefore.mk,v 1.5 2020/08/22 11:53:18 rillig Exp $ +# +# Tests for the special source .USEBEFORE in dependency declarations, +# which allows to prepend common commands to other targets. + +all: action directly + +first: .USEBEFORE + @echo first 1 # Using ${.TARGET} here would expand to "action" + @echo first 2 # Using ${.TARGET} here would expand to "action" + +second: .USEBEFORE + @echo second 1 + @echo second 2 + +# It is possible but uncommon to have a .USEBEFORE target with no commands. +# This may happen as the result of expanding a .for loop. +empty: .USEBEFORE + +# It is possible but uncommon to directly make a .USEBEFORE target. +directly: .USEBEFORE + @echo directly + +action: second first empty diff --git a/unit-tests/depsrc-wait.exp b/unit-tests/depsrc-wait.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/depsrc-wait.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/depsrc-wait.mk b/unit-tests/depsrc-wait.mk new file mode 100644 index 000000000000..bdf3be4608bb --- /dev/null +++ b/unit-tests/depsrc-wait.mk @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-wait.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .WAIT in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/depsrc.exp b/unit-tests/depsrc.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/depsrc.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/depsrc.mk b/unit-tests/depsrc.mk new file mode 100644 index 000000000000..d461e1111d0f --- /dev/null +++ b/unit-tests/depsrc.mk @@ -0,0 +1,9 @@ +# $NetBSD: depsrc.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for special sources (those starting with a dot, followed by +# uppercase letters) in dependency declarations, such as .PHONY. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-begin.exp b/unit-tests/deptgt-begin.exp new file mode 100644 index 000000000000..527020f6818d --- /dev/null +++ b/unit-tests/deptgt-begin.exp @@ -0,0 +1,4 @@ +: parse time +: .BEGIN +: all +exit status 0 diff --git a/unit-tests/deptgt-begin.mk b/unit-tests/deptgt-begin.mk new file mode 100644 index 000000000000..c6ca2f4aa3c7 --- /dev/null +++ b/unit-tests/deptgt-begin.mk @@ -0,0 +1,13 @@ +# $NetBSD: deptgt-begin.mk,v 1.3 2020/08/29 17:34:21 rillig Exp $ +# +# Tests for the special target .BEGIN in dependency declarations, +# which is a container for commands that are run before any other +# commands from the shell lines. + +.BEGIN: + : $@ + +all: + : $@ + +_!= echo : parse time 1>&2 diff --git a/unit-tests/deptgt-default.exp b/unit-tests/deptgt-default.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-default.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-default.mk b/unit-tests/deptgt-default.mk new file mode 100644 index 000000000000..814eaf72aed3 --- /dev/null +++ b/unit-tests/deptgt-default.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-default.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .DEFAULT in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-delete_on_error.exp b/unit-tests/deptgt-delete_on_error.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-delete_on_error.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-delete_on_error.mk b/unit-tests/deptgt-delete_on_error.mk new file mode 100644 index 000000000000..476bb8a54c52 --- /dev/null +++ b/unit-tests/deptgt-delete_on_error.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-delete_on_error.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .DELETE_ON_ERROR in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-end.exp b/unit-tests/deptgt-end.exp new file mode 100644 index 000000000000..3effe2e3182f --- /dev/null +++ b/unit-tests/deptgt-end.exp @@ -0,0 +1,4 @@ +: .BEGIN +: all +: .END +exit status 0 diff --git a/unit-tests/deptgt-end.mk b/unit-tests/deptgt-end.mk new file mode 100644 index 000000000000..b3d59a610a2f --- /dev/null +++ b/unit-tests/deptgt-end.mk @@ -0,0 +1,13 @@ +# $NetBSD: deptgt-end.mk,v 1.3 2020/08/29 17:34:21 rillig Exp $ +# +# Tests for the special target .END in dependency declarations, +# which is run after making the desired targets. + +.BEGIN: + : $@ + +.END: + : $@ + +all: + : $@ diff --git a/unit-tests/deptgt-error.exp b/unit-tests/deptgt-error.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-error.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-error.mk b/unit-tests/deptgt-error.mk new file mode 100644 index 000000000000..07bc1b5d3408 --- /dev/null +++ b/unit-tests/deptgt-error.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-error.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .ERROR in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-ignore.exp b/unit-tests/deptgt-ignore.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-ignore.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-ignore.mk b/unit-tests/deptgt-ignore.mk new file mode 100644 index 000000000000..6ace0841f28b --- /dev/null +++ b/unit-tests/deptgt-ignore.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-ignore.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .IGNORE in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-interrupt.exp b/unit-tests/deptgt-interrupt.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-interrupt.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-interrupt.mk b/unit-tests/deptgt-interrupt.mk new file mode 100644 index 000000000000..d555f864563e --- /dev/null +++ b/unit-tests/deptgt-interrupt.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-interrupt.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .INTERRUPT in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-main.exp b/unit-tests/deptgt-main.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-main.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-main.mk b/unit-tests/deptgt-main.mk new file mode 100644 index 000000000000..cf1b1b4fd340 --- /dev/null +++ b/unit-tests/deptgt-main.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-main.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .MAIN in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-makeflags.exp b/unit-tests/deptgt-makeflags.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-makeflags.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-makeflags.mk b/unit-tests/deptgt-makeflags.mk new file mode 100644 index 000000000000..958751b64cfa --- /dev/null +++ b/unit-tests/deptgt-makeflags.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-makeflags.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .MAKEFLAGS in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-no_parallel.exp b/unit-tests/deptgt-no_parallel.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-no_parallel.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-no_parallel.mk b/unit-tests/deptgt-no_parallel.mk new file mode 100644 index 000000000000..247f6a9ac8a5 --- /dev/null +++ b/unit-tests/deptgt-no_parallel.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-no_parallel.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .NO_PARALLEL in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-nopath.exp b/unit-tests/deptgt-nopath.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-nopath.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-nopath.mk b/unit-tests/deptgt-nopath.mk new file mode 100644 index 000000000000..a8cde2657dde --- /dev/null +++ b/unit-tests/deptgt-nopath.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-nopath.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .NOPATH in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-notparallel.exp b/unit-tests/deptgt-notparallel.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-notparallel.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-notparallel.mk b/unit-tests/deptgt-notparallel.mk new file mode 100644 index 000000000000..db08aa9d3558 --- /dev/null +++ b/unit-tests/deptgt-notparallel.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-notparallel.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .NOTPARALLEL in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-objdir.exp b/unit-tests/deptgt-objdir.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-objdir.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-objdir.mk b/unit-tests/deptgt-objdir.mk new file mode 100644 index 000000000000..f5e9a15d8cb3 --- /dev/null +++ b/unit-tests/deptgt-objdir.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-objdir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .OBJDIR in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-order.exp b/unit-tests/deptgt-order.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-order.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-order.mk b/unit-tests/deptgt-order.mk new file mode 100644 index 000000000000..003552f57a49 --- /dev/null +++ b/unit-tests/deptgt-order.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-order.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .ORDER in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-path-suffix.exp b/unit-tests/deptgt-path-suffix.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-path-suffix.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-path-suffix.mk b/unit-tests/deptgt-path-suffix.mk new file mode 100644 index 000000000000..3a7e697bc748 --- /dev/null +++ b/unit-tests/deptgt-path-suffix.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-path-suffix.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .PATH.suffix in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-path.exp b/unit-tests/deptgt-path.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-path.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-path.mk b/unit-tests/deptgt-path.mk new file mode 100644 index 000000000000..f74d1a46d1b1 --- /dev/null +++ b/unit-tests/deptgt-path.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-path.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .PATH in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-phony.exp b/unit-tests/deptgt-phony.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-phony.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-phony.mk b/unit-tests/deptgt-phony.mk new file mode 100644 index 000000000000..7f9909fa89a2 --- /dev/null +++ b/unit-tests/deptgt-phony.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-phony.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .PHONY in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-precious.exp b/unit-tests/deptgt-precious.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-precious.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-precious.mk b/unit-tests/deptgt-precious.mk new file mode 100644 index 000000000000..07b2bf719a4f --- /dev/null +++ b/unit-tests/deptgt-precious.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-precious.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .PRECIOUS in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-shell.exp b/unit-tests/deptgt-shell.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-shell.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-shell.mk b/unit-tests/deptgt-shell.mk new file mode 100644 index 000000000000..71b535d89115 --- /dev/null +++ b/unit-tests/deptgt-shell.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-shell.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .SHELL in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-silent.exp b/unit-tests/deptgt-silent.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-silent.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-silent.mk b/unit-tests/deptgt-silent.mk new file mode 100644 index 000000000000..9ae64567fb97 --- /dev/null +++ b/unit-tests/deptgt-silent.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-silent.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .SILENT in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-stale.exp b/unit-tests/deptgt-stale.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt-stale.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt-stale.mk b/unit-tests/deptgt-stale.mk new file mode 100644 index 000000000000..cfde5a2bfd49 --- /dev/null +++ b/unit-tests/deptgt-stale.mk @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-stale.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .STALE in dependency declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/deptgt-suffixes.exp b/unit-tests/deptgt-suffixes.exp new file mode 100644 index 000000000000..b24f47becdaf --- /dev/null +++ b/unit-tests/deptgt-suffixes.exp @@ -0,0 +1,7 @@ +#*** Suffixes: +# `.custom-null' [1] (SUFF_NULL) +# To: +# From: +# Search Path: . .. +#*** Transformations: +exit status 0 diff --git a/unit-tests/deptgt-suffixes.mk b/unit-tests/deptgt-suffixes.mk new file mode 100644 index 000000000000..791ff5eb5f03 --- /dev/null +++ b/unit-tests/deptgt-suffixes.mk @@ -0,0 +1,18 @@ +# $NetBSD: deptgt-suffixes.mk,v 1.3 2020/08/28 04:05:35 rillig Exp $ +# +# Tests for the special target .SUFFIXES in dependency declarations. +# +# See also: +# varname-dot-includes.mk +# varname-dot-libs.mk + +.MAKEFLAGS: -dg1 + +.SUFFIXES: .custom-null + +# TODO: What is the effect of this? How is it useful? +.NULL: .custom-null +.PATH.custom-null: . .. + +all: + @:; diff --git a/unit-tests/deptgt.exp b/unit-tests/deptgt.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/deptgt.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/deptgt.mk b/unit-tests/deptgt.mk new file mode 100644 index 000000000000..add21dc0baee --- /dev/null +++ b/unit-tests/deptgt.mk @@ -0,0 +1,9 @@ +# $NetBSD: deptgt.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for special targets like .BEGIN or .SUFFIXES in dependency +# declarations. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/dir-expand-path.exp b/unit-tests/dir-expand-path.exp new file mode 100644 index 000000000000..74de255298bb --- /dev/null +++ b/unit-tests/dir-expand-path.exp @@ -0,0 +1,4 @@ +dir-expand-path.dir.1/file1.src +dir-expand-path.dir.1/file2.src +dir-expand-path.dir.2/file3.src +exit status 0 diff --git a/unit-tests/dir-expand-path.mk b/unit-tests/dir-expand-path.mk new file mode 100755 index 000000000000..1ce349cce385 --- /dev/null +++ b/unit-tests/dir-expand-path.mk @@ -0,0 +1,19 @@ +# $NetBSD: dir-expand-path.mk,v 1.1 2020/08/22 21:55:54 rillig Exp $ +# +# Tests for filename expansion in the search path. + +_!= rm -rf dir-expand-path.dir.* +_!= mkdir dir-expand-path.dir.1 +_!= mkdir dir-expand-path.dir.2 +_!= touch dir-expand-path.dir.1/file1.src +_!= touch dir-expand-path.dir.1/file2.src +_!= touch dir-expand-path.dir.2/file3.src + +.PATH: dir-expand-path.dir.1 +.PATH: dir-expand-path.dir.2 + +all: *.src + @printf '%s\n' ${.ALLSRC:O} + +.END: + @rm -rf dir-expand-path.dir.* diff --git a/unit-tests/dir.exp b/unit-tests/dir.exp new file mode 100644 index 000000000000..874a081e97ab --- /dev/null +++ b/unit-tests/dir.exp @@ -0,0 +1,19 @@ +1 +2 +3 +4 +5 +13 +14 +15 +pre-patch +pre-configure +patch +configure +fetch +fetch-post +extract +extract-post +dup-1 +single-word +exit status 0 diff --git a/unit-tests/dir.mk b/unit-tests/dir.mk new file mode 100644 index 000000000000..f5926375312e --- /dev/null +++ b/unit-tests/dir.mk @@ -0,0 +1,58 @@ +# $NetBSD: dir.mk,v 1.4 2020/07/31 20:16:21 rillig Exp $ +# +# Tests for dir.c. + +# Dependency lines may use braces for expansion. +all: {one,two,three} + +one: + @echo 1 +two: + @echo 2 +three: + @echo 3 + +# The braces may start in the middle of a word. +all: f{our,ive} + +four: + @echo 4 +five: + @echo 5 +six: + @echo 6 + +# But nested braces don't work. +all: {{thi,fou}r,fif}teen + +thirteen: + @echo 13 +fourteen: + @echo 14 +fifteen: + @echo 15 + +# There may be multiple brace groups side by side. +all: {pre-,}{patch,configure} + +pre-patch patch pre-configure configure: + @echo $@ + +# Empty pieces are allowed in the braces. +all: {fetch,extract}{,-post} + +fetch fetch-post extract extract-post: + @echo $@ + +# The expansions may have duplicates. +# These are merged together because of the dependency line. +all: dup-{1,1,1,1,1,1,1} + +dup-1: + @echo $@ + +# Other than in Bash, the braces are also expanded if there is no comma. +all: {{{{{{{{{{single-word}}}}}}}}}} + +single-word: + @echo $@ diff --git a/unit-tests/directive-elif.exp b/unit-tests/directive-elif.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-elif.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-elif.mk b/unit-tests/directive-elif.mk new file mode 100644 index 000000000000..fc8bb7e7d197 --- /dev/null +++ b/unit-tests/directive-elif.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive-elif.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .elif directive. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directive-elifdef.exp b/unit-tests/directive-elifdef.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-elifdef.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-elifdef.mk b/unit-tests/directive-elifdef.mk new file mode 100644 index 000000000000..f960c1513e8e --- /dev/null +++ b/unit-tests/directive-elifdef.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive-elifdef.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .elifdef directive. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directive-elifmake.exp b/unit-tests/directive-elifmake.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-elifmake.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-elifmake.mk b/unit-tests/directive-elifmake.mk new file mode 100644 index 000000000000..c78e3aa91332 --- /dev/null +++ b/unit-tests/directive-elifmake.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive-elifmake.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .elifmake directive. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directive-elifndef.exp b/unit-tests/directive-elifndef.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-elifndef.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-elifndef.mk b/unit-tests/directive-elifndef.mk new file mode 100644 index 000000000000..19bb66c11b01 --- /dev/null +++ b/unit-tests/directive-elifndef.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive-elifndef.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .elifndef directive. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directive-elifnmake.exp b/unit-tests/directive-elifnmake.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-elifnmake.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-elifnmake.mk b/unit-tests/directive-elifnmake.mk new file mode 100644 index 000000000000..11c4b973c1c8 --- /dev/null +++ b/unit-tests/directive-elifnmake.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive-elifnmake.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .elifnmake directive. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directive-else.exp b/unit-tests/directive-else.exp new file mode 100644 index 000000000000..387577099b81 --- /dev/null +++ b/unit-tests/directive-else.exp @@ -0,0 +1,8 @@ +make: "directive-else.mk" line 10: ok +make: "directive-else.mk" line 14: ok +make: "directive-else.mk" line 20: if-less else +make: "directive-else.mk" line 26: ok +make: "directive-else.mk" line 27: warning: extra else +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/directive-else.mk b/unit-tests/directive-else.mk new file mode 100644 index 000000000000..3afa5648af67 --- /dev/null +++ b/unit-tests/directive-else.mk @@ -0,0 +1,32 @@ +# $NetBSD: directive-else.mk,v 1.3 2020/08/29 18:50:25 rillig Exp $ +# +# Tests for the .else directive. + +# The .else directive does not take any arguments. +# As of 2020-08-29, make doesn't warn about this. +.if 0 +.warning must not be reached +.else 123 +.info ok +.endif + +.if 1 +.info ok +.else 123 +.warning must not be reached +.endif + +# An .else without a corresponding .if is an error. +.else + +# Accidental extra .else directives are detected too. +.if 0 +.warning must not be reached +.else +.info ok +.else +.info After an extra .else, everything is skipped. +.endif + +all: + @:; diff --git a/unit-tests/directive-endif.exp b/unit-tests/directive-endif.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-endif.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-endif.mk b/unit-tests/directive-endif.mk new file mode 100644 index 000000000000..12b608ffbeee --- /dev/null +++ b/unit-tests/directive-endif.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive-endif.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .endif directive. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directive-error.exp b/unit-tests/directive-error.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-error.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-error.mk b/unit-tests/directive-error.mk new file mode 100644 index 000000000000..3980016221ab --- /dev/null +++ b/unit-tests/directive-error.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive-error.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .error directive. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directive-export-env.exp b/unit-tests/directive-export-env.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-export-env.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-export-env.mk b/unit-tests/directive-export-env.mk new file mode 100644 index 000000000000..49d1edb9f6fe --- /dev/null +++ b/unit-tests/directive-export-env.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive-export-env.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .export-env directive. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directive-export-literal.exp b/unit-tests/directive-export-literal.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-export-literal.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-export-literal.mk b/unit-tests/directive-export-literal.mk new file mode 100644 index 000000000000..ed3957b288d6 --- /dev/null +++ b/unit-tests/directive-export-literal.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive-export-literal.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .export-literal directive. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directive-export.exp b/unit-tests/directive-export.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-export.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-export.mk b/unit-tests/directive-export.mk new file mode 100644 index 000000000000..c7f9181b4b5a --- /dev/null +++ b/unit-tests/directive-export.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive-export.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .export directive. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directive-for-generating-endif.exp b/unit-tests/directive-for-generating-endif.exp new file mode 100755 index 000000000000..9e1301abf96f --- /dev/null +++ b/unit-tests/directive-for-generating-endif.exp @@ -0,0 +1,7 @@ +make: "directive-for-generating-endif.mk" line 21: if-less endif +make: "directive-for-generating-endif.mk" line 21: if-less endif +make: "directive-for-generating-endif.mk" line 21: if-less endif +make: "directive-for-generating-endif.mk" line 0: 3 open conditionals +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/directive-for-generating-endif.mk b/unit-tests/directive-for-generating-endif.mk new file mode 100755 index 000000000000..b4d709551003 --- /dev/null +++ b/unit-tests/directive-for-generating-endif.mk @@ -0,0 +1,25 @@ +# $NetBSD: directive-for-generating-endif.mk,v 1.1 2020/08/29 18:50:25 rillig Exp $ +# +# Test whether a .for loop can be used to generate multiple .endif +# directives to close nested .if directives. Depending on the exact +# implementation, this might have been possible. +# +# If it were possible, the 3 .if directives would perfectly match the +# 3 .endif directives generated by the .for loop. +# +# After the "included file" from the .for loop, the 3 .if directives +# are still open. +# +# See For_Run and ParseReadLine. Each .for loop is treated like a separately +# included file, and in each included file the .if/.endif directives must be +# balanced. + +.if 1 +. if 2 +. if 3 +.for i in 3 2 1 +.endif +.endfor + +all: + @:; diff --git a/unit-tests/directive-for.exp b/unit-tests/directive-for.exp new file mode 100755 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-for.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-for.mk b/unit-tests/directive-for.mk new file mode 100755 index 000000000000..5b3f299e5711 --- /dev/null +++ b/unit-tests/directive-for.mk @@ -0,0 +1,97 @@ +# $NetBSD: directive-for.mk,v 1.2 2020/09/02 22:58:59 rillig Exp $ +# +# Tests for the .for directive. + +# Using the .for loop, lists of values can be produced. +# In simple cases, the :@var@${var}@ variable modifier can be used to +# reach the same effects. +# +.undef NUMBERS +.for num in 1 2 3 +NUMBERS+= ${num} +.endfor +.if ${NUMBERS} != "1 2 3" +. error +.endif + +# The .for loop also works for multiple iteration variables. +.for name value in VARNAME value NAME2 value2 +${name}= ${value} +.endfor +.if ${VARNAME} != "value" || ${NAME2} != "value2" +. error +.endif + +# The .for loop splits the items at whitespace, taking quotes into account, +# just like the :M or :S variable modifiers. +# +# Until 2012-06-03, it had split the items exactly at whitespace, without +# taking the quotes into account. +# +.undef WORDS +.for var in one t\ w\ o "three three" 'four four' `five six` +WORDS+= counted +.endfor +.if ${WORDS:[#]} != 6 +. error +.endif + +# In the body of the .for loop, the iteration variables can be accessed +# like normal variables, even though they are not really variables. +# +# Instead, the expression ${var} is transformed into ${:U1}, ${:U2} and so +# on, before the loop body is evaluated. +# +# A notable effect of this implementation technique is that the .for +# iteration variables and the normal global variables live in separate +# namespaces and do not influence each other. +# +var= value before +var2= value before +.for var var2 in 1 2 3 4 +.endfor +.if ${var} != "value before" +. warning After the .for loop, var must still have its original value. +.endif +.if ${var2} != "value before" +. warning After the .for loop, var2 must still have its original value. +.endif + +# Everything from the paragraph above also applies if the loop body is +# empty, even if there is no actual iteration since the loop items are +# also empty. +# +var= value before +var2= value before +.for var var2 in ${:U} +.endfor +.if ${var} != "value before" +. warning After the .for loop, var must still have its original value. +.endif +.if ${var2} != "value before" +. warning After the .for loop, var2 must still have its original value. +.endif + +# Until 2008-12-21, the values of the iteration variables were simply +# inserted as plain text and then parsed as usual, which made it possible +# to achieve all kinds of strange effects. +# +# Before that date, the .for loop expanded to: +# EXPANSION+= value +# Since that date, the .for loop expands to: +# EXPANSION${:U+}= value +# +EXPANSION= before +EXPANSION+ = before +.for plus in + +EXPANSION${plus}= value +.endfor +.if ${EXPANSION} != "before" +. error This must be a make from before 2009. +.endif +.if ${EXPANSION+} != "value" +. error This must be a make from before 2009. +.endif + +all: + @:; diff --git a/unit-tests/directive-if.exp b/unit-tests/directive-if.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-if.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-if.mk b/unit-tests/directive-if.mk new file mode 100644 index 000000000000..72d7d0e2d920 --- /dev/null +++ b/unit-tests/directive-if.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive-if.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .if directive. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directive-ifdef.exp b/unit-tests/directive-ifdef.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-ifdef.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-ifdef.mk b/unit-tests/directive-ifdef.mk new file mode 100644 index 000000000000..64f073fb5ae1 --- /dev/null +++ b/unit-tests/directive-ifdef.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive-ifdef.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .ifdef directive. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directive-ifmake.exp b/unit-tests/directive-ifmake.exp new file mode 100644 index 000000000000..5fefeb252b48 --- /dev/null +++ b/unit-tests/directive-ifmake.exp @@ -0,0 +1,10 @@ +make: "directive-ifmake.mk" line 8: ok: positive condition works +make: "directive-ifmake.mk" line 19: ok: negation works +make: "directive-ifmake.mk" line 25: ok: double negation works +make: "directive-ifmake.mk" line 32: ok: both mentioned +make: "directive-ifmake.mk" line 39: ok: only those mentioned +make: "directive-ifmake.mk" line 49: Targets can even be added at parse time. +: first +: second +: late-target +exit status 0 diff --git a/unit-tests/directive-ifmake.mk b/unit-tests/directive-ifmake.mk new file mode 100644 index 000000000000..20329bc5ce25 --- /dev/null +++ b/unit-tests/directive-ifmake.mk @@ -0,0 +1,55 @@ +# $NetBSD: directive-ifmake.mk,v 1.4 2020/08/30 14:25:45 rillig Exp $ +# +# Tests for the .ifmake directive, which provides a shortcut for asking +# whether a certain target is requested to be made from the command line. + +# This is the most basic form. +.ifmake first +.info ok: positive condition works +.else +.warning positive condition fails +.endif + +# The not operator works as expected. +# An alternative interpretation were that this condition is asking whether +# the target "!first" was requested. To distinguish this, see the next test. +.ifmake !first +.warning unexpected +.else +.info ok: negation works +.endif + +# See if the exclamation mark really means "not", or if it is just part of +# the target name. +.ifmake !!first +.info ok: double negation works +.else +.warning double negation fails +.endif + +# Multiple targets can be combined using the && and || operators. +.ifmake first && second +.info ok: both mentioned +.else +.warning && does not work as expected +.endif + +# Negation also works in complex conditions. +.ifmake first && !unmentioned +.info ok: only those mentioned +.else +.warning && with ! does not work as expected +.endif + +# Using the .MAKEFLAGS special dependency target, arbitrary command +# line options can be added at parse time. This means that it is +# possible to extend the targets to be made. +.MAKEFLAGS: late-target +.ifmake late-target +.info Targets can even be added at parse time. +.else +.info No, targets cannot be added at parse time anymore. +.endif + +first second unmentioned late-target: + : $@ diff --git a/unit-tests/directive-ifndef.exp b/unit-tests/directive-ifndef.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-ifndef.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-ifndef.mk b/unit-tests/directive-ifndef.mk new file mode 100644 index 000000000000..0981f817fcfd --- /dev/null +++ b/unit-tests/directive-ifndef.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive-ifndef.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .ifndef directive. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directive-ifnmake.exp b/unit-tests/directive-ifnmake.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-ifnmake.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-ifnmake.mk b/unit-tests/directive-ifnmake.mk new file mode 100644 index 000000000000..2a20249f6c76 --- /dev/null +++ b/unit-tests/directive-ifnmake.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive-ifnmake.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .ifnmake directive. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directive-info.exp b/unit-tests/directive-info.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-info.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-info.mk b/unit-tests/directive-info.mk new file mode 100644 index 000000000000..3eb972ad7a0e --- /dev/null +++ b/unit-tests/directive-info.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive-info.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .info directive. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directive-undef.exp b/unit-tests/directive-undef.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-undef.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-undef.mk b/unit-tests/directive-undef.mk new file mode 100644 index 000000000000..ff91eeddb41f --- /dev/null +++ b/unit-tests/directive-undef.mk @@ -0,0 +1,17 @@ +# $NetBSD: directive-undef.mk,v 1.3 2020/08/23 19:30:13 rillig Exp $ +# +# Tests for the .undef directive. + +# As of 2020-07-28, .undef only undefines the first variable. +# All further variable names are silently ignored. +# See parse.c, string literal "undef". +1= 1 +2= 2 +3= 3 +.undef 1 2 3 +.if ${1:U_}${2:U_}${3:U_} != _23 +.warning $1$2$3 +.endif + +all: + @:; diff --git a/unit-tests/directive-unexport-env.exp b/unit-tests/directive-unexport-env.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-unexport-env.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-unexport-env.mk b/unit-tests/directive-unexport-env.mk new file mode 100644 index 000000000000..34d867d706ef --- /dev/null +++ b/unit-tests/directive-unexport-env.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive-unexport-env.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .unexport-env directive. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directive-unexport.exp b/unit-tests/directive-unexport.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-unexport.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-unexport.mk b/unit-tests/directive-unexport.mk new file mode 100644 index 000000000000..679387aac083 --- /dev/null +++ b/unit-tests/directive-unexport.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive-unexport.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .unexport directive. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directive-warning.exp b/unit-tests/directive-warning.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive-warning.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive-warning.mk b/unit-tests/directive-warning.mk new file mode 100644 index 000000000000..e1e636e3ec9f --- /dev/null +++ b/unit-tests/directive-warning.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive-warning.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .warning directive. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directive.exp b/unit-tests/directive.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/directive.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/directive.mk b/unit-tests/directive.mk new file mode 100644 index 000000000000..8d01a49a34cf --- /dev/null +++ b/unit-tests/directive.mk @@ -0,0 +1,8 @@ +# $NetBSD: directive.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the preprocessing directives, such as .if or .info. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/directives.exp b/unit-tests/directives.exp new file mode 100644 index 000000000000..15dd9aa9e962 --- /dev/null +++ b/unit-tests/directives.exp @@ -0,0 +1,42 @@ +make: "directives.mk" line 10: begin .export tests +make: "directives.mk" line 11: Unknown directive "expor" +make: "directives.mk" line 20: begin .export-env tests +make: "directives.mk" line 30: begin .export-literal tests +make: "directives.mk" line 40: begin .info tests +make: "directives.mk" line 41: Unknown directive "inf" +make: "directives.mk" line 42: Unknown directive "info" +make: "directives.mk" line 43: message +make: "directives.mk" line 44: indented message +make: "directives.mk" line 45: Unknown directive "information" +make: "directives.mk" line 46: message +make: "directives.mk" line 50: begin .undef tests +make: "directives.mk" line 51: Unknown directive "unde" +make: "directives.mk" line 60: begin .unexport tests +make: "directives.mk" line 61: Unknown directive "unexpor" +make: "directives.mk" line 70: begin .unexport-env tests +make: "directives.mk" line 80: begin .warning tests +make: "directives.mk" line 81: Unknown directive "warn" +make: "directives.mk" line 82: Unknown directive "warnin" +make: "directives.mk" line 83: Unknown directive "warning" +make: "directives.mk" line 84: warning: message +make: "directives.mk" line 85: Unknown directive "warnings" +make: "directives.mk" line 86: warning: messages +make: "directives.mk" line 90: begin .elif misspellings tests, part 1 +make: "directives.mk" line 100: begin .elif misspellings tests, part 2 +make: "directives.mk" line 110: begin .elif misspellings tests, part 3 +make: "directives.mk" line 120: which branch is taken on misspelling after false? +make: "directives.mk" line 127: else taken +make: "directives.mk" line 130: which branch is taken on misspelling after true? +make: "directives.mk" line 132: Unknown directive "elsif" +make: "directives.mk" line 133: 1 taken +make: "directives.mk" line 134: Unknown directive "elsif" +make: "directives.mk" line 135: 2 taken +make: "directives.mk" line 140: Unknown directive "indented" +make: "directives.mk" line 141: Unknown directive "indented" +make: "directives.mk" line 142: Unknown directive "indented" +make: "directives.mk" line 143: Unknown directive "info" +make: "directives.mk" line 150: Could not find nonexistent.mk +make: "directives.mk" line 160: end of the tests +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/directives.mk b/unit-tests/directives.mk new file mode 100644 index 000000000000..50e8e1315dda --- /dev/null +++ b/unit-tests/directives.mk @@ -0,0 +1,163 @@ +# $NetBSD: directives.mk,v 1.5 2020/07/28 20:57:59 rillig Exp $ +# +# Tests for parsing directives, in the same order as in the manual page. +# +# Each test group has 10 lines, to keep the line numbers in directives.exp +# stable. +# +# no tests for .error since it exits immediately, see ParseMessage. + +.info begin .export tests +.expor # misspelled +.export # oops: missing argument +.export VARNAME +.exporting works # oops: misspelled + + + + + +.info begin .export-env tests +.export-en # oops: misspelled +.export-env +.export-environment # oops: misspelled + + + + + + +.info begin .export-literal tests +.export-litera # oops: misspelled +.export-literal # oops: missing argument +.export-literal VARNAME +.export-literally # oops: misspelled + + + + + +.info begin .info tests +.inf # misspelled +.info # oops: message should be "missing parameter" +.info message +.info indented message +.information +.information message # oops: misspelled +.info.man: # not a message, but a suffix rule + + +.info begin .undef tests +.unde # misspelled +.undef # oops: missing argument +.undefined # oops: misspelled +.undef VARNAME + + + + + +.info begin .unexport tests +.unexpor # misspelled +.unexport # oops: missing argument +.unexport VARNAME # ok +.unexporting works # oops: misspelled + + + + + +.info begin .unexport-env tests +.unexport-en # misspelled +.unexport-env # ok +.unexport-environment # oops: misspelled + + + + + + +.info begin .warning tests +.warn # misspelled +.warnin # misspelled +.warning # oops: should be "missing argument" +.warning message # ok +.warnings # misspelled +.warnings messages # oops + + + +.info begin .elif misspellings tests, part 1 +.if 1 +.elif 1 # ok +.elsif 1 # oops: misspelled +.elseif 1 # oops: misspelled +.endif + + + + +.info begin .elif misspellings tests, part 2 +.if 0 +.elif 0 # ok +.elsif 0 # oops: misspelled +.elseif 0 # oops: misspelled +.endif + + + + +.info begin .elif misspellings tests, part 3 +.if 0 +.elsif 0 # oops: misspelled +.endif +.if 0 +.elseif 0 # oops: misspelled +.endif + + + +.info which branch is taken on misspelling after false? +.if 0 +.elsif 1 +.info 1 taken +.elsif 2 +.info 2 taken +.else +.info else taken +.endif + +.info which branch is taken on misspelling after true? +.if 1 +.elsif 1 +.info 1 taken +.elsif 2 +.info 2 taken +.else +.info else taken +.endif + +.indented none +. indented 2 spaces +. indented tab +.${:Uinfo} directives cannot be indirect + + + + + + +.include "nonexistent.mk" +.include "/dev/null" # size 0 +# including a directory technically succeeds, but shouldn't. +#.include "." # directory + + + + + + +.info end of the tests + +all: + @: diff --git a/unit-tests/envfirst.exp b/unit-tests/envfirst.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/envfirst.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/envfirst.mk b/unit-tests/envfirst.mk new file mode 100644 index 000000000000..f4744bd61285 --- /dev/null +++ b/unit-tests/envfirst.mk @@ -0,0 +1,42 @@ +# $NetBSD: envfirst.mk,v 1.2 2020/07/27 18:57:42 rillig Exp $ +# +# The -e option makes environment variables stronger than global variables. + +.if ${FROM_ENV} != value-from-env +.error ${FROM_ENV} +.endif + +# Try to override the variable; this does not have any effect. +FROM_ENV= value-from-mk +.if ${FROM_ENV} != value-from-env +.error ${FROM_ENV} +.endif + +# Try to append to the variable; this also doesn't have any effect. +FROM_ENV+= appended +.if ${FROM_ENV} != value-from-env +.error ${FROM_ENV} +.endif + +# The default assignment also cannot change the variable. +FROM_ENV?= default +.if ${FROM_ENV} != value-from-env +.error ${FROM_ENV} +.endif + +# Neither can the assignment modifiers. +.if ${FROM_ENV::=from-condition} +.endif +.if ${FROM_ENV} != value-from-env +.error ${FROM_ENV} +.endif + +# Even .undef doesn't work since it only affects the global context, +# which is independent from the environment variables. +.undef FROM_ENV +.if ${FROM_ENV} != value-from-env +.error ${FROM_ENV} +.endif + +all: + @: nothing diff --git a/unit-tests/export-all.mk b/unit-tests/export-all.mk index 200412f3b90a..c1910cac96b9 100644 --- a/unit-tests/export-all.mk +++ b/unit-tests/export-all.mk @@ -1,4 +1,4 @@ -# $Id: export-all.mk,v 1.1.1.2 2015/04/10 20:43:38 sjg Exp $ +# $Id: export-all.mk,v 1.1.1.3 2020/07/28 16:57:18 sjg Exp $ UT_OK=good UT_F=fine @@ -17,6 +17,7 @@ UT_OKDIR = ${${here}/../${here:T}:L:${M_tA}:T} .export +FILTER_CMD= grep ^UT_ .include "export.mk" UT_TEST=export-all diff --git a/unit-tests/export-variants.exp b/unit-tests/export-variants.exp new file mode 100755 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/export-variants.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/export-variants.mk b/unit-tests/export-variants.mk new file mode 100755 index 000000000000..1e254f1816cf --- /dev/null +++ b/unit-tests/export-variants.mk @@ -0,0 +1,40 @@ +# $NetBSD: export-variants.mk,v 1.2 2020/08/08 13:00:07 rillig Exp $ +# +# Test whether exported variables apply to each variant of running +# external commands: +# +# The != assignments. +# The :!cmd! modifier. +# The :sh modifier. + +SHVAR!= env | grep ^UT_ || true +.if ${SHVAR} != "" +.warning At this point, no variable should be exported. +.endif + +.if ${:!env | grep ^UT_ || true!} != "" +.warning At this point, no variable should be exported. +.endif + +.if ${env | grep ^UT_ || true:L:sh} != "" +.warning At this point, no variable should be exported. +.endif + +UT_VAR= value +.export UT_VAR + +SHVAR!= env | grep ^UT_ || true +.if ${SHVAR} != "UT_VAR=value" +.warning At this point, no variable should be exported. +.endif + +.if ${:!env | grep ^UT_ || true!} != "UT_VAR=value" +.warning At this point, some variables should be exported. +.endif + +.if ${env | grep ^UT_ || true:L:sh} != "UT_VAR=value" +.warning At this point, some variables should be exported. +.endif + +all: + @:; diff --git a/unit-tests/export.exp b/unit-tests/export.exp index 143771ce90ae..c0e9b98c5a44 100644 --- a/unit-tests/export.exp +++ b/unit-tests/export.exp @@ -1,3 +1,5 @@ +&=ampersand +MAKELEVEL=1 UT_DOLLAR=This is $UT_FU UT_FOO=foobar is fubar UT_FU=fubar diff --git a/unit-tests/export.mk b/unit-tests/export.mk index 01f69f918161..b98d175af314 100644 --- a/unit-tests/export.mk +++ b/unit-tests/export.mk @@ -1,22 +1,43 @@ -# $Id: export.mk,v 1.1.1.1 2014/08/30 18:57:18 sjg Exp $ +# $Id: export.mk,v 1.1.1.4 2020/08/08 22:34:25 sjg Exp $ UT_TEST=export UT_FOO=foo${BAR} UT_FU=fubar UT_ZOO=hoopie UT_NO=all -# belive it or not, we expect this one to come out with $UT_FU unexpanded. +# believe it or not, we expect this one to come out with $UT_FU unexpanded. UT_DOLLAR= This is $$UT_FU .export UT_FU UT_FOO .export UT_DOLLAR -# this one will be ignored + +.if !defined(.MAKE.PID) +.error .MAKE.PID must be defined +.endif +@= at +%= percent +*= asterisk +${:U!}= exclamation # A direct != would try to run "exclamation" + # as a shell command and assign its output + # to the empty variable. +&= ampersand + +# This is ignored because it is internal. .export .MAKE.PID +# These are ignored because they are local to the target. +.export @ +.export % +.export * +.export ! +.export & +# This is ignored because it is undefined. +.export UNDEFINED BAR=bar is ${UT_FU} .MAKE.EXPORTED+= UT_ZOO UT_TEST -all: - @env | grep '^UT_' | sort +FILTER_CMD?= egrep -v '^(MAKEFLAGS|PATH|PWD|SHLVL|_)=' +all: + @env | ${FILTER_CMD} | sort diff --git a/unit-tests/forloop.mk b/unit-tests/forloop.mk index dd3f07ddb19a..d61a53816df7 100644 --- a/unit-tests/forloop.mk +++ b/unit-tests/forloop.mk @@ -1,4 +1,4 @@ -# $Id: forloop.mk,v 1.1.1.2 2020/05/05 00:54:40 sjg Exp $ +# $Id: forloop.mk,v 1.1.1.3 2020/09/02 18:35:14 sjg Exp $ all: for-loop @@ -36,7 +36,7 @@ X!= echo 'a=$a b=$b' >&2; echo # Since at least 1993, iteration stops at the first newline. # Back then, the .newline variable didn't exist, therefore it was unlikely -# that a newline ever occured. +# that a newline ever occurred. .for var in a${.newline}b${.newline}c X!= echo 'newline-item=('${var:Q}')' 1>&2; echo .endfor diff --git a/unit-tests/impsrc.exp b/unit-tests/impsrc.exp index 23e8347d205d..3eb050e080c5 100644 --- a/unit-tests/impsrc.exp +++ b/unit-tests/impsrc.exp @@ -1,13 +1,13 @@ -expected: source4 -actual: source4 +expected: +actual: expected: target1.x actual: target1.x expected: target1.y actual: target1.y -expected: source1 -actual: source1 -expected: source2 -actual: source2 -expected: source1 -actual: source1 +expected: +actual: +expected: +actual: +expected: +actual: exit status 0 diff --git a/unit-tests/impsrc.mk b/unit-tests/impsrc.mk index 95ae0c34f3b5..6fb31adac938 100644 --- a/unit-tests/impsrc.mk +++ b/unit-tests/impsrc.mk @@ -1,4 +1,4 @@ -# $NetBSD: impsrc.mk,v 1.2 2014/08/30 22:21:07 sjg Exp $ +# $NetBSD: impsrc.mk,v 1.3 2020/08/07 13:43:50 rillig Exp $ # Does ${.IMPSRC} work properly? # It should be set, in order of precedence, to ${.TARGET} of: @@ -6,6 +6,8 @@ # 2) the first prerequisite from the dependency line of an explicit rule, or # 3) the first prerequisite of an explicit rule. # +# Items 2 and 3 work in GNU make. +# Items 2 and 3 are not required by POSIX 2018. all: target1.z target2 target3 target4 @@ -19,25 +21,40 @@ all: target1.z target2 target3 target4 @echo 'expected: target1.y' @echo 'actual: $<' +# (3) Making target1.z out of target1.y is done because of an inference rule. +# Therefore $< is available here. + +# (2) This is an additional dependency on the inference rule .x.y. +# The dependency target1.x comes from the inference rule, +# therefore it is available as $<. target1.y: source3 +# (1) This is an explicit dependency, not an inference rule. +# Therefore POSIX does not specify that $< be available here. target1.x: source4 - @echo 'expected: source4' + @echo 'expected: ' # either 'source4' or '' @echo 'actual: $<' +# (4) This is an explicit dependency, independent of any inference rule. +# Therefore $< is not available here. target2: source1 source2 - @echo 'expected: source1' + @echo 'expected: ' @echo 'actual: $<' +# (5) These are two explicit dependency rules. +# The first doesn't have any dependencies, only the second has. +# If any, the value of $< would be 'source2'. target3: source1 target3: source2 source3 - @echo 'expected: source2' + @echo 'expected: ' @echo 'actual: $<' +# (6) The explicit rule does not have associated commands. +# The value of $< might come from that rule, +# but it's equally fine to leave $< undefined. target4: source1 target4: - @echo 'expected: source1' + @echo 'expected: ' @echo 'actual: $<' source1 source2 source3 source4: - diff --git a/unit-tests/include-main.mk b/unit-tests/include-main.mk index 452b6102a5ce..a53dd886b800 100644 --- a/unit-tests/include-main.mk +++ b/unit-tests/include-main.mk @@ -1,11 +1,11 @@ -# $NetBSD: include-main.mk,v 1.1 2020/05/17 12:36:26 rillig Exp $ +# $NetBSD: include-main.mk,v 1.2 2020/07/27 20:55:59 rillig Exp $ # # Demonstrates that the .INCLUDEDFROMFILE magic variable does not behave # as described in the manual page. # # The manual page says that it is the "filename of the file this Makefile # was included from", while in reality it is the "filename in which the -# latest .include happened". +# latest .include happened". See parse.c, function ParseSetIncludeFile. # .if !defined(.INCLUDEDFROMFILE) diff --git a/unit-tests/lint.exp b/unit-tests/lint.exp new file mode 100755 index 000000000000..9cd0eeb1ddf4 --- /dev/null +++ b/unit-tests/lint.exp @@ -0,0 +1,4 @@ +make: In the :@ modifier of "VAR", the variable name "${:Ubar:S,b,v,}" must not contain a dollar. +y@:Q} +xvaluey +exit status 0 diff --git a/unit-tests/lint.mk b/unit-tests/lint.mk new file mode 100755 index 000000000000..56cd6be27860 --- /dev/null +++ b/unit-tests/lint.mk @@ -0,0 +1,17 @@ +# $NetBSD: lint.mk,v 1.2 2020/08/08 13:00:07 rillig Exp $ +# +# Demonstrates stricter checks that are only enabled in the lint mode, +# using the -dL option. + +# Ouch: as of 2020-08-03, make exits successfully even though the error +# message has been issued as PARSE_FATAL. + +# Ouch: as of 2020-08-03, the variable is malformed and parsing stops +# for a moment, but is continued after the wrongly-guessed end of the +# variable, which echoes "y@:Q}". + +all: mod-loop-varname + +mod-loop-varname: + @echo ${VAR:Uvalue:@${:Ubar:S,b,v,}@x${var}y@:Q} + @echo ${VAR:Uvalue:@!@x$!y@:Q} # surprisingly allowed diff --git a/unit-tests/make-exported.exp b/unit-tests/make-exported.exp new file mode 100755 index 000000000000..7408f16c4e6c --- /dev/null +++ b/unit-tests/make-exported.exp @@ -0,0 +1,3 @@ +-literal=make-exported-value +UT_VAR= +exit status 0 diff --git a/unit-tests/make-exported.mk b/unit-tests/make-exported.mk new file mode 100755 index 000000000000..36f4b356ae53 --- /dev/null +++ b/unit-tests/make-exported.mk @@ -0,0 +1,16 @@ +# $NetBSD: make-exported.mk,v 1.1 2020/08/09 12:59:16 rillig Exp $ +# +# As of 2020-08-09, the code in Var_Export is shared between the .export +# directive and the .MAKE.EXPORTED variable. This leads to non-obvious +# behavior for certain variable assignments. + +-env= make-exported-value +-literal= make-exported-value +UT_VAR= ${UNEXPANDED} + +# The following behavior is probably not intended. +.MAKE.EXPORTED= -env # like .export-env +.MAKE.EXPORTED= -literal UT_VAR # like .export-literal PATH + +all: + @env | sort | grep -E '^UT_|make-exported-value' || true diff --git a/unit-tests/moderrs.exp b/unit-tests/moderrs.exp index cb51aa09d909..f017cae4633a 100644 --- a/unit-tests/moderrs.exp +++ b/unit-tests/moderrs.exp @@ -10,7 +10,132 @@ VAR:S,V,v,=Thevariable Expect: Unclosed variable specification for VAR make: Unclosed variable specification after complex modifier (expecting '}') for VAR VAR:S,V,v,=Thevariable -Expect: Unclosed substitution for VAR (, missing) -make: Unclosed substitution for VAR (, missing) +Expect: Unfinished modifier for VAR (',' missing) +make: Unfinished modifier for VAR (',' missing) VAR:S,V,v= +Expect: 2 errors about missing @ delimiter +make: Unfinished modifier for UNDEF ('@' missing) + +make: Unfinished modifier for UNDEF ('@' missing) + +1 2 3 +modloop-close: +make: Unclosed variable specification (expecting '}') for "UNDEF" (value "1}... 2}... 3}...") modifier @ +1}... 2}... 3}... +1}... 2}... 3}... +Expect: 2 errors about missing ] delimiter +make: Unfinished modifier for UNDEF (']' missing) + +make: Unfinished modifier for UNDEF (']' missing) + +13= +12345=ok +Expect: 2 errors about missing ! delimiter +make: Unfinished modifier for VARNAME ('!' missing) + +make: Unfinished modifier for ! ('!' missing) + +mod-subst-delimiter: +make: Missing delimiter for :S modifier + +make: Unfinished modifier for VAR (',' missing) + +make: Unfinished modifier for VAR (',' missing) + +make: Unfinished modifier for VAR (',' missing) + +make: Unfinished modifier for VAR (',' missing) + +make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier S +TheVariable +TheVariable +make: Missing delimiter for :S modifier +1: +make: Unfinished modifier for VAR (',' missing) +2: +make: Unfinished modifier for VAR (',' missing) +3: +make: Unfinished modifier for VAR (',' missing) + +make: Unfinished modifier for VAR (',' missing) + +make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier S +TheVariable +TheVariable +mod-regex-delimiter: +make: Missing delimiter for :C modifier + +make: Unfinished modifier for VAR (',' missing) + +make: Unfinished modifier for VAR (',' missing) + +make: Unfinished modifier for VAR (',' missing) + +make: Unfinished modifier for VAR (',' missing) + +make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier C +TheVariable +TheVariable +make: Missing delimiter for :C modifier +1: +make: Unfinished modifier for VAR (',' missing) +2: +make: Unfinished modifier for VAR (',' missing) +3: +make: Unfinished modifier for VAR (',' missing) + +make: Unfinished modifier for VAR (',' missing) + +make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier C +TheVariable +TheVariable +mod-regex-undefined-subexpression: +one one 2 3 5 8 one3 2one 34 +make: No match for subexpression \2 +make: No match for subexpression \2 +make: No match for subexpression \1 +make: No match for subexpression \2 +make: No match for subexpression \1 +()+() ()+() ()+() 3 5 8 (3)+() ()+(1) 34 +mod-ts-parse: +112358132134 +15152535558513521534 +make: Bad modifier `:ts\65oct' for FIB +65oct} +make: Bad modifier `:tsxy' for FIB +xy} +mod-t-parse: +make: Bad modifier `:t' for FIB + +make: Bad modifier `:txy' for FIB +y} +make: Bad modifier `:t' for FIB + +make: Bad modifier `:t' for FIB +M*} +mod-ifelse-parse: +make: Unfinished modifier for FIB (':' missing) + +make: Unfinished modifier for FIB (':' missing) + +make: Unfinished modifier for FIB ('}' missing) + +make: Unfinished modifier for FIB ('}' missing) + +then +mod-remember-parse: +1 1 2 3 5 8 13 21 34 +make: Unknown modifier '_' + +mod-sysv-parse: +make: Unknown modifier '3' +make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3 + +make: Unknown modifier '3' +make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3 + +make: Unknown modifier '3' +make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3 + +1 1 2 x3 5 8 1x3 21 34 exit status 0 diff --git a/unit-tests/moderrs.mk b/unit-tests/moderrs.mk index d0eb17ff6df6..931158c8ecb9 100644 --- a/unit-tests/moderrs.mk +++ b/unit-tests/moderrs.mk @@ -1,4 +1,4 @@ -# $Id: moderrs.mk,v 1.1.1.1 2014/08/30 18:57:18 sjg Exp $ +# $Id: moderrs.mk,v 1.1.1.8 2020/08/26 16:40:43 sjg Exp $ # # various modifier error tests @@ -8,7 +8,20 @@ MOD_UNKN=Z MOD_TERM=S,V,v MOD_S:= ${MOD_TERM}, -all: modunkn modunknV varterm vartermV modtermV +FIB= 1 1 2 3 5 8 13 21 34 + +all: modunkn modunknV varterm vartermV modtermV modloop +all: modloop-close +all: modwords +all: modexclam +all: mod-subst-delimiter +all: mod-regex-delimiter +all: mod-regex-undefined-subexpression +all: mod-ts-parse +all: mod-t-parse +all: mod-ifelse-parse +all: mod-remember-parse +all: mod-sysv-parse modunkn: @echo "Expect: Unknown modifier 'Z'" @@ -27,5 +40,137 @@ vartermV: @echo VAR:${MOD_TERM},=${VAR:${MOD_S} modtermV: - @echo "Expect: Unclosed substitution for VAR (, missing)" + @echo "Expect: Unfinished modifier for VAR (',' missing)" -@echo "VAR:${MOD_TERM}=${VAR:${MOD_TERM}}" + +modloop: + @echo "Expect: 2 errors about missing @ delimiter" + @echo ${UNDEF:U1 2 3:@var} + @echo ${UNDEF:U1 2 3:@var@...} + @echo ${UNDEF:U1 2 3:@var@${var}@} + +# The closing brace after the ${var} is part of the replacement string. +# In ParseModifierPart, braces and parentheses don't have to be balanced. +# This is contrary to the :M, :N modifiers, where both parentheses and +# braces must be balanced. +# This is also contrary to the SysV modifier, where only the actually +# used delimiter (either braces or parentheses) must be balanced. +modloop-close: + @echo $@: + @echo ${UNDEF:U1 2 3:@var@${var}}...@ + @echo ${UNDEF:U1 2 3:@var@${var}}...@} + +modwords: + @echo "Expect: 2 errors about missing ] delimiter" + @echo ${UNDEF:U1 2 3:[} + @echo ${UNDEF:U1 2 3:[#} + + # out of bounds => empty + @echo 13=${UNDEF:U1 2 3:[13]} + + # Word index out of bounds. + # + # On LP64I32, strtol returns LONG_MAX, + # which is then truncated to int (undefined behavior), + # typically resulting in -1. + # This -1 is interpreted as "the last word". + # + # On ILP32, strtol returns LONG_MAX, + # which is a large number. + # This results in a range from LONG_MAX - 1 to 3, + # which is empty. + @echo 12345=${UNDEF:U1 2 3:[123451234512345123451234512345]:S,^$,ok,:S,^3$,ok,} + +modexclam: + @echo "Expect: 2 errors about missing ! delimiter" + @echo ${VARNAME:!echo} + # When the final exclamation mark is missing, there is no + # fallback to the SysV substitution modifier. + # If there were a fallback, the output would be "exclam", + # and the above would have produced an "Unknown modifier '!'". + @echo ${!:L:!=exclam} + +mod-subst-delimiter: + @echo $@: + @echo ${VAR:S + @echo ${VAR:S, + @echo ${VAR:S,from + @echo ${VAR:S,from, + @echo ${VAR:S,from,to + @echo ${VAR:S,from,to, + @echo ${VAR:S,from,to,} + @echo 1: ${VAR:S + @echo 2: ${VAR:S, + @echo 3: ${VAR:S,from + @echo ${VAR:S,from, + @echo ${VAR:S,from,to + @echo ${VAR:S,from,to, + @echo ${VAR:S,from,to,} + +mod-regex-delimiter: + @echo $@: + @echo ${VAR:C + @echo ${VAR:C, + @echo ${VAR:C,from + @echo ${VAR:C,from, + @echo ${VAR:C,from,to + @echo ${VAR:C,from,to, + @echo ${VAR:C,from,to,} + @echo 1: ${VAR:C + @echo 2: ${VAR:C, + @echo 3: ${VAR:C,from + @echo ${VAR:C,from, + @echo ${VAR:C,from,to + @echo ${VAR:C,from,to, + @echo ${VAR:C,from,to,} + +# In regular expressions with alternatives, not all capturing groups are +# always set; some may be missing. Warn about these. +# +# Since there is no way to turn off this warning, the combination of +# alternative matches and capturing groups is not widely used. +# +# A newly added modifier 'U' such as in :C,(a.)|(b.),\1\2,U might be added +# for treating undefined capturing groups as empty, but that would create a +# syntactical ambiguity since the :S and :C modifiers are open-ended (see +# mod-subst-chain). Luckily the modifier :U does not make sense after :C, +# therefore this case does not happen in practice. +# The sub-modifier for the :C modifier would have to be chosen wisely. +mod-regex-undefined-subexpression: + @echo $@: + @echo ${FIB:C,1(.*),one\1,} # all ok + @echo ${FIB:C,1(.*)|2(.*),(\1)+(\2),:Q} # no match for subexpression + +mod-ts-parse: + @echo $@: + @echo ${FIB:ts} + @echo ${FIB:ts\65} # octal 065 == U+0035 == '5' + @echo ${FIB:ts\65oct} # bad modifier + @echo ${FIB:tsxy} # modifier too long + +mod-t-parse: + @echo $@: + @echo ${FIB:t + @echo ${FIB:txy} + @echo ${FIB:t} + @echo ${FIB:t:M*} + +mod-ifelse-parse: + @echo $@: + @echo ${FIB:? + @echo ${FIB:?then + @echo ${FIB:?then: + @echo ${FIB:?then:else + @echo ${FIB:?then:else} + +mod-remember-parse: + @echo $@: + @echo ${FIB:_} # ok + @echo ${FIB:__} # modifier name too long + +mod-sysv-parse: + @echo $@: + @echo ${FIB:3 + @echo ${FIB:3= + @echo ${FIB:3=x3 + @echo ${FIB:3=x3} # ok diff --git a/unit-tests/modmatch.exp b/unit-tests/modmatch.exp index a7bf8b748f5b..fcaf6c02ed69 100644 --- a/unit-tests/modmatch.exp +++ b/unit-tests/modmatch.exp @@ -14,7 +14,4 @@ LIB=e X_LIBS:M${LIB${LIB:tu}} is "/tmp/libe.a" LIB=e X_LIBS:M*/lib${LIB}.a is "/tmp/libe.a" LIB=e X_LIBS:M*/lib${LIB}.a:tu is "/TMP/LIBE.A" Mscanner=OK -Upper=One Two Three Four -Lower=five six seven -nose=One Three five exit status 0 diff --git a/unit-tests/modmatch.mk b/unit-tests/modmatch.mk index c631bbd3440f..f15b3d699a8e 100644 --- a/unit-tests/modmatch.mk +++ b/unit-tests/modmatch.mk @@ -1,3 +1,6 @@ +# $NetBSD: modmatch.mk,v 1.8 2020/08/16 20:03:53 rillig Exp $ +# +# Tests for the :M and :S modifiers. X=a b c d e @@ -15,7 +18,7 @@ res = no res = OK .endif -all: show-libs check-cclass slow +all: show-libs show-libs: @for x in $X; do ${.MAKE} -f ${MAKEFILE} show LIB=$$x; done @@ -25,15 +28,3 @@ show: @echo 'LIB=${LIB} X_LIBS:M$${LIB$${LIB:tu}} is "${X_LIBS:M${LIB${LIB:tu}}}"' @echo 'LIB=${LIB} X_LIBS:M*/lib$${LIB}.a is "${X_LIBS:M*/lib${LIB}.a}"' @echo 'LIB=${LIB} X_LIBS:M*/lib$${LIB}.a:tu is "${X_LIBS:M*/lib${LIB}.a:tu}"' - -LIST= One Two Three Four five six seven - -check-cclass: - @echo Upper=${LIST:M[A-Z]*} - @echo Lower=${LIST:M[^A-Z]*} - @echo nose=${LIST:M[^s]*[ex]} - -# Before 2020-06-13, this expression took quite a long time in Str_Match, -# calling itself 601080390 times for 16 asterisks. -slow: .PHONY - @:;: ${:U****************:M****************b:Q} diff --git a/unit-tests/modmisc.exp b/unit-tests/modmisc.exp index 619ae3c7fc11..94f131052fdc 100644 --- a/unit-tests/modmisc.exp +++ b/unit-tests/modmisc.exp @@ -1,3 +1,4 @@ +make: Unknown modifier '$' path=':/bin:/tmp::/:.:/no/such/dir:.' path='/bin:/tmp:/:/no/such/dir' path='/bin:/tmp:/:/no/such/dir' @@ -7,45 +8,14 @@ path_/usr/xbin=/opt/xbin/ paths=/bin /tmp / /no/such/dir /opt/xbin PATHS=/BIN /TMP / /NO/SUCH/DIR /OPT/XBIN The answer is 42 -dirname of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'a/b . . a.b . . . . .' -basename of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'c def a.b.c c a a.a .gitignore a a.a' -suffix of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'c b/c a gitignore a' -root of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'a/b/c def a.b a a a a a' S: C: @: S:empty C:empty @: -:a b b c: -:a b b c: -: b c: -:a c: -:x__ 3 x__ 3: -:a b b c: -:a b b c: -: b c: -make: RE substitution error: (details omitted) -make: Unclosed substitution for (, missing) -:C,word,____,:Q}: -:a c: -:x__ 3 x__ 3: -:+one+ +two+ +three+: -mod-at-resolve:w1d2d3w w2i3w w1i2d3 2i${RES3}w w1d2d3 2i${RES3} 1i${RES2}w: -mod-subst-dollar:$1: -mod-subst-dollar:$2: -mod-subst-dollar:$3: -mod-subst-dollar:$4: -mod-subst-dollar:$5: -mod-subst-dollar:$6: -mod-subst-dollar:$7: -mod-subst-dollar:$8: -mod-subst-dollar:U8: -mod-subst-dollar:$$$$: -mod-loop-dollar:1: -mod-loop-dollar:${word}: -mod-loop-dollar:$3$: -mod-loop-dollar:$${word}$: -mod-loop-dollar:$$5$$: -mod-loop-dollar:$$${word}$$: +mod-quote: new + +line +mod-break-many-words: 500 exit status 0 diff --git a/unit-tests/modmisc.mk b/unit-tests/modmisc.mk index d0c334342934..f2977da8a0ba 100644 --- a/unit-tests/modmisc.mk +++ b/unit-tests/modmisc.mk @@ -1,4 +1,4 @@ -# $Id: modmisc.mk,v 1.1.1.3 2020/07/09 22:35:19 sjg Exp $ +# $Id: modmisc.mk,v 1.1.1.15 2020/08/23 15:52:08 sjg Exp $ # # miscellaneous modifier tests @@ -15,13 +15,15 @@ MOD_HOMES=S,/home/,/homes/, MOD_OPT=@d@$${exists($$d):?$$d:$${d:S,/usr,/opt,}}@ MOD_SEP=S,:, ,g -all: modvar modvarloop modsysv mod-HTE emptyvar undefvar -all: mod-S mod-C mod-at-varname mod-at-resolve -all: mod-subst-dollar mod-loop-dollar +all: modvar modvarloop modsysv emptyvar undefvar +all: mod-quote +all: mod-break-many-words +# See also sysv.mk. modsysv: @echo "The answer is ${libfoo.a:L:libfoo.a=42}" +# Demonstrates modifiers that are given indirectly from a variable. modvar: @echo "path='${path}'" @echo "path='${path:${MOD_NODOT}}'" @@ -39,13 +41,6 @@ modvarloop: @echo "paths=${paths}" @echo "PATHS=${paths:tu}" -PATHNAMES= a/b/c def a.b.c a.b/c a a.a .gitignore a a.a -mod-HTE: - @echo "dirname of '"${PATHNAMES:Q}"' is '"${PATHNAMES:H:Q}"'" - @echo "basename of '"${PATHNAMES:Q}"' is '"${PATHNAMES:T:Q}"'" - @echo "suffix of '"${PATHNAMES:Q}"' is '"${PATHNAMES:E:Q}"'" - @echo "root of '"${PATHNAMES:Q}"' is '"${PATHNAMES:R:Q}"'" - # When a modifier is applied to the "" variable, the result is discarded. emptyvar: @echo S:${:S,^$,empty,} @@ -61,67 +56,37 @@ undefvar: @echo C:${:U:C,^$,empty,} @echo @:${:U:@var@empty@} -mod-S: - @echo :${:Ua b b c:S,a b,,:Q}: - @echo :${:Ua b b c:S,a b,,1:Q}: - @echo :${:Ua b b c:S,a b,,W:Q}: - @echo :${:Ua b b c:S,b,,g:Q}: - @echo :${:U1 2 3 1 2 3:S,1 2,___,Wg:S,_,x,:Q}: - -mod-C: - @echo :${:Ua b b c:C,a b,,:Q}: - @echo :${:Ua b b c:C,a b,,1:Q}: - @echo :${:Ua b b c:C,a b,,W:Q}: - @echo :${:Uword1 word2:C,****,____,g:C,word,____,:Q}: - @echo :${:Ua b b c:C,b,,g:Q}: - @echo :${:U1 2 3 1 2 3:C,1 2,___,Wg:C,_,x,:Q}: - -# In the :@ modifier, the name of the loop variable can even be generated -# dynamically. There's no practical use-case for this, and hopefully nobody -# will ever depend on this, but technically it's possible. -mod-at-varname: - @echo :${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@:Q}: - -# The :@ modifier resolves the variables a little more often than expected. -# In particular, it resolves _all_ variables from the context, and not only -# the loop variable (in this case v). -# -# The d means direct reference, the i means indirect reference. -RESOLVE= ${RES1} $${RES1} -RES1= 1d${RES2} 1i$${RES2} -RES2= 2d${RES3} 2i$${RES3} -RES3= 3 - -mod-at-resolve: - @echo $@:${RESOLVE:@v@w${v}w@:Q}: - -# No matter how many dollar characters there are, they all get merged -# into a single dollar by the :S modifier. -mod-subst-dollar: - @echo $@:${:U1:S,^,$,:Q}: - @echo $@:${:U2:S,^,$$,:Q}: - @echo $@:${:U3:S,^,$$$,:Q}: - @echo $@:${:U4:S,^,$$$$,:Q}: - @echo $@:${:U5:S,^,$$$$$,:Q}: - @echo $@:${:U6:S,^,$$$$$$,:Q}: - @echo $@:${:U7:S,^,$$$$$$$,:Q}: - @echo $@:${:U8:S,^,$$$$$$$$,:Q}: -# This generates no dollar at all: - @echo $@:${:UU8:S,^,${:U$$$$$$$$},:Q}: -# Here is an alternative way to generate dollar characters. -# It's unexpectedly complicated though. - @echo $@:${:U:range=5:ts\x24:C,[0-9],,g:Q}: - -# Demonstrate that it is possible to generate dollar characters using the -# :@ modifier. + +mod-quote: + @echo $@: new${.newline:Q}${.newline:Q}line + +# Cover the bmake_realloc in brk_string. +mod-break-many-words: + @echo $@: ${UNDEF:U:range=500:[#]} + +# To apply a modifier indirectly via another variable, the whole +# modifier must be put into a single variable. +.if ${value:L:${:US}${:U,value,replacement,}} != "S,value,replacement,}" +.warning unexpected +.endif + +# Adding another level of indirection (the 2 nested :U expressions) helps. +.if ${value:L:${:U${:US}${:U,value,replacement,}}} != "replacement" +.warning unexpected +.endif + +# Multiple indirect modifiers can be applied one after another as long as +# they are separated with colons. +.if ${value:L:${:US,a,A,}:${:US,e,E,}} != "vAluE" +.warning unexpected +.endif + +# An indirect variable that evaluates to the empty string is allowed though. +# This makes it possible to define conditional modifiers, like this: # -# These are edge cases that could have resulted in a parse error as well -# since the $@ at the end could have been interpreted as a variable, which -# would mean a missing closing @ delimiter. -mod-loop-dollar: - @echo $@:${:U1:@word@${word}$@:Q}: - @echo $@:${:U2:@word@$${word}$$@:Q}: - @echo $@:${:U3:@word@$$${word}$$$@:Q}: - @echo $@:${:U4:@word@$$$${word}$$$$@:Q}: - @echo $@:${:U5:@word@$$$$${word}$$$$$@:Q}: - @echo $@:${:U6:@word@$$$$$${word}$$$$$$@:Q}: +# M.little-endian= S,1234,4321, +# M.big-endian= # none +.if ${value:L:${:Dempty}S,a,A,} != "vAlue" +.warning unexpected +.endif + diff --git a/unit-tests/modorder.exp b/unit-tests/modorder.exp deleted file mode 100644 index 8e0aad2e2027..000000000000 --- a/unit-tests/modorder.exp +++ /dev/null @@ -1,12 +0,0 @@ -LIST = one two three four five six seven eight nine ten -LIST:O = eight five four nine one seven six ten three two -LIST:Or = two three ten six seven one nine four five eight -LIST:Ox = Ok -LIST:O:Ox = Ok -LISTX = Ok -LISTSX = Ok -make: Bad modifier `:OX' for LIST -BADMOD 1 = } -make: Bad modifier `:OxXX' for LIST -BADMOD 2 = XX} -exit status 0 diff --git a/unit-tests/modorder.mk b/unit-tests/modorder.mk deleted file mode 100644 index 89e64b43c57c..000000000000 --- a/unit-tests/modorder.mk +++ /dev/null @@ -1,24 +0,0 @@ -# $NetBSD: modorder.mk,v 1.3 2020/06/09 01:48:17 sjg Exp $ - -LIST= one two three four five six seven eight nine ten -LISTX= ${LIST:Ox} -LISTSX:= ${LIST:Ox} -TEST_RESULT= && echo Ok || echo Failed - -# unit-tests have to produce the same results on each run -# so we cannot actually include :Ox output. -all: - @echo "LIST = ${LIST}" - @echo "LIST:O = ${LIST:O}" - @echo "LIST:Or = ${LIST:Or}" - # Note that 1 in every 10! trials two independently generated - # randomized orderings will be the same. The test framework doesn't - # support checking probabilistic output, so we accept that each of the - # 3 :Ox tests will incorrectly fail with probability 2.756E-7, which - # lets the whole test fail once in 1.209.600 runs, on average. - @echo "LIST:Ox = `test '${LIST:Ox}' != '${LIST:Ox}' ${TEST_RESULT}`" - @echo "LIST:O:Ox = `test '${LIST:O:Ox}' != '${LIST:O:Ox}' ${TEST_RESULT}`" - @echo "LISTX = `test '${LISTX}' != '${LISTX}' ${TEST_RESULT}`" - @echo "LISTSX = `test '${LISTSX}' = '${LISTSX}' ${TEST_RESULT}`" - @echo "BADMOD 1 = ${LIST:OX}" - @echo "BADMOD 2 = ${LIST:OxXX}" diff --git a/unit-tests/modts.exp b/unit-tests/modts.exp index 338964963a86..5db79fc96586 100644 --- a/unit-tests/modts.exp +++ b/unit-tests/modts.exp @@ -1,34 +1,3 @@ -LIST="one two three four five six" -LIST:ts,="one,two,three,four,five,six" -LIST:ts/:tu="ONE/TWO/THREE/FOUR/FIVE/SIX" -LIST:ts::tu="ONE:TWO:THREE:FOUR:FIVE:SIX" -LIST:ts:tu="ONETWOTHREEFOURFIVESIX" -LIST:tu:ts/="ONE/TWO/THREE/FOUR/FIVE/SIX" -LIST:ts:="one:two:three:four:five:six" -LIST:ts="onetwothreefourfivesix" -LIST:ts:S/two/2/="one2threefourfivesix" -LIST:S/two/2/:ts="one2threefourfivesix" -LIST:ts/:S/two/2/="one/2/three/four/five/six" -Pretend the '/' in '/n' etc. below are back-slashes. -LIST:ts/n="one -two -three -four -five -six" -LIST:ts/t="one two three four five six" -LIST:ts/012:tu="ONE -TWO -THREE -FOUR -FIVE -SIX" -LIST:ts/xa:tu="ONE -TWO -THREE -FOUR -FIVE -SIX" make: Bad modifier `:tx' for LIST LIST:tx="}" make: Bad modifier `:ts\X' for LIST @@ -36,4 +5,10 @@ LIST:ts/x:tu="\X:tu}" FU_mod-ts="a/b/cool" FU_mod-ts:ts:T="cool" == cool? B.${AAA:ts}="Baaa" == Baaa? +:ts :S => aaxBbxaaxbbxaaxbb +:ts :S space => axa a axc +:ts :S space :M => axaxaxaxc +:ts :S => axa a axc +:ts :S :@ => axa a axc +:ts :S :@ :M => axaxaxaxc exit status 0 diff --git a/unit-tests/modts.mk b/unit-tests/modts.mk index e66dc25a2a02..3219538d5e4a 100644 --- a/unit-tests/modts.mk +++ b/unit-tests/modts.mk @@ -7,7 +7,7 @@ FU_mod-ts = a / b / cool AAA= a a a B.aaa= Baaa -all: mod-ts +all: mod-ts mod-ts-space # Use print or printf iff they are builtin. # XXX note that this causes problems, when make decides @@ -21,24 +21,27 @@ PRINT= echo .endif mod-ts: - @echo 'LIST="${LIST}"' - @echo 'LIST:ts,="${LIST:ts,}"' - @echo 'LIST:ts/:tu="${LIST:ts/:tu}"' - @echo 'LIST:ts::tu="${LIST:ts::tu}"' - @echo 'LIST:ts:tu="${LIST:ts:tu}"' - @echo 'LIST:tu:ts/="${LIST:tu:ts/}"' - @echo 'LIST:ts:="${LIST:ts:}"' - @echo 'LIST:ts="${LIST:ts}"' - @echo 'LIST:ts:S/two/2/="${LIST:ts:S/two/2/}"' - @echo 'LIST:S/two/2/:ts="${LIST:S/two/2/:ts}"' - @echo 'LIST:ts/:S/two/2/="${LIST:ts/:S/two/2/}"' - @echo "Pretend the '/' in '/n' etc. below are back-slashes." - @${PRINT} 'LIST:ts/n="${LIST:ts\n}"' - @${PRINT} 'LIST:ts/t="${LIST:ts\t}"' - @${PRINT} 'LIST:ts/012:tu="${LIST:ts\012:tu}"' - @${PRINT} 'LIST:ts/xa:tu="${LIST:ts\xa:tu}"' @${PRINT} 'LIST:tx="${LIST:tx}"' @${PRINT} 'LIST:ts/x:tu="${LIST:ts\X:tu}"' @${PRINT} 'FU_$@="${FU_${@:ts}:ts}"' @${PRINT} 'FU_$@:ts:T="${FU_${@:ts}:ts:T}" == cool?' @${PRINT} 'B.$${AAA:ts}="${B.${AAA:ts}}" == Baaa?' + +mod-ts-space: + # After the :ts modifier, the whole string is interpreted as a single + # word since all spaces have been replaced with x. + @${PRINT} ':ts :S => '${aa bb aa bb aa bb:L:tsx:S,b,B,:Q} + + # The :ts modifier also applies to word separators that are added + # afterwards. + @${PRINT} ':ts :S space => '${a ababa c:L:tsx:S,b, ,g:Q} + @${PRINT} ':ts :S space :M => '${a ababa c:L:tsx:S,b, ,g:M*:Q} + + # Not all modifiers behave this way though. Some of them always use + # a space as word separator instead of the :ts separator. + # This seems like an oversight during implementation. + @${PRINT} ':ts :S => '${a ababa c:L:tsx:S,b, ,g:Q} + @${PRINT} ':ts :S :@ => '${a ababa c:L:tsx:S,b, ,g:@v@${v}@:Q} + + # A final :M* modifier applies the :ts separator again, though. + @${PRINT} ':ts :S :@ :M => '${a ababa c:L:tsx:S,b, ,g:@v@${v}@:M*:Q} diff --git a/unit-tests/opt-backwards.exp b/unit-tests/opt-backwards.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/opt-backwards.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/opt-backwards.mk b/unit-tests/opt-backwards.mk new file mode 100644 index 000000000000..bc0b1eff8530 --- /dev/null +++ b/unit-tests/opt-backwards.mk @@ -0,0 +1,8 @@ +# $NetBSD: opt-backwards.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -B command line option. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/opt-chdir.exp b/unit-tests/opt-chdir.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/opt-chdir.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/opt-chdir.mk b/unit-tests/opt-chdir.mk new file mode 100644 index 000000000000..8735fddbef9e --- /dev/null +++ b/unit-tests/opt-chdir.mk @@ -0,0 +1,8 @@ +# $NetBSD: opt-chdir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -C command line option. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/opt-debug-g1.exp b/unit-tests/opt-debug-g1.exp new file mode 100755 index 000000000000..d6d014a0353f --- /dev/null +++ b/unit-tests/opt-debug-g1.exp @@ -0,0 +1,15 @@ +#*** Input graph: +# all, made UNMADE, type OP_DEPENDS, flags none +# made-target, made UNMADE, type OP_DEPENDS, flags none +# made-target-no-sources, made UNMADE, type OP_DEPENDS, flags none +# made-source, made UNMADE, type OP_DEPENDS, flags none +# unmade-target, made UNMADE, type OP_DEPENDS, flags none +# unmade-sources, made UNMADE, type none, flags none +# unmade-target-no-sources, made UNMADE, type OP_DEPENDS, flags none + + +# +# Files that are only sources: +# unmade-sources [unmade-sources] +#*** Transformations: +exit status 0 diff --git a/unit-tests/opt-debug-g1.mk b/unit-tests/opt-debug-g1.mk new file mode 100755 index 000000000000..3104fbf91bc1 --- /dev/null +++ b/unit-tests/opt-debug-g1.mk @@ -0,0 +1,19 @@ +# $NetBSD: opt-debug-g1.mk,v 1.1 2020/08/27 19:00:17 rillig Exp $ +# +# Tests for the -dg1 command line option, which prints the input +# graph before making anything. + +all: made-target made-target-no-sources + +made-target: made-source + +made-source: + +made-target-no-sources: + +unmade-target: unmade-sources + +unmade-target-no-sources: + +all: + @:; diff --git a/unit-tests/opt-debug.exp b/unit-tests/opt-debug.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/opt-debug.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/opt-debug.mk b/unit-tests/opt-debug.mk new file mode 100644 index 000000000000..afd740a6caab --- /dev/null +++ b/unit-tests/opt-debug.mk @@ -0,0 +1,8 @@ +# $NetBSD: opt-debug.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -d command line option. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/opt-define.exp b/unit-tests/opt-define.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/opt-define.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/opt-define.mk b/unit-tests/opt-define.mk new file mode 100644 index 000000000000..ce0516ba44bc --- /dev/null +++ b/unit-tests/opt-define.mk @@ -0,0 +1,8 @@ +# $NetBSD: opt-define.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -D command line option. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/opt-env.exp b/unit-tests/opt-env.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/opt-env.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/opt-env.mk b/unit-tests/opt-env.mk new file mode 100644 index 000000000000..32e95ef41f5a --- /dev/null +++ b/unit-tests/opt-env.mk @@ -0,0 +1,8 @@ +# $NetBSD: opt-env.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -e command line option. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/opt-file.exp b/unit-tests/opt-file.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/opt-file.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/opt-file.mk b/unit-tests/opt-file.mk new file mode 100644 index 000000000000..86bc100bebc2 --- /dev/null +++ b/unit-tests/opt-file.mk @@ -0,0 +1,8 @@ +# $NetBSD: opt-file.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -f command line option. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/opt-ignore.exp b/unit-tests/opt-ignore.exp new file mode 100644 index 000000000000..265e143e3471 --- /dev/null +++ b/unit-tests/opt-ignore.exp @@ -0,0 +1,12 @@ +dependency 1 +dependency 2 +dependency 3 +other 1 +other 2 +main 1 +main 2 +*** Error code 1 (ignored) +*** Error code 7 (ignored) +*** Error code 1 (ignored) +*** Error code 1 (ignored) +exit status 0 diff --git a/unit-tests/opt-ignore.mk b/unit-tests/opt-ignore.mk new file mode 100644 index 000000000000..6d6d8dc3a339 --- /dev/null +++ b/unit-tests/opt-ignore.mk @@ -0,0 +1,30 @@ +# $NetBSD: opt-ignore.mk,v 1.3 2020/08/23 14:28:04 rillig Exp $ +# +# Tests for the -i command line option, which ignores the exit status of the +# shell commands, and just continues with the next command, even from the same +# target. +# +# Is there a situation in which this option is useful? +# +# Why are the "Error code" lines all collected at the bottom of the output +# file, where they cannot be related to the individual shell commands that +# failed? + +all: dependency other + +dependency: + @echo dependency 1 + @false + @echo dependency 2 + @:; exit 7 + @echo dependency 3 + +other: + @echo other 1 + @false + @echo other 2 + +all: + @echo main 1 + @false + @echo main 2 diff --git a/unit-tests/opt-include-dir.exp b/unit-tests/opt-include-dir.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/opt-include-dir.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/opt-include-dir.mk b/unit-tests/opt-include-dir.mk new file mode 100644 index 000000000000..d61a6c979ea5 --- /dev/null +++ b/unit-tests/opt-include-dir.mk @@ -0,0 +1,8 @@ +# $NetBSD: opt-include-dir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -I command line option. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/opt-jobs-internal.exp b/unit-tests/opt-jobs-internal.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/opt-jobs-internal.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/opt-jobs-internal.mk b/unit-tests/opt-jobs-internal.mk new file mode 100644 index 000000000000..5426807ca98b --- /dev/null +++ b/unit-tests/opt-jobs-internal.mk @@ -0,0 +1,8 @@ +# $NetBSD: opt-jobs-internal.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the (intentionally undocumented) -J command line option. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/opt-jobs.exp b/unit-tests/opt-jobs.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/opt-jobs.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/opt-jobs.mk b/unit-tests/opt-jobs.mk new file mode 100644 index 000000000000..7d54d08a8421 --- /dev/null +++ b/unit-tests/opt-jobs.mk @@ -0,0 +1,8 @@ +# $NetBSD: opt-jobs.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -j command line option. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/opt-keep-going.exp b/unit-tests/opt-keep-going.exp new file mode 100644 index 000000000000..de1b1ae582f4 --- /dev/null +++ b/unit-tests/opt-keep-going.exp @@ -0,0 +1,6 @@ +dependency 1 +other 1 +*** Error code 1 (continuing) +*** Error code 1 (continuing) +`all' not remade because of errors. +exit status 0 diff --git a/unit-tests/opt-keep-going.mk b/unit-tests/opt-keep-going.mk new file mode 100644 index 000000000000..1a2124ccb5f1 --- /dev/null +++ b/unit-tests/opt-keep-going.mk @@ -0,0 +1,24 @@ +# $NetBSD: opt-keep-going.mk,v 1.3 2020/08/23 14:28:04 rillig Exp $ +# +# Tests for the -k command line option, which stops building a target as soon +# as an error is detected, but continues building the other, independent +# targets, as far as possible. + +all: dependency other + +dependency: + @echo dependency 1 + @false + @echo dependency 2 + @:; exit 7 + @echo dependency 3 + +other: + @echo other 1 + @false + @echo other 2 + +all: + @echo main 1 + @false + @echo main 2 diff --git a/unit-tests/opt-m-include-dir.exp b/unit-tests/opt-m-include-dir.exp new file mode 100644 index 000000000000..bce0e4be05ac --- /dev/null +++ b/unit-tests/opt-m-include-dir.exp @@ -0,0 +1,2 @@ +ok +exit status 0 diff --git a/unit-tests/opt-m-include-dir.mk b/unit-tests/opt-m-include-dir.mk new file mode 100644 index 000000000000..6e0801390395 --- /dev/null +++ b/unit-tests/opt-m-include-dir.mk @@ -0,0 +1,61 @@ +# $NetBSD: opt-m-include-dir.mk,v 1.4 2020/09/01 20:14:34 rillig Exp $ +# +# Tests for the -m command line option, which adds a directory to the +# search path for the .include <...> directive. +# +# The .../canary.mk special argument starts searching in the current +# directory and walks towards the file system root, until it finds a +# directory that contains a file called canary.mk. +# +# To set up this scenario, the file step2.mk is created deep in a hierarchy +# of subdirectories. Another file called opt-m-step3.mk is created a few +# steps up in the directory hierarchy, serving as the canary file. +# +# Next to the canary file, there is opt-m-step3.mk. This file is found +# by mentioning its simple name in an .include directive. It defines the +# target "step2" that is needed by "step2.mk". + +.if ${.PARSEFILE:T} == "opt-m-include-dir.mk" + +# Set up the other files needed for this test. + +TEST_DIR:= ${.PARSEFILE:R}.tmp/sub/sub/sub/workdir +CANARY_FILE:= ${.PARSEFILE:R}.tmp/sub/opt-m-canary.mk +ACTUAL_FILE:= ${.PARSEFILE:R}.tmp/sub/opt-m-step3.mk + +_!= mkdir -p ${TEST_DIR} +_!= > ${CANARY_FILE} +_!= cp ${MAKEFILE} ${TEST_DIR}/step2.mk +_!= cp ${MAKEFILE} ${ACTUAL_FILE} + +step1: + @${.MAKE} -C ${TEST_DIR} -f step2.mk step2 + +.END: + @rm -rf ${MAKEFILE:R}.tmp + +.elif ${.PARSEFILE:T} == "step2.mk" + +# This is the file deep in the directory hierarchy. It sets up the +# search path for the .include <...> directive and then includes a +# single file from that search path. + +# This option adds .tmp/sub to the search path for .include <...>. +.MAKEFLAGS: -m .../opt-m-canary.mk + +# This option does not add any directory to the search path since the +# canary file does not exist. +.MAKEFLAGS: -m .../does-not-exist + +.include <opt-m-step3.mk> + +.elif ${.PARSEFILE:T} == "opt-m-step3.mk" + +# This file is included by step2.mk. + +step2: + @echo ok + +.else +. error +.endif diff --git a/unit-tests/opt-no-action-at-all.exp b/unit-tests/opt-no-action-at-all.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/opt-no-action-at-all.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/opt-no-action-at-all.mk b/unit-tests/opt-no-action-at-all.mk new file mode 100644 index 000000000000..6ab385946691 --- /dev/null +++ b/unit-tests/opt-no-action-at-all.mk @@ -0,0 +1,8 @@ +# $NetBSD: opt-no-action-at-all.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -N command line option. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/opt-no-action.exp b/unit-tests/opt-no-action.exp new file mode 100644 index 000000000000..381d68e2f2d9 --- /dev/null +++ b/unit-tests/opt-no-action.exp @@ -0,0 +1,13 @@ +command during parsing +echo '.BEGIN: hidden command' +echo '.BEGIN: run always' +.BEGIN: run always +echo 'main: hidden command' +echo 'main: run always' +main: run always +run-always: hidden command +run-always: run always +echo '.END: hidden command' +echo '.END: run always' +.END: run always +exit status 0 diff --git a/unit-tests/opt-no-action.mk b/unit-tests/opt-no-action.mk new file mode 100644 index 000000000000..32b3b1564acb --- /dev/null +++ b/unit-tests/opt-no-action.mk @@ -0,0 +1,33 @@ +# $NetBSD: opt-no-action.mk,v 1.3 2020/08/19 05:25:26 rillig Exp $ +# +# Tests for the -n command line option, which runs almost no commands. +# It just outputs them, to be inspected by human readers. +# Only commands that are in a .MAKE target or prefixed by '+' are run. + +# This command cannot be prevented from being run since it is used at parse +# time, and any later variable assignments may depend on its result. +!= echo 'command during parsing' 1>&2; echo + +all: main +all: run-always + +# Both of these commands are printed, but only the '+' command is run. +.BEGIN: + @echo '$@: hidden command' + @+echo '$@: run always' + +# Both of these commands are printed, but only the '+' command is run. +main: + @echo '$@: hidden command' + @+echo '$@: run always' + +# None of these commands is printed, but both are run, because this target +# depends on the special source ".MAKE". +run-always: .MAKE + @echo '$@: hidden command' + @+echo '$@: run always' + +# Both of these commands are printed, but only the '+' command is run. +.END: + @echo '$@: hidden command' + @+echo '$@: run always' diff --git a/unit-tests/opt-query.exp b/unit-tests/opt-query.exp new file mode 100644 index 000000000000..38025dcf4d3a --- /dev/null +++ b/unit-tests/opt-query.exp @@ -0,0 +1,2 @@ +command during parsing +exit status 1 diff --git a/unit-tests/opt-query.mk b/unit-tests/opt-query.mk new file mode 100644 index 000000000000..04e605991140 --- /dev/null +++ b/unit-tests/opt-query.mk @@ -0,0 +1,24 @@ +# $NetBSD: opt-query.mk,v 1.3 2020/08/19 05:13:18 rillig Exp $ +# +# Tests for the -q command line option. +# +# The -q option only looks at the dependencies between the targets. +# None of the commands in the targets are run, not even those that are +# prefixed with '+'. + +# This command cannot be prevented from being run since it is used at parse +# time, and any later variable assignments may depend on its result. +!= echo 'command during parsing' 1>&2; echo + +# None of these commands are run. +.BEGIN: + @echo '$@: hidden command' + @+echo '$@: run always' + +# None of these commands are run. +all: + @echo '$@: hidden command' + @+echo '$@: run always' + +# The exit status 1 is because the "all" target has to be made, that is, +# it is not up-to-date. diff --git a/unit-tests/opt-raw.exp b/unit-tests/opt-raw.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/opt-raw.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/opt-raw.mk b/unit-tests/opt-raw.mk new file mode 100644 index 000000000000..d3591bb99dab --- /dev/null +++ b/unit-tests/opt-raw.mk @@ -0,0 +1,8 @@ +# $NetBSD: opt-raw.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -r command line option. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/opt-silent.exp b/unit-tests/opt-silent.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/opt-silent.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/opt-silent.mk b/unit-tests/opt-silent.mk new file mode 100644 index 000000000000..7822d46ac48a --- /dev/null +++ b/unit-tests/opt-silent.mk @@ -0,0 +1,8 @@ +# $NetBSD: opt-silent.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -s command line option. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/opt-touch.exp b/unit-tests/opt-touch.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/opt-touch.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/opt-touch.mk b/unit-tests/opt-touch.mk new file mode 100644 index 000000000000..5093c5cad6ac --- /dev/null +++ b/unit-tests/opt-touch.mk @@ -0,0 +1,8 @@ +# $NetBSD: opt-touch.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -t command line option. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/opt-tracefile.exp b/unit-tests/opt-tracefile.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/opt-tracefile.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/opt-tracefile.mk b/unit-tests/opt-tracefile.mk new file mode 100644 index 000000000000..b62392ca913c --- /dev/null +++ b/unit-tests/opt-tracefile.mk @@ -0,0 +1,8 @@ +# $NetBSD: opt-tracefile.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -T command line option. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/opt-var-expanded.exp b/unit-tests/opt-var-expanded.exp new file mode 100644 index 000000000000..4fcc0b99ae1b --- /dev/null +++ b/unit-tests/opt-var-expanded.exp @@ -0,0 +1,3 @@ +other value $$ +value +exit status 0 diff --git a/unit-tests/opt-var-expanded.mk b/unit-tests/opt-var-expanded.mk new file mode 100644 index 000000000000..0b4088a82082 --- /dev/null +++ b/unit-tests/opt-var-expanded.mk @@ -0,0 +1,6 @@ +# $NetBSD: opt-var-expanded.mk,v 1.3 2020/08/23 14:28:04 rillig Exp $ +# +# Tests for the -v command line option. + +VAR= other ${VALUE} $$$$ +VALUE= value diff --git a/unit-tests/opt-var-literal.exp b/unit-tests/opt-var-literal.exp new file mode 100644 index 000000000000..e7f653b769b9 --- /dev/null +++ b/unit-tests/opt-var-literal.exp @@ -0,0 +1,3 @@ +other ${VALUE} $$$$ +value +exit status 0 diff --git a/unit-tests/opt-var-literal.mk b/unit-tests/opt-var-literal.mk new file mode 100644 index 000000000000..a819e7537105 --- /dev/null +++ b/unit-tests/opt-var-literal.mk @@ -0,0 +1,6 @@ +# $NetBSD: opt-var-literal.mk,v 1.3 2020/08/23 14:28:04 rillig Exp $ +# +# Tests for the -V command line option. + +VAR= other ${VALUE} $$$$ +VALUE= value diff --git a/unit-tests/opt-warnings-as-errors.exp b/unit-tests/opt-warnings-as-errors.exp new file mode 100644 index 000000000000..bd54bb673f08 --- /dev/null +++ b/unit-tests/opt-warnings-as-errors.exp @@ -0,0 +1,7 @@ +make: "opt-warnings-as-errors.mk" line 5: warning: message 1 +make: parsing warnings being treated as errors +make: "opt-warnings-as-errors.mk" line 6: warning: message 2 +parsing continues +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/opt-warnings-as-errors.mk b/unit-tests/opt-warnings-as-errors.mk new file mode 100644 index 000000000000..905753410db0 --- /dev/null +++ b/unit-tests/opt-warnings-as-errors.mk @@ -0,0 +1,11 @@ +# $NetBSD: opt-warnings-as-errors.mk,v 1.3 2020/08/23 14:28:04 rillig Exp $ +# +# Tests for the -W command line option, which turns warnings into errors. + +.warning message 1 +.warning message 2 + +_!= echo 'parsing continues' 1>&2 + +all: + @:; diff --git a/unit-tests/opt-where-am-i.exp b/unit-tests/opt-where-am-i.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/opt-where-am-i.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/opt-where-am-i.mk b/unit-tests/opt-where-am-i.mk new file mode 100644 index 000000000000..9158a598174c --- /dev/null +++ b/unit-tests/opt-where-am-i.mk @@ -0,0 +1,8 @@ +# $NetBSD: opt-where-am-i.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -w command line option. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/opt-x-reduce-exported.exp b/unit-tests/opt-x-reduce-exported.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/opt-x-reduce-exported.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/opt-x-reduce-exported.mk b/unit-tests/opt-x-reduce-exported.mk new file mode 100644 index 000000000000..7ee8e7c7eff0 --- /dev/null +++ b/unit-tests/opt-x-reduce-exported.mk @@ -0,0 +1,8 @@ +# $NetBSD: opt-x-reduce-exported.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -x command line option. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/opt.exp b/unit-tests/opt.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/opt.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/opt.mk b/unit-tests/opt.mk new file mode 100644 index 000000000000..eae430965df7 --- /dev/null +++ b/unit-tests/opt.mk @@ -0,0 +1,8 @@ +# $NetBSD: opt.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the command line options. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/phony-end.exp b/unit-tests/phony-end.exp index c3c517ccc25c..f1bff4ef6d5e 100644 --- a/unit-tests/phony-end.exp +++ b/unit-tests/phony-end.exp @@ -1,5 +1,5 @@ .TARGET="phony" .PREFIX="phony" .IMPSRC="" -.TARGET="all" .PREFIX="all" .IMPSRC="phony" +.TARGET="all" .PREFIX="all" .IMPSRC="" .TARGET="ok" .PREFIX="ok" .IMPSRC="" .TARGET="also.ok" .PREFIX="also.ok" .IMPSRC="" .TARGET="bug" .PREFIX="bug" .IMPSRC="" diff --git a/unit-tests/posix1.mk b/unit-tests/posix1.mk index 50b0a63ee696..f5d8e21678ea 100644 --- a/unit-tests/posix1.mk +++ b/unit-tests/posix1.mk @@ -1,4 +1,4 @@ -# $NetBSD: posix1.mk,v 1.3 2014/08/30 22:21:08 sjg Exp $ +# $NetBSD: posix1.mk,v 1.4 2020/08/10 18:19:58 rillig Exp $ # Keep the default suffixes from interfering, just in case. .SUFFIXES: @@ -9,6 +9,8 @@ all: line-continuations suffix-substitution localvars .BEGIN: clean clean: @rm -f lib.a dir/* dummy obj* +.END: + @rm -f lib.a dir/* dummy obj* # # Line continuations diff --git a/unit-tests/recursive.exp b/unit-tests/recursive.exp new file mode 100644 index 000000000000..bb5db75a474c --- /dev/null +++ b/unit-tests/recursive.exp @@ -0,0 +1,5 @@ +make: "recursive.mk" line 34: Unclosed variable "MISSING_PAREN" +make: "recursive.mk" line 35: Unclosed variable "MISSING_BRACE" +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/recursive.mk b/unit-tests/recursive.mk new file mode 100644 index 000000000000..bc5a2817b333 --- /dev/null +++ b/unit-tests/recursive.mk @@ -0,0 +1,37 @@ +# $NetBSD: recursive.mk,v 1.2 2020/08/06 05:52:45 rillig Exp $ +# +# In -dL mode, a variable may get expanded before it makes sense. +# This would stop make from doing anything since the "recursive" error +# is fatal and exits immediately. +# +# The purpose of evaluating that variable early was just to detect +# whether there are unclosed variables. It might be enough to parse the +# variable value without VARE_WANTRES for that purpose. +# +# Seen in pkgsrc/x11/libXfixes, and probably many more package that use +# GNU Automake. + +AM_V_lt = $(am__v_lt_$(V)) +am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) +am__v_lt_0 = --silent +am__v_lt_1 = + +# On 2020-08-06, make reported: "Variable am__v_lt_ is recursive." +libXfixes_la_LINK = ... $(AM_V_lt) ... + +# somewhere later ... +AM_DEFAULT_VERBOSITY = 1 + + +# The purpose of the -dL flag is to detect unclosed variables. This +# can be achieved by just parsing the variable and not evaluating it. +# +# When the variable is only parsed but not evaluated, bugs in nested +# variables are not discovered. But these are hard to produce anyway, +# therefore that's acceptable. In most practical cases, the missing +# brace would be detected directly in the line where it is produced. +MISSING_BRACE_INDIRECT:= ${:U\${MISSING_BRACE} +UNCLOSED = $(MISSING_PAREN +UNCLOSED = ${MISSING_BRACE +UNCLOSED = ${MISSING_BRACE_INDIRECT} + diff --git a/unit-tests/sh-dots.exp b/unit-tests/sh-dots.exp new file mode 100755 index 000000000000..19482717087b --- /dev/null +++ b/unit-tests/sh-dots.exp @@ -0,0 +1,15 @@ +first first +hidden hidden +make: exec(...) failed (No such file or directory) +hidden delayed hidden +repeated repeated +commented commented +*** Error code 1 (ignored) +... # Run the below commands later +<normalized: ...: not found> +commented delayed commented +first delayed first +repeated delayed repeated +repeated delayed twice repeated +*** Error code 127 (ignored) +exit status 0 diff --git a/unit-tests/sh-dots.mk b/unit-tests/sh-dots.mk new file mode 100755 index 000000000000..36da5bce7a53 --- /dev/null +++ b/unit-tests/sh-dots.mk @@ -0,0 +1,37 @@ +# $NetBSD: sh-dots.mk,v 1.1 2020/08/22 11:27:02 rillig Exp $ +# +# Tests for the special shell command line "...", which does not run the +# commands below it but appends them to the list of commands that are run +# at the end. + +all: first hidden repeated commented + +# The ${.TARGET} correctly expands to the target name, even though the +# commands are run separately from the main commands. +first: + @echo first ${.TARGET} + ... + @echo first delayed ${.TARGET} + +# The dots cannot be prefixed by the usual @-+ characters. +# They must be written exactly as dots. +hidden: .IGNORE + @echo hidden ${.TARGET} + @... + @echo hidden delayed ${.TARGET} + +# Since the shell command lines don't recognize '#' as comment character, +# the "..." is not interpreted specially here. +commented: .IGNORE + @echo commented ${.TARGET} + ... # Run the below commands later + @echo commented delayed ${.TARGET} + +# The "..." can appear more than once, even though that doesn't make sense. +# The second "..." is a no-op. +repeated: .IGNORE + @echo repeated ${.TARGET} + ... + @echo repeated delayed ${.TARGET} + ... + @echo repeated delayed twice ${.TARGET} diff --git a/unit-tests/sh-jobs-error.exp b/unit-tests/sh-jobs-error.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/sh-jobs-error.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/sh-jobs-error.mk b/unit-tests/sh-jobs-error.mk new file mode 100644 index 000000000000..febc06999366 --- /dev/null +++ b/unit-tests/sh-jobs-error.mk @@ -0,0 +1,9 @@ +# $NetBSD: sh-jobs-error.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for error handling in the "run in jobs mode" part of the "Shell +# Commands" section from the manual page. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/sh-jobs.exp b/unit-tests/sh-jobs.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/sh-jobs.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/sh-jobs.mk b/unit-tests/sh-jobs.mk new file mode 100644 index 000000000000..62172c2a0c86 --- /dev/null +++ b/unit-tests/sh-jobs.mk @@ -0,0 +1,9 @@ +# $NetBSD: sh-jobs.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the "run in jobs mode" part of the "Shell Commands" section +# from the manual page. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/sh-leading-at.exp b/unit-tests/sh-leading-at.exp new file mode 100644 index 000000000000..5ffa84690a40 --- /dev/null +++ b/unit-tests/sh-leading-at.exp @@ -0,0 +1,5 @@ +ok +space after @ +echo 'echoed' +echoed +exit status 0 diff --git a/unit-tests/sh-leading-at.mk b/unit-tests/sh-leading-at.mk new file mode 100644 index 000000000000..19a6e59e4e6a --- /dev/null +++ b/unit-tests/sh-leading-at.mk @@ -0,0 +1,10 @@ +# $NetBSD: sh-leading-at.mk,v 1.3 2020/08/22 09:16:08 rillig Exp $ +# +# Tests for shell commands preceded by an '@', to suppress printing +# the command to stdout. + +all: + @ + @echo 'ok' + @ echo 'space after @' + echo 'echoed' diff --git a/unit-tests/sh-leading-hyphen.exp b/unit-tests/sh-leading-hyphen.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/sh-leading-hyphen.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/sh-leading-hyphen.mk b/unit-tests/sh-leading-hyphen.mk new file mode 100644 index 000000000000..94be43495afb --- /dev/null +++ b/unit-tests/sh-leading-hyphen.mk @@ -0,0 +1,9 @@ +# $NetBSD: sh-leading-hyphen.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for shell commands preceded by a '-', to ignore the exit status of +# the command line. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/sh-leading-plus.exp b/unit-tests/sh-leading-plus.exp new file mode 100644 index 000000000000..eb586d29f1c2 --- /dev/null +++ b/unit-tests/sh-leading-plus.exp @@ -0,0 +1,4 @@ +echo 'this command is not run' +echo 'this command is run' +this command is run +exit status 0 diff --git a/unit-tests/sh-leading-plus.mk b/unit-tests/sh-leading-plus.mk new file mode 100644 index 000000000000..75279d7d57fd --- /dev/null +++ b/unit-tests/sh-leading-plus.mk @@ -0,0 +1,8 @@ +# $NetBSD: sh-leading-plus.mk,v 1.3 2020/08/23 14:46:33 rillig Exp $ +# +# Tests for shell commands preceded by a '+', to run them even if +# the command line option -n is given. + +all: + @echo 'this command is not run' + @+echo 'this command is run' diff --git a/unit-tests/sh-meta-chars.exp b/unit-tests/sh-meta-chars.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/sh-meta-chars.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/sh-meta-chars.mk b/unit-tests/sh-meta-chars.mk new file mode 100644 index 000000000000..126ca2ceb118 --- /dev/null +++ b/unit-tests/sh-meta-chars.mk @@ -0,0 +1,11 @@ +# $NetBSD: sh-meta-chars.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for running shell commands that contain meta-characters. +# +# These meta-characters decide whether the command is run by the shell +# or executed directly via execv. See Cmd_Exec for details. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/sh-multi-line.exp b/unit-tests/sh-multi-line.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/sh-multi-line.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/sh-multi-line.mk b/unit-tests/sh-multi-line.mk new file mode 100644 index 000000000000..35a773f5dde6 --- /dev/null +++ b/unit-tests/sh-multi-line.mk @@ -0,0 +1,9 @@ +# $NetBSD: sh-multi-line.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for multi-line shell commands, to ensure that the line breaks +# are preserved. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/sh-single-line.exp b/unit-tests/sh-single-line.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/sh-single-line.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/sh-single-line.mk b/unit-tests/sh-single-line.mk new file mode 100644 index 000000000000..9dae7f80c9a9 --- /dev/null +++ b/unit-tests/sh-single-line.mk @@ -0,0 +1,12 @@ +# $NetBSD: sh-single-line.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for running single-line shell commands. +# +# In jobs mode, the shell commands are combined into a single shell +# program, as described in the manual page, section "Shell Commands", +# "the entire script". + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/sh.exp b/unit-tests/sh.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/sh.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/sh.mk b/unit-tests/sh.mk new file mode 100644 index 000000000000..f79a4099e990 --- /dev/null +++ b/unit-tests/sh.mk @@ -0,0 +1,9 @@ +# $NetBSD: sh.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for running shell commands from the targets, or from the != variable +# assignment operator or the :sh variable modifier. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/sysv.exp b/unit-tests/sysv.exp index 780a187783f3..610f97c39e85 100644 --- a/unit-tests/sysv.exp +++ b/unit-tests/sysv.exp @@ -12,5 +12,4 @@ asam.c.c asam.c a.c.c -ax:Q b c d eb exit status 0 diff --git a/unit-tests/sysv.mk b/unit-tests/sysv.mk index 3a987441ee42..5c87579cc11b 100644 --- a/unit-tests/sysv.mk +++ b/unit-tests/sysv.mk @@ -1,4 +1,6 @@ -# $Id: sysv.mk,v 1.5 2020/07/04 18:16:55 sjg Exp $ +# $Id: sysv.mk,v 1.9 2020/08/23 16:08:32 sjg Exp $ + +all: foo fun sam bla FOO ?= FOOBAR = ${FOO:=bar} @@ -11,8 +13,6 @@ FUN = ${B}${S}fun SUN = the Sun # we expect nothing when FOO is empty -all: foo fun sam bla words - foo: @echo FOOBAR = ${FOOBAR} .if empty(FOO) @@ -41,8 +41,3 @@ BLA= bla: @echo $(BLA:%=foo/%x) - -# The :Q looks like a modifier but isn't. -# It is part of the replacement string. -words: - @echo a${a b c d e:L:%a=x:Q}b diff --git a/unit-tests/unexport-env.mk b/unit-tests/unexport-env.mk index aaabcd46464a..bc5fb4914ddc 100644 --- a/unit-tests/unexport-env.mk +++ b/unit-tests/unexport-env.mk @@ -1,6 +1,7 @@ -# $Id: unexport-env.mk,v 1.1.1.1 2014/08/30 18:57:18 sjg Exp $ +# $Id: unexport-env.mk,v 1.1.1.2 2020/07/28 16:57:18 sjg Exp $ # pick up a bunch of exported vars +FILTER_CMD= grep ^UT_ .include "export.mk" # an example of setting up a minimal environment. diff --git a/unit-tests/unexport.mk b/unit-tests/unexport.mk index 0f1245292ba5..7d2e8f275173 100644 --- a/unit-tests/unexport.mk +++ b/unit-tests/unexport.mk @@ -1,8 +1,19 @@ -# $Id: unexport.mk,v 1.1.1.1 2014/08/30 18:57:18 sjg Exp $ +# $Id: unexport.mk,v 1.1.1.3 2020/08/08 22:34:25 sjg Exp $ # pick up a bunch of exported vars +FILTER_CMD= grep ^UT_ .include "export.mk" .unexport UT_ZOO UT_FOO UT_TEST = unexport + +# Until 2020-08-08, Var_UnExport had special handling for '\n', that code +# was not reachable though. At that point, backslash-newline has already +# been replaced with a simple space, and variables are not yet expanded. +UT_BEFORE_NL= before +UT_AFTER_NL= after +.export UT_BEFORE_NL UT_AFTER_NL +.unexport \ + UT_BEFORE_NL +.unexport ${.newline} UT_AFTER_NL diff --git a/unit-tests/use-inference.exp b/unit-tests/use-inference.exp new file mode 100644 index 000000000000..14ecf0550574 --- /dev/null +++ b/unit-tests/use-inference.exp @@ -0,0 +1,4 @@ +Building use-inference.from from nothing +make: don't know how to make use-inference.to (continuing) +`all' not remade because of errors. +exit status 0 diff --git a/unit-tests/use-inference.mk b/unit-tests/use-inference.mk new file mode 100644 index 000000000000..b0e5017bc6fb --- /dev/null +++ b/unit-tests/use-inference.mk @@ -0,0 +1,35 @@ +# $NetBSD: use-inference.mk,v 1.1 2020/08/09 16:32:28 rillig Exp $ +# +# Demonstrate that .USE rules do not have an effect on inference rules. +# At least not in the special case where the inference rule does not +# have any associated commands. + +.SUFFIXES: +.SUFFIXES: .from .to + +all: use-inference.to + +verbose: .USE + @echo 'Verbosely making $@ out of $>' + +.from.to: verbose +# Since this inference rule does not have any associated commands, it +# is ignored. +# +# @echo 'Building $@ from $<' + +use-inference.from: # assume it exists + @echo 'Building $@ from nothing' + +# Possible but unproven explanation: +# +# The main target is "all", which depends on "use-inference.to". +# The inference connects the .from to the .to file, otherwise make +# would not know that the .from file would need to be built. +# +# The .from file is then built. +# +# After this, make stops since it doesn't know how to make the .to file. +# This is strange since make definitely knows about the .from.to suffix +# inference rule. But it seems to ignore it, maybe because it doesn't +# have any associated commands. diff --git a/unit-tests/var-class-cmdline.exp b/unit-tests/var-class-cmdline.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/var-class-cmdline.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/var-class-cmdline.mk b/unit-tests/var-class-cmdline.mk new file mode 100644 index 000000000000..c43b5351c329 --- /dev/null +++ b/unit-tests/var-class-cmdline.mk @@ -0,0 +1,8 @@ +# $NetBSD: var-class-cmdline.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for variables specified on the command line. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/var-class-env.exp b/unit-tests/var-class-env.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/var-class-env.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/var-class-env.mk b/unit-tests/var-class-env.mk new file mode 100644 index 000000000000..6e6b4891d3fd --- /dev/null +++ b/unit-tests/var-class-env.mk @@ -0,0 +1,8 @@ +# $NetBSD: var-class-env.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for variables specified in the process environment. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/var-class-global.exp b/unit-tests/var-class-global.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/var-class-global.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/var-class-global.mk b/unit-tests/var-class-global.mk new file mode 100644 index 000000000000..81345ffda463 --- /dev/null +++ b/unit-tests/var-class-global.mk @@ -0,0 +1,8 @@ +# $NetBSD: var-class-global.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for global variables, which are the most common variables. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/var-class-local-legacy.exp b/unit-tests/var-class-local-legacy.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/var-class-local-legacy.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/var-class-local-legacy.mk b/unit-tests/var-class-local-legacy.mk new file mode 100644 index 000000000000..bfd9733fd42b --- /dev/null +++ b/unit-tests/var-class-local-legacy.mk @@ -0,0 +1,8 @@ +# $NetBSD: var-class-local-legacy.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for legacy target-local variables, such as ${<F} or ${@D}. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/var-class-local.exp b/unit-tests/var-class-local.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/var-class-local.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/var-class-local.mk b/unit-tests/var-class-local.mk new file mode 100644 index 000000000000..e75f08ba75a3 --- /dev/null +++ b/unit-tests/var-class-local.mk @@ -0,0 +1,8 @@ +# $NetBSD: var-class-local.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for target-local variables, such as ${.TARGET} or $@. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/var-class.exp b/unit-tests/var-class.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/var-class.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/var-class.mk b/unit-tests/var-class.mk new file mode 100644 index 000000000000..b20fca565e16 --- /dev/null +++ b/unit-tests/var-class.mk @@ -0,0 +1,9 @@ +# $NetBSD: var-class.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the different variable classes (local, command-line, global, +# environment), and which of them takes precedence over the others. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/var-op-append.exp b/unit-tests/var-op-append.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/var-op-append.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/var-op-append.mk b/unit-tests/var-op-append.mk new file mode 100644 index 000000000000..b75880f95838 --- /dev/null +++ b/unit-tests/var-op-append.mk @@ -0,0 +1,9 @@ +# $NetBSD: var-op-append.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the += variable assignment operator, which appends to a variable, +# creating it if necessary. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/var-op-assign.exp b/unit-tests/var-op-assign.exp new file mode 100644 index 000000000000..0e9e2d211a5f --- /dev/null +++ b/unit-tests/var-op-assign.exp @@ -0,0 +1,6 @@ +this will be evaluated later +make: "var-op-assign.mk" line 52: Need an operator +make: "var-op-assign.mk" line 86: Parsing still continues until here. +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/var-op-assign.mk b/unit-tests/var-op-assign.mk new file mode 100644 index 000000000000..dadff6ed4b87 --- /dev/null +++ b/unit-tests/var-op-assign.mk @@ -0,0 +1,89 @@ +# $NetBSD: var-op-assign.mk,v 1.4 2020/08/25 16:20:32 rillig Exp $ +# +# Tests for the = variable assignment operator, which overwrites an existing +# variable or creates it. + +# This is a simple variable assignment. +# To the left of the assignment operator '=' there is the variable name, +# and to the right is the variable value. +# +VAR= value + +# This condition demonstrates that whitespace around the assignment operator +# is discarded. Otherwise the value would start with a single tab. +# +.if ${VAR} != "value" +.error +.endif + +# Whitespace to the left of the assignment operator is ignored as well. +# The variable value can contain arbitrary characters. +# +# The '#' needs to be escaped with a backslash, this happens in a very +# early stage of parsing and applies to all line types, except for the +# commands, which are indented with a tab. +# +# The '$' needs to be escaped with another '$', otherwise it would refer to +# another variable. +# +VAR =new value and \# some $$ special characters # comment + +# When a string literal appears in a condition, the escaping rules are +# different. Run make with the -dc option to see the details. +.if ${VAR} != "new value and \# some \$ special characters" +.error ${VAR} +.endif + +# The variable value may contain references to other variables. +# In this example, the reference is to the variable with the empty name, +# which always expands to an empty string. This alone would not produce +# any side-effects, therefore the variable has a :!...! modifier that +# executes a shell command. +VAR= ${:! echo 'not yet evaluated' 1>&2 !} +VAR= ${:! echo 'this will be evaluated later' 1>&2 !} + +# Now force the variable to be evaluated. +# This outputs the line to stderr. +.if ${VAR} +.endif + +# In a variable assignment, the variable name must consist of a single word. +# +VARIABLE NAME= variable value + +# But if the whitespace appears inside parentheses or braces, everything is +# fine. +# +# XXX: This was not an intentional decision, as variable names typically +# neither contain parentheses nor braces. This is only a side-effect from +# the implementation of the parser, which cheats when parsing a variable +# name. It only counts parentheses and braces instead of properly parsing +# nested variable expressions such as VAR.${param}. +# +VAR(spaces in parentheses)= () +VAR{spaces in braces}= {} + +# Be careful and use indirect variable names here, to prevent accidentally +# accepting the test in case the parser just uses "VAR" as the variable name, +# ignoring all the rest. +# +VARNAME_PAREN= VAR(spaces in parentheses) +VARNAME_BRACES= VAR{spaces in braces} + +.if ${${VARNAME_PAREN}} != "()" +.error +.endif + +.if ${${VARNAME_BRACES}} != "{}" +.error +.endif + +# In safe mode, parsing would stop immediately after the "VARIABLE NAME=" +# line, since any commands run after that are probably working with +# unexpected variable values. +# +# Therefore, just output an info message. +.info Parsing still continues until here. + +all: + @:; diff --git a/unit-tests/var-op-default.exp b/unit-tests/var-op-default.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/var-op-default.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/var-op-default.mk b/unit-tests/var-op-default.mk new file mode 100644 index 000000000000..afb0c55f827c --- /dev/null +++ b/unit-tests/var-op-default.mk @@ -0,0 +1,9 @@ +# $NetBSD: var-op-default.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the ?= variable assignment operator, which only assigns +# if the variable is still undefined. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/var-op-expand.exp b/unit-tests/var-op-expand.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/var-op-expand.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/var-op-expand.mk b/unit-tests/var-op-expand.mk new file mode 100644 index 000000000000..07c5fb647759 --- /dev/null +++ b/unit-tests/var-op-expand.mk @@ -0,0 +1,9 @@ +# $NetBSD: var-op-expand.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the := variable assignment operator, which expands its +# right-hand side. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/var-op-shell.exp b/unit-tests/var-op-shell.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/var-op-shell.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/var-op-shell.mk b/unit-tests/var-op-shell.mk new file mode 100644 index 000000000000..83580a89e6c2 --- /dev/null +++ b/unit-tests/var-op-shell.mk @@ -0,0 +1,9 @@ +# $NetBSD: var-op-shell.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the != variable assignment operator, which runs its right-hand +# side through the shell. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/var-op.exp b/unit-tests/var-op.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/var-op.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/var-op.mk b/unit-tests/var-op.mk new file mode 100644 index 000000000000..9c9ffb8782f5 --- /dev/null +++ b/unit-tests/var-op.mk @@ -0,0 +1,8 @@ +# $NetBSD: var-op.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the variable assignment operators. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/vardebug.exp b/unit-tests/vardebug.exp new file mode 100644 index 000000000000..8a9eefad4b0c --- /dev/null +++ b/unit-tests/vardebug.exp @@ -0,0 +1,80 @@ +Global:RELEVANT = yes +Global:VAR = added +Global:VAR = overwritten +Global:delete VAR +Global:delete VAR (not found) +Var_Parse: ${:U} with VARE_WANTRES +Applying ${:U} to "" (eflags = VARE_WANTRES, vflags = VAR_JUNK) +Result of ${:U} is "" (eflags = VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Var_Set("${:U}", "empty name", ...) name expands to empty string - ignored +Var_Parse: ${:U} with VARE_WANTRES +Applying ${:U} to "" (eflags = VARE_WANTRES, vflags = VAR_JUNK) +Result of ${:U} is "" (eflags = VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Var_Append("${:U}", "empty name", ...) name expands to empty string - ignored +Global:FROM_CMDLINE = overwritten ignored! +Global:VAR = 1 +Global:VAR = 1 2 +Global:VAR = 1 2 3 +Var_Parse: ${VAR:M[2]} with VARE_UNDEFERR|VARE_WANTRES +Applying ${VAR:M...} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Pattern[VAR] for [1 2 3] is [[2]] +ModifyWords: split "1 2 3" into 3 words +VarMatch [1] [[2]] +VarMatch [2] [[2]] +VarMatch [3] [[2]] +Result of ${VAR:M[2]} is "2" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Var_Parse: ${VAR:N[2]} with VARE_UNDEFERR|VARE_WANTRES +Applying ${VAR:N...} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Pattern[VAR] for [1 2 3] is [[2]] +ModifyWords: split "1 2 3" into 3 words +Result of ${VAR:N[2]} is "1 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Var_Parse: ${VAR:S,2,two,} with VARE_UNDEFERR|VARE_WANTRES +Applying ${VAR:S...} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Modifier part: "2" +Modifier part: "two" +ModifyWords: split "1 2 3" into 3 words +Result of ${VAR:S,2,two,} is "1 two 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Var_Parse: ${VAR:Q} with VARE_UNDEFERR|VARE_WANTRES +Applying ${VAR:Q} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +QuoteMeta: [1\ 2\ 3] +Result of ${VAR:Q} is "1\ 2\ 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Var_Parse: ${VAR:tu:tl:Q} with VARE_UNDEFERR|VARE_WANTRES +Applying ${VAR:t...} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Result of ${VAR:tu} is "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Applying ${VAR:t...} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Result of ${VAR:tl} is "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Applying ${VAR:Q} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +QuoteMeta: [1\ 2\ 3] +Result of ${VAR:Q} is "1\ 2\ 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Var_Parse: ${:Uvalue:${:UM*e}:Mvalu[e]} with VARE_UNDEFERR|VARE_WANTRES +Applying ${:U...} to "" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK) +Result of ${:Uvalue} is "value" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Var_Parse: ${:UM*e}:Mvalu[e]} with VARE_UNDEFERR|VARE_WANTRES +Applying ${:U...} to "" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK) +Result of ${:UM*e} is "M*e" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Indirect modifier "M*e" from "${:UM*e}" +Applying ${:M...} to "value" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Pattern[] for [value] is [*e] +ModifyWords: split "value" into 1 words +VarMatch [value] [*e] +Result of ${:M*e} is "value" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Applying ${:M...} to "value" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Pattern[] for [value] is [valu[e]] +ModifyWords: split "value" into 1 words +VarMatch [value] [valu[e]] +Result of ${:Mvalu[e]} is "value" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Var_Parse: ${:UVAR} with VARE_WANTRES +Applying ${:U...} to "" (eflags = VARE_WANTRES, vflags = VAR_JUNK) +Result of ${:UVAR} is "VAR" (eflags = VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Global:delete VAR +Var_Parse: ${:Uvariable:unknown} with VARE_UNDEFERR|VARE_WANTRES +Applying ${:U...} to "" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK) +Result of ${:Uvariable} is "variable" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Applying ${:u...} to "variable" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +make: Unknown modifier 'u' +Result of ${:unknown} is error (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +make: "vardebug.mk" line 44: Malformed conditional (${:Uvariable:unknown}) +Var_Parse: ${UNDEFINED} with VARE_UNDEFERR|VARE_WANTRES +make: "vardebug.mk" line 53: Malformed conditional (${UNDEFINED}) +Global:RELEVANT = no +exit status 1 diff --git a/unit-tests/vardebug.mk b/unit-tests/vardebug.mk new file mode 100644 index 000000000000..f61df98d50de --- /dev/null +++ b/unit-tests/vardebug.mk @@ -0,0 +1,59 @@ +# $NetBSD: vardebug.mk,v 1.3 2020/08/08 14:28:46 rillig Exp $ +# +# Demonstrates the debugging output for var.c. + +RELEVANT= yes + +VAR= added # VarAdd +VAR= overwritten # Var_Set +.undef VAR # Var_Delete (found) +.undef VAR # Var_Delete (not found) + +# The variable with the empty name cannot be set at all. +${:U}= empty name # Var_Set +${:U}+= empty name # Var_Append + +FROM_CMDLINE= overwritten # Var_Set (ignored) + +VAR= 1 +VAR+= 2 +VAR+= 3 + +.if ${VAR:M[2]} # VarMatch +.endif +.if ${VAR:N[2]} # VarNoMatch (no debug output) +.endif + +.if ${VAR:S,2,two,} # VarGetPattern +.endif + +.if ${VAR:Q} # VarQuote +.endif + +.if ${VAR:tu:tl:Q} # ApplyModifiers +.endif + +# ApplyModifiers, "Got ..." +.if ${:Uvalue:${:UM*e}:Mvalu[e]} +.endif + +.undef ${:UVAR} # Var_Delete + +# When ApplyModifiers results in an error, this appears in the debug log +# as "is error", without surrounding quotes. +.if ${:Uvariable:unknown} +.endif + +# XXX: The error message is "Malformed conditional", which is wrong. +# The condition is syntactically fine, it just contains an undefined variable. +# +# There is a specialized error message for "Undefined variable", but as of +# 2020-08-08, that is not covered by any unit tests. It might even be +# unreachable. +.if ${UNDEFINED} +.endif + +RELEVANT= no + +all: + @: diff --git a/unit-tests/varfind.exp b/unit-tests/varfind.exp new file mode 100644 index 000000000000..13d8d60226a9 --- /dev/null +++ b/unit-tests/varfind.exp @@ -0,0 +1,15 @@ +VarFind-aliases.to: long explicit-dependency VarFind-aliases.from +VarFind-aliases.to: abbr explicit-dependency VarFind-aliases.from +VarFind-aliases.to: long +VarFind-aliases.to: abbr +VarFind-aliases.to: long VarFind-aliases.from +VarFind-aliases.to: abbr VarFind-aliases.from +VarFind-aliases.to: long +VarFind-aliases.to: abbr +VarFind-aliases.to: long explicit-dependency VarFind-aliases.from +VarFind-aliases.to: abbr explicit-dependency VarFind-aliases.from +VarFind-aliases.to: long VarFind-aliases +VarFind-aliases.to: abbr VarFind-aliases +VarFind-aliases.to: long VarFind-aliases.to +VarFind-aliases.to: abbr VarFind-aliases.to +exit status 0 diff --git a/unit-tests/varfind.mk b/unit-tests/varfind.mk new file mode 100644 index 000000000000..6fb7bc630eaf --- /dev/null +++ b/unit-tests/varfind.mk @@ -0,0 +1,31 @@ +# $NetBSD: varfind.mk,v 1.1 2020/07/25 21:19:29 rillig Exp $ +# +# Demonstrates variable name aliases in VarFind. + +all: VarFind-aliases.to + +.SUFFIXES: .from .to + +VarFind-aliases.from: + @: do nothing + +VarFind-aliases.to: explicit-dependency + +explicit-dependency: + @: do nothing + +.from.to: + @echo $@: long ${.ALLSRC:Q} + @echo $@: abbr ${>:Q} + @echo $@: long ${.ARCHIVE:Q} + @echo $@: abbr ${!:Q} + @echo $@: long ${.IMPSRC:Q} + @echo $@: abbr ${<:Q} + @echo $@: long ${.MEMBER:Q} + @echo $@: abbr ${%:Q} + @echo $@: long ${.OODATE:Q} + @echo $@: abbr ${?:Q} + @echo $@: long ${.PREFIX:Q} + @echo $@: abbr ${*:Q} + @echo $@: long ${.TARGET:Q} + @echo $@: abbr ${@:Q} diff --git a/unit-tests/varmisc.exp b/unit-tests/varmisc.exp index b9a29141ce6b..e8f88d9ca51f 100644 --- a/unit-tests/varmisc.exp +++ b/unit-tests/varmisc.exp @@ -23,4 +23,52 @@ Version=123.456.789 == 123456789 Literal=3.4.5 == 3004005 We have target specific vars MAN= make.1 +save-dollars: 0 = $ +save-dollars: 1 = $$ +save-dollars: 2 = $$ +save-dollars: False = $ +save-dollars: True = $$ +save-dollars: false = $ +save-dollars: true = $$ +save-dollars: Yes = $$ +save-dollars: No = $ +save-dollars: yes = $$ +save-dollars: no = $ +save-dollars: On = $$ +save-dollars: Off = $ +save-dollars: ON = $$ +save-dollars: OFF = $ +save-dollars: on = $$ +save-dollars: off = $ +export-appended: env +export-appended: env +export-appended: env mk +parse-dynamic: parse-dynamic parse-dynamic before +parse-dynamic: parse-dynamic parse-dynamic after +parse-dynamic: parse-dynamic parse-dynamic after +varerror-unclosed:begin +make: Unclosed variable "" + +make: Unclosed variable "UNCLOSED" + +make: Unclosed variable "UNCLOSED" + +make: Unclosed variable "PATTERN" +make: Unclosed variable specification (expecting '}') for "UNCLOSED" (value "") modifier M + +make: Unclosed variable "param" +make: Unclosed variable "UNCLOSED." + + +make: Unclosed variable "UNCLOSED.1" + +make: Unclosed variable "UNCLOSED.2" + +make: Unclosed variable "UNCLOSED.3" + +make: Unclosed variable "UNCLOSED_ORIG" + +varerror-unclosed:end +target1-flags: we have: one two +target2-flags: we have: one two three four exit status 0 diff --git a/unit-tests/varmisc.mk b/unit-tests/varmisc.mk index ab591db5c4fd..4c58b2a5dc77 100644 --- a/unit-tests/varmisc.mk +++ b/unit-tests/varmisc.mk @@ -1,9 +1,13 @@ -# $Id: varmisc.mk,v 1.11 2020/07/02 15:43:43 sjg Exp $ +# $Id: varmisc.mk,v 1.19 2020/08/31 16:28:10 sjg Exp $ # # Miscellaneous variable tests. all: unmatched_var_paren D_true U_true D_false U_false Q_lhs Q_rhs NQ_none \ strftime cmpv manok +all: save-dollars +all: export-appended +all: parse-dynamic +all: varerror-unclosed unmatched_var_paren: @echo ${foo::=foo-text} @@ -74,7 +78,7 @@ manok: @echo MAN=${MAN} # This is an expanded variant of the above .for loop. -# Between 2020-08-28 and 2020-07-02 this paragraph generated a wrong +# Between 2020-06-28 and 2020-07-02 this paragraph generated a wrong # error message "Variable VARNAME is recursive". # When evaluating the !empty expression, the ${:U1} was not expanded and # thus resulted in the seeming definition VARNAME=${VARNAME}, which is @@ -82,3 +86,142 @@ manok: VARNAME= ${VARNAME${:U1}} .if defined(VARNAME${:U2}) && !empty(VARNAME${:U2}) .endif + +# begin .MAKE.SAVE_DOLLARS; see Var_Set_with_flags and s2Boolean. +SD_VALUES= 0 1 2 False True false true Yes No yes no On Off ON OFF on off +SD_4_DOLLARS= $$$$ + +.for val in ${SD_VALUES} +.MAKE.SAVE_DOLLARS:= ${val} # Must be := since a simple = has no effect. +SD.${val}:= ${SD_4_DOLLARS} +.endfor +.MAKE.SAVE_DOLLARS:= yes + +save-dollars: +.for val in ${SD_VALUES} + @printf '%s: %-8s = %s\n' $@ ${val} ${SD.${val}:Q} +.endfor + +# Appending to an undefined variable does not add a space in front. +.undef APPENDED +APPENDED+= value +.if ${APPENDED} != "value" +.error "${APPENDED}" +.endif + +# Appending to an empty variable adds a space between the old value +# and the additional value. +APPENDED= # empty +APPENDED+= value +.if ${APPENDED} != " value" +.error "${APPENDED}" +.endif + +# Appending to parameterized variables works as well. +PARAM= param +VAR.${PARAM}= 1 +VAR.${PARAM}+= 2 +.if ${VAR.param} != "1 2" +.error "${VAR.param}" +.endif + +# The variable name can contain arbitrary characters. +# If the expanded variable name ends in a +, this still does not influence +# the parser. The assignment operator is still a simple assignment. +# Therefore, there is no need to add a space between the variable name +# and the assignment operator. +PARAM= + +VAR.${PARAM}= 1 +VAR.${PARAM}+= 2 +.if ${VAR.+} != "1 2" +.error "${VAR.+}" +.endif +.for param in + ! ? +VAR.${param}= ${param} +.endfor +.if ${VAR.+} != "+" || ${VAR.!} != "!" || ${VAR.?} != "?" +.error "${VAR.+}" "${VAR.!}" "${VAR.?}" +.endif + +# Appending to a variable from the environment creates a copy of that variable +# in the global context. +# The appended value is not exported automatically. +# When a variable is exported, the exported value is taken at the time of the +# .export directive. Later changes to the variable have no effect. +.export FROM_ENV_BEFORE +FROM_ENV+= mk +FROM_ENV_BEFORE+= mk +FROM_ENV_AFTER+= mk +.export FROM_ENV_AFTER + +export-appended: + @echo $@: "$$FROM_ENV" + @echo $@: "$$FROM_ENV_BEFORE" + @echo $@: "$$FROM_ENV_AFTER" + +# begin parse-dynamic +# +# Demonstrate that the target-specific variables are not evaluated in +# the global context. They are preserved until there is a local context +# in which resolving them makes sense. + +# There are different code paths for short names ... +${:U>}= before +GS_TARGET:= $@ +GS_MEMBER:= $% +GS_PREFIX:= $* +GS_ARCHIVE:= $! +GS_ALLSRC:= $> +${:U>}= after +# ... and for braced short names ... +GB_TARGET:= ${@} +GB_MEMBER:= ${%} +GB_PREFIX:= ${*} +GB_ARCHIVE:= ${!} +GB_ALLSRC:= ${>} +# ... and for long names. +GL_TARGET:= ${.TARGET} +GL_MEMBER:= ${.MEMBER} +GL_PREFIX:= ${.PREFIX} +GL_ARCHIVE:= ${.ARCHIVE} +GL_ALLSRC:= ${.ALLSRC} + +parse-dynamic: + @echo $@: ${GS_TARGET} ${GS_MEMBER} ${GS_PREFIX} ${GS_ARCHIVE} ${GS_ALLSRC} + @echo $@: ${GB_TARGET} ${GB_MEMBER} ${GB_PREFIX} ${GB_ARCHIVE} ${GB_ALLSRC} + @echo $@: ${GL_TARGET} ${GL_MEMBER} ${GL_PREFIX} ${GL_ARCHIVE} ${GL_ALLSRC} + +# Since 2020-07-28, make complains about unclosed variables. +# Before that, it had complained about unclosed variables only when +# parsing the modifiers, but not when parsing the variable name. + +UNCLOSED_INDIR_1= ${UNCLOSED_ORIG +UNCLOSED_INDIR_2= ${UNCLOSED_INDIR_1} + +FLAGS= one two +FLAGS+= ${FLAGS.${.ALLSRC:M*.c:T:u}} +FLAGS.target2.c = three four + +target1.c: +target2.c: + +all: target1-flags target2-flags +target1-flags: target1.c + @echo $@: we have: ${FLAGS} + +target2-flags: target2.c + @echo $@: we have: ${FLAGS} + +varerror-unclosed: + @echo $@:begin + @echo $( + @echo $(UNCLOSED + @echo ${UNCLOSED + @echo ${UNCLOSED:M${PATTERN + @echo ${UNCLOSED.${param + @echo $ +.for i in 1 2 3 + @echo ${UNCLOSED.${i} +.endfor + @echo ${UNCLOSED_INDIR_2} + @echo $@:end diff --git a/unit-tests/varmod-assign.exp b/unit-tests/varmod-assign.exp new file mode 100644 index 000000000000..120c775d5f69 --- /dev/null +++ b/unit-tests/varmod-assign.exp @@ -0,0 +1,26 @@ +mod-assign: first=1. +mod-assign: last=3. +mod-assign: appended=1 2 3. +1 +2 +3 +mod-assign: ran:3. +mod-assign: global: 1, 3, 1 2 3, 3. +mod-assign-nested: then1t1 +mod-assign-nested: else2e2 +mod-assign-nested: then3t3 +mod-assign-nested: else4e4 +make: Bad modifier `:' for +value} +make: Bad modifier `:' for +mod-assign-empty: overwritten} +mod-assign-empty: VAR=overwritten +make: Unknown modifier ':' + +sysv:y +make: Unfinished modifier for ASSIGN ('}' missing) + +ok=word +make: " echo word; false " returned non-zero status +err=previous +exit status 0 diff --git a/unit-tests/varmod-assign.mk b/unit-tests/varmod-assign.mk new file mode 100644 index 000000000000..82b7d947c484 --- /dev/null +++ b/unit-tests/varmod-assign.mk @@ -0,0 +1,81 @@ +# $NetBSD: varmod-assign.mk,v 1.6 2020/08/25 21:16:53 rillig Exp $ +# +# Tests for the obscure ::= variable modifiers, which perform variable +# assignments during evaluation, just like the = operator in C. + +all: mod-assign +all: mod-assign-nested +all: mod-assign-empty +all: mod-assign-parse +all: mod-assign-shell-error + +mod-assign: + # The ::?= modifier applies the ?= assignment operator 3 times. + # The ?= operator only has an effect for the first time, therefore + # the variable FIRST ends up with the value 1. + @echo $@: ${1 2 3:L:@i@${FIRST::?=$i}@} first=${FIRST}. + + # The ::= modifier applies the = assignment operator 3 times. + # The = operator overwrites the previous value, therefore the + # variable LAST ends up with the value 3. + @echo $@: ${1 2 3:L:@i@${LAST::=$i}@} last=${LAST}. + + # The ::+= modifier applies the += assignment operator 3 times. + # The += operator appends 3 times to the variable, therefore + # the variable APPENDED ends up with the value "1 2 3". + @echo $@: ${1 2 3:L:@i@${APPENDED::+=$i}@} appended=${APPENDED}. + + # The ::!= modifier applies the != assignment operator 3 times. + # The side effects of the shell commands are visible in the output. + # Just as with the ::= modifier, the last value is stored in the + # RAN variable. + @echo $@: ${echo.1 echo.2 echo.3:L:@i@${RAN::!=${i:C,.*,&; & 1>\&2,:S,., ,g}}@} ran:${RAN}. + + # The assignments happen in the global scope and thus are + # preserved even after the shell command has been run. + @echo $@: global: ${FIRST:Q}, ${LAST:Q}, ${APPENDED:Q}, ${RAN:Q}. + +mod-assign-nested: + # The condition "1" is true, therefore THEN1 gets assigned a value, + # and IT1 as well. Nothing surprising here. + @echo $@: ${1:?${THEN1::=then1${IT1::=t1}}:${ELSE1::=else1${IE1::=e1}}}${THEN1}${ELSE1}${IT1}${IE1} + + # The condition "0" is false, therefore ELSE1 gets assigned a value, + # and IE1 as well. Nothing surprising here as well. + @echo $@: ${0:?${THEN2::=then2${IT2::=t2}}:${ELSE2::=else2${IE2::=e2}}}${THEN2}${ELSE2}${IT2}${IE2} + + # The same effects happen when the variables are defined elsewhere. + @echo $@: ${SINK3:Q} + @echo $@: ${SINK4:Q} +SINK3:= ${1:?${THEN3::=then3${IT3::=t3}}:${ELSE3::=else3${IE3::=e3}}}${THEN3}${ELSE3}${IT3}${IE3} +SINK4:= ${0:?${THEN4::=then4${IT4::=t4}}:${ELSE4::=else4${IE4::=e4}}}${THEN4}${ELSE4}${IT4}${IE4} + +mod-assign-empty: + # Assigning to the empty variable would obviously not work since that variable + # is write-protected. Therefore it is rejected early as a "bad modifier". + @echo ${::=value} + @echo $@: ${:Uvalue::=overwritten} + + # The :L modifier sets the variable's value to its name. + # Since the name is still "VAR", assigning to that variable works. + @echo $@: ${VAR:L::=overwritten} VAR=${VAR} + +mod-assign-parse: + # The modifier for assignment operators starts with a ':'. + # An 'x' after that is an invalid modifier. + @echo ${ASSIGN::x} # 'x' is an unknown assignment operator + + # When parsing an assignment operator fails because the operator is + # incomplete, make falls back to the SysV modifier. + @echo ${SYSV::=sysv\:x}${SYSV::x=:y} + + @echo ${ASSIGN::=value # missing closing brace + +mod-assign-shell-error: + # If the command succeeds, the variable is assigned. + @${SH_OK::!= echo word; true } echo ok=${SH_OK} + + # If the command fails, the variable keeps its previous value. + # FIXME: the error message says: "previous" returned non-zero status + @${SH_ERR::=previous} + @${SH_ERR::!= echo word; false } echo err=${SH_ERR} diff --git a/unit-tests/varmod-defined.exp b/unit-tests/varmod-defined.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-defined.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-defined.mk b/unit-tests/varmod-defined.mk new file mode 100644 index 000000000000..fa5bf5c2245c --- /dev/null +++ b/unit-tests/varmod-defined.mk @@ -0,0 +1,28 @@ +# $NetBSD: varmod-defined.mk,v 1.3 2020/08/25 21:58:08 rillig Exp $ +# +# Tests for the :D variable modifier, which returns the given string +# if the variable is defined. It is closely related to the :U modifier. + +DEF= defined +.undef UNDEF + +# Since DEF is defined, the value of the expression is "value", not +# "defined". +# +.if ${DEF:Dvalue} != "value" +.error +.endif + +# Since UNDEF is not defined, the "value" is ignored. Instead of leaving the +# expression undefined, it is set to "", exactly to allow the expression to +# be used in .if conditions. In this place, other undefined expressions +# would generate an error message. +# XXX: Ideally the error message would be "undefined variable", but as of +# 2020-08-25 it is "Malformed conditional". +# +.if ${UNDEF:Dvalue} != "" +.error +.endif + +all: + @:; diff --git a/unit-tests/varmod-edge.exp b/unit-tests/varmod-edge.exp index b3b2e3a92f95..94ba81e2e4f0 100644 --- a/unit-tests/varmod-edge.exp +++ b/unit-tests/varmod-edge.exp @@ -1,17 +1,22 @@ +make: "varmod-edge.mk" line omitted: ok M-paren +make: "varmod-edge.mk" line omitted: ok M-mixed +make: "varmod-edge.mk" line omitted: ok M-unescape make: Unclosed variable specification (expecting '}') for "" (value "*)") modifier U -make: Unclosed substitution for INP.eq-esc (= missing) -ok M-paren -ok M-mixed -ok M-unescape -ok M-nest-mix -ok M-nest-brk -ok M-pat-err -ok M-bsbs -ok M-bs1-par -ok M-bs2-par -ok M-128 -ok eq-ext -ok eq-q -ok eq-bs -ok eq-esc +make: "varmod-edge.mk" line omitted: ok M-nest-mix +make: "varmod-edge.mk" line omitted: ok M-nest-brk +make: "varmod-edge.mk" line omitted: ok M-pat-err +make: "varmod-edge.mk" line omitted: ok M-bsbs +make: "varmod-edge.mk" line omitted: ok M-bs1-par +make: "varmod-edge.mk" line omitted: ok M-bs2-par +make: "varmod-edge.mk" line omitted: ok M-128 +make: "varmod-edge.mk" line omitted: ok eq-ext +make: "varmod-edge.mk" line omitted: ok eq-q +make: "varmod-edge.mk" line omitted: ok eq-bs +make: Unfinished modifier for INP.eq-esc ('=' missing) +make: "varmod-edge.mk" line omitted: ok eq-esc +make: "varmod-edge.mk" line omitted: ok colon +make: Unknown modifier ':' +make: Unknown modifier ':' +make: "varmod-edge.mk" line omitted: ok colons +ok exit status 0 diff --git a/unit-tests/varmod-edge.mk b/unit-tests/varmod-edge.mk index 561eb6116891..e6f7f91c95c7 100644 --- a/unit-tests/varmod-edge.mk +++ b/unit-tests/varmod-edge.mk @@ -1,4 +1,4 @@ -# $NetBSD: varmod-edge.mk,v 1.7 2020/04/27 14:07:22 christos Exp $ +# $NetBSD: varmod-edge.mk,v 1.12 2020/08/08 13:29:09 rillig Exp $ # # Tests for edge cases in variable modifiers. # @@ -143,20 +143,31 @@ INP.eq-bs= file.c file.c=%.o MOD.eq-bs= ${INP.eq-bs:%.c\=%.o=%.ext} EXP.eq-bs= file.c file.ext -# Having only an escaped = results in a parse error. -# The call to "pattern.lhs = VarGetPattern" fails. +# Having only an escaped '=' results in a parse error. +# The call to "pattern.lhs = ParseModifierPart" fails. TESTS+= eq-esc INP.eq-esc= file.c file... MOD.eq-esc= ${INP.eq-esc:a\=b} EXP.eq-esc= # empty -# make: Unclosed substitution for INP.eq-esc (= missing) +# make: Unfinished modifier for INP.eq-esc ('=' missing) + +TESTS+= colon +INP.colon= value +MOD.colon= ${INP.colon:} +EXP.colon= value + +TESTS+= colons +INP.colons= value +MOD.colons= ${INP.colons::::} +EXP.colons= # empty -all: .for test in ${TESTS} . if ${MOD.${test}} == ${EXP.${test}} - @printf 'ok %s\n' ${test:Q}'' +.info ok ${test} . else - @printf 'error in %s: expected %s, got %s\n' \ - ${test:Q}'' ${EXP.${test}:Q}'' ${MOD.${test}:Q}'' +.warning error in ${test}: expected "${EXP.${test}}", got "${MOD.${test}}" . endif .endfor + +all: + @echo ok diff --git a/unit-tests/varmod-exclam-shell.exp b/unit-tests/varmod-exclam-shell.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-exclam-shell.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-exclam-shell.mk b/unit-tests/varmod-exclam-shell.mk new file mode 100644 index 000000000000..2e811ddb4f5c --- /dev/null +++ b/unit-tests/varmod-exclam-shell.mk @@ -0,0 +1,28 @@ +# $NetBSD: varmod-exclam-shell.mk,v 1.2 2020/08/16 12:48:55 rillig Exp $ +# +# Tests for the :!cmd! variable modifier. + +.if ${:!echo hello | tr 'l' 'l'!} != "hello" +.warning unexpected +.endif + +# The output is truncated at the first null byte. +# Cmd_Exec returns only a string pointer without length information. +.if ${:!echo hello | tr 'l' '\0'!} != "he" +.warning unexpected +.endif + +.if ${:!echo!} != "" +.warning A newline at the end of the output must be stripped. +.endif + +.if ${:!echo;echo!} != " " +.warning Only a single newline at the end of the output is stripped. +.endif + +.if ${:!echo;echo;echo;echo!} != " " +.warning Other newlines in the output are converted to spaces. +.endif + +all: + @:; diff --git a/unit-tests/varmod-extension.exp b/unit-tests/varmod-extension.exp new file mode 100644 index 000000000000..24f7403c7f3f --- /dev/null +++ b/unit-tests/varmod-extension.exp @@ -0,0 +1,10 @@ +extension of 'a/b/c' is '' +extension of 'def' is '' +extension of 'a.b.c' is 'c' +extension of 'a.b/c' is 'b/c' +extension of 'a' is '' +extension of 'a.a' is 'a' +extension of '.gitignore' is 'gitignore' +extension of 'a' is '' +extension of 'a.a' is 'a' +exit status 0 diff --git a/unit-tests/varmod-extension.mk b/unit-tests/varmod-extension.mk new file mode 100644 index 000000000000..db501f7234c7 --- /dev/null +++ b/unit-tests/varmod-extension.mk @@ -0,0 +1,9 @@ +# $NetBSD: varmod-extension.mk,v 1.3 2020/08/23 15:09:15 rillig Exp $ +# +# Tests for the :E variable modifier, which returns the filename extension +# of each word in the variable. + +all: +.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a + @echo "extension of '"${path:Q}"' is '"${path:E:Q}"'" +.endfor diff --git a/unit-tests/varmod-gmtime.exp b/unit-tests/varmod-gmtime.exp new file mode 100644 index 000000000000..373e8de1a271 --- /dev/null +++ b/unit-tests/varmod-gmtime.exp @@ -0,0 +1,9 @@ +mod-gmtime: +%Y +2020 +%Y +%Y +mod-gmtime-indirect: +make: Unknown modifier '1' + +exit status 0 diff --git a/unit-tests/varmod-gmtime.mk b/unit-tests/varmod-gmtime.mk new file mode 100644 index 000000000000..0a05ad58d982 --- /dev/null +++ b/unit-tests/varmod-gmtime.mk @@ -0,0 +1,35 @@ +# $NetBSD: varmod-gmtime.mk,v 1.2 2020/08/16 12:48:55 rillig Exp $ +# +# Tests for the :gmtime variable modifier, which formats a timestamp +# using strftime(3). + +all: mod-gmtime +all: mod-gmtime-indirect + +mod-gmtime: + @echo $@: + @echo ${%Y:L:gmtim=1593536400} # modifier name too short + @echo ${%Y:L:gmtime=1593536400} # 2020-07-01T00:00:00Z + @echo ${%Y:L:gmtimer=1593536400} # modifier name too long + @echo ${%Y:L:gm=gm:M*} + +mod-gmtime-indirect: + @echo $@: + + # As of 2020-08-16, it is not possible to pass the seconds via a + # variable expression. This is because parsing of the :gmtime + # modifier stops at the '$' and returns to ApplyModifiers. + # + # There, a colon would be skipped but not a dollar. + # Parsing therefore continues at the '$' of the ${:U159...}, looking + # for an ordinary variable modifier. + # + # At this point, the ${:U} is expanded and interpreted as a variable + # modifier, which results in the error message "Unknown modifier '1'". + # + # If ApplyModifier_Gmtime were to pass its argument through + # ParseModifierPart, this would work. + @echo ${%Y:L:gmtime=${:U1593536400}} + +all: + @:; diff --git a/unit-tests/varmod-hash.exp b/unit-tests/varmod-hash.exp new file mode 100644 index 000000000000..f16f30903539 --- /dev/null +++ b/unit-tests/varmod-hash.exp @@ -0,0 +1,9 @@ +make: Unknown modifier 'h' + +26bb0f5f +12345 +make: Unknown modifier 'h' + +make: Unknown modifier 'h' + +exit status 0 diff --git a/unit-tests/varmod-hash.mk b/unit-tests/varmod-hash.mk new file mode 100644 index 000000000000..ef59268cb82c --- /dev/null +++ b/unit-tests/varmod-hash.mk @@ -0,0 +1,10 @@ +# $NetBSD: varmod-hash.mk,v 1.3 2020/08/23 15:13:21 rillig Exp $ +# +# Tests for the :hash variable modifier. + +all: + @echo ${12345:L:has} # modifier name too short + @echo ${12345:L:hash} # ok + @echo ${12345:L:hash=SHA-256} # :hash does not accept '=' + @echo ${12345:L:hasX} # misspelled + @echo ${12345:L:hashed} # modifier name too long diff --git a/unit-tests/varmod-head.exp b/unit-tests/varmod-head.exp new file mode 100644 index 000000000000..f0bf87f03012 --- /dev/null +++ b/unit-tests/varmod-head.exp @@ -0,0 +1,10 @@ +head (dirname) of 'a/b/c' is 'a/b' +head (dirname) of 'def' is '.' +head (dirname) of 'a.b.c' is '.' +head (dirname) of 'a.b/c' is 'a.b' +head (dirname) of 'a' is '.' +head (dirname) of 'a.a' is '.' +head (dirname) of '.gitignore' is '.' +head (dirname) of 'a' is '.' +head (dirname) of 'a.a' is '.' +exit status 0 diff --git a/unit-tests/varmod-head.mk b/unit-tests/varmod-head.mk new file mode 100644 index 000000000000..eda4820c5f14 --- /dev/null +++ b/unit-tests/varmod-head.mk @@ -0,0 +1,9 @@ +# $NetBSD: varmod-head.mk,v 1.3 2020/08/23 15:09:15 rillig Exp $ +# +# Tests for the :H variable modifier, which returns the dirname of +# each of the words in the variable value. + +all: +.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a + @echo "head (dirname) of '"${path:Q}"' is '"${path:H:Q}"'" +.endfor diff --git a/unit-tests/varmod-ifelse.exp b/unit-tests/varmod-ifelse.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-ifelse.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-ifelse.mk b/unit-tests/varmod-ifelse.mk new file mode 100644 index 000000000000..e8e92b35d1c1 --- /dev/null +++ b/unit-tests/varmod-ifelse.mk @@ -0,0 +1,9 @@ +# $NetBSD: varmod-ifelse.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the ${cond:?then:else} variable modifier, which evaluates either +# the then-expression or the else-expression, depending on the condition. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varmod-l-name-to-value.exp b/unit-tests/varmod-l-name-to-value.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-l-name-to-value.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-l-name-to-value.mk b/unit-tests/varmod-l-name-to-value.mk new file mode 100644 index 000000000000..b8a5877e1100 --- /dev/null +++ b/unit-tests/varmod-l-name-to-value.mk @@ -0,0 +1,31 @@ +# $NetBSD: varmod-l-name-to-value.mk,v 1.3 2020/08/25 22:25:05 rillig Exp $ +# +# Tests for the :L modifier, which returns the variable name as the new value. + +# The empty variable name leads to an empty string. +.if ${:L} != "" +.error +.endif + +# The variable name is converted into an expression with the variable name +# "VARNAME" and the value "VARNAME". +.if ${VARNAME:L} != "VARNAME" +.error +.endif + +# The value of the expression can be modified afterwards. +.if ${VARNAME:L:S,VAR,,} != "NAME" +.error +.endif + +# The name of the expression is still the same as before. Using the :L +# modifier, it can be restored. +# +# Hmmm, this can be used as a double storage or a backup mechanism. +# Probably unintended, but maybe useful. +.if ${VARNAME:L:S,VAR,,:L} != "VARNAME" +.error +.endif + +all: + @:; diff --git a/unit-tests/varmod-localtime.exp b/unit-tests/varmod-localtime.exp new file mode 100644 index 000000000000..69e4335be187 --- /dev/null +++ b/unit-tests/varmod-localtime.exp @@ -0,0 +1,4 @@ +%Y +2020 +%Y +exit status 0 diff --git a/unit-tests/varmod-localtime.mk b/unit-tests/varmod-localtime.mk new file mode 100644 index 000000000000..fa4fd4f9cfb1 --- /dev/null +++ b/unit-tests/varmod-localtime.mk @@ -0,0 +1,9 @@ +# $NetBSD: varmod-localtime.mk,v 1.3 2020/08/23 15:13:21 rillig Exp $ +# +# Tests for the :localtime variable modifier, which returns the given time, +# formatted as a local timestamp. + +all: + @echo ${%Y:L:localtim=1593536400} # modifier name too short + @echo ${%Y:L:localtime=1593536400} # 2020-07-01T00:00:00Z + @echo ${%Y:L:localtimer=1593536400} # modifier name too long diff --git a/unit-tests/varmod-loop.exp b/unit-tests/varmod-loop.exp new file mode 100644 index 000000000000..ce93eb5bc0c8 --- /dev/null +++ b/unit-tests/varmod-loop.exp @@ -0,0 +1,16 @@ +:+one+ +two+ +three+: +:x1y x2y x3y: +:x1y x2y x3y: +:mod-loop-varname: :x1y x2y x3y: :: +:x1y x2y x3y: +mod-loop-resolve:w1d2d3w w2i3w w1i2d3 2i${RES3}w w1d2d3 2i${RES3} 1i${RES2}w: +mod-loop-varname-dollar:(1) (2) (3). +mod-loop-varname-dollar:() () (). +mod-loop-varname-dollar:() () (). +mod-loop-dollar:1: +mod-loop-dollar:${word}$: +mod-loop-dollar:$3$: +mod-loop-dollar:$${word}$$: +mod-loop-dollar:$$5$$: +mod-loop-dollar:$$${word}$$$: +exit status 0 diff --git a/unit-tests/varmod-loop.mk b/unit-tests/varmod-loop.mk new file mode 100644 index 000000000000..561f4b95baa0 --- /dev/null +++ b/unit-tests/varmod-loop.mk @@ -0,0 +1,63 @@ +# $NetBSD: varmod-loop.mk,v 1.2 2020/08/16 12:30:45 rillig Exp $ +# +# Tests for the :@var@...${var}...@ variable modifier. + +all: mod-loop-varname +all: mod-loop-resolve +all: mod-loop-varname-dollar +all: mod-loop-dollar + +# In the :@ modifier, the name of the loop variable can even be generated +# dynamically. There's no practical use-case for this, and hopefully nobody +# will ever depend on this, but technically it's possible. +# Therefore, in -dL mode, this is forbidden, see lint.mk. +mod-loop-varname: + @echo :${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@:Q}: + # ":::" is a very creative variable name, unlikely in practice + # The expression ${\:\:\:} would not work since backslashes can only + # be escaped in the modifiers, but not in the variable name. + @echo :${:U1 2 3:@:::@x${${:U\:\:\:}}y@}: + # "@@" is another creative variable name. + @echo :${:U1 2 3:@\@\@@x${@@}y@}: + # Even "@" works as a variable name since the variable is installed + # in the "current" scope, which in this case is the one from the + # target. + @echo :$@: :${:U1 2 3:@\@@x${@}y@}: :$@: + # In extreme cases, even the backslash can be used as variable name. + # It needs to be doubled though. + @echo :${:U1 2 3:@\\@x${${:Ux:S,x,\\,}}y@}: + +# The :@ modifier resolves the variables a little more often than expected. +# In particular, it resolves _all_ variables from the context, and not only +# the loop variable (in this case v). +# +# The d means direct reference, the i means indirect reference. +RESOLVE= ${RES1} $${RES1} +RES1= 1d${RES2} 1i$${RES2} +RES2= 2d${RES3} 2i$${RES3} +RES3= 3 + +mod-loop-resolve: + @echo $@:${RESOLVE:@v@w${v}w@:Q}: + +# Until 2020-07-20, the variable name of the :@ modifier could end with one +# or two dollar signs, which were silently ignored. +# There's no point in allowing a dollar sign in that position. +mod-loop-varname-dollar: + @echo $@:${1 2 3:L:@v$@($v)@:Q}. + @echo $@:${1 2 3:L:@v$$@($v)@:Q}. + @echo $@:${1 2 3:L:@v$$$@($v)@:Q}. + +# Demonstrate that it is possible to generate dollar characters using the +# :@ modifier. +# +# These are edge cases that could have resulted in a parse error as well +# since the $@ at the end could have been interpreted as a variable, which +# would mean a missing closing @ delimiter. +mod-loop-dollar: + @echo $@:${:U1:@word@${word}$@:Q}: + @echo $@:${:U2:@word@$${word}$$@:Q}: + @echo $@:${:U3:@word@$$${word}$$$@:Q}: + @echo $@:${:U4:@word@$$$${word}$$$$@:Q}: + @echo $@:${:U5:@word@$$$$${word}$$$$$@:Q}: + @echo $@:${:U6:@word@$$$$$${word}$$$$$$@:Q}: diff --git a/unit-tests/varmod-match-escape.exp b/unit-tests/varmod-match-escape.exp new file mode 100755 index 000000000000..1e4c030c5b42 --- /dev/null +++ b/unit-tests/varmod-match-escape.exp @@ -0,0 +1,3 @@ +Pattern[SPECIALS] for [\: : \\ * \*] is [\:] +Pattern[SPECIALS] for [\: : \\ * \*] is [:] +exit status 0 diff --git a/unit-tests/varmod-match-escape.mk b/unit-tests/varmod-match-escape.mk new file mode 100755 index 000000000000..7913bb476ae7 --- /dev/null +++ b/unit-tests/varmod-match-escape.mk @@ -0,0 +1,20 @@ +# $NetBSD: varmod-match-escape.mk,v 1.1 2020/08/16 20:03:53 rillig Exp $ +# +# As of 2020-08-01, the :M and :N modifiers interpret backslashes differently, +# depending on whether there was a variable expression somewhere before the +# first backslash or not. See ApplyModifier_Match, "copy = TRUE". +# +# Apart from the different and possibly confusing debug output, there is no +# difference in behavior. When parsing the modifier text, only \{, \} and \: +# are unescaped, and in the pattern matching these have the same meaning as +# their plain variants '{', '}' and ':'. In the pattern matching from +# Str_Match, only \*, \? or \[ would make a noticeable difference. +SPECIALS= \: : \\ * \* +RELEVANT= yes +.if ${SPECIALS:M${:U}\:} != ${SPECIALS:M\:${:U}} +.warning unexpected +.endif +RELEVANT= no + +all: + @:; diff --git a/unit-tests/varmod-match.exp b/unit-tests/varmod-match.exp new file mode 100644 index 000000000000..11777e086d4f --- /dev/null +++ b/unit-tests/varmod-match.exp @@ -0,0 +1,5 @@ +match-char-class: + uppercase numbers: One Two Three Four + all the others: five six seven + starts with non-s, ends with [ex]: One Three five +exit status 0 diff --git a/unit-tests/varmod-match.mk b/unit-tests/varmod-match.mk new file mode 100644 index 000000000000..805426a0fda9 --- /dev/null +++ b/unit-tests/varmod-match.mk @@ -0,0 +1,22 @@ +# $NetBSD: varmod-match.mk,v 1.3 2020/08/16 20:03:53 rillig Exp $ +# +# Tests for the :M variable modifier, which filters words that match the +# given pattern. + +all: match-char-class +all: slow + + +NUMBERS= One Two Three Four five six seven + +match-char-class: + @echo '$@:' + @echo ' uppercase numbers: ${NUMBERS:M[A-Z]*}' + @echo ' all the others: ${NUMBERS:M[^A-Z]*}' + @echo ' starts with non-s, ends with [ex]: ${NUMBERS:M[^s]*[ex]}' + + +# Before 2020-06-13, this expression took quite a long time in Str_Match, +# calling itself 601080390 times for 16 asterisks. +slow: + @: ${:U****************:M****************b} diff --git a/unit-tests/varmod-no-match.exp b/unit-tests/varmod-no-match.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-no-match.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-no-match.mk b/unit-tests/varmod-no-match.mk new file mode 100644 index 000000000000..2acb27e2e727 --- /dev/null +++ b/unit-tests/varmod-no-match.mk @@ -0,0 +1,9 @@ +# $NetBSD: varmod-no-match.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the :N variable modifier, which filters words that do not match +# the given pattern. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varmod-order-reverse.exp b/unit-tests/varmod-order-reverse.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-order-reverse.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-order-reverse.mk b/unit-tests/varmod-order-reverse.mk new file mode 100644 index 000000000000..fa9d179560c8 --- /dev/null +++ b/unit-tests/varmod-order-reverse.mk @@ -0,0 +1,13 @@ +# $NetBSD: varmod-order-reverse.mk,v 1.3 2020/08/16 20:13:10 rillig Exp $ +# +# Tests for the :Or variable modifier, which returns the words, sorted in +# descending order. + +NUMBERS= one two three four five six seven eight nine ten + +.if ${NUMBERS:Or} != "two three ten six seven one nine four five eight" +.error ${NUMBERS:Or} +.endif + +all: + @:; diff --git a/unit-tests/varmod-order-shuffle.exp b/unit-tests/varmod-order-shuffle.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-order-shuffle.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-order-shuffle.mk b/unit-tests/varmod-order-shuffle.mk new file mode 100644 index 000000000000..b6eb6be4b1bc --- /dev/null +++ b/unit-tests/varmod-order-shuffle.mk @@ -0,0 +1,39 @@ +# $NetBSD: varmod-order-shuffle.mk,v 1.3 2020/08/16 20:43:01 rillig Exp $ +# +# Tests for the :Ox variable modifier, which returns the words of the +# variable, shuffled. +# +# As of 2020-08-16, make uses random(3) seeded by the current time in seconds. +# This makes the random numbers completely predictable since there is no other +# part of make that uses random numbers. + +NUMBERS= one two three four five six seven eight nine ten + +# Note that 1 in every 10! trials two independently generated +# randomized orderings will be the same. The test framework doesn't +# support checking probabilistic output, so we accept that each of the +# 3 :Ox tests will incorrectly fail with probability 2.756E-7, which +# lets the whole test fail once in 1.209.600 runs, on average. + +# Create two shuffles using the := assignment operator. +shuffled1:= ${NUMBERS:Ox} +shuffled2:= ${NUMBERS:Ox} +.if ${shuffled1} == ${shuffled2} +.error ${shuffled1} == ${shuffled2} +.endif + +# Sorting the list before shuffling it has no effect. +shuffled1:= ${NUMBERS:O:Ox} +shuffled2:= ${NUMBERS:O:Ox} +.if ${shuffled1} == ${shuffled2} +.error ${shuffled1} == ${shuffled2} +.endif + +# Sorting after shuffling must produce the original numbers. +sorted:= ${NUMBERS:Ox:O} +.if ${sorted} != ${NUMBERS:O} +.error ${sorted} != ${NUMBERS:O} +.endif + +all: + @:; diff --git a/unit-tests/varmod-order.exp b/unit-tests/varmod-order.exp new file mode 100644 index 000000000000..99d1d6ef164c --- /dev/null +++ b/unit-tests/varmod-order.exp @@ -0,0 +1,7 @@ +make: Bad modifier `:OX' for NUMBERS +make: "varmod-order.mk" line 13: Undefined variable "${NUMBERS:OX" +make: Bad modifier `:OxXX' for NUMBERS +make: "varmod-order.mk" line 16: Undefined variable "${NUMBERS:Ox" +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/varmod-order.mk b/unit-tests/varmod-order.mk new file mode 100644 index 000000000000..b079bdc34217 --- /dev/null +++ b/unit-tests/varmod-order.mk @@ -0,0 +1,19 @@ +# $NetBSD: varmod-order.mk,v 1.4 2020/08/16 20:43:01 rillig Exp $ +# +# Tests for the :O variable modifier, which returns the words, sorted in +# ascending order. + +NUMBERS= one two three four five six seven eight nine ten + +.if ${NUMBERS:O} != "eight five four nine one seven six ten three two" +.error ${NUMBERS:O} +.endif + +# Unknown modifier "OX" +_:= ${NUMBERS:OX} + +# Unknown modifier "OxXX" +_:= ${NUMBERS:OxXX} + +all: + @:; diff --git a/unit-tests/varmod-path.exp b/unit-tests/varmod-path.exp new file mode 100644 index 000000000000..ae7f6cc85748 --- /dev/null +++ b/unit-tests/varmod-path.exp @@ -0,0 +1,4 @@ +varmod-path.subdir/varmod-path.phony +varmod-path.subdir/varmod-path.real +varmod-path.enoent +exit status 0 diff --git a/unit-tests/varmod-path.mk b/unit-tests/varmod-path.mk new file mode 100644 index 000000000000..ebbf755ddbec --- /dev/null +++ b/unit-tests/varmod-path.mk @@ -0,0 +1,35 @@ +# $NetBSD: varmod-path.mk,v 1.3 2020/08/23 08:10:49 rillig Exp $ +# +# Tests for the :P variable modifier, which looks up the path for a given +# target. +# +# The phony target does not have a corresponding path, therefore ... oops, +# as of 2020-08-23 it is nevertheless resolved to a path. This is probably +# unintended. +# +# The real target is located in a subdirectory, and its full path is returned. +# If it had been in the current directory, the difference between its path and +# its name would not be visible. +# +# The enoent target does not exist, therefore the target name is returned. + +.MAIN: all + +_!= rm -rf varmod-path.subdir +_!= mkdir varmod-path.subdir +_!= > varmod-path.subdir/varmod-path.phony +_!= > varmod-path.subdir/varmod-path.real + +# To have an effect, this .PATH declaration must be after the directory is created. +.PATH: varmod-path.subdir + +varmod-path.phony: .PHONY +varmod-path.real: + +all: varmod-path.phony varmod-path.real + @echo ${varmod-path.phony:P} + @echo ${varmod-path.real:P} + @echo ${varmod-path.enoent:P} + +.END: + @rm -rf varmod-path.subdir diff --git a/unit-tests/varmod-quote-dollar.exp b/unit-tests/varmod-quote-dollar.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-quote-dollar.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-quote-dollar.mk b/unit-tests/varmod-quote-dollar.mk new file mode 100644 index 000000000000..fedbe8a10f4b --- /dev/null +++ b/unit-tests/varmod-quote-dollar.mk @@ -0,0 +1,10 @@ +# $NetBSD: varmod-quote-dollar.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the :q variable modifier, which quotes the string for the shell +# and doubles dollar signs, to prevent them from being interpreted by a +# child process of make. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varmod-quote.exp b/unit-tests/varmod-quote.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-quote.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-quote.mk b/unit-tests/varmod-quote.mk new file mode 100644 index 000000000000..adf736048e76 --- /dev/null +++ b/unit-tests/varmod-quote.mk @@ -0,0 +1,9 @@ +# $NetBSD: varmod-quote.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the :Q variable modifier, which quotes the variable value +# to be used in a shell program. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varmod-range.exp b/unit-tests/varmod-range.exp new file mode 100644 index 000000000000..bdbd68edeb45 --- /dev/null +++ b/unit-tests/varmod-range.exp @@ -0,0 +1,8 @@ +make: Unknown modifier 'r' + +1 2 3 +make: Unknown modifier 'r' + +make: Unknown modifier 'r' + +exit status 0 diff --git a/unit-tests/varmod-range.mk b/unit-tests/varmod-range.mk new file mode 100644 index 000000000000..9fd79cc97a81 --- /dev/null +++ b/unit-tests/varmod-range.mk @@ -0,0 +1,10 @@ +# $NetBSD: varmod-range.mk,v 1.3 2020/08/23 15:13:21 rillig Exp $ +# +# Tests for the :range variable modifier, which generates sequences +# of integers from the given range. + +all: + @echo ${a b c:L:rang} # modifier name too short + @echo ${a b c:L:range} # ok + @echo ${a b c:L:rango} # misspelled + @echo ${a b c:L:ranger} # modifier name too long diff --git a/unit-tests/varmod-remember.exp b/unit-tests/varmod-remember.exp new file mode 100644 index 000000000000..448f817d8969 --- /dev/null +++ b/unit-tests/varmod-remember.exp @@ -0,0 +1,3 @@ +1 2 3 1 2 3 1 2 3 +1 2 3, SAVED=3 +exit status 0 diff --git a/unit-tests/varmod-remember.mk b/unit-tests/varmod-remember.mk new file mode 100644 index 000000000000..68eb96a122c4 --- /dev/null +++ b/unit-tests/varmod-remember.mk @@ -0,0 +1,12 @@ +# $NetBSD: varmod-remember.mk,v 1.3 2020/08/23 15:18:43 rillig Exp $ +# +# Tests for the :_ modifier, which saves the current variable value +# in the _ variable or another, to be used later again. + +# In the parameterized form, having the variable name on the right side of +# the = assignment operator is confusing. In almost all other situations +# the variable name is on the left-hand side of the = operator. Luckily +# this modifier is only rarely needed. +all: + @echo ${1 2 3:L:_:@var@${_}@} + @echo ${1 2 3:L:@var@${var:_=SAVED:}@}, SAVED=${SAVED} diff --git a/unit-tests/varmod-root.exp b/unit-tests/varmod-root.exp new file mode 100644 index 000000000000..24ecbb875f77 --- /dev/null +++ b/unit-tests/varmod-root.exp @@ -0,0 +1,10 @@ +root of 'a/b/c' is 'a/b/c' +root of 'def' is 'def' +root of 'a.b.c' is 'a.b' +root of 'a.b/c' is 'a' +root of 'a' is 'a' +root of 'a.a' is 'a' +root of '.gitignore' is '' +root of 'a' is 'a' +root of 'a.a' is 'a' +exit status 0 diff --git a/unit-tests/varmod-root.mk b/unit-tests/varmod-root.mk new file mode 100644 index 000000000000..88af42d82510 --- /dev/null +++ b/unit-tests/varmod-root.mk @@ -0,0 +1,9 @@ +# $NetBSD: varmod-root.mk,v 1.3 2020/08/23 15:09:15 rillig Exp $ +# +# Tests for the :R variable modifier, which returns the filename root +# without the extension. + +all: +.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a + @echo "root of '"${path:Q}"' is '"${path:R:Q}"'" +.endfor diff --git a/unit-tests/varmod-select-words.exp b/unit-tests/varmod-select-words.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-select-words.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-select-words.mk b/unit-tests/varmod-select-words.mk new file mode 100644 index 000000000000..a9df25f9ff32 --- /dev/null +++ b/unit-tests/varmod-select-words.mk @@ -0,0 +1,9 @@ +# $NetBSD: varmod-select-words.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the :[...] variable modifier, which selects a single word +# or a range of words from a variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varmod-shell.exp b/unit-tests/varmod-shell.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-shell.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-shell.mk b/unit-tests/varmod-shell.mk new file mode 100644 index 000000000000..052968004f1b --- /dev/null +++ b/unit-tests/varmod-shell.mk @@ -0,0 +1,9 @@ +# $NetBSD: varmod-shell.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the :sh variable modifier, which runs the shell command +# given by the variable value and returns its output. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varmod-subst-regex.exp b/unit-tests/varmod-subst-regex.exp new file mode 100644 index 000000000000..eb9ae7f41fb9 --- /dev/null +++ b/unit-tests/varmod-subst-regex.exp @@ -0,0 +1,23 @@ +make: Regex compilation error: (details omitted) +mod-regex-compile-error: C,word,____,:Q}. +make: No subexpression \1 +make: No subexpression \1 +make: No subexpression \1 +make: No subexpression \1 +mod-regex-limits:11-missing:1 6 +mod-regex-limits:11-ok:1 22 446 +make: No subexpression \2 +make: No subexpression \2 +make: No subexpression \2 +make: No subexpression \2 +mod-regex-limits:22-missing:1 6 +make: No subexpression \2 +make: No subexpression \2 +make: No subexpression \2 +make: No subexpression \2 +mod-regex-limits:22-missing:1 6 +mod-regex-limits:22-ok:1 33 556 +mod-regex-limits:capture:ihgfedcbaabcdefghijABCDEFGHIJa0a1a2rest +make: Regex compilation error: (details omitted) +mod-regex-errors: +exit status 0 diff --git a/unit-tests/varmod-subst-regex.mk b/unit-tests/varmod-subst-regex.mk new file mode 100644 index 000000000000..fe2758e401ec --- /dev/null +++ b/unit-tests/varmod-subst-regex.mk @@ -0,0 +1,87 @@ +# $NetBSD: varmod-subst-regex.mk,v 1.3 2020/08/28 17:15:04 rillig Exp $ +# +# Tests for the :C,from,to, variable modifier. + +all: mod-regex-compile-error +all: mod-regex-limits +all: mod-regex-errors + +# The variable expression expands to 4 words. Of these words, none matches +# the regular expression "a b" since these words don't contain any +# whitespace. +.if ${:Ua b b c:C,a b,,} != "a b b c" +.error +.endif + +# Using the '1' modifier does not change anything. The '1' modifier just +# means to apply at most 1 replacement in the whole variable expression. +.if ${:Ua b b c:C,a b,,1} != "a b b c" +.error +.endif + +# The 'W' modifier treats the whole variable value as a single big word, +# containing whitespace. This big word matches the regular expression, +# therefore it gets replaced. Whitespace is preserved after replacing. +.if ${:Ua b b c:C,a b,,W} != " b c" +.error +.endif + +# The 'g' modifier does not have any effect here since each of the words +# contains the character 'b' a single time. +.if ${:Ua b b c:C,b,,g} != "a c" +.error +.endif + +# The first :C modifier has the 'W' modifier, which makes the whole +# expression a single word. The 'g' modifier then replaces all occurrences +# of "1 2" with "___". The 'W' modifier only applies to this single :C +# modifier. This is demonstrated by the :C modifier that follows. If the +# 'W' modifier would be preserved, only a single underscore would have been +# replaced with an 'x'. +.if ${:U1 2 3 1 2 3:C,1 2,___,Wg:C,_,x,} != "x__ 3 x__ 3" +.error +.endif + +# The regular expression does not match in the first word. +# It matches once in the second word, and the \0\0 doubles that word. +# In the third word, the regular expression matches as early as possible, +# and since the matches must not overlap, the next possible match would +# start at the 6, but at that point, there is only one character left, +# and that cannot match the regular expression "..". Therefore only the +# "45" is doubled in the result. +.if ${:U1 23 456:C,..,\0\0,} != "1 2323 45456" +.error +.endif + +# The modifier '1' applies the replacement at most once, across the whole +# variable value, no matter whether it is a single big word or many small +# words. +# +# Up to 2020-08-28, the manual page said that the modifiers '1' and 'g' +# were orthogonal, which was wrong. +.if ${:U12345 12345:C,.,\0\0,1} != "112345 12345" +.error +.endif + +# Multiple asterisks form an invalid regular expression. This produces an +# error message and (as of 2020-08-28) stops parsing in the middle of the +# variable expression. The unparsed part of the expression is then copied +# verbatim to the output, which is unexpected and can lead to strange shell +# commands being run. +mod-regex-compile-error: + @echo $@: ${:Uword1 word2:C,****,____,g:C,word,____,:Q}. + +# These tests generate error messages but as of 2020-08-28 just continue +# parsing and execution as if nothing bad had happened. +mod-regex-limits: + @echo $@:11-missing:${:U1 23 456:C,..,\1\1,:Q} + @echo $@:11-ok:${:U1 23 456:C,(.).,\1\1,:Q} + @echo $@:22-missing:${:U1 23 456:C,..,\2\2,:Q} + @echo $@:22-missing:${:U1 23 456:C,(.).,\2\2,:Q} + @echo $@:22-ok:${:U1 23 456:C,(.)(.),\2\2,:Q} + # The :C modifier only handles single-digit capturing groups, + # which is more than enough for daily use. + @echo $@:capture:${:UabcdefghijABCDEFGHIJrest:C,(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.),\9\8\7\6\5\4\3\2\1\0\10\11\12,} + +mod-regex-errors: + @echo $@: ${UNDEF:Uvalue:C,[,,} diff --git a/unit-tests/varmod-subst.exp b/unit-tests/varmod-subst.exp new file mode 100644 index 000000000000..e752fb8058a8 --- /dev/null +++ b/unit-tests/varmod-subst.exp @@ -0,0 +1,51 @@ +mod-subst: +:a b b c: +:a b b c: +: b c: +:a c: +:x__ 3 x__ 3: +12345 +mod-subst-delimiter: +1 two 3 horizontal tabulator +1 two 3 space +1 two 3 exclamation mark +1 two 3 double quotes +1 two 3 hash +1 two 3 dollar +1 two 3 percent +1 two 3 apostrophe +1 two 3 opening parenthesis +1 two 3 closing parenthesis +1 two 3 digit +1 two 3 colon +1 two 3 less than sign +1 two 3 equal sign +1 two 3 greater than sign +1 two 3 question mark +1 two 3 at +1 two 3 letter +1 two 3 opening bracket +1 two 3 backslash +1 two 3 closing bracket +1 two 3 caret +1 two 3 opening brace +1 two 3 vertical line +1 two 3 closing brace +1 two 3 tilde +mod-subst-chain: +A B c. +make: Unknown modifier 'i' +. +mod-subst-dollar:$1: +mod-subst-dollar:$2: +mod-subst-dollar:$3: +mod-subst-dollar:$4: +mod-subst-dollar:$5: +mod-subst-dollar:$6: +mod-subst-dollar:$7: +mod-subst-dollar:$8: +mod-subst-dollar:$40: +mod-subst-dollar:U8: +mod-subst-dollar:$$$$: +mod-subst-dollar:$$$good3 +exit status 0 diff --git a/unit-tests/varmod-subst.mk b/unit-tests/varmod-subst.mk new file mode 100644 index 000000000000..de8d54df8506 --- /dev/null +++ b/unit-tests/varmod-subst.mk @@ -0,0 +1,153 @@ +# $NetBSD: varmod-subst.mk,v 1.3 2020/08/19 06:10:06 rillig Exp $ +# +# Tests for the :S,from,to, variable modifier. + +all: mod-subst +all: mod-subst-delimiter +all: mod-subst-chain +all: mod-subst-dollar + +WORDS= sequences of letters +.if ${WORDS:S,,,} != ${WORDS} +.warning The empty pattern matches something. +.endif +.if ${WORDS:S,e,*,1} != "s*quences of letters" +.warning The :S modifier flag '1' is not applied exactly once. +.endif +.if ${WORDS:S,f,*,1} != "sequences o* letters" +.warning The :S modifier flag '1' is only applied to the first word,\ + not to the first occurrence. +.endif +.if ${WORDS:S,e,*,} != "s*quences of l*tters" +.warning The :S modifier does not replace every first match per word. +.endif +.if ${WORDS:S,e,*,g} != "s*qu*nc*s of l*tt*rs" +.warning The :S modifier flag 'g' does not replace every occurrence. +.endif +.if ${WORDS:S,^sequ,occurr,} != "occurrences of letters" +.warning The :S modifier fails for a short match anchored at the start. +.endif +.if ${WORDS:S,^of,with,} != "sequences with letters" +.warning The :S modifier fails for an exact match anchored at the start. +.endif +.if ${WORDS:S,^office,does not match,} != ${WORDS} +.warning The :S modifier matches a too long pattern anchored at the start. +.endif +.if ${WORDS:S,f$,r,} != "sequences or letters" +.warning The :S modifier fails for a short match anchored at the end. +.endif +.if ${WORDS:S,s$,,} != "sequence of letter" +.warning The :S modifier fails to replace one occurrence per word. +.endif +.if ${WORDS:S,of$,,} != "sequences letters" +.warning The :S modifier fails for an exact match anchored at the end. +.endif +.if ${WORDS:S,eof$,,} != ${WORDS} +.warning The :S modifier matches a too long pattern anchored at the end. +.endif +.if ${WORDS:S,^of$,,} != "sequences letters" +.warning The :S modifier does not match a word anchored at both ends. +.endif +.if ${WORDS:S,^o$,,} != ${WORDS} +.warning The :S modifier matches a prefix anchored at both ends. +.endif +.if ${WORDS:S,^f$,,} != ${WORDS} +.warning The :S modifier matches a suffix anchored at both ends. +.endif +.if ${WORDS:S,^eof$,,} != ${WORDS} +.warning The :S modifier matches a too long prefix anchored at both ends. +.endif +.if ${WORDS:S,^office$,,} != ${WORDS} +.warning The :S modifier matches a too long suffix anchored at both ends. +.endif + +mod-subst: + @echo $@: + @echo :${:Ua b b c:S,a b,,:Q}: + @echo :${:Ua b b c:S,a b,,1:Q}: + @echo :${:Ua b b c:S,a b,,W:Q}: + @echo :${:Ua b b c:S,b,,g:Q}: + @echo :${:U1 2 3 1 2 3:S,1 2,___,Wg:S,_,x,:Q}: + @echo ${:U12345:S,,sep,g:Q} + +# The :S and :C modifiers accept an arbitrary character as the delimiter, +# including characters that are otherwise used as escape characters or +# interpreted in a special way. This can be used to confuse humans. +mod-subst-delimiter: + @echo $@: + @echo ${:U1 2 3:S 2 two :Q} horizontal tabulator + @echo ${:U1 2 3:S 2 two :Q} space + @echo ${:U1 2 3:S!2!two!:Q} exclamation mark + @echo ${:U1 2 3:S"2"two":Q} double quotes + # In shell command lines, the hash does not need to be escaped. + # It needs to be escaped in variable assignment lines though. + @echo ${:U1 2 3:S#2#two#:Q} hash + @echo ${:U1 2 3:S$2$two$:Q} dollar + @echo ${:U1 2 3:S%2%two%:Q} percent + @echo ${:U1 2 3:S'2'two':Q} apostrophe + @echo ${:U1 2 3:S(2(two(:Q} opening parenthesis + @echo ${:U1 2 3:S)2)two):Q} closing parenthesis + @echo ${:U1 2 3:S121two1:Q} digit + @echo ${:U1 2 3:S:2:two::Q} colon + @echo ${:U1 2 3:S<2<two<:Q} less than sign + @echo ${:U1 2 3:S=2=two=:Q} equal sign + @echo ${:U1 2 3:S>2>two>:Q} greater than sign + @echo ${:U1 2 3:S?2?two?:Q} question mark + @echo ${:U1 2 3:S@2@two@:Q} at + @echo ${:U1 2 3:Sa2atwoa:Q} letter + @echo ${:U1 2 3:S[2[two[:Q} opening bracket + @echo ${:U1 2 3:S\2\two\:Q} backslash + @echo ${:U1 2 3:S]2]two]:Q} closing bracket + @echo ${:U1 2 3:S^2^two^:Q} caret + @echo ${:U1 2 3:S{2{two{:Q} opening brace + @echo ${:U1 2 3:S|2|two|:Q} vertical line + @echo ${:U1 2 3:S}2}two}:Q} closing brace + @echo ${:U1 2 3:S~2~two~:Q} tilde + +# The :S and :C modifiers can be chained without a separating ':'. +# This is not documented in the manual page. +# It works because ApplyModifier_Subst scans for the known modifiers g1W +# and then just returns to ApplyModifiers. There, the colon is optionally +# skipped (see the *st.next == ':' at the end of the loop). +# +# Most other modifiers cannot be chained since their parsers skip until +# the next ':' or '}' or ')'. +mod-subst-chain: + @echo $@: + @echo ${:Ua b c:S,a,A,S,b,B,}. + # There is no 'i' modifier for the :S or :C modifiers. + # The error message is "make: Unknown modifier 'i'", which is + # kind of correct, although it is mixing the terms for variable + # modifiers with the matching modifiers. + @echo ${:Uvalue:S,a,x,i}. + +# No matter how many dollar characters there are, they all get merged +# into a single dollar by the :S modifier. +# +# As of 2020-08-09, this is because ParseModifierPart sees a '$' and +# calls Var_Parse to expand the variable. In all other places, the "$$" +# is handled outside of Var_Parse. Var_Parse therefore considers "$$" +# one of the "really stupid names", skips the first dollar, and parsing +# continues with the next character. This repeats for the other dollar +# signs, except the one before the delimiter. That one is handled by +# the code that optionally interprets the '$' as the end-anchor in the +# first part of the :S modifier. That code doesn't call Var_Parse but +# simply copies the dollar to the result. +mod-subst-dollar: + @echo $@:${:U1:S,^,$,:Q}: + @echo $@:${:U2:S,^,$$,:Q}: + @echo $@:${:U3:S,^,$$$,:Q}: + @echo $@:${:U4:S,^,$$$$,:Q}: + @echo $@:${:U5:S,^,$$$$$,:Q}: + @echo $@:${:U6:S,^,$$$$$$,:Q}: + @echo $@:${:U7:S,^,$$$$$$$,:Q}: + @echo $@:${:U8:S,^,$$$$$$$$,:Q}: + @echo $@:${:U40:S,^,$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$,:Q}: +# This generates no dollar at all: + @echo $@:${:UU8:S,^,${:U$$$$$$$$},:Q}: +# Here is an alternative way to generate dollar characters. +# It's unexpectedly complicated though. + @echo $@:${:U:range=5:ts\x24:C,[0-9],,g:Q}: +# In modifiers, dollars are escaped using the backslash, not using another +# dollar sign. Therefore, creating a dollar sign is pretty simple: + @echo $@:${:Ugood3:S,^,\$\$\$,:Q} diff --git a/unit-tests/varmod-sysv.exp b/unit-tests/varmod-sysv.exp new file mode 100644 index 000000000000..d9049c889823 --- /dev/null +++ b/unit-tests/varmod-sysv.exp @@ -0,0 +1,8 @@ +ax:Q b c d eb +bcd.e +& +anchor-dollar: value +anchor-dollar: valux +mismatch: file.cpp file.h +mismatch: renamed.c other.c +exit status 0 diff --git a/unit-tests/varmod-sysv.mk b/unit-tests/varmod-sysv.mk new file mode 100644 index 000000000000..c8410ed900d8 --- /dev/null +++ b/unit-tests/varmod-sysv.mk @@ -0,0 +1,61 @@ +# $NetBSD: varmod-sysv.mk,v 1.3 2020/08/23 14:52:06 rillig Exp $ +# +# Tests for the ${VAR:from=to} variable modifier, which replaces the suffix +# "from" with "to". It can also use '%' as a wildcard. +# +# This modifier is applied when the other modifiers don't match exactly. + +all: words ampersand anchor-dollar mismatch + +# The :Q looks like a modifier but isn't. +# It is part of the replacement string. +words: + @echo a${a b c d e:L:%a=x:Q}b + +# Before 2020-07-19, an ampersand could be used in the replacement part +# of a SysV substitution modifier. This was probably a copy-and-paste +# mistake since the SysV modifier code looked a lot like the code for the +# :S and :C modifiers. The ampersand is not mentioned in the manual page. +ampersand: + @echo ${:U${a.bcd.e:L:a.%=%}:Q} + @echo ${:U${a.bcd.e:L:a.%=&}:Q} + +# Before 2020-07-20, when a SysV modifier was parsed, a single dollar +# before the '=' was interpreted as an anchor, which doesn't make sense +# since the anchor was discarded immediately. +anchor-dollar: + @echo $@: ${:U${value:L:e$=x}:Q} + @echo $@: ${:U${value:L:e=x}:Q} + +# Words that don't match are copied unmodified. +# The % placeholder can be anywhere in the string. +mismatch: + @echo $@: ${:Ufile.c file.h:%.c=%.cpp} + @echo $@: ${:Ufile.c other.c:file.%=renamed.%} + +# Trying to cover all possible variants of the SysV modifier. +LIST= one two +EXPR.1= ${LIST:o=X} +EXP.1= one twX +EXPR.2= ${LIST:o=} +EXP.2= one tw +EXPR.3= ${LIST:o=%} +EXP.3= one tw% +EXPR.4= ${LIST:%o=X} +EXP.4= one X +EXPR.5= ${LIST:o%=X} +EXP.5= X two +EXPR.6= ${LIST:o%e=X} +EXP.6= X two +EXPR.7= ${LIST:o%%e=X} # Only the first '%' is the wildcard. +EXP.7= one two # None of the words contains a literal '%'. +EXPR.8= ${LIST:%=%%} +EXP.8= one% two% +EXPR.9= ${LIST:%nes=%xxx} # lhs is longer than the word "one" +EXP.9= one two + +.for i in ${:U:range=9} +.if ${EXPR.$i} != ${EXP.$i} +.warning test case $i expected "${EXP.$i}", got "${EXPR.$i} +.endif +.endfor diff --git a/unit-tests/varmod-tail.exp b/unit-tests/varmod-tail.exp new file mode 100644 index 000000000000..e25c1cc4b914 --- /dev/null +++ b/unit-tests/varmod-tail.exp @@ -0,0 +1,10 @@ +tail (basename) of 'a/b/c' is 'c' +tail (basename) of 'def' is 'def' +tail (basename) of 'a.b.c' is 'a.b.c' +tail (basename) of 'a.b/c' is 'c' +tail (basename) of 'a' is 'a' +tail (basename) of 'a.a' is 'a.a' +tail (basename) of '.gitignore' is '.gitignore' +tail (basename) of 'a' is 'a' +tail (basename) of 'a.a' is 'a.a' +exit status 0 diff --git a/unit-tests/varmod-tail.mk b/unit-tests/varmod-tail.mk new file mode 100644 index 000000000000..a8078cc67335 --- /dev/null +++ b/unit-tests/varmod-tail.mk @@ -0,0 +1,9 @@ +# $NetBSD: varmod-tail.mk,v 1.3 2020/08/23 15:09:15 rillig Exp $ +# +# Tests for the :T variable modifier, which returns the basename of each of +# the words in the variable value. + +all: +.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a + @echo "tail (basename) of '"${path:Q}"' is '"${path:T:Q}"'" +.endfor diff --git a/unit-tests/varmod-to-abs.exp b/unit-tests/varmod-to-abs.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-to-abs.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-to-abs.mk b/unit-tests/varmod-to-abs.mk new file mode 100644 index 000000000000..7a74e89088e5 --- /dev/null +++ b/unit-tests/varmod-to-abs.mk @@ -0,0 +1,9 @@ +# $NetBSD: varmod-to-abs.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the :tA variable modifier, which returns the absolute path for +# each of the words in the variable value. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varmod-to-lower.exp b/unit-tests/varmod-to-lower.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-to-lower.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-to-lower.mk b/unit-tests/varmod-to-lower.mk new file mode 100644 index 000000000000..38a4add4cfe5 --- /dev/null +++ b/unit-tests/varmod-to-lower.mk @@ -0,0 +1,19 @@ +# $NetBSD: varmod-to-lower.mk,v 1.3 2020/08/28 17:21:02 rillig Exp $ +# +# Tests for the :tl variable modifier, which returns the words in the +# variable value, converted to lowercase. + +.if ${:UUPPER:tl} != "upper" +.error +.endif + +.if ${:Ulower:tl} != "lower" +.error +.endif + +.if ${:UMixeD case.:tl} != "mixed case." +.error +.endif + +all: + @:; diff --git a/unit-tests/varmod-to-many-words.exp b/unit-tests/varmod-to-many-words.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-to-many-words.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-to-many-words.mk b/unit-tests/varmod-to-many-words.mk new file mode 100644 index 000000000000..10cddb00c5e4 --- /dev/null +++ b/unit-tests/varmod-to-many-words.mk @@ -0,0 +1,9 @@ +# $NetBSD: varmod-to-many-words.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the :tw modifier, which treats the variable as many words, +# to undo a previous :tW modifier. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varmod-to-one-word.exp b/unit-tests/varmod-to-one-word.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-to-one-word.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-to-one-word.mk b/unit-tests/varmod-to-one-word.mk new file mode 100644 index 000000000000..0865ce8fb41f --- /dev/null +++ b/unit-tests/varmod-to-one-word.mk @@ -0,0 +1,9 @@ +# $NetBSD: varmod-to-one-word.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the :tW variable modifier, which treats the variable value +# as a single word, for all following modifiers. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varmod-to-separator.exp b/unit-tests/varmod-to-separator.exp new file mode 100644 index 000000000000..07dd5b5e7c5b --- /dev/null +++ b/unit-tests/varmod-to-separator.exp @@ -0,0 +1,9 @@ +make: Bad modifier `:tx' for WORDS +make: "varmod-to-separator.mk" line 104: Malformed conditional (${WORDS:tx} != "anything") +make: "varmod-to-separator.mk" line 108: Parsing continues here. +make: Bad modifier `:t\X' for WORDS +make: "varmod-to-separator.mk" line 112: Malformed conditional (${WORDS:t\X} != "anything") +make: "varmod-to-separator.mk" line 115: Parsing continues here. +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/varmod-to-separator.mk b/unit-tests/varmod-to-separator.mk new file mode 100644 index 000000000000..68e728c276dd --- /dev/null +++ b/unit-tests/varmod-to-separator.mk @@ -0,0 +1,118 @@ +# $NetBSD: varmod-to-separator.mk,v 1.3 2020/08/31 19:58:21 rillig Exp $ +# +# Tests for the :ts variable modifier, which joins the words of the variable +# using an arbitrary character as word separator. + +WORDS= one two three four five six + +# The words are separated by a single space, just as usual. +.if ${WORDS:ts } != "one two three four five six" +. warning Space as separator does not work. +.endif + +# The separator can be an arbitrary character, for example a comma. +.if ${WORDS:ts,} != "one,two,three,four,five,six" +. warning Comma as separator does not work. +.endif + +# After the :ts modifier, other modifiers can follow. +.if ${WORDS:ts/:tu} != "ONE/TWO/THREE/FOUR/FIVE/SIX" +. warning Chaining modifiers does not work. +.endif + +# To use the ':' as the separator, just write it normally. +# The first colon is the separator, the second ends the modifier. +.if ${WORDS:ts::tu} != "ONE:TWO:THREE:FOUR:FIVE:SIX" +. warning Colon as separator does not work. +.endif + +# When there is just a colon but no other character, the words are +# "separated" by an empty string, that is, they are all squashed +# together. +.if ${WORDS:ts:tu} != "ONETWOTHREEFOURFIVESIX" +. warning Colon as separator does not work. +.endif + +# Applying the :tu modifier first and then the :ts modifier does not change +# anything since neither of these modifiers is related to how the string is +# split into words. Beware of separating the words using a single or double +# quote though, or other special characters like dollar or backslash. +# +# This example also demonstrates that the closing brace is not interpreted +# as a separator, but as the closing delimiter of the whole variable +# expression. +.if ${WORDS:tu:ts} != "ONETWOTHREEFOURFIVESIX" +. warning Colon as separator does not work. +.endif + +# The '}' plays the same role as the ':' in the preceding examples. +# Since there is a single character before it, that character is taken as +# the separator. +.if ${WORDS:tu:ts/} != "ONE/TWO/THREE/FOUR/FIVE/SIX" +. warning Colon as separator does not work. +.endif + +# Now it gets interesting and ambiguous: The separator could either be empty +# since it is followed by a colon. Or it could be the colon since that +# colon is followed by the closing brace. It's the latter case. +.if ${WORDS:ts:} != "one:two:three:four:five:six" +. warning Colon followed by closing brace does not work. +.endif + +# As in the ${WORDS:tu:ts} example above, the separator is empty. +.if ${WORDS:ts} != "onetwothreefourfivesix" +. warning Empty separator before closing brace does not work. +.endif + +# The :ts modifier can be followed by other modifiers. +.if ${WORDS:ts:S/two/2/} != "one2threefourfivesix" +. warning Separator followed by :S modifier does not work. +.endif + +# The :ts modifier can follow other modifiers. +.if ${WORDS:S/two/2/:ts} != "one2threefourfivesix" +. warning :S modifier followed by :ts modifier does not work. +.endif + +# The :ts modifier with an actual separator can be followed by other +# modifiers. +.if ${WORDS:ts/:S/two/2/} != "one/2/three/four/five/six" +. warning The :ts modifier followed by an :S modifier does not work. +.endif + +# The separator can be \n, which is a newline. +.if ${WORDS:[1..3]:ts\n} != "one${.newline}two${.newline}three" +. warning The separator \n does not produce a newline. +.endif + +# The separator can be \t, which is a tab. +.if ${WORDS:[1..3]:ts\t} != "one two three" +. warning The separator \t does not produce a tab. +.endif + +# The separator can be given as octal number. +.if ${WORDS:[1..3]:ts\012:tu} != "ONE${.newline}TWO${.newline}THREE" +. warning The separator \012 is not interpreted in octal ASCII. +.endif + +# The separator can be given as hexadecimal number. +.if ${WORDS:[1..3]:ts\xa:tu} != "ONE${.newline}TWO${.newline}THREE" +. warning The separator \xa is not interpreted in hexadecimal ASCII. +.endif + +# In the :t modifier, the :t must be followed by any of A, l, s, u. +.if ${WORDS:tx} != "anything" +. info This line is not reached because of the malformed condition. +. info If this line were reached, it would be visible in the -dcpv log. +.endif +.info Parsing continues here. + +# After the backslash, only n, t, an octal number, or x and a hexadecimal +# number are allowed. +.if ${WORDS:t\X} != "anything" +. info This line is not reached. +.endif +.info Parsing continues here. + +all: + @:; diff --git a/unit-tests/varmod-to-upper.exp b/unit-tests/varmod-to-upper.exp new file mode 100644 index 000000000000..8453da538d3b --- /dev/null +++ b/unit-tests/varmod-to-upper.exp @@ -0,0 +1,2 @@ +mod-tu-space: A B +exit status 0 diff --git a/unit-tests/varmod-to-upper.mk b/unit-tests/varmod-to-upper.mk new file mode 100644 index 000000000000..26c0b25e5142 --- /dev/null +++ b/unit-tests/varmod-to-upper.mk @@ -0,0 +1,21 @@ +# $NetBSD: varmod-to-upper.mk,v 1.4 2020/08/28 17:21:02 rillig Exp $ +# +# Tests for the :tu variable modifier, which returns the words in the +# variable value, converted to uppercase. + +.if ${:UUPPER:tu} != "UPPER" +.error +.endif + +.if ${:Ulower:tu} != "LOWER" +.error +.endif + +.if ${:UMixeD case.:tu} != "MIXED CASE." +.error +.endif + +# The :tu and :tl modifiers operate on the variable value as a single string, +# not as a list of words. Therefore, the adjacent spaces are preserved. +mod-tu-space: + @echo $@: ${a b:L:tu:Q} diff --git a/unit-tests/varmod-undefined.exp b/unit-tests/varmod-undefined.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-undefined.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-undefined.mk b/unit-tests/varmod-undefined.mk new file mode 100644 index 000000000000..9ed35a8406fe --- /dev/null +++ b/unit-tests/varmod-undefined.mk @@ -0,0 +1,55 @@ +# $NetBSD: varmod-undefined.mk,v 1.3 2020/08/23 20:49:33 rillig Exp $ +# +# Tests for the :U variable modifier, which returns the given string +# if the variable is undefined. +# +# The pattern ${:Uword} is heavily used when expanding .for loops. + +# This is how an expanded .for loop looks like. +# .for word in one +# . if ${word} != one +# . error ${word} +# . endif +# .endfor + +.if ${:Uone} != one +. error ${:Uone} +.endif + +# The variable expressions in the text of the :U modifier may be arbitrarily +# nested. + +.if ${:U${:Unested}${${${:Udeeply}}}} != nested +.error +.endif + +# The nested variable expressions may contain braces, and these braces don't +# need to match pairwise. In the following example, the :S modifier uses '{' +# as delimiter, which confuses both editors and humans because the opening +# and # closing braces don't match anymore. It's syntactically valid though. +# For more similar examples, see varmod-subst.mk, mod-subst-delimiter. + +.if ${:U${:Uvalue:S{a{X{}} != vXlue +.error +.endif + +# The escaping rules for the :U modifier (left-hand side) and condition +# string literals (right-hand side) are completely different. +# +# In the :U modifier, the backslash only escapes very few characters, all +# other backslashes are retained. +# +# In condition string literals, the backslash always escapes the following +# character, no matter whether it would be necessary or not. +# +# In both contexts, \n is an escaped letter n, not a newline; that's what +# the .newline variable is for. +# +# Whitespace at the edges is preserved, on both sides of the comparison. + +.if ${:U \: \} \$ \\ \a \b \n } != " : } \$ \\ \\a \\b \\n " +.error +.endif + +all: + @:; diff --git a/unit-tests/varmod-unique.exp b/unit-tests/varmod-unique.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod-unique.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod-unique.mk b/unit-tests/varmod-unique.mk new file mode 100644 index 000000000000..ea4698764947 --- /dev/null +++ b/unit-tests/varmod-unique.mk @@ -0,0 +1,39 @@ +# $NetBSD: varmod-unique.mk,v 1.4 2020/08/31 17:41:38 rillig Exp $ +# +# Tests for the :u variable modifier, which discards adjacent duplicate +# words. + +.if ${:U1 2 1:u} != "1 2 1" +. warning The :u modifier only merges _adjacent_ duplicate words. +.endif + +.if ${:U1 2 2 3:u} != "1 2 3" +. warning The :u modifier must merge adjacent duplicate words. +.endif + +.if ${:U:u} != "" +. warning The :u modifier must do nothing with an empty word list. +.endif + +.if ${:U1:u} != "1" +. warning The :u modifier must do nothing with a single-element word list. +.endif + +.if ${:U1 1 1 1 1 1 1 1:u} != "1" +. warning The :u modifier must merge _all_ adjacent duplicate words. +.endif + +.if ${:U 1 2 1 1 :u} != "1 2 1" +. warning The :u modifier must normalize whitespace between the words. +.endif + +.if ${:U1 1 1 1 2:u} != "1 2" +. warning Duplicate words at the beginning must be merged. +.endif + +.if ${:U1 2 2 2 2:u} != "1 2" +. warning Duplicate words at the end must be merged. +.endif + +all: + @:; diff --git a/unit-tests/varmod.exp b/unit-tests/varmod.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varmod.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varmod.mk b/unit-tests/varmod.mk new file mode 100644 index 000000000000..68bf165bf72b --- /dev/null +++ b/unit-tests/varmod.mk @@ -0,0 +1,8 @@ +# $NetBSD: varmod.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for variable modifiers, such as :Q, :S,from,to or :Ufallback. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dollar.exp b/unit-tests/varname-dollar.exp new file mode 100644 index 000000000000..c880e82f0170 --- /dev/null +++ b/unit-tests/varname-dollar.exp @@ -0,0 +1,5 @@ +make: "varname-dollar.mk" line 16: dollar is $. +make: "varname-dollar.mk" line 17: dollar in braces is . +make: "varname-dollar.mk" line 25: dollar is $. +make: "varname-dollar.mk" line 26: dollar in braces is dollar. +exit status 0 diff --git a/unit-tests/varname-dollar.mk b/unit-tests/varname-dollar.mk new file mode 100644 index 000000000000..d1db9f833306 --- /dev/null +++ b/unit-tests/varname-dollar.mk @@ -0,0 +1,29 @@ +# $NetBSD: varname-dollar.mk,v 1.3 2020/08/19 05:40:06 rillig Exp $ +# +# Tests for the expression "$$", which looks as if it referred to a variable, +# but simply expands to a single '$' sign. +# +# If there really were a special variable named '$', the expressions ${${DOLLAR}} +# and $$ would always expand to the same value. + +# Using the dollar sign in variable names is tricky and not recommended. +# To see that using this variable indeed affects the variable '$', run the +# test individually with the -dv option. +DOLLAR= $$ + +# At this point, the variable '$' is not defined. Therefore the second line +# returns an empty string. +.info dollar is $$. +.info dollar in braces is ${${DOLLAR}}. + +# Now overwrite the '$' variable to see whether '$$' really expands to that +# variable, or whether '$$' is handled by the parser. +${DOLLAR}= dollar + +# At this point, the variable '$' is defined, therefore its value is printed +# in the second .info directive. +.info dollar is $$. +.info dollar in braces is ${${DOLLAR}}. + +all: + @:; diff --git a/unit-tests/varname-dot-alltargets.exp b/unit-tests/varname-dot-alltargets.exp new file mode 100644 index 000000000000..992c2573b519 --- /dev/null +++ b/unit-tests/varname-dot-alltargets.exp @@ -0,0 +1,4 @@ + +first second source +first second source all .END +exit status 0 diff --git a/unit-tests/varname-dot-alltargets.mk b/unit-tests/varname-dot-alltargets.mk new file mode 100644 index 000000000000..0d16c8e6fef0 --- /dev/null +++ b/unit-tests/varname-dot-alltargets.mk @@ -0,0 +1,25 @@ +# $NetBSD: varname-dot-alltargets.mk,v 1.3 2020/08/25 22:51:54 rillig Exp $ +# +# Tests for the special .ALLTARGETS variable. + +.MAIN: all + +TARGETS_1:= ${.ALLTARGETS} + +first second: source + +TARGETS_2:= ${.ALLTARGETS} + +all: + # Since the tests are run with the -r option, no targets are + # defined at the beginning. + @echo ${TARGETS_1} + + # Only first and second are "real" targets. + # The .ALLTARGETS variable is not about targets though, but + # about all nodes, therefore source is also included. + @echo ${TARGETS_2} + + # Interestingly, the .END target is also implicitly defined at + # this point. + @echo ${.ALLTARGETS} diff --git a/unit-tests/varname-dot-curdir.exp b/unit-tests/varname-dot-curdir.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-curdir.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-curdir.mk b/unit-tests/varname-dot-curdir.mk new file mode 100644 index 000000000000..3795d87b030f --- /dev/null +++ b/unit-tests/varname-dot-curdir.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-curdir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .CURDIR variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-includedfromdir.exp b/unit-tests/varname-dot-includedfromdir.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-includedfromdir.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-includedfromdir.mk b/unit-tests/varname-dot-includedfromdir.mk new file mode 100644 index 000000000000..e5a61793baaf --- /dev/null +++ b/unit-tests/varname-dot-includedfromdir.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-includedfromdir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .INCLUDEDFROMDIR variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-includedfromfile.exp b/unit-tests/varname-dot-includedfromfile.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-includedfromfile.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-includedfromfile.mk b/unit-tests/varname-dot-includedfromfile.mk new file mode 100644 index 000000000000..403f562c27fb --- /dev/null +++ b/unit-tests/varname-dot-includedfromfile.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-includedfromfile.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .INCLUDEDFROMFILE variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-includes.exp b/unit-tests/varname-dot-includes.exp new file mode 100755 index 000000000000..edc90f068899 --- /dev/null +++ b/unit-tests/varname-dot-includes.exp @@ -0,0 +1,2 @@ +.INCLUDES= -I. -I.. +exit status 0 diff --git a/unit-tests/varname-dot-includes.mk b/unit-tests/varname-dot-includes.mk new file mode 100755 index 000000000000..33e8669c27be --- /dev/null +++ b/unit-tests/varname-dot-includes.mk @@ -0,0 +1,20 @@ +# $NetBSD: varname-dot-includes.mk,v 1.1 2020/08/28 03:51:06 rillig Exp $ +# +# Tests for the special .INCLUDES variable, which is not documented in the +# manual page. +# +# It is yet unclear in which situations this feature is useful. + +.SUFFIXES: .h + +.PATH.h: . .. + +.INCLUDES: .h + +# The .INCLUDES variable is not yet available. +.if defined(${.INCLUDES:Q}) +.error +.endif + +all: + @echo .INCLUDES=${.INCLUDES:Q} diff --git a/unit-tests/varname-dot-libs.exp b/unit-tests/varname-dot-libs.exp new file mode 100755 index 000000000000..97b7e720bdd4 --- /dev/null +++ b/unit-tests/varname-dot-libs.exp @@ -0,0 +1,2 @@ +.LIBS= -L. -L.. +exit status 0 diff --git a/unit-tests/varname-dot-libs.mk b/unit-tests/varname-dot-libs.mk new file mode 100755 index 000000000000..80abeb50b48c --- /dev/null +++ b/unit-tests/varname-dot-libs.mk @@ -0,0 +1,20 @@ +# $NetBSD: varname-dot-libs.mk,v 1.1 2020/08/28 03:51:06 rillig Exp $ +# +# Tests for the special .LIBS variable, which is not documented in the +# manual page. +# +# It is yet unclear in which situations this feature is useful. + +.SUFFIXES: .a + +.PATH.a: . .. + +.LIBS: .a + +# The .LIBS variable is not yet available. +.if defined(${.LIBS:Q}) +.error +.endif + +all: + @echo .LIBS=${.LIBS:Q} diff --git a/unit-tests/varname-dot-make-dependfile.exp b/unit-tests/varname-dot-make-dependfile.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-dependfile.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-dependfile.mk b/unit-tests/varname-dot-make-dependfile.mk new file mode 100644 index 000000000000..882c5344d9be --- /dev/null +++ b/unit-tests/varname-dot-make-dependfile.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-dependfile.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.DEPENDFILE variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-expand_variables.exp b/unit-tests/varname-dot-make-expand_variables.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-expand_variables.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-expand_variables.mk b/unit-tests/varname-dot-make-expand_variables.mk new file mode 100644 index 000000000000..cff2db4b9a21 --- /dev/null +++ b/unit-tests/varname-dot-make-expand_variables.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-expand_variables.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.EXPAND_VARIABLES variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-exported.exp b/unit-tests/varname-dot-make-exported.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-exported.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-exported.mk b/unit-tests/varname-dot-make-exported.mk new file mode 100644 index 000000000000..123762dea9d0 --- /dev/null +++ b/unit-tests/varname-dot-make-exported.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-exported.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.EXPORTED variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-jobs-prefix.exp b/unit-tests/varname-dot-make-jobs-prefix.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-jobs-prefix.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-jobs-prefix.mk b/unit-tests/varname-dot-make-jobs-prefix.mk new file mode 100644 index 000000000000..bf445ab2d349 --- /dev/null +++ b/unit-tests/varname-dot-make-jobs-prefix.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-jobs-prefix.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.JOBS.PREFIX variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-jobs.exp b/unit-tests/varname-dot-make-jobs.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-jobs.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-jobs.mk b/unit-tests/varname-dot-make-jobs.mk new file mode 100644 index 000000000000..1e99b3d28ea8 --- /dev/null +++ b/unit-tests/varname-dot-make-jobs.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-jobs.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.JOBS variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-level.exp b/unit-tests/varname-dot-make-level.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-level.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-level.mk b/unit-tests/varname-dot-make-level.mk new file mode 100644 index 000000000000..c4f2c0db7da6 --- /dev/null +++ b/unit-tests/varname-dot-make-level.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-level.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.LEVEL variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-makefile_preference.exp b/unit-tests/varname-dot-make-makefile_preference.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-makefile_preference.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-makefile_preference.mk b/unit-tests/varname-dot-make-makefile_preference.mk new file mode 100644 index 000000000000..58cc8a2bf516 --- /dev/null +++ b/unit-tests/varname-dot-make-makefile_preference.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-makefile_preference.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.MAKEFILE_PREFERENCE variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-makefiles.exp b/unit-tests/varname-dot-make-makefiles.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-makefiles.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-makefiles.mk b/unit-tests/varname-dot-make-makefiles.mk new file mode 100644 index 000000000000..f7449bbfc729 --- /dev/null +++ b/unit-tests/varname-dot-make-makefiles.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-makefiles.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.MAKEFILES variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-meta-bailiwick.exp b/unit-tests/varname-dot-make-meta-bailiwick.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-meta-bailiwick.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-meta-bailiwick.mk b/unit-tests/varname-dot-make-meta-bailiwick.mk new file mode 100644 index 000000000000..130a7b483291 --- /dev/null +++ b/unit-tests/varname-dot-make-meta-bailiwick.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-meta-bailiwick.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.META.BAILIWICK variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-meta-created.exp b/unit-tests/varname-dot-make-meta-created.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-meta-created.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-meta-created.mk b/unit-tests/varname-dot-make-meta-created.mk new file mode 100644 index 000000000000..bc149b60ddf4 --- /dev/null +++ b/unit-tests/varname-dot-make-meta-created.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-meta-created.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.META.CREATED variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-meta-files.exp b/unit-tests/varname-dot-make-meta-files.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-meta-files.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-meta-files.mk b/unit-tests/varname-dot-make-meta-files.mk new file mode 100644 index 000000000000..e6fe9e92bfca --- /dev/null +++ b/unit-tests/varname-dot-make-meta-files.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-meta-files.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.META.FILES variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-meta-ignore_filter.exp b/unit-tests/varname-dot-make-meta-ignore_filter.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-meta-ignore_filter.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-meta-ignore_filter.mk b/unit-tests/varname-dot-make-meta-ignore_filter.mk new file mode 100644 index 000000000000..c41aec4acdf8 --- /dev/null +++ b/unit-tests/varname-dot-make-meta-ignore_filter.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-meta-ignore_filter.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.META.IGNORE_FILTER variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-meta-ignore_paths.exp b/unit-tests/varname-dot-make-meta-ignore_paths.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-meta-ignore_paths.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-meta-ignore_paths.mk b/unit-tests/varname-dot-make-meta-ignore_paths.mk new file mode 100644 index 000000000000..4ae34f51608b --- /dev/null +++ b/unit-tests/varname-dot-make-meta-ignore_paths.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-meta-ignore_paths.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.META.IGNORE_PATHS variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-meta-ignore_patterns.exp b/unit-tests/varname-dot-make-meta-ignore_patterns.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-meta-ignore_patterns.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-meta-ignore_patterns.mk b/unit-tests/varname-dot-make-meta-ignore_patterns.mk new file mode 100644 index 000000000000..ea9fc49f1718 --- /dev/null +++ b/unit-tests/varname-dot-make-meta-ignore_patterns.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-meta-ignore_patterns.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.META.IGNORE_PATTERNS variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-meta-prefix.exp b/unit-tests/varname-dot-make-meta-prefix.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-meta-prefix.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-meta-prefix.mk b/unit-tests/varname-dot-make-meta-prefix.mk new file mode 100644 index 000000000000..d4fdf64e6db3 --- /dev/null +++ b/unit-tests/varname-dot-make-meta-prefix.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-meta-prefix.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.META.PREFIX variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-mode.exp b/unit-tests/varname-dot-make-mode.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-mode.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-mode.mk b/unit-tests/varname-dot-make-mode.mk new file mode 100644 index 000000000000..ee75a54ebd74 --- /dev/null +++ b/unit-tests/varname-dot-make-mode.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-mode.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.MODE variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-path_filemon.exp b/unit-tests/varname-dot-make-path_filemon.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-path_filemon.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-path_filemon.mk b/unit-tests/varname-dot-make-path_filemon.mk new file mode 100644 index 000000000000..8c07529f1b4c --- /dev/null +++ b/unit-tests/varname-dot-make-path_filemon.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-path_filemon.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.PATH_FILEMON variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-pid.exp b/unit-tests/varname-dot-make-pid.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-pid.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-pid.mk b/unit-tests/varname-dot-make-pid.mk new file mode 100644 index 000000000000..bea114d33547 --- /dev/null +++ b/unit-tests/varname-dot-make-pid.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-pid.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.PID variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-ppid.exp b/unit-tests/varname-dot-make-ppid.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-ppid.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-ppid.mk b/unit-tests/varname-dot-make-ppid.mk new file mode 100644 index 000000000000..c9471542ca35 --- /dev/null +++ b/unit-tests/varname-dot-make-ppid.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-ppid.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.PPID variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-make-save_dollars.exp b/unit-tests/varname-dot-make-save_dollars.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-make-save_dollars.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-make-save_dollars.mk b/unit-tests/varname-dot-make-save_dollars.mk new file mode 100644 index 000000000000..97f37a646d2d --- /dev/null +++ b/unit-tests/varname-dot-make-save_dollars.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-save_dollars.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.SAVE_DOLLARS variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-makeoverrides.exp b/unit-tests/varname-dot-makeoverrides.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-makeoverrides.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-makeoverrides.mk b/unit-tests/varname-dot-makeoverrides.mk new file mode 100644 index 000000000000..a897f4667175 --- /dev/null +++ b/unit-tests/varname-dot-makeoverrides.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-makeoverrides.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.MAKEOVERRIDES variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-newline.exp b/unit-tests/varname-dot-newline.exp new file mode 100644 index 000000000000..13943539bf3b --- /dev/null +++ b/unit-tests/varname-dot-newline.exp @@ -0,0 +1,4 @@ +make: "varname-dot-newline.mk" line 16: The .newline variable can be overwritten. Just don't do that. +first +second +exit status 0 diff --git a/unit-tests/varname-dot-newline.mk b/unit-tests/varname-dot-newline.mk new file mode 100644 index 000000000000..fa9ac0759649 --- /dev/null +++ b/unit-tests/varname-dot-newline.mk @@ -0,0 +1,23 @@ +# $NetBSD: varname-dot-newline.mk,v 1.3 2020/08/19 05:51:18 rillig Exp $ +# +# Tests for the special .newline variable. +# +# Contrary to the special variable named "" that is used in expressions like +# ${:Usome-value}, the variable ".newline" is not protected against +# modification. Nobody exploits that though. + +NEWLINE:= ${.newline} + +.newline= overwritten + +.if ${.newline} == ${NEWLINE} +.info The .newline variable cannot be overwritten. Good. +.else +.info The .newline variable can be overwritten. Just don't do that. +.endif + +# Restore the original value. +.newline= ${NEWLINE} + +all: + @echo 'first${.newline}second' diff --git a/unit-tests/varname-dot-objdir.exp b/unit-tests/varname-dot-objdir.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-objdir.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-objdir.mk b/unit-tests/varname-dot-objdir.mk new file mode 100644 index 000000000000..e662e8ac56fa --- /dev/null +++ b/unit-tests/varname-dot-objdir.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-objdir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .OBJDIR variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-parsedir.exp b/unit-tests/varname-dot-parsedir.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-parsedir.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-parsedir.mk b/unit-tests/varname-dot-parsedir.mk new file mode 100644 index 000000000000..6aeb5878a457 --- /dev/null +++ b/unit-tests/varname-dot-parsedir.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-parsedir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .PARSEDIR variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-parsefile.exp b/unit-tests/varname-dot-parsefile.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-parsefile.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-parsefile.mk b/unit-tests/varname-dot-parsefile.mk new file mode 100644 index 000000000000..9deff34a70d0 --- /dev/null +++ b/unit-tests/varname-dot-parsefile.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-parsefile.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .PARSEFILE variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-path.exp b/unit-tests/varname-dot-path.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-path.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-path.mk b/unit-tests/varname-dot-path.mk new file mode 100644 index 000000000000..8f4e3a5dcfa4 --- /dev/null +++ b/unit-tests/varname-dot-path.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-path.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .PATH variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-dot-shell.exp b/unit-tests/varname-dot-shell.exp new file mode 100755 index 000000000000..af4baaf44c0b --- /dev/null +++ b/unit-tests/varname-dot-shell.exp @@ -0,0 +1,19 @@ +ParseReadLine (8): 'ORIG_SHELL:= ${.SHELL}' +Var_Parse: ${.SHELL} with VARE_WANTRES|VARE_ASSIGN +Global:delete .SHELL (not found) +Command:.SHELL = (details omitted) +ParseReadLine (10): '.SHELL= overwritten' +Global:.SHELL = overwritten +Var_Parse: ${.SHELL} != ${ORIG_SHELL} with VARE_UNDEFERR|VARE_WANTRES +ParseReadLine (18): '.undef .SHELL' +Global:delete .SHELL +ParseReadLine (19): '.SHELL= newly overwritten' +Global:.SHELL = newly overwritten +Var_Parse: ${.SHELL} != ${ORIG_SHELL} with VARE_UNDEFERR|VARE_WANTRES +ParseReadLine (24): 'all:' +ParseReadLine (25): ' @echo ${.SHELL:M*}' +Var_Parse: ${.SHELL:M*} with VARE_WANTRES +Applying ${.SHELL:M...} to "(details omitted)" (eflags = VARE_WANTRES, vflags = VAR_READONLY) +Pattern[.SHELL] for [(details omitted)] is [*] +Result of ${.SHELL:M*} is "(details omitted)" (eflags = VARE_WANTRES, vflags = VAR_READONLY) +exit status 0 diff --git a/unit-tests/varname-dot-shell.mk b/unit-tests/varname-dot-shell.mk new file mode 100755 index 000000000000..4a2c52b39cbc --- /dev/null +++ b/unit-tests/varname-dot-shell.mk @@ -0,0 +1,25 @@ +# $NetBSD: varname-dot-shell.mk,v 1.2 2020/08/23 09:28:52 rillig Exp $ +# +# Tests for the special .SHELL variable, which contains the shell used for +# running the commands. +# +# This variable is read-only. + +ORIG_SHELL:= ${.SHELL} + +.SHELL= overwritten +.if ${.SHELL} != ${ORIG_SHELL} +.error +.endif + +# Trying to delete the variable. +# This has no effect since the variable is not defined in the global context, +# but in the command-line context. +.undef .SHELL +.SHELL= newly overwritten +.if ${.SHELL} != ${ORIG_SHELL} +.error +.endif + +all: + @echo ${.SHELL:M*} diff --git a/unit-tests/varname-dot-targets.exp b/unit-tests/varname-dot-targets.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-dot-targets.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-dot-targets.mk b/unit-tests/varname-dot-targets.mk new file mode 100644 index 000000000000..2d1665744040 --- /dev/null +++ b/unit-tests/varname-dot-targets.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-targets.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .TARGETS variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-empty.exp b/unit-tests/varname-empty.exp new file mode 100644 index 000000000000..77cb7c517e9d --- /dev/null +++ b/unit-tests/varname-empty.exp @@ -0,0 +1,11 @@ +Var_Set("${:U}", "cmdline-u", ...) name expands to empty string - ignored +Var_Set("", "cmline-plain", ...) name expands to empty string - ignored +Var_Set("", "default", ...) name expands to empty string - ignored +Var_Set("", "assigned", ...) name expands to empty string - ignored +Var_Set("", "appended", ...) name expands to empty string - ignored +Var_Set("", "", ...) name expands to empty string - ignored +Var_Set("", "subst", ...) name expands to empty string - ignored +Var_Set("", "shell-output", ...) name expands to empty string - ignored +out: fallback +out: 1 2 3 +exit status 0 diff --git a/unit-tests/varname-empty.mk b/unit-tests/varname-empty.mk new file mode 100755 index 000000000000..b4ce05c3017b --- /dev/null +++ b/unit-tests/varname-empty.mk @@ -0,0 +1,26 @@ +# $NetBSD: varname-empty.mk,v 1.5 2020/08/22 21:22:24 rillig Exp $ +# +# Tests for the special variable with the empty name. +# +# The variable "" is not supposed to be assigned any value. +# This is because it is heavily used in the .for loop expansion, +# as well as to generate arbitrary strings, as in ${:Ufallback}. + +# Until 2020-08-22 it was possible to assign a value to the variable with +# the empty name, leading to all kinds of unexpected effects. +?= default += assigned # undefined behavior until 2020-08-22 ++= appended +:= subst +!= echo 'shell-output' + +# The .for loop expands the expression ${i} to ${:U1}, ${:U2} and so on. +# This only works if the variable with the empty name is guaranteed to +# be undefined. +.for i in 1 2 3 +NUMBERS+= ${i} +.endfor + +all: + @echo out: ${:Ufallback} + @echo out: ${NUMBERS} diff --git a/unit-tests/varname-make.exp b/unit-tests/varname-make.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-make.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-make.mk b/unit-tests/varname-make.mk new file mode 100644 index 000000000000..049f00ef741b --- /dev/null +++ b/unit-tests/varname-make.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-make.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special MAKE variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-make_print_var_on_error.exp b/unit-tests/varname-make_print_var_on_error.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-make_print_var_on_error.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-make_print_var_on_error.mk b/unit-tests/varname-make_print_var_on_error.mk new file mode 100644 index 000000000000..757d55780be1 --- /dev/null +++ b/unit-tests/varname-make_print_var_on_error.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-make_print_var_on_error.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.PRINT_VAR_ON_ERROR variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-makeflags.exp b/unit-tests/varname-makeflags.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-makeflags.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-makeflags.mk b/unit-tests/varname-makeflags.mk new file mode 100644 index 000000000000..b2e5f68b4e08 --- /dev/null +++ b/unit-tests/varname-makeflags.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-makeflags.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special MAKEFLAGS variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-pwd.exp b/unit-tests/varname-pwd.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-pwd.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-pwd.mk b/unit-tests/varname-pwd.mk new file mode 100644 index 000000000000..e11ee828117b --- /dev/null +++ b/unit-tests/varname-pwd.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-pwd.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special PWD variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname-vpath.exp b/unit-tests/varname-vpath.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname-vpath.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname-vpath.mk b/unit-tests/varname-vpath.mk new file mode 100644 index 000000000000..8924647a5072 --- /dev/null +++ b/unit-tests/varname-vpath.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname-vpath.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special VPATH variable. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varname.exp b/unit-tests/varname.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/varname.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/varname.mk b/unit-tests/varname.mk new file mode 100644 index 000000000000..9dd965083f3f --- /dev/null +++ b/unit-tests/varname.mk @@ -0,0 +1,8 @@ +# $NetBSD: varname.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for special variables, such as .MAKE or .PARSEDIR. + +# TODO: Implementation + +all: + @:; diff --git a/unit-tests/varparse-dynamic.exp b/unit-tests/varparse-dynamic.exp new file mode 100644 index 000000000000..a2ff29413167 --- /dev/null +++ b/unit-tests/varparse-dynamic.exp @@ -0,0 +1,5 @@ +make: "varparse-dynamic.mk" line 8: Malformed conditional (${.TARGEX}) +make: "varparse-dynamic.mk" line 10: Malformed conditional (${.TARGXX}) +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/varparse-dynamic.mk b/unit-tests/varparse-dynamic.mk new file mode 100644 index 000000000000..724cca3a5035 --- /dev/null +++ b/unit-tests/varparse-dynamic.mk @@ -0,0 +1,14 @@ +# $NetBSD: varparse-dynamic.mk,v 1.1 2020/07/26 22:15:36 rillig Exp $ + +# Before 2020-07-27, there was an off-by-one error in Var_Parse that skipped +# the last character in the variable name. +# To trigger the bug, the variable must not be defined. +.if ${.TARGET} # exact match, may be undefined +.endif +.if ${.TARGEX} # 1 character difference, must be defined +.endif +.if ${.TARGXX} # 2 characters difference, must be defined +.endif + +all: + @: @@ -1,9 +1,9 @@ -/* $NetBSD: util.c,v 1.57 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: util.c,v 1.58 2020/08/01 14:47:49 rillig Exp $ */ /* * Missing stuff from OS's * - * $Id: util.c,v 1.35 2020/07/04 18:16:55 sjg Exp $ + * $Id: util.c,v 1.36 2020/08/01 23:08:14 sjg Exp $ */ #if defined(__MINT__) || defined(__linux__) #include <signal.h> @@ -12,10 +12,10 @@ #include "make.h" #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: util.c,v 1.57 2020/07/03 08:13:23 rillig Exp $"; +static char rcsid[] = "$NetBSD: util.c,v 1.58 2020/08/01 14:47:49 rillig Exp $"; #else #ifndef lint -__RCSID("$NetBSD: util.c,v 1.57 2020/07/03 08:13:23 rillig Exp $"); +__RCSID("$NetBSD: util.c,v 1.58 2020/08/01 14:47:49 rillig Exp $"); #endif #endif @@ -178,39 +178,39 @@ strrcpy(char *ptr, char *str) char *sys_siglist[] = { - "Signal 0", - "Hangup", /* SIGHUP */ - "Interrupt", /* SIGINT */ - "Quit", /* SIGQUIT */ - "Illegal instruction", /* SIGILL */ - "Trace/BPT trap", /* SIGTRAP */ - "IOT trap", /* SIGIOT */ - "EMT trap", /* SIGEMT */ - "Floating point exception", /* SIGFPE */ - "Killed", /* SIGKILL */ - "Bus error", /* SIGBUS */ - "Segmentation fault", /* SIGSEGV */ - "Bad system call", /* SIGSYS */ - "Broken pipe", /* SIGPIPE */ - "Alarm clock", /* SIGALRM */ - "Terminated", /* SIGTERM */ - "User defined signal 1", /* SIGUSR1 */ - "User defined signal 2", /* SIGUSR2 */ - "Child exited", /* SIGCLD */ - "Power-fail restart", /* SIGPWR */ - "Virtual timer expired", /* SIGVTALRM */ - "Profiling timer expired", /* SIGPROF */ - "I/O possible", /* SIGIO */ - "Window size changes", /* SIGWINDOW */ - "Stopped (signal)", /* SIGSTOP */ - "Stopped", /* SIGTSTP */ - "Continued", /* SIGCONT */ - "Stopped (tty input)", /* SIGTTIN */ - "Stopped (tty output)", /* SIGTTOU */ - "Urgent I/O condition", /* SIGURG */ - "Remote lock lost (NFS)", /* SIGLOST */ - "Signal 31", /* reserved */ - "DIL signal" /* SIGDIL */ + "Signal 0", + "Hangup", /* SIGHUP */ + "Interrupt", /* SIGINT */ + "Quit", /* SIGQUIT */ + "Illegal instruction", /* SIGILL */ + "Trace/BPT trap", /* SIGTRAP */ + "IOT trap", /* SIGIOT */ + "EMT trap", /* SIGEMT */ + "Floating point exception", /* SIGFPE */ + "Killed", /* SIGKILL */ + "Bus error", /* SIGBUS */ + "Segmentation fault", /* SIGSEGV */ + "Bad system call", /* SIGSYS */ + "Broken pipe", /* SIGPIPE */ + "Alarm clock", /* SIGALRM */ + "Terminated", /* SIGTERM */ + "User defined signal 1", /* SIGUSR1 */ + "User defined signal 2", /* SIGUSR2 */ + "Child exited", /* SIGCLD */ + "Power-fail restart", /* SIGPWR */ + "Virtual timer expired", /* SIGVTALRM */ + "Profiling timer expired", /* SIGPROF */ + "I/O possible", /* SIGIO */ + "Window size changes", /* SIGWINDOW */ + "Stopped (signal)", /* SIGSTOP */ + "Stopped", /* SIGTSTP */ + "Continued", /* SIGCONT */ + "Stopped (tty input)", /* SIGTTIN */ + "Stopped (tty output)", /* SIGTTOU */ + "Urgent I/O condition", /* SIGURG */ + "Remote lock lost (NFS)", /* SIGLOST */ + "Signal 31", /* reserved */ + "DIL signal" /* SIGDIL */ }; #endif /* __hpux__ || __hpux */ @@ -1,4 +1,4 @@ -/* $NetBSD: var.c,v 1.255 2020/07/04 17:41:04 rillig Exp $ */ +/* $NetBSD: var.c,v 1.484 2020/09/02 06:25:48 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -69,14 +69,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: var.c,v 1.255 2020/07/04 17:41:04 rillig Exp $"; +static char rcsid[] = "$NetBSD: var.c,v 1.484 2020/09/02 06:25:48 rillig Exp $"; #else #include <sys/cdefs.h> #ifndef lint #if 0 static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: var.c,v 1.255 2020/07/04 17:41:04 rillig Exp $"); +__RCSID("$NetBSD: var.c,v 1.484 2020/09/02 06:25:48 rillig Exp $"); #endif #endif /* not lint */ #endif @@ -88,8 +88,7 @@ __RCSID("$NetBSD: var.c,v 1.255 2020/07/04 17:41:04 rillig Exp $"); * Interface: * Var_Set Set the value of a variable in the given * context. The variable is created if it doesn't - * yet exist. The value and variable name need not - * be preserved. + * yet exist. * * Var_Append Append more characters to an existing variable * in the given context. The variable needn't @@ -99,12 +98,11 @@ __RCSID("$NetBSD: var.c,v 1.255 2020/07/04 17:41:04 rillig Exp $"); * * Var_Exists See if a variable exists. * - * Var_Value Return the value of a variable in a context or - * NULL if the variable is undefined. + * Var_Value Return the unexpanded value of a variable in a + * context or NULL if the variable is undefined. * * Var_Subst Substitute either a single variable or all - * variables in a string, using the given context as - * the top-most one. + * variables in a string, using the given context. * * Var_Parse Parse a variable expansion from a string and * return the result and the number of characters @@ -126,23 +124,32 @@ __RCSID("$NetBSD: var.c,v 1.255 2020/07/04 17:41:04 rillig Exp $"); #ifndef NO_REGEX #include <regex.h> #endif -#include <ctype.h> -#include <stdlib.h> -#include <limits.h> #include <time.h> #include "make.h" -#ifdef HAVE_STDINT_H -#include <stdint.h> +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#elif defined(HAVE_STDINT_H) +#include <stdint.h> #endif -#include "buf.h" +#include "enum.h" #include "dir.h" #include "job.h" #include "metachar.h" -extern int makelevel; +#define VAR_DEBUG_IF(cond, fmt, ...) \ + if (!(DEBUG(VAR) && (cond))) \ + (void) 0; \ + else \ + fprintf(debug_file, fmt, __VA_ARGS__) + +#define VAR_DEBUG(fmt, ...) VAR_DEBUG_IF(TRUE, fmt, __VA_ARGS__) + +ENUM_FLAGS_RTTI_3(VarEvalFlags, + VARE_UNDEFERR, VARE_WANTRES, VARE_ASSIGN); + /* * This lets us tell if we have replaced the original environ * (which we cannot free). @@ -157,9 +164,11 @@ char **savedEnv = NULL; char var_Error[] = ""; /* - * Similar to var_Error, but returned when the 'VARF_UNDEFERR' flag for - * Var_Parse is not set. Why not just use a constant? Well, gcc likes - * to condense identical string instances... + * Similar to var_Error, but returned when the 'VARE_UNDEFERR' flag for + * Var_Parse is not set. + * + * Why not just use a constant? Well, GCC likes to condense identical string + * instances... */ static char varNoError[] = ""; @@ -167,139 +176,107 @@ static char varNoError[] = ""; * Traditionally we consume $$ during := like any other expansion. * Other make's do not. * This knob allows controlling the behavior. - * FALSE for old behavior. - * TRUE for new compatible. + * FALSE to consume $$ during := assignment. + * TRUE to preserve $$ during := assignment. */ #define SAVE_DOLLARS ".MAKE.SAVE_DOLLARS" static Boolean save_dollars = FALSE; /* * Internally, variables are contained in four different contexts. - * 1) the environment. They may not be changed. If an environment - * variable is appended-to, the result is placed in the global + * 1) the environment. They cannot be changed. If an environment + * variable is appended to, the result is placed in the global * context. * 2) the global context. Variables set in the Makefile are located in - * the global context. It is the penultimate context searched when - * substituting. + * the global context. * 3) the command-line context. All variables set on the command line * are placed in this context. They are UNALTERABLE once placed here. * 4) the local context. Each target has associated with it a context * list. On this list are located the structures describing such * local variables as $(@) and $(*) * The four contexts are searched in the reverse order from which they are - * listed. + * listed (but see checkEnvFirst). */ GNode *VAR_INTERNAL; /* variables from make itself */ GNode *VAR_GLOBAL; /* variables from the makefile */ GNode *VAR_CMD; /* variables defined on the command-line */ -#define FIND_CMD 0x1 /* look in VAR_CMD when searching */ -#define FIND_GLOBAL 0x2 /* look in VAR_GLOBAL as well */ -#define FIND_ENV 0x4 /* look in the environment also */ +typedef enum { + FIND_CMD = 0x01, /* look in VAR_CMD when searching */ + FIND_GLOBAL = 0x02, /* look in VAR_GLOBAL as well */ + FIND_ENV = 0x04 /* look in the environment also */ +} VarFindFlags; typedef enum { - VAR_IN_USE = 0x01, /* Variable's value is currently being used. - * Used to avoid endless recursion */ - VAR_FROM_ENV = 0x02, /* Variable comes from the environment */ - VAR_JUNK = 0x04, /* Variable is a junk variable that - * should be destroyed when done with - * it. Used by Var_Parse for undefined, - * modified variables */ - VAR_KEEP = 0x08, /* Variable is VAR_JUNK, but we found - * a use for it in some modifier and - * the value is therefore valid */ - VAR_EXPORTED = 0x10, /* Variable is exported */ - VAR_REEXPORT = 0x20, /* Indicate if var needs re-export. - * This would be true if it contains $'s */ - VAR_FROM_CMD = 0x40 /* Variable came from command line */ -} Var_Flags; + /* The variable's value is currently being used by Var_Parse or Var_Subst. + * This marker is used to avoid endless recursion. */ + VAR_IN_USE = 0x01, + /* The variable comes from the environment. + * These variables are not registered in any GNode, therefore they must + * be freed as soon as they are not used anymore. */ + VAR_FROM_ENV = 0x02, + /* The variable is a junk variable that should be destroyed when done with + * it. Used by Var_Parse for undefined, modified variables. */ + VAR_JUNK = 0x04, + /* Variable is VAR_JUNK, but we found a use for it in some modifier and + * the value is therefore valid. */ + VAR_KEEP = 0x08, + /* The variable is exported to the environment, to be used by child + * processes. */ + VAR_EXPORTED = 0x10, + /* At the point where this variable was exported, it contained an + * unresolved reference to another variable. Before any child process is + * started, it needs to be exported again, in the hope that the referenced + * variable can then be resolved. */ + VAR_REEXPORT = 0x20, + /* The variable came from command line. */ + VAR_FROM_CMD = 0x40, + VAR_READONLY = 0x80 +} VarFlags; + +ENUM_FLAGS_RTTI_8(VarFlags, + VAR_IN_USE, VAR_FROM_ENV, VAR_JUNK, VAR_KEEP, + VAR_EXPORTED, VAR_REEXPORT, VAR_FROM_CMD, VAR_READONLY); typedef struct Var { - char *name; /* the variable's name */ + char *name; /* the variable's name; it is allocated for + * environment variables and aliased to the + * Hash_Entry name for all other variables, + * and thus must not be modified */ Buffer val; /* its value */ - Var_Flags flags; /* miscellaneous status flags */ -} Var; + VarFlags flags; /* miscellaneous status flags */ +} Var; /* * Exporting vars is expensive so skip it if we can */ -#define VAR_EXPORTED_NONE 0 -#define VAR_EXPORTED_YES 1 -#define VAR_EXPORTED_ALL 2 -static int var_exportedVars = VAR_EXPORTED_NONE; -/* - * We pass this to Var_Export when doing the initial export - * or after updating an exported var. - */ -#define VAR_EXPORT_PARENT 1 -/* - * We pass this to Var_Export1 to tell it to leave the value alone. - */ -#define VAR_EXPORT_LITERAL 2 - typedef enum { - VAR_SUB_GLOBAL = 0x01, /* Apply substitution globally */ - VAR_SUB_ONE = 0x02, /* Apply substitution to one word */ - VAR_SUB_MATCHED = 0x04, /* There was a match */ - VAR_MATCH_START = 0x08, /* Match at start of word */ - VAR_MATCH_END = 0x10, /* Match at end of word */ - VAR_NOSUBST = 0x20 /* don't expand vars in VarGetPattern */ -} VarPattern_Flags; + VAR_EXPORTED_NONE, + VAR_EXPORTED_YES, + VAR_EXPORTED_ALL +} VarExportedMode; -typedef enum { - VAR_NO_EXPORT = 0x01 /* do not export */ -} VarSet_Flags; +static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE; -typedef struct { +typedef enum { /* - * The following fields are set by Var_Parse() when it - * encounters modifiers that need to keep state for use by - * subsequent modifiers within the same variable expansion. + * We pass this to Var_Export when doing the initial export + * or after updating an exported var. */ - Byte varSpace; /* Word separator in expansions */ - Boolean oneBigWord; /* TRUE if we will treat the variable as a - * single big word, even if it contains - * embedded spaces (as opposed to the - * usual behaviour of treating it as - * several space-separated words). */ -} Var_Parse_State; - -/* struct passed as 'void *' to VarSubstitute() for ":S/lhs/rhs/", - * to VarSYSVMatch() for ":lhs=rhs". */ -typedef struct { - const char *lhs; /* String to match */ - int leftLen; /* Length of string */ - const char *rhs; /* Replacement string (w/ &'s removed) */ - int rightLen; /* Length of replacement */ - VarPattern_Flags flags; -} VarPattern; - -/* struct passed as 'void *' to VarLoopExpand() for ":@tvar@str@" */ -typedef struct { - GNode *ctxt; /* variable context */ - char *tvar; /* name of temp var */ - int tvarLen; - char *str; /* string to expand */ - int strLen; - Varf_Flags flags; -} VarLoop; - -#ifndef NO_REGEX -/* struct passed as 'void *' to VarRESubstitute() for ":C///" */ -typedef struct { - regex_t re; - int nsub; - regmatch_t *matches; - char *replace; - int flags; -} VarREPattern; -#endif + VAR_EXPORT_PARENT = 0x01, + /* + * We pass this to Var_Export1 to tell it to leave the value alone. + */ + VAR_EXPORT_LITERAL = 0x02 +} VarExportFlags; -/* struct passed to VarSelectWords() for ":[start..end]" */ -typedef struct { - int start; /* first word to select */ - int end; /* last word to select */ -} VarSelectWords_t; +/* Flags for pattern matching in the :S and :C modifiers */ +typedef enum { + VARP_SUB_GLOBAL = 0x01, /* Apply substitution globally */ + VARP_SUB_ONE = 0x02, /* Apply substitution to one word */ + VARP_ANCHOR_START = 0x04, /* Match at start of word */ + VARP_ANCHOR_END = 0x08 /* Match at end of word */ +} VarPatternFlags; #define BROPEN '{' #define BRCLOSE '}' @@ -315,24 +292,19 @@ typedef struct { * Input: * name name to find * ctxt context in which to find it - * flags FIND_GLOBAL set means to look in the - * VAR_GLOBAL context as well. FIND_CMD set means - * to look in the VAR_CMD context also. FIND_ENV - * set means to look in the environment + * flags FIND_GLOBAL look in VAR_GLOBAL as well + * FIND_CMD look in VAR_CMD as well + * FIND_ENV look in the environment as well * * Results: * A pointer to the structure describing the desired variable or * NULL if the variable does not exist. - * - * Side Effects: - * None *----------------------------------------------------------------------- */ static Var * -VarFind(const char *name, GNode *ctxt, int flags) +VarFind(const char *name, GNode *ctxt, VarFindFlags flags) { - Hash_Entry *var; - Var *v; + Hash_Entry *var; /* * If the variable name begins with a '.', it could very well be one of @@ -340,32 +312,38 @@ VarFind(const char *name, GNode *ctxt, int flags) * and substitute the short version in for 'name' if it matches one of * them. */ - if (*name == '.' && isupper((unsigned char) name[1])) { + if (*name == '.' && isupper((unsigned char)name[1])) { switch (name[1]) { case 'A': - if (!strcmp(name, ".ALLSRC")) + if (strcmp(name, ".ALLSRC") == 0) name = ALLSRC; - if (!strcmp(name, ".ARCHIVE")) + if (strcmp(name, ".ARCHIVE") == 0) name = ARCHIVE; break; case 'I': - if (!strcmp(name, ".IMPSRC")) + if (strcmp(name, ".IMPSRC") == 0) name = IMPSRC; break; case 'M': - if (!strcmp(name, ".MEMBER")) + if (strcmp(name, ".MEMBER") == 0) name = MEMBER; break; case 'O': - if (!strcmp(name, ".OODATE")) + if (strcmp(name, ".OODATE") == 0) name = OODATE; break; case 'P': - if (!strcmp(name, ".PREFIX")) + if (strcmp(name, ".PREFIX") == 0) name = PREFIX; break; + case 'S': + if (strcmp(name, ".SHELL") == 0 ) { + if (!shellPath) + Shell_Init(); + } + break; case 'T': - if (!strcmp(name, ".TARGET")) + if (strcmp(name, ".TARGET") == 0) name = TARGET; break; } @@ -384,9 +362,9 @@ VarFind(const char *name, GNode *ctxt, int flags) */ var = Hash_FindEntry(&ctxt->context, name); - if (var == NULL && (flags & FIND_CMD) && ctxt != VAR_CMD) { + if (var == NULL && (flags & FIND_CMD) && ctxt != VAR_CMD) var = Hash_FindEntry(&VAR_CMD->context, name); - } + if (!checkEnvFirst && var == NULL && (flags & FIND_GLOBAL) && ctxt != VAR_GLOBAL) { @@ -396,42 +374,40 @@ VarFind(const char *name, GNode *ctxt, int flags) var = Hash_FindEntry(&VAR_INTERNAL->context, name); } } + if (var == NULL && (flags & FIND_ENV)) { char *env; if ((env = getenv(name)) != NULL) { - int len; - - v = bmake_malloc(sizeof(Var)); + Var *v = bmake_malloc(sizeof(Var)); + size_t len; v->name = bmake_strdup(name); len = strlen(env); - Buf_Init(&v->val, len + 1); - Buf_AddBytes(&v->val, len, env); + Buf_AddBytes(&v->val, env, len); v->flags = VAR_FROM_ENV; return v; - } else if (checkEnvFirst && (flags & FIND_GLOBAL) && - ctxt != VAR_GLOBAL) - { + } + + if (checkEnvFirst && (flags & FIND_GLOBAL) && ctxt != VAR_GLOBAL) { var = Hash_FindEntry(&VAR_GLOBAL->context, name); - if (var == NULL && ctxt != VAR_INTERNAL) { + if (var == NULL && ctxt != VAR_INTERNAL) var = Hash_FindEntry(&VAR_INTERNAL->context, name); - } - if (var == NULL) { + if (var == NULL) return NULL; - } else { + else return (Var *)Hash_GetValue(var); - } - } else { - return NULL; } - } else if (var == NULL) { + return NULL; - } else { - return (Var *)Hash_GetValue(var); } + + if (var == NULL) + return NULL; + else + return (Var *)Hash_GetValue(var); } /*- @@ -444,16 +420,13 @@ VarFind(const char *name, GNode *ctxt, int flags) * destroy true if the value buffer should be destroyed. * * Results: - * 1 if it is an environment variable 0 ow. - * - * Side Effects: - * The variable is free'ed if it is an environent variable. + * TRUE if it is an environment variable, FALSE otherwise. *----------------------------------------------------------------------- */ static Boolean VarFreeEnv(Var *v, Boolean destroy) { - if ((v->flags & VAR_FROM_ENV) == 0) + if (!(v->flags & VAR_FROM_ENV)) return FALSE; free(v->name); Buf_Destroy(&v->val, destroy); @@ -461,87 +434,52 @@ VarFreeEnv(Var *v, Boolean destroy) return TRUE; } -/*- - *----------------------------------------------------------------------- - * VarAdd -- - * Add a new variable of name name and value val to the given context - * - * Input: - * name name of variable to add - * val value to set it to - * ctxt context in which to set it - * - * Side Effects: - * The new variable is placed at the front of the given context - * The name and val arguments are duplicated so they may - * safely be freed. - *----------------------------------------------------------------------- - */ +/* Add a new variable of the given name and value to the given context. + * The name and val arguments are duplicated so they may safely be freed. */ static void -VarAdd(const char *name, const char *val, GNode *ctxt) +VarAdd(const char *name, const char *val, GNode *ctxt, VarSet_Flags flags) { - Var *v; - int len; - Hash_Entry *h; + Var *v = bmake_malloc(sizeof(Var)); + size_t len = strlen(val); + Hash_Entry *he; - v = bmake_malloc(sizeof(Var)); - - len = val ? strlen(val) : 0; Buf_Init(&v->val, len + 1); - Buf_AddBytes(&v->val, len, val); + Buf_AddBytes(&v->val, val, len); v->flags = 0; - - h = Hash_CreateEntry(&ctxt->context, name, NULL); - Hash_SetValue(h, v); - v->name = h->name; - if (DEBUG(VAR) && (ctxt->flags & INTERNAL) == 0) { - fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val); - } + if (flags & VAR_SET_READONLY) + v->flags |= VAR_READONLY; + + he = Hash_CreateEntry(&ctxt->context, name, NULL); + Hash_SetValue(he, v); + v->name = he->name; + VAR_DEBUG_IF(!(ctxt->flags & INTERNAL), + "%s:%s = %s\n", ctxt->name, name, val); } -/*- - *----------------------------------------------------------------------- - * Var_Delete -- - * Remove a variable from a context. - * - * Side Effects: - * The Var structure is removed and freed. - * - *----------------------------------------------------------------------- - */ +/* Remove a variable from a context, freeing the Var structure as well. */ void Var_Delete(const char *name, GNode *ctxt) { - Hash_Entry *ln; - char *cp; - - if (strchr(name, '$')) { - cp = Var_Subst(NULL, name, VAR_GLOBAL, VARF_WANTRES); - } else { - cp = (char *)name; - } - ln = Hash_FindEntry(&ctxt->context, cp); - if (DEBUG(VAR)) { - fprintf(debug_file, "%s:delete %s%s\n", - ctxt->name, cp, ln ? "" : " (not found)"); - } - if (cp != name) { - free(cp); - } - if (ln != NULL) { - Var *v; - - v = (Var *)Hash_GetValue(ln); - if ((v->flags & VAR_EXPORTED)) { + char *name_freeIt = NULL; + Hash_Entry *he; + + if (strchr(name, '$') != NULL) + name = name_freeIt = Var_Subst(name, VAR_GLOBAL, VARE_WANTRES); + he = Hash_FindEntry(&ctxt->context, name); + VAR_DEBUG("%s:delete %s%s\n", + ctxt->name, name, he != NULL ? "" : " (not found)"); + free(name_freeIt); + + if (he != NULL) { + Var *v = (Var *)Hash_GetValue(he); + if (v->flags & VAR_EXPORTED) unsetenv(v->name); - } - if (strcmp(MAKE_EXPORTED, v->name) == 0) { + if (strcmp(v->name, MAKE_EXPORTED) == 0) var_exportedVars = VAR_EXPORTED_NONE; - } - if (v->name != ln->name) + if (v->name != he->name) free(v->name); - Hash_DeleteEntry(&ctxt->context, ln); + Hash_DeleteEntry(&ctxt->context, he); Buf_Destroy(&v->val, TRUE); free(v); } @@ -549,24 +487,22 @@ Var_Delete(const char *name, GNode *ctxt) /* - * Export a var. - * We ignore make internal variables (those which start with '.') + * Export a single variable. + * We ignore make internal variables (those which start with '.'). * Also we jump through some hoops to avoid calling setenv * more than necessary since it can leak. * We only manipulate flags of vars if 'parent' is set. */ -static int -Var_Export1(const char *name, int flags) +static Boolean +Var_Export1(const char *name, VarExportFlags flags) { - char tmp[BUFSIZ]; + VarExportFlags parent = flags & VAR_EXPORT_PARENT; Var *v; - char *val = NULL; - int n; - int parent = (flags & VAR_EXPORT_PARENT); + char *val; - if (*name == '.') - return 0; /* skip internals */ - if (!name[1]) { + if (name[0] == '.') + return FALSE; /* skip internals */ + if (name[1] == '\0') { /* * A single char. * If it is one of the vars that should only appear in @@ -578,48 +514,48 @@ Var_Export1(const char *name, int flags) case '%': case '*': case '!': - return 0; + return FALSE; } } + v = VarFind(name, VAR_GLOBAL, 0); - if (v == NULL) { - return 0; - } - if (!parent && - (v->flags & (VAR_EXPORTED|VAR_REEXPORT)) == VAR_EXPORTED) { - return 0; /* nothing to do */ - } + if (v == NULL) + return FALSE; + + if (!parent && (v->flags & VAR_EXPORTED) && !(v->flags & VAR_REEXPORT)) + return FALSE; /* nothing to do */ + val = Buf_GetAll(&v->val, NULL); - if ((flags & VAR_EXPORT_LITERAL) == 0 && strchr(val, '$')) { + if (!(flags & VAR_EXPORT_LITERAL) && strchr(val, '$') != NULL) { + char *expr; + if (parent) { /* * Flag this as something we need to re-export. * No point actually exporting it now though, * the child can do it at the last minute. */ - v->flags |= (VAR_EXPORTED|VAR_REEXPORT); - return 1; + v->flags |= VAR_EXPORTED | VAR_REEXPORT; + return TRUE; } if (v->flags & VAR_IN_USE) { /* * We recursed while exporting in a child. * This isn't going to end well, just skip it. */ - return 0; - } - n = snprintf(tmp, sizeof(tmp), "${%s}", name); - if (n < (int)sizeof(tmp)) { - val = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES); - setenv(name, val, 1); - free(val); + return FALSE; } + + expr = str_concat3("${", name, "}"); + val = Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES); + setenv(name, val, 1); + free(val); + free(expr); } else { - if (parent) { - v->flags &= ~VAR_REEXPORT; /* once will do */ - } - if (parent || !(v->flags & VAR_EXPORTED)) { + if (parent) + v->flags &= ~(unsigned)VAR_REEXPORT; /* once will do */ + if (parent || !(v->flags & VAR_EXPORTED)) setenv(name, val, 1); - } } /* * This is so Var_Set knows to call Var_Export again... @@ -627,7 +563,7 @@ Var_Export1(const char *name, int flags) if (parent) { v->flags |= VAR_EXPORTED; } - return 1; + return TRUE; } static void @@ -643,9 +579,7 @@ Var_ExportVars_callback(void *entry, void *unused MAKE_ATTR_UNUSED) void Var_ExportVars(void) { - char tmp[BUFSIZ]; char *val; - int n; /* * Several make's support this sort of mechanism for tracking @@ -653,57 +587,46 @@ Var_ExportVars(void) * We allow the makefiles to update MAKELEVEL and ensure * children see a correctly incremented value. */ + char tmp[BUFSIZ]; snprintf(tmp, sizeof(tmp), "%d", makelevel + 1); setenv(MAKE_LEVEL_ENV, tmp, 1); - if (VAR_EXPORTED_NONE == var_exportedVars) + if (var_exportedVars == VAR_EXPORTED_NONE) return; - if (VAR_EXPORTED_ALL == var_exportedVars) { + if (var_exportedVars == VAR_EXPORTED_ALL) { /* Ouch! This is crazy... */ Hash_ForEach(&VAR_GLOBAL->context, Var_ExportVars_callback, NULL); return; } - /* - * We have a number of exported vars, - */ - n = snprintf(tmp, sizeof(tmp), "${" MAKE_EXPORTED ":O:u}"); - if (n < (int)sizeof(tmp)) { - char **av; - char *as; - int ac; - int i; - - val = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES); - if (*val) { - av = brk_string(val, &ac, FALSE, &as); - for (i = 0; i < ac; i++) { - Var_Export1(av[i], 0); - } - free(as); - free(av); - } - free(val); + + val = Var_Subst("${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL, VARE_WANTRES); + if (*val) { + Words words = Str_Words(val, FALSE); + size_t i; + + for (i = 0; i < words.len; i++) + Var_Export1(words.words[i], 0); + Words_Free(words); } + free(val); } /* - * This is called when .export is seen or - * .MAKE.EXPORTED is modified. - * It is also called when any exported var is modified. + * This is called when .export is seen or .MAKE.EXPORTED is modified. + * + * It is also called when any exported variable is modified. + * XXX: Is it really? + * + * str has the format "[-env|-literal] varname...". */ void -Var_Export(char *str, int isExport) +Var_Export(const char *str, Boolean isExport) { - char *name; + VarExportFlags flags; char *val; - char **av; - char *as; - int flags; - int ac; - int i; - if (isExport && (!str || !str[0])) { + if (isExport && str[0] == '\0') { var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */ return; } @@ -717,64 +640,49 @@ Var_Export(char *str, int isExport) } else { flags |= VAR_EXPORT_PARENT; } - val = Var_Subst(NULL, str, VAR_GLOBAL, VARF_WANTRES); - if (*val) { - av = brk_string(val, &ac, FALSE, &as); - for (i = 0; i < ac; i++) { - name = av[i]; - if (!name[1]) { - /* - * A single char. - * If it is one of the vars that should only appear in - * local context, skip it, else we can get Var_Subst - * into a loop. - */ - switch (name[0]) { - case '@': - case '%': - case '*': - case '!': - continue; - } - } + + val = Var_Subst(str, VAR_GLOBAL, VARE_WANTRES); + if (val[0] != '\0') { + Words words = Str_Words(val, FALSE); + + size_t i; + for (i = 0; i < words.len; i++) { + const char *name = words.words[i]; if (Var_Export1(name, flags)) { - if (VAR_EXPORTED_ALL != var_exportedVars) + if (var_exportedVars != VAR_EXPORTED_ALL) var_exportedVars = VAR_EXPORTED_YES; if (isExport && (flags & VAR_EXPORT_PARENT)) { Var_Append(MAKE_EXPORTED, name, VAR_GLOBAL); } } } - free(as); - free(av); + Words_Free(words); } free(val); } +extern char **environ; + /* * This is called when .unexport[-env] is seen. + * + * str must have the form "unexport[-env] varname...". */ -extern char **environ; - void -Var_UnExport(char *str) +Var_UnExport(const char *str) { - char tmp[BUFSIZ]; - char *vlist; - char *cp; + const char *varnames; + char *varnames_freeIt; Boolean unexport_env; - int n; - if (!str || !str[0]) { - return; /* assert? */ - } + varnames = NULL; + varnames_freeIt = NULL; - vlist = NULL; - - str += 8; - unexport_env = (strncmp(str, "-env", 4) == 0); + str += strlen("unexport"); + unexport_env = strncmp(str, "-env", 4) == 0; if (unexport_env) { + const char *cp; char **newenv; cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */ @@ -788,8 +696,7 @@ Var_UnExport(char *str) } newenv = bmake_malloc(2 * sizeof(char *)); } - if (!newenv) - return; + /* Note: we cannot safely free() the original environ. */ environ = savedEnv = newenv; newenv[0] = NULL; @@ -797,102 +704,100 @@ Var_UnExport(char *str) if (cp && *cp) setenv(MAKE_LEVEL_ENV, cp, 1); } else { - for (; *str != '\n' && isspace((unsigned char) *str); str++) + for (; isspace((unsigned char)*str); str++) continue; - if (str[0] && str[0] != '\n') { - vlist = str; - } + if (str[0] != '\0') + varnames = str; } - if (!vlist) { + if (varnames == NULL) { /* Using .MAKE.EXPORTED */ - n = snprintf(tmp, sizeof(tmp), "${" MAKE_EXPORTED ":O:u}"); - if (n < (int)sizeof(tmp)) { - vlist = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES); - } + varnames = varnames_freeIt = Var_Subst("${" MAKE_EXPORTED ":O:u}", + VAR_GLOBAL, VARE_WANTRES); } - if (vlist) { + + { Var *v; - char **av; - char *as; - int ac; - int i; - - av = brk_string(vlist, &ac, FALSE, &as); - for (i = 0; i < ac; i++) { - v = VarFind(av[i], VAR_GLOBAL, 0); - if (!v) + size_t i; + + Words words = Str_Words(varnames, FALSE); + for (i = 0; i < words.len; i++) { + const char *varname = words.words[i]; + v = VarFind(varname, VAR_GLOBAL, 0); + if (v == NULL) { + VAR_DEBUG("Not unexporting \"%s\" (not found)\n", varname); continue; - if (!unexport_env && - (v->flags & (VAR_EXPORTED|VAR_REEXPORT)) == VAR_EXPORTED) { - unsetenv(v->name); } - v->flags &= ~(VAR_EXPORTED|VAR_REEXPORT); + + VAR_DEBUG("Unexporting \"%s\"\n", varname); + if (!unexport_env && (v->flags & VAR_EXPORTED) && + !(v->flags & VAR_REEXPORT)) + unsetenv(v->name); + v->flags &= ~(unsigned)(VAR_EXPORTED | VAR_REEXPORT); + /* * If we are unexporting a list, * remove each one from .MAKE.EXPORTED. * If we are removing them all, * just delete .MAKE.EXPORTED below. */ - if (vlist == str) { - n = snprintf(tmp, sizeof(tmp), - "${" MAKE_EXPORTED ":N%s}", v->name); - if (n < (int)sizeof(tmp)) { - cp = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES); - Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL); - free(cp); - } + if (varnames == str) { + char *expr = str_concat3("${" MAKE_EXPORTED ":N", v->name, "}"); + char *cp = Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES); + Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL); + free(cp); + free(expr); } } - free(as); - free(av); - if (vlist != str) { + Words_Free(words); + if (varnames != str) { Var_Delete(MAKE_EXPORTED, VAR_GLOBAL); - free(vlist); + free(varnames_freeIt); } } } -static void +/* See Var_Set for documentation. */ +void Var_Set_with_flags(const char *name, const char *val, GNode *ctxt, VarSet_Flags flags) { + const char *unexpanded_name = name; + char *name_freeIt = NULL; Var *v; - char *expanded_name = NULL; + + assert(val != NULL); /* * We only look for a variable in the given context since anything set * here will override anything in a lower context, so there's not much * point in searching them all just to save a bit of memory... */ - if (strchr(name, '$') != NULL) { - expanded_name = Var_Subst(NULL, name, ctxt, VARF_WANTRES); - if (expanded_name[0] == 0) { - if (DEBUG(VAR)) { - fprintf(debug_file, "Var_Set(\"%s\", \"%s\", ...) " - "name expands to empty string - ignored\n", - name, val); - } - free(expanded_name); - return; - } - name = expanded_name; + if (strchr(name, '$') != NULL) + name = name_freeIt = Var_Subst(name, ctxt, VARE_WANTRES); + + if (name[0] == '\0') { + VAR_DEBUG("Var_Set(\"%s\", \"%s\", ...) " + "name expands to empty string - ignored\n", + unexpanded_name, val); + free(name_freeIt); + return; } + if (ctxt == VAR_GLOBAL) { v = VarFind(name, VAR_CMD, 0); if (v != NULL) { - if ((v->flags & VAR_FROM_CMD)) { - if (DEBUG(VAR)) { - fprintf(debug_file, "%s:%s = %s ignored!\n", ctxt->name, name, val); - } + if (v->flags & VAR_FROM_CMD) { + VAR_DEBUG("%s:%s = %s ignored!\n", ctxt->name, name, val); goto out; } VarFreeEnv(v, TRUE); } } + v = VarFind(name, ctxt, 0); if (v == NULL) { - if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) { + if (ctxt == VAR_CMD && !(flags & VAR_NO_EXPORT)) { /* * This var would normally prevent the same name being added * to VAR_GLOBAL, so delete it from there if needed. @@ -900,24 +805,28 @@ Var_Set_with_flags(const char *name, const char *val, GNode *ctxt, */ Var_Delete(name, VAR_GLOBAL); } - VarAdd(name, val, ctxt); + VarAdd(name, val, ctxt, flags); } else { + if ((v->flags & VAR_READONLY) && !(flags & VAR_SET_READONLY)) { + VAR_DEBUG("%s:%s = %s ignored (read-only)\n", + ctxt->name, name, val); + goto out; + } Buf_Empty(&v->val); if (val) - Buf_AddBytes(&v->val, strlen(val), val); + Buf_AddStr(&v->val, val); - if (DEBUG(VAR)) { - fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val); - } - if ((v->flags & VAR_EXPORTED)) { + VAR_DEBUG("%s:%s = %s\n", ctxt->name, name, val); + if (v->flags & VAR_EXPORTED) { Var_Export1(name, VAR_EXPORT_PARENT); } } /* * Any variables given on the command line are automatically exported * to the environment (as per POSIX standard) + * Other than internals. */ - if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) { + if (ctxt == VAR_CMD && !(flags & VAR_NO_EXPORT) && name[0] != '.') { if (v == NULL) { /* we just added it */ v = VarFind(name, ctxt, 0); @@ -930,18 +839,16 @@ Var_Set_with_flags(const char *name, const char *val, GNode *ctxt, * that the command-line settings continue to override * Makefile settings. */ - if (varNoExportEnv != TRUE) + if (!varNoExportEnv) setenv(name, val ? val : "", 1); Var_Append(MAKEOVERRIDES, name, VAR_GLOBAL); } - if (*name == '.') { - if (strcmp(name, SAVE_DOLLARS) == 0) - save_dollars = s2Boolean(val, save_dollars); - } + if (name[0] == '.' && strcmp(name, SAVE_DOLLARS) == 0) + save_dollars = s2Boolean(val, save_dollars); out: - free(expanded_name); + free(name_freeIt); if (v != NULL) VarFreeEnv(v, TRUE); } @@ -951,15 +858,14 @@ out: * Var_Set -- * Set the variable name to the value val in the given context. * + * If the variable doesn't yet exist, it is created. + * Otherwise the new value overwrites and replaces the old value. + * * Input: * name name of variable to set * val value to give to the variable * ctxt context in which to set it * - * Side Effects: - * If the variable doesn't yet exist, a new record is created for it. - * Else the old value is freed and the new one stuck in its place - * * Notes: * The variable is searched for only in its context before being * created in that context. I.e. if the context is VAR_GLOBAL, @@ -983,14 +889,13 @@ Var_Set(const char *name, const char *val, GNode *ctxt) * The variable of the given name has the given value appended to it in * the given context. * + * If the variable doesn't exist, it is created. Otherwise the strings + * are concatenated, with a space in between. + * * Input: * name name of variable to modify - * val String to append to it - * ctxt Context in which this should occur - * - * Side Effects: - * If the variable doesn't exist, it is created. Else the strings - * are concatenated (with a space in between). + * val string to append to it + * ctxt context in which this should occur * * Notes: * Only if the variable is being sought in the global context is the @@ -1004,83 +909,71 @@ Var_Set(const char *name, const char *val, GNode *ctxt) void Var_Append(const char *name, const char *val, GNode *ctxt) { + char *name_freeIt = NULL; Var *v; - Hash_Entry *h; - char *expanded_name = NULL; + + assert(val != NULL); if (strchr(name, '$') != NULL) { - expanded_name = Var_Subst(NULL, name, ctxt, VARF_WANTRES); - if (expanded_name[0] == 0) { - if (DEBUG(VAR)) { - fprintf(debug_file, "Var_Append(\"%s\", \"%s\", ...) " - "name expands to empty string - ignored\n", - name, val); - } - free(expanded_name); + const char *unexpanded_name = name; + name = name_freeIt = Var_Subst(name, ctxt, VARE_WANTRES); + if (name[0] == '\0') { + VAR_DEBUG("Var_Append(\"%s\", \"%s\", ...) " + "name expands to empty string - ignored\n", + unexpanded_name, val); + free(name_freeIt); return; } - name = expanded_name; } - v = VarFind(name, ctxt, ctxt == VAR_GLOBAL ? (FIND_CMD|FIND_ENV) : 0); + v = VarFind(name, ctxt, ctxt == VAR_GLOBAL ? (FIND_CMD | FIND_ENV) : 0); if (v == NULL) { Var_Set(name, val, ctxt); } else if (ctxt == VAR_CMD || !(v->flags & VAR_FROM_CMD)) { Buf_AddByte(&v->val, ' '); - Buf_AddBytes(&v->val, strlen(val), val); + Buf_AddStr(&v->val, val); - if (DEBUG(VAR)) { - fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, - Buf_GetAll(&v->val, NULL)); - } + VAR_DEBUG("%s:%s = %s\n", ctxt->name, name, + Buf_GetAll(&v->val, NULL)); if (v->flags & VAR_FROM_ENV) { + Hash_Entry *h; + /* * If the original variable came from the environment, we * have to install it in the global context (we could place * it in the environment, but then we should provide a way to * export other variables...) */ - v->flags &= ~VAR_FROM_ENV; + v->flags &= ~(unsigned)VAR_FROM_ENV; h = Hash_CreateEntry(&ctxt->context, name, NULL); Hash_SetValue(h, v); } } - free(expanded_name); + free(name_freeIt); } -/*- - *----------------------------------------------------------------------- - * Var_Exists -- - * See if the given variable exists. +/* See if the given variable exists, in the given context or in other + * fallback contexts. * * Input: * name Variable to find * ctxt Context in which to start search - * - * Results: - * TRUE if it does, FALSE if it doesn't - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- */ Boolean Var_Exists(const char *name, GNode *ctxt) { - Var *v; - char *cp; + char *name_freeIt = NULL; + Var *v; - if ((cp = strchr(name, '$')) != NULL) { - cp = Var_Subst(NULL, name, ctxt, VARF_WANTRES); - } - v = VarFind(cp ? cp : name, ctxt, FIND_CMD|FIND_GLOBAL|FIND_ENV); - free(cp); - if (v == NULL) { + if (strchr(name, '$') != NULL) + name = name_freeIt = Var_Subst(name, ctxt, VARE_WANTRES); + + v = VarFind(name, ctxt, FIND_CMD | FIND_GLOBAL | FIND_ENV); + free(name_freeIt); + if (v == NULL) return FALSE; - } (void)VarFreeEnv(v, TRUE); return TRUE; @@ -1089,614 +982,552 @@ Var_Exists(const char *name, GNode *ctxt) /*- *----------------------------------------------------------------------- * Var_Value -- - * Return the value of the named variable in the given context + * Return the unexpanded value of the given variable in the given + * context, or the usual contexts. * * Input: * name name to find * ctxt context in which to search for it * * Results: - * The value if the variable exists, NULL if it doesn't - * - * Side Effects: - * None + * The value if the variable exists, NULL if it doesn't. + * If the returned value is not NULL, the caller must free *freeIt + * as soon as the returned value is no longer needed. *----------------------------------------------------------------------- */ -char * -Var_Value(const char *name, GNode *ctxt, char **frp) +const char * +Var_Value(const char *name, GNode *ctxt, char **freeIt) { - Var *v; + Var *v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); + char *p; - v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); - *frp = NULL; + *freeIt = NULL; if (v == NULL) return NULL; - char *p = (Buf_GetAll(&v->val, NULL)); + p = Buf_GetAll(&v->val, NULL); if (VarFreeEnv(v, FALSE)) - *frp = p; + *freeIt = p; return p; } -/* This callback for VarModify gets a single word from an expression and +/* SepBuf is a string being built from "words", interleaved with separators. */ +typedef struct { + Buffer buf; + Boolean needSep; + char sep; /* usually ' ', but see the :ts modifier */ +} SepBuf; + +static void +SepBuf_Init(SepBuf *buf, char sep) +{ + Buf_Init(&buf->buf, 32 /* bytes */); + buf->needSep = FALSE; + buf->sep = sep; +} + +static void +SepBuf_Sep(SepBuf *buf) +{ + buf->needSep = TRUE; +} + +static void +SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size) +{ + if (mem_size == 0) + return; + if (buf->needSep && buf->sep != '\0') { + Buf_AddByte(&buf->buf, buf->sep); + buf->needSep = FALSE; + } + Buf_AddBytes(&buf->buf, mem, mem_size); +} + +static void +SepBuf_AddBytesBetween(SepBuf *buf, const char *start, const char *end) +{ + SepBuf_AddBytes(buf, start, (size_t)(end - start)); +} + +static void +SepBuf_AddStr(SepBuf *buf, const char *str) +{ + SepBuf_AddBytes(buf, str, strlen(str)); +} + +static char * +SepBuf_Destroy(SepBuf *buf, Boolean free_buf) +{ + return Buf_Destroy(&buf->buf, free_buf); +} + + +/* This callback for ModifyWords gets a single word from an expression and * typically adds a modification of this word to the buffer. It may also do - * nothing or add several words. - * - * If addSpaces is TRUE, it must add a space before adding anything else to - * the buffer. - * - * It returns the addSpace value for the next call of this callback. Typical - * return values are the current addSpaces or TRUE. */ -typedef Boolean (*VarModifyCallback)(GNode *ctxt, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, void *data); + * nothing or add several words. */ +typedef void (*ModifyWordsCallback)(const char *word, SepBuf *buf, void *data); -/* Callback function for VarModify to implement the :H modifier. +/* Callback for ModifyWords to implement the :H modifier. * Add the dirname of the given word to the buffer. */ -static Boolean -VarHead(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *dummy MAKE_ATTR_UNUSED) +static void +ModifyWord_Head(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) { const char *slash = strrchr(word, '/'); - - if (addSpace && vpstate->varSpace) - Buf_AddByte(buf, vpstate->varSpace); if (slash != NULL) - Buf_AddBytes(buf, slash - word, word); + SepBuf_AddBytesBetween(buf, word, slash); else - Buf_AddByte(buf, '.'); - - return TRUE; + SepBuf_AddStr(buf, "."); } -/* Callback function for VarModify to implement the :T modifier. +/* Callback for ModifyWords to implement the :T modifier. * Add the basename of the given word to the buffer. */ -static Boolean -VarTail(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *dummy MAKE_ATTR_UNUSED) +static void +ModifyWord_Tail(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) { const char *slash = strrchr(word, '/'); const char *base = slash != NULL ? slash + 1 : word; - - if (addSpace && vpstate->varSpace) - Buf_AddByte(buf, vpstate->varSpace); - Buf_AddBytes(buf, strlen(base), base); - return TRUE; + SepBuf_AddStr(buf, base); } -/* Callback function for VarModify to implement the :E modifier. +/* Callback for ModifyWords to implement the :E modifier. * Add the filename suffix of the given word to the buffer, if it exists. */ -static Boolean -VarSuffix(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *dummy MAKE_ATTR_UNUSED) +static void +ModifyWord_Suffix(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) { const char *dot = strrchr(word, '.'); - if (dot == NULL) - return addSpace; - - if (addSpace && vpstate->varSpace) - Buf_AddByte(buf, vpstate->varSpace); - Buf_AddBytes(buf, strlen(dot + 1), dot + 1); - return TRUE; + if (dot != NULL) + SepBuf_AddStr(buf, dot + 1); } -/* Callback function for VarModify to implement the :R modifier. - * Add the filename basename of the given word to the buffer. */ -static Boolean -VarRoot(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *dummy MAKE_ATTR_UNUSED) +/* Callback for ModifyWords to implement the :R modifier. + * Add the basename of the given word to the buffer. */ +static void +ModifyWord_Root(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) { - char *dot = strrchr(word, '.'); + const char *dot = strrchr(word, '.'); size_t len = dot != NULL ? (size_t)(dot - word) : strlen(word); - - if (addSpace && vpstate->varSpace) - Buf_AddByte(buf, vpstate->varSpace); - Buf_AddBytes(buf, len, word); - return TRUE; + SepBuf_AddBytes(buf, word, len); } -/* Callback function for VarModify to implement the :M modifier. +/* Callback for ModifyWords to implement the :M modifier. * Place the word in the buffer if it matches the given pattern. */ -static Boolean -VarMatch(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *data) +static void +ModifyWord_Match(const char *word, SepBuf *buf, void *data) +{ + const char *pattern = data; + VAR_DEBUG("VarMatch [%s] [%s]\n", word, pattern); + if (Str_Match(word, pattern)) + SepBuf_AddStr(buf, word); +} + +/* Callback for ModifyWords to implement the :N modifier. + * Place the word in the buffer if it doesn't match the given pattern. */ +static void +ModifyWord_NoMatch(const char *word, SepBuf *buf, void *data) { const char *pattern = data; - if (DEBUG(VAR)) - fprintf(debug_file, "VarMatch [%s] [%s]\n", word, pattern); if (!Str_Match(word, pattern)) - return addSpace; - if (addSpace && vpstate->varSpace) - Buf_AddByte(buf, vpstate->varSpace); - Buf_AddBytes(buf, strlen(word), word); - return TRUE; + SepBuf_AddStr(buf, word); } #ifdef SYSVVARSUB -/* Callback function for VarModify to implement the :%.from=%.to modifier. */ -static Boolean -VarSYSVMatch(GNode *ctx, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *data) +/*- + *----------------------------------------------------------------------- + * Str_SYSVMatch -- + * Check word against pattern for a match (% is wild), + * + * Input: + * word Word to examine + * pattern Pattern to examine against + * + * Results: + * Returns the start of the match, or NULL. + * *match_len returns the length of the match, if any. + * *hasPercent returns whether the pattern contains a percent. + *----------------------------------------------------------------------- + */ +static const char * +Str_SYSVMatch(const char *word, const char *pattern, size_t *match_len, + Boolean *hasPercent) { - size_t len; - char *ptr; - Boolean hasPercent; - VarPattern *pat = data; - - if (addSpace && vpstate->varSpace) - Buf_AddByte(buf, vpstate->varSpace); + const char *p = pattern; + const char *w = word; + const char *percent; + size_t w_len; + size_t p_len; + const char *w_tail; + + *hasPercent = FALSE; + if (*p == '\0') { /* ${VAR:=suffix} */ + *match_len = strlen(w); /* Null pattern is the whole string */ + return w; + } + + percent = strchr(p, '%'); + if (percent != NULL) { /* ${VAR:...%...=...} */ + *hasPercent = TRUE; + if (*w == '\0') + return NULL; /* empty word does not match pattern */ + + /* check that the prefix matches */ + for (; p != percent && *w != '\0' && *w == *p; w++, p++) + continue; + if (p != percent) + return NULL; /* No match */ - if ((ptr = Str_SYSVMatch(word, pat->lhs, &len, &hasPercent)) != NULL) { - char *varexp = Var_Subst(NULL, pat->rhs, ctx, VARF_WANTRES); - Str_SYSVSubst(buf, varexp, ptr, len, hasPercent); - free(varexp); - } else { - Buf_AddBytes(buf, strlen(word), word); + p++; /* Skip the percent */ + if (*p == '\0') { + /* No more pattern, return the rest of the string */ + *match_len = strlen(w); + return w; + } } - return TRUE; + /* Test whether the tail matches */ + w_len = strlen(w); + p_len = strlen(p); + if (w_len < p_len) + return NULL; + + w_tail = w + w_len - p_len; + if (memcmp(p, w_tail, p_len) != 0) + return NULL; + + *match_len = (size_t)(w_tail - w); + return w; } -#endif -/* Callback function for VarModify to implement the :N modifier. - * Place the word in the buffer if it doesn't match the given pattern. */ -static Boolean -VarNoMatch(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *data) +typedef struct { + GNode *ctx; + const char *lhs; + const char *rhs; +} ModifyWord_SYSVSubstArgs; + +/* Callback for ModifyWords to implement the :%.from=%.to modifier. */ +static void +ModifyWord_SYSVSubst(const char *word, SepBuf *buf, void *data) { - const char *pattern = data; - if (Str_Match(word, pattern)) - return addSpace; - if (addSpace && vpstate->varSpace) - Buf_AddByte(buf, vpstate->varSpace); - Buf_AddBytes(buf, strlen(word), word); - return TRUE; + const ModifyWord_SYSVSubstArgs *args = data; + char *rhs_expanded; + const char *rhs; + const char *percent; + + size_t match_len; + Boolean lhsPercent; + const char *match = Str_SYSVMatch(word, args->lhs, &match_len, &lhsPercent); + if (match == NULL) { + SepBuf_AddStr(buf, word); + return; + } + + /* Append rhs to the buffer, substituting the first '%' with the + * match, but only if the lhs had a '%' as well. */ + + rhs_expanded = Var_Subst(args->rhs, args->ctx, VARE_WANTRES); + + rhs = rhs_expanded; + percent = strchr(rhs, '%'); + + if (percent != NULL && lhsPercent) { + /* Copy the prefix of the replacement pattern */ + SepBuf_AddBytesBetween(buf, rhs, percent); + rhs = percent + 1; + } + if (percent != NULL || !lhsPercent) + SepBuf_AddBytes(buf, match, match_len); + + /* Append the suffix of the replacement pattern */ + SepBuf_AddStr(buf, rhs); + + free(rhs_expanded); } +#endif + -/* Callback function for VarModify to implement the :S,from,to, modifier. +typedef struct { + const char *lhs; + size_t lhsLen; + const char *rhs; + size_t rhsLen; + VarPatternFlags pflags; + Boolean matched; +} ModifyWord_SubstArgs; + +/* Callback for ModifyWords to implement the :S,from,to, modifier. * Perform a string substitution on the given word. */ -static Boolean -VarSubstitute(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *data) +static void +ModifyWord_Subst(const char *word, SepBuf *buf, void *data) { - int wordLen = strlen(word); - const char *cp; /* General pointer */ - VarPattern *pattern = data; + size_t wordLen = strlen(word); + ModifyWord_SubstArgs *args = data; + const char *match; - if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) != - (VAR_SUB_ONE|VAR_SUB_MATCHED)) { - /* - * Still substituting -- break it down into simple anchored cases - * and if none of them fits, perform the general substitution case. - */ - if ((pattern->flags & VAR_MATCH_START) && - (strncmp(word, pattern->lhs, pattern->leftLen) == 0)) { - /* - * Anchored at start and beginning of word matches pattern - */ - if ((pattern->flags & VAR_MATCH_END) && - (wordLen == pattern->leftLen)) { - /* - * Also anchored at end and matches to the end (word - * is same length as pattern) add space and rhs only - * if rhs is non-null. - */ - if (pattern->rightLen != 0) { - if (addSpace && vpstate->varSpace) { - Buf_AddByte(buf, vpstate->varSpace); - } - addSpace = TRUE; - Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); - } - pattern->flags |= VAR_SUB_MATCHED; - } else if (pattern->flags & VAR_MATCH_END) { - /* - * Doesn't match to end -- copy word wholesale - */ - goto nosub; - } else { - /* - * Matches at start but need to copy in trailing characters - */ - if ((pattern->rightLen + wordLen - pattern->leftLen) != 0) { - if (addSpace && vpstate->varSpace) { - Buf_AddByte(buf, vpstate->varSpace); - } - addSpace = TRUE; - } - Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); - Buf_AddBytes(buf, wordLen - pattern->leftLen, - (word + pattern->leftLen)); - pattern->flags |= VAR_SUB_MATCHED; - } - } else if (pattern->flags & VAR_MATCH_START) { - /* - * Had to match at start of word and didn't -- copy whole word. - */ + if ((args->pflags & VARP_SUB_ONE) && args->matched) + goto nosub; + + if (args->pflags & VARP_ANCHOR_START) { + if (wordLen < args->lhsLen || + memcmp(word, args->lhs, args->lhsLen) != 0) goto nosub; - } else if (pattern->flags & VAR_MATCH_END) { - /* - * Anchored at end, Find only place match could occur (leftLen - * characters from the end of the word) and see if it does. Note - * that because the $ will be left at the end of the lhs, we have - * to use strncmp. - */ - cp = word + (wordLen - pattern->leftLen); - if ((cp >= word) && - (strncmp(cp, pattern->lhs, pattern->leftLen) == 0)) { - /* - * Match found. If we will place characters in the buffer, - * add a space before hand as indicated by addSpace, then - * stuff in the initial, unmatched part of the word followed - * by the right-hand-side. - */ - if (((cp - word) + pattern->rightLen) != 0) { - if (addSpace && vpstate->varSpace) { - Buf_AddByte(buf, vpstate->varSpace); - } - addSpace = TRUE; - } - Buf_AddBytes(buf, cp - word, word); - Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); - pattern->flags |= VAR_SUB_MATCHED; - } else { - /* - * Had to match at end and didn't. Copy entire word. - */ + + if (args->pflags & VARP_ANCHOR_END) { + if (wordLen != args->lhsLen) goto nosub; - } + + /* :S,^whole$,replacement, */ + SepBuf_AddBytes(buf, args->rhs, args->rhsLen); + args->matched = TRUE; } else { - /* - * Pattern is unanchored: search for the pattern in the word using - * String_FindSubstring, copying unmatched portions and the - * right-hand-side for each match found, handling non-global - * substitutions correctly, etc. When the loop is done, any - * remaining part of the word (word and wordLen are adjusted - * accordingly through the loop) is copied straight into the - * buffer. - * addSpace is set FALSE as soon as a space is added to the - * buffer. - */ - Boolean done; - int origSize; - - done = FALSE; - origSize = Buf_Size(buf); - while (!done) { - cp = Str_FindSubstring(word, pattern->lhs); - if (cp != NULL) { - if (addSpace && (((cp - word) + pattern->rightLen) != 0)) { - Buf_AddByte(buf, vpstate->varSpace); - addSpace = FALSE; - } - Buf_AddBytes(buf, cp - word, word); - Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); - wordLen -= (cp - word) + pattern->leftLen; - word = cp + pattern->leftLen; - if (wordLen == 0) { - done = TRUE; - } - if ((pattern->flags & VAR_SUB_GLOBAL) == 0) { - done = TRUE; - } - pattern->flags |= VAR_SUB_MATCHED; - } else { - done = TRUE; - } - } - if (wordLen != 0) { - if (addSpace && vpstate->varSpace) { - Buf_AddByte(buf, vpstate->varSpace); - } - Buf_AddBytes(buf, wordLen, word); - } - /* - * If added characters to the buffer, need to add a space - * before we add any more. If we didn't add any, just return - * the previous value of addSpace. - */ - return (Buf_Size(buf) != origSize) || addSpace; + /* :S,^prefix,replacement, */ + SepBuf_AddBytes(buf, args->rhs, args->rhsLen); + SepBuf_AddBytes(buf, word + args->lhsLen, wordLen - args->lhsLen); + args->matched = TRUE; } - return addSpace; + return; } -nosub: - if (addSpace && vpstate->varSpace) { - Buf_AddByte(buf, vpstate->varSpace); + + if (args->pflags & VARP_ANCHOR_END) { + const char *start; + + if (wordLen < args->lhsLen) + goto nosub; + + start = word + (wordLen - args->lhsLen); + if (memcmp(start, args->lhs, args->lhsLen) != 0) + goto nosub; + + /* :S,suffix$,replacement, */ + SepBuf_AddBytesBetween(buf, word, start); + SepBuf_AddBytes(buf, args->rhs, args->rhsLen); + args->matched = TRUE; + return; } - Buf_AddBytes(buf, wordLen, word); - return TRUE; + + /* unanchored case, may match more than once */ + while ((match = Str_FindSubstring(word, args->lhs)) != NULL) { + SepBuf_AddBytesBetween(buf, word, match); + SepBuf_AddBytes(buf, args->rhs, args->rhsLen); + args->matched = TRUE; + wordLen -= (size_t)(match - word) + args->lhsLen; + word += (size_t)(match - word) + args->lhsLen; + if (wordLen == 0 || !(args->pflags & VARP_SUB_GLOBAL)) + break; + } +nosub: + SepBuf_AddBytes(buf, word, wordLen); } #ifndef NO_REGEX -/*- - *----------------------------------------------------------------------- - * VarREError -- - * Print the error caused by a regcomp or regexec call. - * - * Side Effects: - * An error gets printed. - * - *----------------------------------------------------------------------- - */ +/* Print the error caused by a regcomp or regexec call. */ static void VarREError(int reerr, regex_t *pat, const char *str) { - char *errbuf; - int errlen; - - errlen = regerror(reerr, pat, 0, 0); - errbuf = bmake_malloc(errlen); + size_t errlen = regerror(reerr, pat, 0, 0); + char *errbuf = bmake_malloc(errlen); regerror(reerr, pat, errbuf, errlen); Error("%s: %s", str, errbuf); free(errbuf); } -/* Callback function for VarModify to implement the :C/from/to/ modifier. +typedef struct { + regex_t re; + size_t nsub; + char *replace; + VarPatternFlags pflags; + Boolean matched; +} ModifyWord_SubstRegexArgs; + +/* Callback for ModifyWords to implement the :C/from/to/ modifier. * Perform a regex substitution on the given word. */ -static Boolean -VarRESubstitute(GNode *ctx MAKE_ATTR_UNUSED, - Var_Parse_State *vpstate MAKE_ATTR_UNUSED, - const char *word, Boolean addSpace, Buffer *buf, - void *data) +static void +ModifyWord_SubstRegex(const char *word, SepBuf *buf, void *data) { - VarREPattern *pat = data; + ModifyWord_SubstRegexArgs *args = data; int xrv; const char *wp = word; char *rp; - int added = 0; int flags = 0; + regmatch_t m[10]; -#define MAYBE_ADD_SPACE() \ - if (addSpace && !added) \ - Buf_AddByte(buf, ' '); \ - added = 1 - - if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) == - (VAR_SUB_ONE|VAR_SUB_MATCHED)) - xrv = REG_NOMATCH; - else { - tryagain: - xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, flags); - } + if ((args->pflags & VARP_SUB_ONE) && args->matched) + goto nosub; + +tryagain: + xrv = regexec(&args->re, wp, args->nsub, m, flags); switch (xrv) { case 0: - pat->flags |= VAR_SUB_MATCHED; - if (pat->matches[0].rm_so > 0) { - MAYBE_ADD_SPACE(); - Buf_AddBytes(buf, pat->matches[0].rm_so, wp); - } + args->matched = TRUE; + SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so); - for (rp = pat->replace; *rp; rp++) { - if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) { - MAYBE_ADD_SPACE(); - Buf_AddByte(buf, rp[1]); + for (rp = args->replace; *rp; rp++) { + if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) { + SepBuf_AddBytes(buf, rp + 1, 1); rp++; - } else if ((*rp == '&') || - ((*rp == '\\') && isdigit((unsigned char)rp[1]))) { - int n; - const char *subbuf; - int sublen; - char errstr[3]; - - if (*rp == '&') { - n = 0; - errstr[0] = '&'; - errstr[1] = '\0'; - } else { - n = rp[1] - '0'; - errstr[0] = '\\'; - errstr[1] = rp[1]; - errstr[2] = '\0'; - rp++; - } + continue; + } - if (n > pat->nsub) { - Error("No subexpression %s", &errstr[0]); - subbuf = ""; - sublen = 0; - } else if ((pat->matches[n].rm_so == -1) && - (pat->matches[n].rm_eo == -1)) { - Error("No match for subexpression %s", &errstr[0]); - subbuf = ""; - sublen = 0; - } else { - subbuf = wp + pat->matches[n].rm_so; - sublen = pat->matches[n].rm_eo - pat->matches[n].rm_so; - } + if (*rp == '&') { + SepBuf_AddBytesBetween(buf, wp + m[0].rm_so, wp + m[0].rm_eo); + continue; + } + + if (*rp != '\\' || !isdigit((unsigned char)rp[1])) { + SepBuf_AddBytes(buf, rp, 1); + continue; + } + + { /* \0 to \9 backreference */ + size_t n = (size_t)(rp[1] - '0'); + rp++; - if (sublen > 0) { - MAYBE_ADD_SPACE(); - Buf_AddBytes(buf, sublen, subbuf); + if (n >= args->nsub) { + Error("No subexpression \\%zu", n); + } else if (m[n].rm_so == -1 && m[n].rm_eo == -1) { + Error("No match for subexpression \\%zu", n); + } else { + SepBuf_AddBytesBetween(buf, wp + m[n].rm_so, + wp + m[n].rm_eo); } - } else { - MAYBE_ADD_SPACE(); - Buf_AddByte(buf, *rp); } } - wp += pat->matches[0].rm_eo; - if (pat->flags & VAR_SUB_GLOBAL) { + + wp += m[0].rm_eo; + if (args->pflags & VARP_SUB_GLOBAL) { flags |= REG_NOTBOL; - if (pat->matches[0].rm_so == 0 && pat->matches[0].rm_eo == 0) { - MAYBE_ADD_SPACE(); - Buf_AddByte(buf, *wp); + if (m[0].rm_so == 0 && m[0].rm_eo == 0) { + SepBuf_AddBytes(buf, wp, 1); wp++; - } if (*wp) goto tryagain; } if (*wp) { - MAYBE_ADD_SPACE(); - Buf_AddBytes(buf, strlen(wp), wp); + SepBuf_AddStr(buf, wp); } break; default: - VarREError(xrv, &pat->re, "Unexpected regex error"); + VarREError(xrv, &args->re, "Unexpected regex error"); /* fall through */ case REG_NOMATCH: - if (*wp) { - MAYBE_ADD_SPACE(); - Buf_AddBytes(buf, strlen(wp), wp); - } + nosub: + SepBuf_AddStr(buf, wp); break; } - return addSpace || added; } #endif -/* Callback function for VarModify to implement the :@var@...@ modifier of - * ODE make. We set the temp variable named in pattern.lhs to word and - * expand pattern.rhs. */ -static Boolean -VarLoopExpand(GNode *ctx MAKE_ATTR_UNUSED, - Var_Parse_State *vpstate MAKE_ATTR_UNUSED, - const char *word, Boolean addSpace, Buffer *buf, - void *data) +typedef struct { + GNode *ctx; + char *tvar; /* name of temporary variable */ + char *str; /* string to expand */ + VarEvalFlags eflags; +} ModifyWord_LoopArgs; + +/* Callback for ModifyWords to implement the :@var@...@ modifier of ODE make. */ +static void +ModifyWord_Loop(const char *word, SepBuf *buf, void *data) { - VarLoop *loop = data; + const ModifyWord_LoopArgs *args; char *s; - int slen; - if (*word) { - Var_Set_with_flags(loop->tvar, word, loop->ctxt, VAR_NO_EXPORT); - s = Var_Subst(NULL, loop->str, loop->ctxt, loop->flags); - if (DEBUG(VAR)) { - fprintf(debug_file, - "VarLoopExpand: in \"%s\", replace \"%s\" with \"%s\" " - "to \"%s\"\n", - word, loop->tvar, loop->str, s ? s : "(null)"); - } - if (s != NULL && *s != '\0') { - if (addSpace && *s != '\n') - Buf_AddByte(buf, ' '); - Buf_AddBytes(buf, (slen = strlen(s)), s); - addSpace = (slen > 0 && s[slen - 1] != '\n'); - } - free(s); - } - return addSpace; + if (word[0] == '\0') + return; + + args = data; + Var_Set_with_flags(args->tvar, word, args->ctx, VAR_NO_EXPORT); + s = Var_Subst(args->str, args->ctx, args->eflags); + + VAR_DEBUG("ModifyWord_Loop: in \"%s\", replace \"%s\" with \"%s\" " + "to \"%s\"\n", + word, args->tvar, args->str, s); + + if (s[0] == '\n' || (buf->buf.count > 0 && + buf->buf.buffer[buf->buf.count - 1] == '\n')) + buf->needSep = FALSE; + SepBuf_AddStr(buf, s); + free(s); } /*- - *----------------------------------------------------------------------- - * VarSelectWords -- - * Implements the :[start..end] modifier. - * This is a special case of VarModify since we want to be able - * to scan the list backwards if start > end. - * - * Input: - * str String whose words should be trimmed - * seldata words to select - * - * Results: - * A string of all the words selected. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- + * Implements the :[first..last] modifier. + * This is a special case of ModifyWords since we want to be able + * to scan the list backwards if first > last. */ static char * -VarSelectWords(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *str, VarSelectWords_t *seldata) +VarSelectWords(char sep, Boolean oneBigWord, const char *str, int first, + int last) { - Buffer buf; /* Buffer for the new string */ - Boolean addSpace; /* TRUE if need to add a space to the - * buffer before adding the trimmed - * word */ - char **av; /* word list */ - char *as; /* word list memory */ - int ac, i; + Words words; int start, end, step; + int i; - Buf_Init(&buf, 0); - addSpace = FALSE; - - if (vpstate->oneBigWord) { - /* fake what brk_string() would do if there were only one word */ - ac = 1; - av = bmake_malloc((ac + 1) * sizeof(char *)); - as = bmake_strdup(str); - av[0] = as; - av[1] = NULL; + SepBuf buf; + SepBuf_Init(&buf, sep); + + if (oneBigWord) { + /* fake what Str_Words() would do if there were only one word */ + words.len = 1; + words.words = bmake_malloc((words.len + 1) * sizeof(char *)); + words.freeIt = bmake_strdup(str); + words.words[0] = words.freeIt; + words.words[1] = NULL; } else { - av = brk_string(str, &ac, FALSE, &as); + words = Str_Words(str, FALSE); } /* - * Now sanitize seldata. - * If seldata->start or seldata->end are negative, convert them to - * the positive equivalents (-1 gets converted to argc, -2 gets - * converted to (argc-1), etc.). + * Now sanitize the given range. + * If first or last are negative, convert them to the positive equivalents + * (-1 gets converted to ac, -2 gets converted to (ac - 1), etc.). */ - if (seldata->start < 0) - seldata->start = ac + seldata->start + 1; - if (seldata->end < 0) - seldata->end = ac + seldata->end + 1; + if (first < 0) + first += (int)words.len + 1; + if (last < 0) + last += (int)words.len + 1; /* * We avoid scanning more of the list than we need to. */ - if (seldata->start > seldata->end) { - start = MIN(ac, seldata->start) - 1; - end = MAX(0, seldata->end - 1); + if (first > last) { + start = MIN((int)words.len, first) - 1; + end = MAX(0, last - 1); step = -1; } else { - start = MAX(0, seldata->start - 1); - end = MIN(ac, seldata->end); + start = MAX(0, first - 1); + end = MIN((int)words.len, last); step = 1; } - for (i = start; - (step < 0 && i >= end) || (step > 0 && i < end); - i += step) { - if (av[i] && *av[i]) { - if (addSpace && vpstate->varSpace) { - Buf_AddByte(&buf, vpstate->varSpace); - } - Buf_AddBytes(&buf, strlen(av[i]), av[i]); - addSpace = TRUE; - } + for (i = start; (step < 0) == (i >= end); i += step) { + SepBuf_AddStr(&buf, words.words[i]); + SepBuf_Sep(&buf); } - free(as); - free(av); + Words_Free(words); - return Buf_Destroy(&buf, FALSE); + return SepBuf_Destroy(&buf, FALSE); } -/* Callback function for VarModify to implement the :tA modifier. +/* Callback for ModifyWords to implement the :tA modifier. * Replace each word with the result of realpath() if successful. */ -static Boolean -VarRealpath(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *patternp MAKE_ATTR_UNUSED) +static void +ModifyWord_Realpath(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) { struct stat st; char rbuf[MAXPATHLEN]; - char *rp; - if (addSpace && vpstate->varSpace) - Buf_AddByte(buf, vpstate->varSpace); - rp = cached_realpath(word, rbuf); - if (rp && *rp == '/' && stat(rp, &st) == 0) + const char *rp = cached_realpath(word, rbuf); + if (rp != NULL && *rp == '/' && stat(rp, &st) == 0) word = rp; - Buf_AddBytes(buf, strlen(word), word); - return TRUE; + SepBuf_AddStr(buf, word); } /*- @@ -1704,273 +1535,116 @@ VarRealpath(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, * Modify each of the words of the passed string using the given function. * * Input: - * str String whose words should be trimmed - * modProc Function to use to modify them - * data Custom data for the modProc + * str String whose words should be modified + * modifyWord Function that modifies a single word + * modifyWord_args Custom arguments for modifyWord * * Results: * A string of all the words modified appropriately. - * - * Side Effects: - * None. - * *----------------------------------------------------------------------- */ static char * -VarModify(GNode *ctx, Var_Parse_State *vpstate, - const char *str, VarModifyCallback modProc, void *datum) +ModifyWords(GNode *ctx, char sep, Boolean oneBigWord, const char *str, + ModifyWordsCallback modifyWord, void *modifyWord_args) { - Buffer buf; /* Buffer for the new string */ - Boolean addSpace; /* TRUE if need to add a space to the - * buffer before adding the trimmed word */ - char **av; /* word list */ - char *as; /* word list memory */ - int ac, i; - - Buf_Init(&buf, 0); - addSpace = FALSE; - - if (vpstate->oneBigWord) { - /* fake what brk_string() would do if there were only one word */ - ac = 1; - av = bmake_malloc((ac + 1) * sizeof(char *)); - as = bmake_strdup(str); - av[0] = as; - av[1] = NULL; - } else { - av = brk_string(str, &ac, FALSE, &as); - } + SepBuf result; + Words words; + size_t i; - if (DEBUG(VAR)) { - fprintf(debug_file, "VarModify: split \"%s\" into %d words\n", - str, ac); + if (oneBigWord) { + SepBuf_Init(&result, sep); + modifyWord(str, &result, modifyWord_args); + return SepBuf_Destroy(&result, FALSE); } - for (i = 0; i < ac; i++) - addSpace = modProc(ctx, vpstate, av[i], addSpace, &buf, datum); - - free(as); - free(av); + SepBuf_Init(&result, sep); - return Buf_Destroy(&buf, FALSE); -} + words = Str_Words(str, FALSE); + VAR_DEBUG("ModifyWords: split \"%s\" into %zu words\n", str, words.len); -static int -VarWordCompare(const void *a, const void *b) -{ - int r = strcmp(*(const char * const *)a, *(const char * const *)b); - return r; -} - -static int -VarWordCompareReverse(const void *a, const void *b) -{ - int r = strcmp(*(const char * const *)b, *(const char * const *)a); - return r; -} - -/*- - *----------------------------------------------------------------------- - * VarOrder -- - * Order the words in the string. - * - * Input: - * str String whose words should be sorted. - * otype How to order: s - sort, x - random. - * - * Results: - * A string containing the words ordered. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -static char * -VarOrder(const char *str, const char otype) -{ - Buffer buf; /* Buffer for the new string */ - char **av; /* word list [first word does not count] */ - char *as; /* word list memory */ - int ac, i; - - Buf_Init(&buf, 0); - - av = brk_string(str, &ac, FALSE, &as); - - if (ac > 0) { - switch (otype) { - case 'r': /* reverse sort alphabetically */ - qsort(av, ac, sizeof(char *), VarWordCompareReverse); - break; - case 's': /* sort alphabetically */ - qsort(av, ac, sizeof(char *), VarWordCompare); - break; - case 'x': /* randomize */ - { - /* - * We will use [ac..2] range for mod factors. This will produce - * random numbers in [(ac-1)..0] interval, and minimal - * reasonable value for mod factor is 2 (the mod 1 will produce - * 0 with probability 1). - */ - for (i = ac - 1; i > 0; i--) { - int rndidx = random() % (i + 1); - char *t = av[i]; - av[i] = av[rndidx]; - av[rndidx] = t; - } - } - } + for (i = 0; i < words.len; i++) { + modifyWord(words.words[i], &result, modifyWord_args); + if (result.buf.count > 0) + SepBuf_Sep(&result); } - for (i = 0; i < ac; i++) { - Buf_AddBytes(&buf, strlen(av[i]), av[i]); - if (i != ac - 1) - Buf_AddByte(&buf, ' '); - } + Words_Free(words); - free(as); - free(av); - - return Buf_Destroy(&buf, FALSE); + return SepBuf_Destroy(&result, FALSE); } -/*- - *----------------------------------------------------------------------- - * VarUniq -- - * Remove adjacent duplicate words. - * - * Input: - * str String whose words should be sorted - * - * Results: - * A string containing the resulting words. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ static char * -VarUniq(const char *str) +Words_JoinFree(Words words) { - Buffer buf; /* Buffer for new string */ - char **av; /* List of words to affect */ - char *as; /* Word list memory */ - int ac, i, j; + Buffer buf; + size_t i; Buf_Init(&buf, 0); - av = brk_string(str, &ac, FALSE, &as); - - if (ac > 1) { - for (j = 0, i = 1; i < ac; i++) - if (strcmp(av[i], av[j]) != 0 && (++j != i)) - av[j] = av[i]; - ac = j + 1; - } - for (i = 0; i < ac; i++) { - Buf_AddBytes(&buf, strlen(av[i]), av[i]); - if (i != ac - 1) - Buf_AddByte(&buf, ' '); + for (i = 0; i < words.len; i++) { + if (i != 0) + Buf_AddByte(&buf, ' '); /* XXX: st->sep, for consistency */ + Buf_AddStr(&buf, words.words[i]); } - free(as); - free(av); + Words_Free(words); return Buf_Destroy(&buf, FALSE); } -/*- - *----------------------------------------------------------------------- - * VarRange -- - * Return an integer sequence - * - * Input: - * str String whose words provide default range - * ac range length, if 0 use str words - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ +/* Remove adjacent duplicate words. */ static char * -VarRange(const char *str, int ac) +VarUniq(const char *str) { - Buffer buf; /* Buffer for new string */ - char tmp[32]; /* each element */ - char **av; /* List of words to affect */ - char *as; /* Word list memory */ - int i, n; + Words words = Str_Words(str, FALSE); - Buf_Init(&buf, 0); - if (ac > 0) { - as = NULL; - av = NULL; - } else { - av = brk_string(str, &ac, FALSE, &as); + if (words.len > 1) { + size_t i, j; + for (j = 0, i = 1; i < words.len; i++) + if (strcmp(words.words[i], words.words[j]) != 0 && (++j != i)) + words.words[j] = words.words[i]; + words.len = j + 1; } - for (i = 0; i < ac; i++) { - n = snprintf(tmp, sizeof(tmp), "%d", 1 + i); - if (n >= (int)sizeof(tmp)) - break; - Buf_AddBytes(&buf, n, tmp); - if (i != ac - 1) - Buf_AddByte(&buf, ' '); - } - - free(as); - free(av); - return Buf_Destroy(&buf, FALSE); + return Words_JoinFree(words); } /*- - *----------------------------------------------------------------------- - * VarGetPattern -- - * During the parsing of a part of a modifier such as :S or :@, - * pass through the tstr looking for 1) escaped delimiters, - * '$'s and backslashes (place the escaped character in - * uninterpreted) and 2) unescaped $'s that aren't before - * the delimiter (expand the variable substitution unless flags - * has VAR_NOSUBST set). - * Return the expanded string or NULL if the delimiter was missing - * If pattern is specified, handle escaped ampersands, and replace - * unescaped ampersands with the lhs of the pattern. - * - * Results: - * A string of all the words modified appropriately. - * If length is specified, return the string length of the buffer - * If flags is specified and the last character of the pattern is a - * $ set the VAR_MATCH_END bit of flags. - * - * Side Effects: - * None. - *----------------------------------------------------------------------- + * Parse a part of a modifier such as the "from" and "to" in :S/from/to/ + * or the "var" or "replacement" in :@var@replacement+${var}@, up to and + * including the next unescaped delimiter. The delimiter, as well as the + * backslash or the dollar, can be escaped with a backslash. + * + * Return the parsed (and possibly expanded) string, or NULL if no delimiter + * was found. On successful return, the parsing position pp points right + * after the delimiter. The delimiter is not included in the returned + * value though. */ static char * -VarGetPattern(GNode *ctxt, Var_Parse_State *vpstate MAKE_ATTR_UNUSED, - VarPattern_Flags flags, const char **tstr, int delim, - VarPattern_Flags *vflags, int *length, VarPattern *pattern) -{ - const char *cp; - char *rstr; +ParseModifierPart( + const char **pp, /* The parsing position, updated upon return */ + int delim, /* Parsing stops at this delimiter */ + VarEvalFlags eflags, /* Flags for evaluating nested variables; + * if VARE_WANTRES is not set, the text is + * only parsed */ + GNode *ctxt, /* For looking up nested variables */ + size_t *out_length, /* Optionally stores the length of the returned + * string, just to save another strlen call. */ + VarPatternFlags *out_pflags,/* For the first part of the :S modifier, + * sets the VARP_ANCHOR_END flag if the last + * character of the pattern is a $. */ + ModifyWord_SubstArgs *subst /* For the second part of the :S modifier, + * allow ampersands to be escaped and replace + * unescaped ampersands with subst->lhs. */ +) { Buffer buf; - int junk; - int errnum = flags & VARF_UNDEFERR; + const char *p; + char *rstr; Buf_Init(&buf, 0); - if (length == NULL) - length = &junk; - -#define IS_A_MATCH(cp, delim) \ - ((cp[0] == '\\') && ((cp[1] == delim) || \ - (cp[1] == '\\') || (cp[1] == '$') || (pattern && (cp[1] == '&')))) /* * Skim through until the matching delimiter is found; @@ -1978,162 +1652,147 @@ VarGetPattern(GNode *ctxt, Var_Parse_State *vpstate MAKE_ATTR_UNUSED, * backslashes to quote the delimiter, $, and \, but don't * touch other backslashes. */ - for (cp = *tstr; *cp && (*cp != delim); cp++) { - if (IS_A_MATCH(cp, delim)) { - Buf_AddByte(&buf, cp[1]); - cp++; - } else if (*cp == '$') { - if (cp[1] == delim) { - if (vflags == NULL) - Buf_AddByte(&buf, *cp); - else - /* - * Unescaped $ at end of pattern => anchor - * pattern at end. - */ - *vflags |= VAR_MATCH_END; - } else { - if (vflags == NULL || (*vflags & VAR_NOSUBST) == 0) { - char *cp2; - int len; - void *freeIt; + p = *pp; + while (*p != '\0' && *p != delim) { + const char *varstart; + + Boolean is_escaped = p[0] == '\\' && ( + p[1] == delim || p[1] == '\\' || p[1] == '$' || + (p[1] == '&' && subst != NULL)); + if (is_escaped) { + Buf_AddByte(&buf, p[1]); + p += 2; + continue; + } - /* - * If unescaped dollar sign not before the - * delimiter, assume it's a variable - * substitution and recurse. - */ - cp2 = Var_Parse(cp, ctxt, errnum | (flags & VARF_WANTRES), - &len, &freeIt); - Buf_AddBytes(&buf, strlen(cp2), cp2); - free(freeIt); - cp += len - 1; - } else { - const char *cp2 = &cp[1]; - - if (*cp2 == PROPEN || *cp2 == BROPEN) { - /* - * Find the end of this variable reference - * and suck it in without further ado. - * It will be interpreted later. - */ - int have = *cp2; - int want = (*cp2 == PROPEN) ? PRCLOSE : BRCLOSE; - int depth = 1; - - for (++cp2; *cp2 != '\0' && depth > 0; ++cp2) { - if (cp2[-1] != '\\') { - if (*cp2 == have) - ++depth; - if (*cp2 == want) - --depth; - } - } - Buf_AddBytes(&buf, cp2 - cp, cp); - cp = --cp2; - } else - Buf_AddByte(&buf, *cp); + if (*p != '$') { /* Unescaped, simple text */ + if (subst != NULL && *p == '&') + Buf_AddBytes(&buf, subst->lhs, subst->lhsLen); + else + Buf_AddByte(&buf, *p); + p++; + continue; + } + + if (p[1] == delim) { /* Unescaped $ at end of pattern */ + if (out_pflags != NULL) + *out_pflags |= VARP_ANCHOR_END; + else + Buf_AddByte(&buf, *p); + p++; + continue; + } + + if (eflags & VARE_WANTRES) { /* Nested variable, evaluated */ + const char *cp2; + int len; + void *freeIt; + VarEvalFlags nested_eflags = eflags & ~(unsigned)VARE_ASSIGN; + + cp2 = Var_Parse(p, ctxt, nested_eflags, &len, &freeIt); + Buf_AddStr(&buf, cp2); + free(freeIt); + p += len; + continue; + } + + /* XXX: This whole block is very similar to Var_Parse without + * VARE_WANTRES. There may be subtle edge cases though that are + * not yet covered in the unit tests and that are parsed differently, + * depending on whether they are evaluated or not. + * + * This subtle difference is not documented in the manual page, + * neither is the difference between parsing :D and :M documented. + * No code should ever depend on these details, but who knows. */ + + varstart = p; /* Nested variable, only parsed */ + if (p[1] == PROPEN || p[1] == BROPEN) { + /* + * Find the end of this variable reference + * and suck it in without further ado. + * It will be interpreted later. + */ + int have = p[1]; + int want = have == PROPEN ? PRCLOSE : BRCLOSE; + int depth = 1; + + for (p += 2; *p != '\0' && depth > 0; p++) { + if (p[-1] != '\\') { + if (*p == have) + depth++; + if (*p == want) + depth--; } } - } else if (pattern && *cp == '&') - Buf_AddBytes(&buf, pattern->leftLen, pattern->lhs); - else - Buf_AddByte(&buf, *cp); + Buf_AddBytesBetween(&buf, varstart, p); + } else { + Buf_AddByte(&buf, *varstart); + p++; + } } - if (*cp != delim) { - *tstr = cp; - *length = 0; + if (*p != delim) { + *pp = p; return NULL; } - *tstr = ++cp; - *length = Buf_Size(&buf); + *pp = ++p; + if (out_length != NULL) + *out_length = Buf_Size(&buf); + rstr = Buf_Destroy(&buf, FALSE); - if (DEBUG(VAR)) - fprintf(debug_file, "Modifier pattern: \"%s\"\n", rstr); + VAR_DEBUG("Modifier part: \"%s\"\n", rstr); return rstr; } -/*- - *----------------------------------------------------------------------- - * VarQuote -- - * Quote shell meta-characters and space characters in the string - * if quoteDollar is set, also quote and double any '$' characters. - * - * Results: - * The quoted string - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ +/* Quote shell meta-characters and space characters in the string. + * If quoteDollar is set, also quote and double any '$' characters. */ static char * -VarQuote(char *str, Boolean quoteDollar) +VarQuote(const char *str, Boolean quoteDollar) { - - Buffer buf; - const char *newline; - size_t nlen; - - if ((newline = Shell_GetNewline()) == NULL) - newline = "\\\n"; - nlen = strlen(newline); - + char *res; + Buffer buf; Buf_Init(&buf, 0); for (; *str != '\0'; str++) { if (*str == '\n') { - Buf_AddBytes(&buf, nlen, newline); + const char *newline = Shell_GetNewline(); + if (newline == NULL) + newline = "\\\n"; + Buf_AddStr(&buf, newline); continue; } if (isspace((unsigned char)*str) || ismeta((unsigned char)*str)) Buf_AddByte(&buf, '\\'); Buf_AddByte(&buf, *str); if (quoteDollar && *str == '$') - Buf_AddBytes(&buf, 2, "\\$"); + Buf_AddStr(&buf, "\\$"); } - str = Buf_Destroy(&buf, FALSE); - if (DEBUG(VAR)) - fprintf(debug_file, "QuoteMeta: [%s]\n", str); - return str; + res = Buf_Destroy(&buf, FALSE); + VAR_DEBUG("QuoteMeta: [%s]\n", res); + return res; } -/*- - *----------------------------------------------------------------------- - * VarHash -- - * Hash the string using the MurmurHash3 algorithm. - * Output is computed using 32bit Little Endian arithmetic. - * - * Input: - * str String to modify - * - * Results: - * Hash value of str, encoded as 8 hex digits. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ +/* Compute the 32-bit hash of the given string, using the MurmurHash3 + * algorithm. Output is encoded as 8 hex digits, in Little Endian order. */ static char * VarHash(const char *str) { static const char hexdigits[16] = "0123456789abcdef"; - Buffer buf; - size_t len, len2; const unsigned char *ustr = (const unsigned char *)str; - uint32_t h, k, c1, c2; - h = 0x971e137bU; - c1 = 0x95543787U; - c2 = 0x2ad7eb25U; - len2 = strlen(str); + uint32_t h = 0x971e137bU; + uint32_t c1 = 0x95543787U; + uint32_t c2 = 0x2ad7eb25U; + size_t len2 = strlen(str); + + char *buf; + size_t i; + size_t len; for (len = len2; len; ) { - k = 0; + uint32_t k = 0; switch (len) { default: k = ((uint32_t)ustr[3] << 24) | @@ -2162,1483 +1821,1562 @@ VarHash(const char *str) h = h * 5 + 0x52dce729U; h ^= k; } - h ^= len2; + h ^= (uint32_t)len2; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; - Buf_Init(&buf, 0); - for (len = 0; len < 8; ++len) { - Buf_AddByte(&buf, hexdigits[h & 15]); + buf = bmake_malloc(9); + for (i = 0; i < 8; i++) { + buf[i] = hexdigits[h & 0x0f]; h >>= 4; } - - return Buf_Destroy(&buf, FALSE); + buf[8] = '\0'; + return buf; } static char * -VarStrftime(const char *fmt, int zulu, time_t utc) +VarStrftime(const char *fmt, Boolean zulu, time_t tim) { char buf[BUFSIZ]; - if (!utc) - time(&utc); + if (!tim) + time(&tim); if (!*fmt) fmt = "%c"; - strftime(buf, sizeof(buf), fmt, zulu ? gmtime(&utc) : localtime(&utc)); + strftime(buf, sizeof(buf), fmt, zulu ? gmtime(&tim) : localtime(&tim)); buf[sizeof(buf) - 1] = '\0'; return bmake_strdup(buf); } -typedef struct { - /* const parameters */ - int startc; - int endc; - Var *v; - GNode *ctxt; - int flags; - int *lengthPtr; - void **freePtr; - - /* read-write */ - char *nstr; - const char *tstr; - const char *start; - const char *cp; /* Secondary pointer into str (place marker - * for tstr) */ - char termc; /* Character which terminated scan */ - int cnt; /* Used to count brace pairs when variable in - * in parens or braces */ - char delim; - int modifier; /* that we are processing */ - Var_Parse_State parsestate; /* Flags passed to helper functions */ +/* The ApplyModifier functions all work in the same way. They get the + * current parsing position (pp) and parse the modifier from there. The + * modifier typically lasts until the next ':', or a closing '}' or ')' + * (taken from st->endc), or the end of the string (parse error). + * + * The high-level behavior of these functions is: + * + * 1. parse the modifier + * 2. evaluate the modifier + * 3. housekeeping + * + * Parsing the modifier + * + * If parsing succeeds, the parsing position *pp is updated to point to the + * first character following the modifier, which typically is either ':' or + * st->endc. + * + * If parsing fails because of a missing delimiter (as in the :S, :C or :@ + * modifiers), set st->missing_delim and return AMR_CLEANUP. + * + * If parsing fails because the modifier is unknown, return AMR_UNKNOWN to + * try the SysV modifier ${VAR:from=to} as fallback. This should only be + * done as long as there have been no side effects from evaluating nested + * variables, to avoid evaluating them more than once. In this case, the + * parsing position must not be updated. (XXX: Why not? The original parsing + * position is well-known in ApplyModifiers.) + * + * If parsing fails and the SysV modifier ${VAR:from=to} should not be used + * as a fallback, either issue an error message using Error or Parse_Error + * and then return AMR_CLEANUP, or return AMR_BAD for the default error + * message. Both of these return values will stop processing the variable + * expression. (XXX: As of 2020-08-23, evaluation of the whole string + * continues nevertheless after skipping a few bytes, which essentially is + * undefined behavior. Not in the sense of C, but still it's impossible to + * predict what happens in the parser.) + * + * Evaluating the modifier + * + * After parsing, the modifier is evaluated. The side effects from evaluating + * nested variable expressions in the modifier text often already happen + * during parsing though. + * + * Evaluating the modifier usually takes the current value of the variable + * expression from st->val, or the variable name from st->v->name and stores + * the result in st->newVal. + * + * If evaluating fails (as of 2020-08-23), an error message is printed using + * Error. This function has no side-effects, it really just prints the error + * message. Processing the expression continues as if everything were ok. + * XXX: This should be fixed by adding proper error handling to Var_Subst, + * Var_Parse, ApplyModifiers and ModifyWords. + * + * Housekeeping + * + * Some modifiers such as :D and :U turn undefined variables into useful + * variables (VAR_JUNK, VAR_KEEP). + * + * Some modifiers need to free some memory. + */ - /* result */ - char *newStr; /* New value to return */ +typedef struct { + const char startc; /* '\0' or '{' or '(' */ + const char endc; /* '\0' or '}' or ')' */ + Var * const v; + GNode * const ctxt; + const VarEvalFlags eflags; + + char *val; /* The old value of the expression, + * before applying the modifier, never NULL */ + char *newVal; /* The new value of the expression, + * after applying the modifier, never NULL */ + char missing_delim; /* For error reporting */ + + char sep; /* Word separator in expansions + * (see the :ts modifier) */ + Boolean oneBigWord; /* TRUE if some modifiers that otherwise split + * the variable value into words, like :S and + * :C, treat the variable value as a single big + * word, possibly containing spaces. */ } ApplyModifiersState; -/* we now have some modifiers with long names */ -#define STRMOD_MATCH(s, want, n) \ - (strncmp(s, want, n) == 0 && (s[n] == st->endc || s[n] == ':')) -#define STRMOD_MATCHX(s, want, n) \ - (strncmp(s, want, n) == 0 && \ - (s[n] == st->endc || s[n] == ':' || s[n] == '=')) -#define CHARMOD_MATCH(c) (c == st->endc || c == ':') - -/* :@var@...${var}...@ */ +typedef enum { + AMR_OK, /* Continue parsing */ + AMR_UNKNOWN, /* Not a match, try other modifiers as well */ + AMR_BAD, /* Error out with "Bad modifier" message */ + AMR_CLEANUP /* Error out, with "Unfinished modifier" + * if st->missing_delim is set. */ +} ApplyModifierResult; + +/* Test whether mod starts with modname, followed by a delimiter. */ static Boolean -ApplyModifier_At(ApplyModifiersState *st) { - VarLoop loop; - VarPattern_Flags vflags = VAR_NOSUBST; - - st->cp = ++(st->tstr); - st->delim = '@'; - loop.tvar = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - &vflags, &loop.tvarLen, NULL); - if (loop.tvar == NULL) - return FALSE; - - loop.str = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - &vflags, &loop.strLen, NULL); - if (loop.str == NULL) - return FALSE; +ModMatch(const char *mod, const char *modname, char endc) +{ + size_t n = strlen(modname); + return strncmp(mod, modname, n) == 0 && + (mod[n] == endc || mod[n] == ':'); +} - st->termc = *st->cp; - st->delim = '\0'; +/* Test whether mod starts with modname, followed by a delimiter or '='. */ +static inline Boolean +ModMatchEq(const char *mod, const char *modname, char endc) +{ + size_t n = strlen(modname); + return strncmp(mod, modname, n) == 0 && + (mod[n] == endc || mod[n] == ':' || mod[n] == '='); +} - loop.flags = st->flags & (VARF_UNDEFERR | VARF_WANTRES); - loop.ctxt = st->ctxt; - st->newStr = VarModify( - st->ctxt, &st->parsestate, st->nstr, VarLoopExpand, &loop); - Var_Delete(loop.tvar, st->ctxt); - free(loop.tvar); - free(loop.str); - return TRUE; +/* :@var@...${var}...@ */ +static ApplyModifierResult +ApplyModifier_Loop(const char **pp, ApplyModifiersState *st) +{ + ModifyWord_LoopArgs args; + char delim; + char prev_sep; + VarEvalFlags eflags = st->eflags & ~(unsigned)VARE_WANTRES; + + args.ctx = st->ctxt; + + (*pp)++; /* Skip the first '@' */ + delim = '@'; + args.tvar = ParseModifierPart(pp, delim, eflags, + st->ctxt, NULL, NULL, NULL); + if (args.tvar == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } + if (DEBUG(LINT) && strchr(args.tvar, '$') != NULL) { + Parse_Error(PARSE_FATAL, + "In the :@ modifier of \"%s\", the variable name \"%s\" " + "must not contain a dollar.", + st->v->name, args.tvar); + return AMR_CLEANUP; + } + + args.str = ParseModifierPart(pp, delim, eflags, + st->ctxt, NULL, NULL, NULL); + if (args.str == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } + + args.eflags = st->eflags & (VARE_UNDEFERR | VARE_WANTRES); + prev_sep = st->sep; + st->sep = ' '; /* XXX: should be st->sep for consistency */ + st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val, + ModifyWord_Loop, &args); + st->sep = prev_sep; + Var_Delete(args.tvar, st->ctxt); + free(args.tvar); + free(args.str); + return AMR_OK; } /* :Ddefined or :Uundefined */ -static void -ApplyModifier_Defined(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Defined(const char **pp, ApplyModifiersState *st) { - Buffer buf; /* Buffer for patterns */ - int nflags; + Buffer buf; + const char *p; - if (st->flags & VARF_WANTRES) { - int wantres; - if (*st->tstr == 'U') - wantres = ((st->v->flags & VAR_JUNK) != 0); - else - wantres = ((st->v->flags & VAR_JUNK) == 0); - nflags = st->flags & ~VARF_WANTRES; - if (wantres) - nflags |= VARF_WANTRES; - } else - nflags = st->flags; + VarEvalFlags eflags = st->eflags & ~(unsigned)VARE_WANTRES; + if (st->eflags & VARE_WANTRES) { + if ((**pp == 'D') == !(st->v->flags & VAR_JUNK)) + eflags |= VARE_WANTRES; + } - /* - * Pass through tstr looking for 1) escaped delimiters, - * '$'s and backslashes (place the escaped character in - * uninterpreted) and 2) unescaped $'s that aren't before - * the delimiter (expand the variable substitution). - * The result is left in the Buffer buf. - */ Buf_Init(&buf, 0); - for (st->cp = st->tstr + 1; - *st->cp != st->endc && *st->cp != ':' && *st->cp != '\0'; - st->cp++) { - if (*st->cp == '\\' && - (st->cp[1] == ':' || st->cp[1] == '$' || st->cp[1] == st->endc || - st->cp[1] == '\\')) { - Buf_AddByte(&buf, st->cp[1]); - st->cp++; - } else if (*st->cp == '$') { - /* - * If unescaped dollar sign, assume it's a - * variable substitution and recurse. - */ - char *cp2; - int len; - void *freeIt; + p = *pp + 1; + while (*p != st->endc && *p != ':' && *p != '\0') { + + /* Escaped delimiter or other special character */ + if (*p == '\\') { + char c = p[1]; + if (c == st->endc || c == ':' || c == '$' || c == '\\') { + Buf_AddByte(&buf, c); + p += 2; + continue; + } + } - cp2 = Var_Parse(st->cp, st->ctxt, nflags, &len, &freeIt); - Buf_AddBytes(&buf, strlen(cp2), cp2); + /* Nested variable expression */ + if (*p == '$') { + const char *cp2; + int len; + void *freeIt; + + cp2 = Var_Parse(p, st->ctxt, eflags, &len, &freeIt); + Buf_AddStr(&buf, cp2); free(freeIt); - st->cp += len - 1; - } else { - Buf_AddByte(&buf, *st->cp); + p += len; + continue; } - } - st->termc = *st->cp; + /* Ordinary text */ + Buf_AddByte(&buf, *p); + p++; + } + *pp = p; - if ((st->v->flags & VAR_JUNK) != 0) + if (st->v->flags & VAR_JUNK) st->v->flags |= VAR_KEEP; - if (nflags & VARF_WANTRES) { - st->newStr = Buf_Destroy(&buf, FALSE); + if (eflags & VARE_WANTRES) { + st->newVal = Buf_Destroy(&buf, FALSE); } else { - st->newStr = st->nstr; + st->newVal = st->val; Buf_Destroy(&buf, TRUE); } + return AMR_OK; } /* :gmtime */ -static Boolean -ApplyModifier_Gmtime(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Gmtime(const char **pp, ApplyModifiersState *st) { time_t utc; - char *ep; - st->cp = st->tstr + 1; /* make sure it is set */ - if (!STRMOD_MATCHX(st->tstr, "gmtime", 6)) - return FALSE; - if (st->tstr[6] == '=') { - utc = strtoul(&st->tstr[7], &ep, 10); - st->cp = ep; + const char *mod = *pp; + if (!ModMatchEq(mod, "gmtime", st->endc)) + return AMR_UNKNOWN; + + if (mod[6] == '=') { + char *ep; + utc = (time_t)strtoul(mod + 7, &ep, 10); + *pp = ep; } else { utc = 0; - st->cp = st->tstr + 6; + *pp = mod + 6; } - st->newStr = VarStrftime(st->nstr, 1, utc); - st->termc = *st->cp; - return TRUE; + st->newVal = VarStrftime(st->val, TRUE, utc); + return AMR_OK; } /* :localtime */ static Boolean -ApplyModifier_Localtime(ApplyModifiersState *st) +ApplyModifier_Localtime(const char **pp, ApplyModifiersState *st) { time_t utc; - char *ep; - st->cp = st->tstr + 1; /* make sure it is set */ - if (!STRMOD_MATCHX(st->tstr, "localtime", 9)) - return FALSE; + const char *mod = *pp; + if (!ModMatchEq(mod, "localtime", st->endc)) + return AMR_UNKNOWN; - if (st->tstr[9] == '=') { - utc = strtoul(&st->tstr[10], &ep, 10); - st->cp = ep; + if (mod[9] == '=') { + char *ep; + utc = (time_t)strtoul(mod + 10, &ep, 10); + *pp = ep; } else { utc = 0; - st->cp = st->tstr + 9; + *pp = mod + 9; } - st->newStr = VarStrftime(st->nstr, 0, utc); - st->termc = *st->cp; - return TRUE; + st->newVal = VarStrftime(st->val, FALSE, utc); + return AMR_OK; } /* :hash */ -static Boolean -ApplyModifier_Hash(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Hash(const char **pp, ApplyModifiersState *st) { - st->cp = st->tstr + 1; /* make sure it is set */ - if (!STRMOD_MATCH(st->tstr, "hash", 4)) - return FALSE; - st->newStr = VarHash(st->nstr); - st->cp = st->tstr + 4; - st->termc = *st->cp; - return TRUE; + if (!ModMatch(*pp, "hash", st->endc)) + return AMR_UNKNOWN; + + st->newVal = VarHash(st->val); + *pp += 4; + return AMR_OK; } /* :P */ -static void -ApplyModifier_Path(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Path(const char **pp, ApplyModifiersState *st) { GNode *gn; + char *path; - if ((st->v->flags & VAR_JUNK) != 0) + if (st->v->flags & VAR_JUNK) st->v->flags |= VAR_KEEP; + gn = Targ_FindNode(st->v->name, TARG_NOCREATE); if (gn == NULL || gn->type & OP_NOPATH) { - st->newStr = NULL; + path = NULL; } else if (gn->path) { - st->newStr = bmake_strdup(gn->path); + path = bmake_strdup(gn->path); } else { - st->newStr = Dir_FindFile(st->v->name, Suff_FindPath(gn)); + Lst searchPath = Suff_FindPath(gn); + path = Dir_FindFile(st->v->name, searchPath); } - if (!st->newStr) - st->newStr = bmake_strdup(st->v->name); - st->cp = ++st->tstr; - st->termc = *st->tstr; + if (path == NULL) + path = bmake_strdup(st->v->name); + st->newVal = path; + + (*pp)++; + return AMR_OK; } /* :!cmd! */ -static Boolean -ApplyModifier_Exclam(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Exclam(const char **pp, ApplyModifiersState *st) { - const char *emsg; - VarPattern pattern; - - pattern.flags = 0; - - st->delim = '!'; - emsg = NULL; - st->cp = ++st->tstr; - pattern.rhs = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - NULL, &pattern.rightLen, NULL); - if (pattern.rhs == NULL) - return FALSE; - if (st->flags & VARF_WANTRES) - st->newStr = Cmd_Exec(pattern.rhs, &emsg); + char delim; + char *cmd; + const char *errfmt; + + (*pp)++; + delim = '!'; + cmd = ParseModifierPart(pp, delim, st->eflags, st->ctxt, + NULL, NULL, NULL); + if (cmd == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } + + errfmt = NULL; + if (st->eflags & VARE_WANTRES) + st->newVal = Cmd_Exec(cmd, &errfmt); else - st->newStr = varNoError; - free(UNCONST(pattern.rhs)); - if (emsg) - Error(emsg, st->nstr); - st->termc = *st->cp; - st->delim = '\0'; + st->newVal = varNoError; + free(cmd); + + if (errfmt != NULL) + Error(errfmt, st->val); /* XXX: why still return AMR_OK? */ + if (st->v->flags & VAR_JUNK) st->v->flags |= VAR_KEEP; - return TRUE; + return AMR_OK; } -/* :range */ -static Boolean -ApplyModifier_Range(ApplyModifiersState *st) +/* The :range modifier generates an integer sequence as long as the words. + * The :range=7 modifier generates an integer sequence from 1 to 7. */ +static ApplyModifierResult +ApplyModifier_Range(const char **pp, ApplyModifiersState *st) { - int n; - char *ep; + size_t n; + Buffer buf; + size_t i; - st->cp = st->tstr + 1; /* make sure it is set */ - if (!STRMOD_MATCHX(st->tstr, "range", 5)) - return FALSE; + const char *mod = *pp; + if (!ModMatchEq(mod, "range", st->endc)) + return AMR_UNKNOWN; - if (st->tstr[5] == '=') { - n = strtoul(&st->tstr[6], &ep, 10); - st->cp = ep; + if (mod[5] == '=') { + char *ep; + n = (size_t)strtoul(mod + 6, &ep, 10); + *pp = ep; } else { n = 0; - st->cp = st->tstr + 5; + *pp = mod + 5; } - st->newStr = VarRange(st->nstr, n); - st->termc = *st->cp; - return TRUE; + + if (n == 0) { + Words words = Str_Words(st->val, FALSE); + n = words.len; + Words_Free(words); + } + + Buf_Init(&buf, 0); + + for (i = 0; i < n; i++) { + if (i != 0) + Buf_AddByte(&buf, ' '); /* XXX: st->sep, for consistency */ + Buf_AddInt(&buf, 1 + (int)i); + } + + st->newVal = Buf_Destroy(&buf, FALSE); + return AMR_OK; } /* :Mpattern or :Npattern */ -static void -ApplyModifier_Match(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Match(const char **pp, ApplyModifiersState *st) { - char *pattern; - const char *endpat; /* points just after end of pattern */ - char *cp2; - Boolean copy; /* pattern should be, or has been, copied */ - Boolean needSubst; - int nest; - - copy = FALSE; - needSubst = FALSE; - nest = 1; + const char *mod = *pp; + Boolean copy = FALSE; /* pattern should be, or has been, copied */ + Boolean needSubst = FALSE; + const char *endpat; + char *pattern; + ModifyWordsCallback callback; + /* - * In the loop below, ignore ':' unless we are at - * (or back to) the original brace level. - * XXX This will likely not work right if $() and ${} - * are intermixed. + * In the loop below, ignore ':' unless we are at (or back to) the + * original brace level. + * XXX This will likely not work right if $() and ${} are intermixed. */ - for (st->cp = st->tstr + 1; - *st->cp != '\0' && !(*st->cp == ':' && nest == 1); - st->cp++) { - if (*st->cp == '\\' && - (st->cp[1] == ':' || st->cp[1] == st->endc || - st->cp[1] == st->startc)) { + int nest = 0; + const char *p; + for (p = mod + 1; *p != '\0' && !(*p == ':' && nest == 0); p++) { + if (*p == '\\' && + (p[1] == ':' || p[1] == st->endc || p[1] == st->startc)) { if (!needSubst) copy = TRUE; - st->cp++; + p++; continue; } - if (*st->cp == '$') + if (*p == '$') needSubst = TRUE; - if (*st->cp == '(' || *st->cp == '{') - ++nest; - if (*st->cp == ')' || *st->cp == '}') { - --nest; - if (nest == 0) + if (*p == '(' || *p == '{') + nest++; + if (*p == ')' || *p == '}') { + nest--; + if (nest < 0) break; } } - st->termc = *st->cp; - endpat = st->cp; + *pp = p; + endpat = p; + if (copy) { - /* - * Need to compress the \:'s out of the pattern, so - * allocate enough room to hold the uncompressed - * pattern (note that st->cp started at st->tstr+1, so - * st->cp - st->tstr takes the null byte into account) and - * compress the pattern into the space. - */ - pattern = bmake_malloc(st->cp - st->tstr); - for (cp2 = pattern, st->cp = st->tstr + 1; - st->cp < endpat; - st->cp++, cp2++) { - if ((*st->cp == '\\') && (st->cp+1 < endpat) && - (st->cp[1] == ':' || st->cp[1] == st->endc)) - st->cp++; - *cp2 = *st->cp; - } - *cp2 = '\0'; - endpat = cp2; + char *dst; + const char *src; + + /* Compress the \:'s out of the pattern. */ + pattern = bmake_malloc((size_t)(endpat - (mod + 1)) + 1); + dst = pattern; + src = mod + 1; + for (; src < endpat; src++, dst++) { + if (src[0] == '\\' && src + 1 < endpat && + /* XXX: st->startc is missing here; see above */ + (src[1] == ':' || src[1] == st->endc)) + src++; + *dst = *src; + } + *dst = '\0'; + endpat = dst; } else { - /* - * Either Var_Subst or VarModify will need a - * nul-terminated string soon, so construct one now. - */ - pattern = bmake_strndup(st->tstr+1, endpat - (st->tstr + 1)); + pattern = bmake_strsedup(mod + 1, endpat); } + if (needSubst) { /* pattern contains embedded '$', so use Var_Subst to expand it. */ - cp2 = pattern; - pattern = Var_Subst(NULL, cp2, st->ctxt, st->flags); - free(cp2); - } - if (DEBUG(VAR)) - fprintf(debug_file, "Pattern[%s] for [%s] is [%s]\n", - st->v->name, st->nstr, pattern); - if (*st->tstr == 'M') { - st->newStr = VarModify(st->ctxt, &st->parsestate, st->nstr, VarMatch, - pattern); - } else { - st->newStr = VarModify(st->ctxt, &st->parsestate, st->nstr, VarNoMatch, - pattern); + char *old_pattern = pattern; + pattern = Var_Subst(pattern, st->ctxt, st->eflags); + free(old_pattern); } + + VAR_DEBUG("Pattern[%s] for [%s] is [%s]\n", st->v->name, st->val, pattern); + + callback = mod[0] == 'M' ? ModifyWord_Match : ModifyWord_NoMatch; + st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val, + callback, pattern); free(pattern); + return AMR_OK; } /* :S,from,to, */ -static Boolean -ApplyModifier_Subst(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Subst(const char **pp, ApplyModifiersState *st) { - VarPattern pattern; - Var_Parse_State tmpparsestate; + ModifyWord_SubstArgs args; + char *lhs, *rhs; + Boolean oneBigWord; + + char delim = (*pp)[1]; + if (delim == '\0') { + Error("Missing delimiter for :S modifier"); + (*pp)++; + return AMR_CLEANUP; + } + + *pp += 2; - pattern.flags = 0; - tmpparsestate = st->parsestate; - st->delim = st->tstr[1]; - st->tstr += 2; + args.pflags = 0; + args.matched = FALSE; /* * If pattern begins with '^', it is anchored to the * start of the word -- skip over it and flag pattern. */ - if (*st->tstr == '^') { - pattern.flags |= VAR_MATCH_START; - st->tstr += 1; + if (**pp == '^') { + args.pflags |= VARP_ANCHOR_START; + (*pp)++; } - st->cp = st->tstr; - pattern.lhs = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - &pattern.flags, &pattern.leftLen, NULL); - if (pattern.lhs == NULL) - return FALSE; + lhs = ParseModifierPart(pp, delim, st->eflags, st->ctxt, + &args.lhsLen, &args.pflags, NULL); + if (lhs == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } + args.lhs = lhs; - pattern.rhs = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - NULL, &pattern.rightLen, &pattern); - if (pattern.rhs == NULL) - return FALSE; + rhs = ParseModifierPart(pp, delim, st->eflags, st->ctxt, + &args.rhsLen, NULL, &args); + if (rhs == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } + args.rhs = rhs; - /* - * Check for global substitution. If 'g' after the final - * delimiter, substitution is global and is marked that - * way. - */ - for (;; st->cp++) { - switch (*st->cp) { + oneBigWord = st->oneBigWord; + for (;; (*pp)++) { + switch (**pp) { case 'g': - pattern.flags |= VAR_SUB_GLOBAL; + args.pflags |= VARP_SUB_GLOBAL; continue; case '1': - pattern.flags |= VAR_SUB_ONE; + args.pflags |= VARP_SUB_ONE; continue; case 'W': - tmpparsestate.oneBigWord = TRUE; + oneBigWord = TRUE; continue; } break; } - st->termc = *st->cp; - st->newStr = VarModify( - st->ctxt, &tmpparsestate, st->nstr, VarSubstitute, &pattern); + st->newVal = ModifyWords(st->ctxt, st->sep, oneBigWord, st->val, + ModifyWord_Subst, &args); - /* Free the two strings. */ - free(UNCONST(pattern.lhs)); - free(UNCONST(pattern.rhs)); - st->delim = '\0'; - return TRUE; + free(lhs); + free(rhs); + return AMR_OK; } #ifndef NO_REGEX + /* :C,from,to, */ -static Boolean -ApplyModifier_Regex(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Regex(const char **pp, ApplyModifiersState *st) { - VarREPattern pattern; - char *re; - int error; - Var_Parse_State tmpparsestate; - - pattern.flags = 0; - tmpparsestate = st->parsestate; - st->delim = st->tstr[1]; - st->tstr += 2; - - st->cp = st->tstr; - - re = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - NULL, NULL, NULL); - if (re == NULL) - return FALSE; + char *re; + ModifyWord_SubstRegexArgs args; + Boolean oneBigWord; + int error; - pattern.replace = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - NULL, NULL, NULL); - if (pattern.replace == NULL) { + char delim = (*pp)[1]; + if (delim == '\0') { + Error("Missing delimiter for :C modifier"); + (*pp)++; + return AMR_CLEANUP; + } + + *pp += 2; + + re = ParseModifierPart(pp, delim, st->eflags, st->ctxt, NULL, NULL, NULL); + if (re == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } + + args.replace = ParseModifierPart(pp, delim, st->eflags, st->ctxt, + NULL, NULL, NULL); + if (args.replace == NULL) { free(re); - return FALSE; + st->missing_delim = delim; + return AMR_CLEANUP; } - for (;; st->cp++) { - switch (*st->cp) { + args.pflags = 0; + args.matched = FALSE; + oneBigWord = st->oneBigWord; + for (;; (*pp)++) { + switch (**pp) { case 'g': - pattern.flags |= VAR_SUB_GLOBAL; + args.pflags |= VARP_SUB_GLOBAL; continue; case '1': - pattern.flags |= VAR_SUB_ONE; + args.pflags |= VARP_SUB_ONE; continue; case 'W': - tmpparsestate.oneBigWord = TRUE; + oneBigWord = TRUE; continue; } break; } - st->termc = *st->cp; - - error = regcomp(&pattern.re, re, REG_EXTENDED); + error = regcomp(&args.re, re, REG_EXTENDED); free(re); if (error) { - *st->lengthPtr = st->cp - st->start + 1; - VarREError(error, &pattern.re, "RE substitution error"); - free(pattern.replace); - return FALSE; + VarREError(error, &args.re, "Regex compilation error"); + free(args.replace); + return AMR_CLEANUP; + } + + args.nsub = args.re.re_nsub + 1; + if (args.nsub > 10) + args.nsub = 10; + st->newVal = ModifyWords(st->ctxt, st->sep, oneBigWord, st->val, + ModifyWord_SubstRegex, &args); + regfree(&args.re); + free(args.replace); + return AMR_OK; +} +#endif + +static void +ModifyWord_Copy(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) +{ + SepBuf_AddStr(buf, word); +} + +/* :ts<separator> */ +static ApplyModifierResult +ApplyModifier_ToSep(const char **pp, ApplyModifiersState *st) +{ + /* XXX: pp points to the 's', for historic reasons only. + * Changing this will influence the error messages. */ + const char *sep = *pp + 1; + + /* ":ts<any><endc>" or ":ts<any>:" */ + if (sep[0] != st->endc && (sep[1] == st->endc || sep[1] == ':')) { + st->sep = sep[0]; + *pp = sep + 1; + goto ok; } - pattern.nsub = pattern.re.re_nsub + 1; - if (pattern.nsub < 1) - pattern.nsub = 1; - if (pattern.nsub > 10) - pattern.nsub = 10; - pattern.matches = bmake_malloc(pattern.nsub * sizeof(regmatch_t)); - st->newStr = VarModify( - st->ctxt, &tmpparsestate, st->nstr, VarRESubstitute, &pattern); - regfree(&pattern.re); - free(pattern.replace); - free(pattern.matches); - st->delim = '\0'; - return TRUE; + /* ":ts<endc>" or ":ts:" */ + if (sep[0] == st->endc || sep[0] == ':') { + st->sep = '\0'; /* no separator */ + *pp = sep; + goto ok; + } + + /* ":ts<unrecognised><unrecognised>". */ + if (sep[0] != '\\') + return AMR_BAD; + + /* ":ts\n" */ + if (sep[1] == 'n') { + st->sep = '\n'; + *pp = sep + 2; + goto ok; + } + + /* ":ts\t" */ + if (sep[1] == 't') { + st->sep = '\t'; + *pp = sep + 2; + goto ok; + } + + /* ":ts\x40" or ":ts\100" */ + { + const char *numStart = sep + 1; + int base = 8; /* assume octal */ + char *end; + + if (sep[1] == 'x') { + base = 16; + numStart++; + } else if (!isdigit((unsigned char)sep[1])) + return AMR_BAD; /* ":ts<backslash><unrecognised>". */ + + st->sep = (char)strtoul(numStart, &end, base); + if (*end != ':' && *end != st->endc) + return AMR_BAD; + *pp = end; + } + +ok: + st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val, + ModifyWord_Copy, NULL); + return AMR_OK; } -#endif -/* :tA, :tu, :tl, etc. */ -static Boolean -ApplyModifier_To(ApplyModifiersState *st) +/* :tA, :tu, :tl, :ts<separator>, etc. */ +static ApplyModifierResult +ApplyModifier_To(const char **pp, ApplyModifiersState *st) { - st->cp = st->tstr + 1; /* make sure it is set */ - if (st->tstr[1] != st->endc && st->tstr[1] != ':') { - if (st->tstr[1] == 's') { - /* Use the char (if any) at st->tstr[2] as the word separator. */ - VarPattern pattern; - - if (st->tstr[2] != st->endc && - (st->tstr[3] == st->endc || st->tstr[3] == ':')) { - /* ":ts<unrecognised><endc>" or - * ":ts<unrecognised>:" */ - st->parsestate.varSpace = st->tstr[2]; - st->cp = st->tstr + 3; - } else if (st->tstr[2] == st->endc || st->tstr[2] == ':') { - /* ":ts<endc>" or ":ts:" */ - st->parsestate.varSpace = 0; /* no separator */ - st->cp = st->tstr + 2; - } else if (st->tstr[2] == '\\') { - const char *xp = &st->tstr[3]; - int base = 8; /* assume octal */ - - switch (st->tstr[3]) { - case 'n': - st->parsestate.varSpace = '\n'; - st->cp = st->tstr + 4; - break; - case 't': - st->parsestate.varSpace = '\t'; - st->cp = st->tstr + 4; - break; - case 'x': - base = 16; - xp++; - goto get_numeric; - case '0': - base = 0; - goto get_numeric; - default: - if (isdigit((unsigned char)st->tstr[3])) { - char *ep; - get_numeric: - st->parsestate.varSpace = strtoul(xp, &ep, base); - if (*ep != ':' && *ep != st->endc) - return FALSE; - st->cp = ep; - } else { - /* ":ts<backslash><unrecognised>". */ - return FALSE; - } - break; - } - } else { - /* Found ":ts<unrecognised><unrecognised>". */ - return FALSE; - } + const char *mod = *pp; + assert(mod[0] == 't'); - st->termc = *st->cp; + *pp = mod + 1; /* make sure it is set */ + if (mod[1] == st->endc || mod[1] == ':' || mod[1] == '\0') + return AMR_BAD; /* Found ":t<endc>" or ":t:". */ - /* - * We cannot be certain that VarModify will be used - even if there - * is a subsequent modifier, so do a no-op VarSubstitute now to for - * str to be re-expanded without the spaces. - */ - pattern.flags = VAR_SUB_ONE; - pattern.lhs = pattern.rhs = "\032"; - pattern.leftLen = pattern.rightLen = 1; - - st->newStr = VarModify( - st->ctxt, &st->parsestate, st->nstr, VarSubstitute, &pattern); - } else if (st->tstr[2] == st->endc || st->tstr[2] == ':') { - /* Check for two-character options: ":tu", ":tl" */ - if (st->tstr[1] == 'A') { /* absolute path */ - st->newStr = VarModify( - st->ctxt, &st->parsestate, st->nstr, VarRealpath, NULL); - st->cp = st->tstr + 2; - st->termc = *st->cp; - } else if (st->tstr[1] == 'u') { - char *dp = bmake_strdup(st->nstr); - for (st->newStr = dp; *dp; dp++) - *dp = toupper((unsigned char)*dp); - st->cp = st->tstr + 2; - st->termc = *st->cp; - } else if (st->tstr[1] == 'l') { - char *dp = bmake_strdup(st->nstr); - for (st->newStr = dp; *dp; dp++) - *dp = tolower((unsigned char)*dp); - st->cp = st->tstr + 2; - st->termc = *st->cp; - } else if (st->tstr[1] == 'W' || st->tstr[1] == 'w') { - st->parsestate.oneBigWord = (st->tstr[1] == 'W'); - st->newStr = st->nstr; - st->cp = st->tstr + 2; - st->termc = *st->cp; - } else { - /* Found ":t<unrecognised>:" or ":t<unrecognised><endc>". */ - return FALSE; - } - } else { - /* Found ":t<unrecognised><unrecognised>". */ - return FALSE; - } - } else { - /* Found ":t<endc>" or ":t:". */ - return FALSE; + if (mod[1] == 's') + return ApplyModifier_ToSep(pp, st); + + if (mod[2] != st->endc && mod[2] != ':') + return AMR_BAD; /* Found ":t<unrecognised><unrecognised>". */ + + /* Check for two-character options: ":tu", ":tl" */ + if (mod[1] == 'A') { /* absolute path */ + st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val, + ModifyWord_Realpath, NULL); + *pp = mod + 2; + return AMR_OK; } - return TRUE; + + if (mod[1] == 'u') { + size_t i; + size_t len = strlen(st->val); + st->newVal = bmake_malloc(len + 1); + for (i = 0; i < len + 1; i++) + st->newVal[i] = (char)toupper((unsigned char)st->val[i]); + *pp = mod + 2; + return AMR_OK; + } + + if (mod[1] == 'l') { + size_t i; + size_t len = strlen(st->val); + st->newVal = bmake_malloc(len + 1); + for (i = 0; i < len + 1; i++) + st->newVal[i] = (char)tolower((unsigned char)st->val[i]); + *pp = mod + 2; + return AMR_OK; + } + + if (mod[1] == 'W' || mod[1] == 'w') { + st->oneBigWord = mod[1] == 'W'; + st->newVal = st->val; + *pp = mod + 2; + return AMR_OK; + } + + /* Found ":t<unrecognised>:" or ":t<unrecognised><endc>". */ + return AMR_BAD; } /* :[#], :[1], etc. */ -static int -ApplyModifier_Words(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Words(const char **pp, ApplyModifiersState *st) { - /* - * Look for the closing ']', recursively - * expanding any embedded variables. - * - * estr is a pointer to the expanded result, - * which we must free(). - */ + char delim; char *estr; + char *ep; + int first, last; - st->cp = st->tstr + 1; /* point to char after '[' */ - st->delim = ']'; /* look for closing ']' */ - estr = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - NULL, NULL, NULL); - if (estr == NULL) - return 'c'; /* report missing ']' */ - /* now st->cp points just after the closing ']' */ - st->delim = '\0'; - if (st->cp[0] != ':' && st->cp[0] != st->endc) { - /* Found junk after ']' */ - free(estr); - return 'b'; + (*pp)++; /* skip the '[' */ + delim = ']'; /* look for closing ']' */ + estr = ParseModifierPart(pp, delim, st->eflags, st->ctxt, + NULL, NULL, NULL); + if (estr == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; } - if (estr[0] == '\0') { - /* Found empty square brackets in ":[]". */ - free(estr); - return 'b'; - } else if (estr[0] == '#' && estr[1] == '\0') { - /* Found ":[#]" */ - /* - * We will need enough space for the decimal - * representation of an int. We calculate the - * space needed for the octal representation, - * and add enough slop to cope with a '-' sign - * (which should never be needed) and a '\0' - * string terminator. - */ - int newStrSize = (sizeof(int) * CHAR_BIT + 2) / 3 + 2; + /* now *pp points just after the closing ']' */ + if (**pp != ':' && **pp != st->endc) + goto bad_modifier; /* Found junk after ']' */ + + if (estr[0] == '\0') + goto bad_modifier; /* empty square brackets in ":[]". */ - st->newStr = bmake_malloc(newStrSize); - if (st->parsestate.oneBigWord) { - strncpy(st->newStr, "1", newStrSize); + if (estr[0] == '#' && estr[1] == '\0') { /* Found ":[#]" */ + if (st->oneBigWord) { + st->newVal = bmake_strdup("1"); } else { - /* XXX: brk_string() is a rather expensive - * way of counting words. */ - char **av; - char *as; - int ac; - - av = brk_string(st->nstr, &ac, FALSE, &as); - snprintf(st->newStr, newStrSize, "%d", ac); - free(as); - free(av); + Buffer buf; + + Words words = Str_Words(st->val, FALSE); + size_t ac = words.len; + Words_Free(words); + + Buf_Init(&buf, 4); /* 3 digits + '\0' is usually enough */ + Buf_AddInt(&buf, (int)ac); + st->newVal = Buf_Destroy(&buf, FALSE); } - st->termc = *st->cp; - free(estr); - return 0; - } else if (estr[0] == '*' && estr[1] == '\0') { + goto ok; + } + + if (estr[0] == '*' && estr[1] == '\0') { /* Found ":[*]" */ - st->parsestate.oneBigWord = TRUE; - st->newStr = st->nstr; - st->termc = *st->cp; - free(estr); - return 0; - } else if (estr[0] == '@' && estr[1] == '\0') { + st->oneBigWord = TRUE; + st->newVal = st->val; + goto ok; + } + + if (estr[0] == '@' && estr[1] == '\0') { /* Found ":[@]" */ - st->parsestate.oneBigWord = FALSE; - st->newStr = st->nstr; - st->termc = *st->cp; - free(estr); - return 0; - } else { - char *ep; - /* - * We expect estr to contain a single - * integer for :[N], or two integers - * separated by ".." for :[start..end]. - */ - VarSelectWords_t seldata = { 0, 0 }; - - seldata.start = strtol(estr, &ep, 0); - if (ep == estr) { - /* Found junk instead of a number */ - free(estr); - return 'b'; - } else if (ep[0] == '\0') { - /* Found only one integer in :[N] */ - seldata.end = seldata.start; - } else if (ep[0] == '.' && ep[1] == '.' && ep[2] != '\0') { - /* Expecting another integer after ".." */ - ep += 2; - seldata.end = strtol(ep, &ep, 0); - if (ep[0] != '\0') { - /* Found junk after ".." */ - free(estr); - return 'b'; - } - } else { - /* Found junk instead of ".." */ - free(estr); - return 'b'; - } - /* - * Now seldata is properly filled in, - * but we still have to check for 0 as - * a special case. - */ - if (seldata.start == 0 && seldata.end == 0) { - /* ":[0]" or perhaps ":[0..0]" */ - st->parsestate.oneBigWord = TRUE; - st->newStr = st->nstr; - st->termc = *st->cp; - free(estr); - return 0; - } else if (seldata.start == 0 || seldata.end == 0) { - /* ":[0..N]" or ":[N..0]" */ - free(estr); - return 'b'; - } - /* Normal case: select the words described by seldata. */ - st->newStr = VarSelectWords( - st->ctxt, &st->parsestate, st->nstr, &seldata); + st->oneBigWord = FALSE; + st->newVal = st->val; + goto ok; + } + + /* + * We expect estr to contain a single integer for :[N], or two integers + * separated by ".." for :[start..end]. + */ + first = (int)strtol(estr, &ep, 0); + if (ep == estr) /* Found junk instead of a number */ + goto bad_modifier; + + if (ep[0] == '\0') { /* Found only one integer in :[N] */ + last = first; + } else if (ep[0] == '.' && ep[1] == '.' && ep[2] != '\0') { + /* Expecting another integer after ".." */ + ep += 2; + last = (int)strtol(ep, &ep, 0); + if (ep[0] != '\0') /* Found junk after ".." */ + goto bad_modifier; + } else + goto bad_modifier; /* Found junk instead of ".." */ - st->termc = *st->cp; - free(estr); - return 0; + /* + * Now seldata is properly filled in, but we still have to check for 0 as + * a special case. + */ + if (first == 0 && last == 0) { + /* ":[0]" or perhaps ":[0..0]" */ + st->oneBigWord = TRUE; + st->newVal = st->val; + goto ok; } + + /* ":[0..N]" or ":[N..0]" */ + if (first == 0 || last == 0) + goto bad_modifier; + + /* Normal case: select the words described by seldata. */ + st->newVal = VarSelectWords(st->sep, st->oneBigWord, st->val, first, last); + +ok: + free(estr); + return AMR_OK; + +bad_modifier: + free(estr); + return AMR_BAD; } -/* :O or :Ox */ -static Boolean -ApplyModifier_Order(ApplyModifiersState *st) +static int +str_cmp_asc(const void *a, const void *b) +{ + return strcmp(*(const char * const *)a, *(const char * const *)b); +} + +static int +str_cmp_desc(const void *a, const void *b) +{ + return strcmp(*(const char * const *)b, *(const char * const *)a); +} + +/* :O (order ascending) or :Or (order descending) or :Ox (shuffle) */ +static ApplyModifierResult +ApplyModifier_Order(const char **pp, ApplyModifiersState *st) { - char otype; - - st->cp = st->tstr + 1; /* skip to the rest in any case */ - if (st->tstr[1] == st->endc || st->tstr[1] == ':') { - otype = 's'; - st->termc = *st->cp; - } else if ((st->tstr[1] == 'r' || st->tstr[1] == 'x') && - (st->tstr[2] == st->endc || st->tstr[2] == ':')) { - otype = st->tstr[1]; - st->cp = st->tstr + 2; - st->termc = *st->cp; + const char *mod = (*pp)++; /* skip past the 'O' in any case */ + + Words words = Str_Words(st->val, FALSE); + + if (mod[1] == st->endc || mod[1] == ':') { + /* :O sorts ascending */ + qsort(words.words, words.len, sizeof(char *), str_cmp_asc); + + } else if ((mod[1] == 'r' || mod[1] == 'x') && + (mod[2] == st->endc || mod[2] == ':')) { + (*pp)++; + + if (mod[1] == 'r') { + /* :Or sorts descending */ + qsort(words.words, words.len, sizeof(char *), str_cmp_desc); + + } else { + /* :Ox shuffles + * + * We will use [ac..2] range for mod factors. This will produce + * random numbers in [(ac-1)..0] interval, and minimal + * reasonable value for mod factor is 2 (the mod 1 will produce + * 0 with probability 1). + */ + size_t i; + for (i = words.len - 1; i > 0; i--) { + size_t rndidx = (size_t)random() % (i + 1); + char *t = words.words[i]; + words.words[i] = words.words[rndidx]; + words.words[rndidx] = t; + } + } } else { - return FALSE; + Words_Free(words); + return AMR_BAD; } - st->newStr = VarOrder(st->nstr, otype); - return TRUE; + + st->newVal = Words_JoinFree(words); + return AMR_OK; } /* :? then : else */ -static Boolean -ApplyModifier_IfElse(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_IfElse(const char **pp, ApplyModifiersState *st) { - VarPattern pattern; - Boolean value; - int cond_rc; - VarPattern_Flags lhs_flags, rhs_flags; + char delim; + char *then_expr, *else_expr; + + Boolean value = FALSE; + VarEvalFlags then_eflags = st->eflags & ~(unsigned)VARE_WANTRES; + VarEvalFlags else_eflags = st->eflags & ~(unsigned)VARE_WANTRES; - /* find ':', and then substitute accordingly */ - if (st->flags & VARF_WANTRES) { + int cond_rc = COND_PARSE; /* anything other than COND_INVALID */ + if (st->eflags & VARE_WANTRES) { cond_rc = Cond_EvalExpression(NULL, st->v->name, &value, 0, FALSE); - if (cond_rc == COND_INVALID) { - lhs_flags = rhs_flags = VAR_NOSUBST; - } else if (value) { - lhs_flags = 0; - rhs_flags = VAR_NOSUBST; - } else { - lhs_flags = VAR_NOSUBST; - rhs_flags = 0; - } - } else { - /* we are just consuming and discarding */ - cond_rc = value = 0; - lhs_flags = rhs_flags = VAR_NOSUBST; + if (cond_rc != COND_INVALID && value) + then_eflags |= VARE_WANTRES; + if (cond_rc != COND_INVALID && !value) + else_eflags |= VARE_WANTRES; } - pattern.flags = 0; - - st->cp = ++st->tstr; - st->delim = ':'; - pattern.lhs = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - &lhs_flags, &pattern.leftLen, NULL); - if (pattern.lhs == NULL) - return FALSE; - /* BROPEN or PROPEN */ - st->delim = st->endc; - pattern.rhs = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - &rhs_flags, &pattern.rightLen, NULL); - if (pattern.rhs == NULL) - return FALSE; + (*pp)++; /* skip past the '?' */ + delim = ':'; + then_expr = ParseModifierPart(pp, delim, then_eflags, st->ctxt, + NULL, NULL, NULL); + if (then_expr == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } - st->termc = *--st->cp; - st->delim = '\0'; + delim = st->endc; /* BRCLOSE or PRCLOSE */ + else_expr = ParseModifierPart(pp, delim, else_eflags, st->ctxt, + NULL, NULL, NULL); + if (else_expr == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } + + (*pp)--; if (cond_rc == COND_INVALID) { Error("Bad conditional expression `%s' in %s?%s:%s", - st->v->name, st->v->name, pattern.lhs, pattern.rhs); - return FALSE; + st->v->name, st->v->name, then_expr, else_expr); + return AMR_CLEANUP; } if (value) { - st->newStr = UNCONST(pattern.lhs); - free(UNCONST(pattern.rhs)); + st->newVal = then_expr; + free(else_expr); } else { - st->newStr = UNCONST(pattern.rhs); - free(UNCONST(pattern.lhs)); + st->newVal = else_expr; + free(then_expr); } if (st->v->flags & VAR_JUNK) st->v->flags |= VAR_KEEP; - return TRUE; + return AMR_OK; } -/* "::=", "::!=", "::+=", or "::?=" */ -static int -ApplyModifier_Assign(ApplyModifiersState *st) +/* + * The ::= modifiers actually assign a value to the variable. + * Their main purpose is in supporting modifiers of .for loop + * iterators and other obscure uses. They always expand to + * nothing. In a target rule that would otherwise expand to an + * empty line they can be preceded with @: to keep make happy. + * Eg. + * + * foo: .USE + * .for i in ${.TARGET} ${.TARGET:R}.gz + * @: ${t::=$i} + * @echo blah ${t:T} + * .endfor + * + * ::=<str> Assigns <str> as the new value of variable. + * ::?=<str> Assigns <str> as value of variable if + * it was not already set. + * ::+=<str> Appends <str> to variable. + * ::!=<cmd> Assigns output of <cmd> as the new value of + * variable. + */ +static ApplyModifierResult +ApplyModifier_Assign(const char **pp, ApplyModifiersState *st) { - if (st->tstr[1] == '=' || - (st->tstr[2] == '=' && - (st->tstr[1] == '!' || st->tstr[1] == '+' || st->tstr[1] == '?'))) { - GNode *v_ctxt; /* context where v belongs */ - const char *emsg; - char *sv_name; - VarPattern pattern; - int how; - VarPattern_Flags vflags; - - if (st->v->name[0] == 0) - return 'b'; - - v_ctxt = st->ctxt; - sv_name = NULL; - ++st->tstr; - if (st->v->flags & VAR_JUNK) { - /* - * We need to bmake_strdup() it incase - * VarGetPattern() recurses. - */ - sv_name = st->v->name; - st->v->name = bmake_strdup(st->v->name); - } else if (st->ctxt != VAR_GLOBAL) { - Var *gv = VarFind(st->v->name, st->ctxt, 0); - if (gv == NULL) - v_ctxt = VAR_GLOBAL; - else - VarFreeEnv(gv, TRUE); - } + GNode *v_ctxt; + char *sv_name; + char delim; + char *val; + + const char *mod = *pp; + const char *op = mod + 1; + if (!(op[0] == '=' || + (op[1] == '=' && + (op[0] == '!' || op[0] == '+' || op[0] == '?')))) + return AMR_UNKNOWN; /* "::<unrecognised>" */ + + + if (st->v->name[0] == 0) { + *pp = mod + 1; + return AMR_BAD; + } + + v_ctxt = st->ctxt; /* context where v belongs */ + sv_name = NULL; + if (st->v->flags & VAR_JUNK) { + /* + * We need to bmake_strdup() it in case ParseModifierPart() recurses. + */ + sv_name = st->v->name; + st->v->name = bmake_strdup(st->v->name); + } else if (st->ctxt != VAR_GLOBAL) { + Var *gv = VarFind(st->v->name, st->ctxt, 0); + if (gv == NULL) + v_ctxt = VAR_GLOBAL; + else + VarFreeEnv(gv, TRUE); + } + + switch (op[0]) { + case '+': + case '?': + case '!': + *pp = mod + 3; + break; + default: + *pp = mod + 2; + break; + } - switch ((how = *st->tstr)) { + delim = st->startc == PROPEN ? PRCLOSE : BRCLOSE; + val = ParseModifierPart(pp, delim, st->eflags, st->ctxt, NULL, NULL, NULL); + if (st->v->flags & VAR_JUNK) { + /* restore original name */ + free(st->v->name); + st->v->name = sv_name; + } + if (val == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } + + (*pp)--; + + if (st->eflags & VARE_WANTRES) { + switch (op[0]) { case '+': - case '?': - case '!': - st->cp = &st->tstr[2]; + Var_Append(st->v->name, val, v_ctxt); break; - default: - st->cp = ++st->tstr; + case '!': { + const char *errfmt; + char *cmd_output = Cmd_Exec(val, &errfmt); + if (errfmt) + Error(errfmt, val); + else + Var_Set(st->v->name, cmd_output, v_ctxt); + free(cmd_output); break; } - st->delim = st->startc == PROPEN ? PRCLOSE : BRCLOSE; - pattern.flags = 0; - - vflags = (st->flags & VARF_WANTRES) ? 0 : VAR_NOSUBST; - pattern.rhs = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - &vflags, &pattern.rightLen, NULL); - if (st->v->flags & VAR_JUNK) { - /* restore original name */ - free(st->v->name); - st->v->name = sv_name; - } - if (pattern.rhs == NULL) - return 'c'; - - st->termc = *--st->cp; - st->delim = '\0'; - - if (st->flags & VARF_WANTRES) { - switch (how) { - case '+': - Var_Append(st->v->name, pattern.rhs, v_ctxt); - break; - case '!': - st->newStr = Cmd_Exec(pattern.rhs, &emsg); - if (emsg) - Error(emsg, st->nstr); - else - Var_Set(st->v->name, st->newStr, v_ctxt); - free(st->newStr); - break; - case '?': - if ((st->v->flags & VAR_JUNK) == 0) - break; - /* FALLTHROUGH */ - default: - Var_Set(st->v->name, pattern.rhs, v_ctxt); + case '?': + if (!(st->v->flags & VAR_JUNK)) break; - } + /* FALLTHROUGH */ + default: + Var_Set(st->v->name, val, v_ctxt); + break; } - free(UNCONST(pattern.rhs)); - st->newStr = varNoError; - return 0; } - return 'd'; /* "::<unrecognised>" */ + free(val); + st->newVal = varNoError; /* XXX: varNoError is kind of an error, + * the intention here is to just return + * an empty string. */ + return AMR_OK; } /* remember current value */ -static Boolean -ApplyModifier_Remember(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Remember(const char **pp, ApplyModifiersState *st) { - st->cp = st->tstr + 1; /* make sure it is set */ - if (!STRMOD_MATCHX(st->tstr, "_", 1)) - return FALSE; - - if (st->tstr[1] == '=') { - char *np; - int n; - - st->cp++; - n = strcspn(st->cp, ":)}"); - np = bmake_strndup(st->cp, n + 1); - np[n] = '\0'; - st->cp = st->tstr + 2 + n; - Var_Set(np, st->nstr, st->ctxt); - free(np); + const char *mod = *pp; + if (!ModMatchEq(mod, "_", st->endc)) + return AMR_UNKNOWN; + + if (mod[1] == '=') { + size_t n = strcspn(mod + 2, ":)}"); + char *name = bmake_strldup(mod + 2, n); + Var_Set(name, st->val, st->ctxt); + free(name); + *pp = mod + 2 + n; } else { - Var_Set("_", st->nstr, st->ctxt); + Var_Set("_", st->val, st->ctxt); + *pp = mod + 1; } - st->newStr = st->nstr; - st->termc = *st->cp; - return TRUE; + st->newVal = st->val; + return AMR_OK; +} + +/* Apply the given function to each word of the variable value. */ +static ApplyModifierResult +ApplyModifier_WordFunc(const char **pp, ApplyModifiersState *st, + ModifyWordsCallback modifyWord) +{ + char delim = (*pp)[1]; + if (delim != st->endc && delim != ':') + return AMR_UNKNOWN; + + st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, + st->val, modifyWord, NULL); + (*pp)++; + return AMR_OK; } #ifdef SYSVVARSUB /* :from=to */ -static int -ApplyModifier_SysV(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_SysV(const char **pp, ApplyModifiersState *st) { - /* - * This can either be a bogus modifier or a System-V - * substitution command. - */ - VarPattern pattern; - Boolean eqFound = FALSE; + char delim; + char *lhs, *rhs; - pattern.flags = 0; + const char *mod = *pp; + Boolean eqFound = FALSE; /* * First we make a pass through the string trying * to verify it is a SYSV-make-style translation: * it must be: <string1>=<string2>) */ - st->cp = st->tstr; - st->cnt = 1; - while (*st->cp != '\0' && st->cnt) { - if (*st->cp == '=') { + int nest = 1; + const char *next = mod; + while (*next != '\0' && nest > 0) { + if (*next == '=') { eqFound = TRUE; /* continue looking for st->endc */ - } else if (*st->cp == st->endc) - st->cnt--; - else if (*st->cp == st->startc) - st->cnt++; - if (st->cnt) - st->cp++; + } else if (*next == st->endc) + nest--; + else if (*next == st->startc) + nest++; + if (nest > 0) + next++; + } + if (*next != st->endc || !eqFound) + return AMR_UNKNOWN; + + delim = '='; + *pp = mod; + lhs = ParseModifierPart(pp, delim, st->eflags, st->ctxt, NULL, NULL, NULL); + if (lhs == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } + + delim = st->endc; + rhs = ParseModifierPart(pp, delim, st->eflags, st->ctxt, NULL, NULL, NULL); + if (rhs == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; } - if (*st->cp != st->endc || !eqFound) - return 0; - - st->delim = '='; - st->cp = st->tstr; - pattern.lhs = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - &pattern.flags, &pattern.leftLen, NULL); - if (pattern.lhs == NULL) - return 'c'; - - st->delim = st->endc; - pattern.rhs = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - NULL, &pattern.rightLen, &pattern); - if (pattern.rhs == NULL) - return 'c'; /* * SYSV modifications happen through the whole * string. Note the pattern is anchored at the end. */ - st->termc = *--st->cp; - st->delim = '\0'; - if (pattern.leftLen == 0 && *st->nstr == '\0') { - st->newStr = st->nstr; /* special case */ + (*pp)--; + if (lhs[0] == '\0' && *st->val == '\0') { + st->newVal = st->val; /* special case */ } else { - st->newStr = VarModify( - st->ctxt, &st->parsestate, st->nstr, VarSYSVMatch, &pattern); + ModifyWord_SYSVSubstArgs args = {st->ctxt, lhs, rhs}; + st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val, + ModifyWord_SYSVSubst, &args); } - free(UNCONST(pattern.lhs)); - free(UNCONST(pattern.rhs)); - return '='; + free(lhs); + free(rhs); + return AMR_OK; } #endif -/* - * Now we need to apply any modifiers the user wants applied. - * These are: - * :M<pattern> words which match the given <pattern>. - * <pattern> is of the standard file - * wildcarding form. - * :N<pattern> words which do not match the given <pattern>. - * :S<d><pat1><d><pat2><d>[1gW] - * Substitute <pat2> for <pat1> in the value - * :C<d><pat1><d><pat2><d>[1gW] - * Substitute <pat2> for regex <pat1> in the value - * :H Substitute the head of each word - * :T Substitute the tail of each word - * :E Substitute the extension (minus '.') of - * each word - * :R Substitute the root of each word - * (pathname minus the suffix). - * :O ("Order") Alphabeticaly sort words in variable. - * :Ox ("intermiX") Randomize words in variable. - * :u ("uniq") Remove adjacent duplicate words. - * :tu Converts the variable contents to uppercase. - * :tl Converts the variable contents to lowercase. - * :ts[c] Sets varSpace - the char used to - * separate words to 'c'. If 'c' is - * omitted then no separation is used. - * :tW Treat the variable contents as a single - * word, even if it contains spaces. - * (Mnemonic: one big 'W'ord.) - * :tw Treat the variable contents as multiple - * space-separated words. - * (Mnemonic: many small 'w'ords.) - * :[index] Select a single word from the value. - * :[start..end] Select multiple words from the value. - * :[*] or :[0] Select the entire value, as a single - * word. Equivalent to :tW. - * :[@] Select the entire value, as multiple - * words. Undoes the effect of :[*]. - * Equivalent to :tw. - * :[#] Returns the number of words in the value. - * - * :?<true-value>:<false-value> - * If the variable evaluates to true, return - * true value, else return the second value. - * :lhs=rhs Like :S, but the rhs goes to the end of - * the invocation. - * :sh Treat the current value as a command - * to be run, new value is its output. - * The following added so we can handle ODE makefiles. - * :@<tmpvar>@<newval>@ - * Assign a temporary local variable <tmpvar> - * to the current value of each word in turn - * and replace each word with the result of - * evaluating <newval> - * :D<newval> Use <newval> as value if variable defined - * :U<newval> Use <newval> as value if variable undefined - * :L Use the name of the variable as the value. - * :P Use the path of the node that has the same - * name as the variable as the value. This - * basically includes an implied :L so that - * the common method of refering to the path - * of your dependent 'x' in a rule is to use - * the form '${x:P}'. - * :!<cmd>! Run cmd much the same as :sh run's the - * current value of the variable. - * The ::= modifiers, actually assign a value to the variable. - * Their main purpose is in supporting modifiers of .for loop - * iterators and other obscure uses. They always expand to - * nothing. In a target rule that would otherwise expand to an - * empty line they can be preceded with @: to keep make happy. - * Eg. - * - * foo: .USE - * .for i in ${.TARGET} ${.TARGET:R}.gz - * @: ${t::=$i} - * @echo blah ${t:T} - * .endfor - * - * ::=<str> Assigns <str> as the new value of variable. - * ::?=<str> Assigns <str> as value of variable if - * it was not already set. - * ::+=<str> Appends <str> to variable. - * ::!=<cmd> Assigns output of <cmd> as the new value of - * variable. - */ +/* Apply any modifiers (such as :Mpattern or :@var@loop@ or :Q or ::=value). */ static char * -ApplyModifiers(char *nstr, const char *tstr, - int const startc, int const endc, - Var * const v, GNode * const ctxt, int const flags, - int * const lengthPtr, void ** const freePtr) -{ +ApplyModifiers( + const char **pp, /* the parsing position, updated upon return */ + char *val, /* the current value of the variable */ + char const startc, /* '(' or '{', or '\0' for indirect modifiers */ + char const endc, /* ')' or '}', or '\0' for indirect modifiers */ + Var * const v, /* the variable may have its flags changed */ + GNode * const ctxt, /* for looking up and modifying variables */ + VarEvalFlags const eflags, + void ** const freePtr /* free this after using the return value */ +) { ApplyModifiersState st = { - startc, endc, v, ctxt, flags, lengthPtr, freePtr, - nstr, tstr, tstr, tstr, - '\0', 0, '\0', 0, {' ', FALSE}, NULL + startc, endc, v, ctxt, eflags, val, + var_Error, /* .newVal */ + '\0', /* .missing_delim */ + ' ', /* .sep */ + FALSE /* .oneBigWord */ }; + const char *p; + const char *mod; + ApplyModifierResult res; - while (*st.tstr && *st.tstr != st.endc) { + assert(startc == '(' || startc == '{' || startc == '\0'); + assert(endc == ')' || endc == '}' || endc == '\0'); + assert(val != NULL); - if (*st.tstr == '$') { + p = *pp; + while (*p != '\0' && *p != endc) { + + if (*p == '$') { /* * We may have some complex modifiers in a variable. */ - void *freeIt; - char *rval; int rlen; - int c; - - rval = Var_Parse(st.tstr, st.ctxt, st.flags, &rlen, &freeIt); + void *freeIt; + const char *rval = Var_Parse(p, st.ctxt, st.eflags, &rlen, &freeIt); /* * If we have not parsed up to st.endc or ':', * we are not interested. */ - if (rval != NULL && *rval && - (c = st.tstr[rlen]) != '\0' && - c != ':' && - c != st.endc) { + int c; + if (rval[0] != '\0' && + (c = p[rlen]) != '\0' && c != ':' && c != st.endc) { free(freeIt); goto apply_mods; } - if (DEBUG(VAR)) { - fprintf(debug_file, "Got '%s' from '%.*s'%.*s\n", - rval, rlen, st.tstr, rlen, st.tstr + rlen); - } - - st.tstr += rlen; + VAR_DEBUG("Indirect modifier \"%s\" from \"%.*s\"\n", + rval, rlen, p); - if (rval != NULL && *rval) { - int used; + p += rlen; - st.nstr = ApplyModifiers(st.nstr, rval, 0, 0, st.v, - st.ctxt, st.flags, &used, st.freePtr); - if (st.nstr == var_Error - || (st.nstr == varNoError && (st.flags & VARF_UNDEFERR) == 0) - || strlen(rval) != (size_t) used) { + if (rval[0] != '\0') { + const char *rval_pp = rval; + st.val = ApplyModifiers(&rval_pp, st.val, '\0', '\0', v, + ctxt, eflags, freePtr); + if (st.val == var_Error + || (st.val == varNoError && !(st.eflags & VARE_UNDEFERR)) + || *rval_pp != '\0') { free(freeIt); goto out; /* error already reported */ } } free(freeIt); - if (*st.tstr == ':') - st.tstr++; - else if (!*st.tstr && st.endc) { + if (*p == ':') + p++; + else if (*p == '\0' && endc != '\0') { Error("Unclosed variable specification after complex " - "modifier (expecting '%c') for %s", st.endc, st.v->name); + "modifier (expecting '%c') for %s", st.endc, st.v->name); goto out; } continue; } apply_mods: + st.newVal = var_Error; /* default value, in case of errors */ + res = AMR_BAD; /* just a safe fallback */ + mod = p; + if (DEBUG(VAR)) { - fprintf(debug_file, "Applying[%s] :%c to \"%s\"\n", st.v->name, - *st.tstr, st.nstr); - } - st.newStr = var_Error; - switch ((st.modifier = *st.tstr)) { + char eflags_str[VarEvalFlags_ToStringSize]; + char vflags_str[VarFlags_ToStringSize]; + Boolean is_single_char = mod[0] != '\0' && + (mod[1] == endc || mod[1] == ':'); + + /* At this point, only the first character of the modifier can + * be used since the end of the modifier is not yet known. */ + VAR_DEBUG("Applying ${%s:%c%s} to \"%s\" " + "(eflags = %s, vflags = %s)\n", + st.v->name, mod[0], is_single_char ? "" : "...", st.val, + Enum_FlagsToString(eflags_str, sizeof eflags_str, + st.eflags, VarEvalFlags_ToStringSpecs), + Enum_FlagsToString(vflags_str, sizeof vflags_str, + st.v->flags, VarFlags_ToStringSpecs)); + } + + switch (*mod) { case ':': - { - int res = ApplyModifier_Assign(&st); - if (res == 'b') - goto bad_modifier; - if (res == 'c') - goto cleanup; - if (res == 'd') - goto default_case; - break; - } + res = ApplyModifier_Assign(&p, &st); + break; case '@': - ApplyModifier_At(&st); + res = ApplyModifier_Loop(&p, &st); break; case '_': - if (!ApplyModifier_Remember(&st)) - goto default_case; + res = ApplyModifier_Remember(&p, &st); break; case 'D': case 'U': - ApplyModifier_Defined(&st); + res = ApplyModifier_Defined(&p, &st); break; case 'L': - { - if ((st.v->flags & VAR_JUNK) != 0) - st.v->flags |= VAR_KEEP; - st.newStr = bmake_strdup(st.v->name); - st.cp = ++st.tstr; - st.termc = *st.tstr; - break; - } + if (st.v->flags & VAR_JUNK) + st.v->flags |= VAR_KEEP; + st.newVal = bmake_strdup(st.v->name); + p++; + res = AMR_OK; + break; case 'P': - ApplyModifier_Path(&st); + res = ApplyModifier_Path(&p, &st); break; case '!': - if (!ApplyModifier_Exclam(&st)) - goto cleanup; + res = ApplyModifier_Exclam(&p, &st); break; case '[': - { - int res = ApplyModifier_Words(&st); - if (res == 'b') - goto bad_modifier; - if (res == 'c') - goto cleanup; - break; - } + res = ApplyModifier_Words(&p, &st); + break; case 'g': - if (!ApplyModifier_Gmtime(&st)) - goto default_case; + res = ApplyModifier_Gmtime(&p, &st); break; case 'h': - if (!ApplyModifier_Hash(&st)) - goto default_case; + res = ApplyModifier_Hash(&p, &st); break; case 'l': - if (!ApplyModifier_Localtime(&st)) - goto default_case; + res = ApplyModifier_Localtime(&p, &st); break; case 't': - if (!ApplyModifier_To(&st)) - goto bad_modifier; + res = ApplyModifier_To(&p, &st); break; case 'N': case 'M': - ApplyModifier_Match(&st); + res = ApplyModifier_Match(&p, &st); break; case 'S': - if (!ApplyModifier_Subst(&st)) - goto cleanup; + res = ApplyModifier_Subst(&p, &st); break; case '?': - if (!ApplyModifier_IfElse(&st)) - goto cleanup; + res = ApplyModifier_IfElse(&p, &st); break; #ifndef NO_REGEX case 'C': - if (!ApplyModifier_Regex(&st)) - goto cleanup; + res = ApplyModifier_Regex(&p, &st); break; #endif case 'q': case 'Q': - if (st.tstr[1] == st.endc || st.tstr[1] == ':') { - st.newStr = VarQuote(st.nstr, st.modifier == 'q'); - st.cp = st.tstr + 1; - st.termc = *st.cp; - break; - } - goto default_case; + if (p[1] == st.endc || p[1] == ':') { + st.newVal = VarQuote(st.val, *mod == 'q'); + p++; + res = AMR_OK; + } else + res = AMR_UNKNOWN; + break; case 'T': - if (st.tstr[1] == st.endc || st.tstr[1] == ':') { - st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarTail, - NULL); - st.cp = st.tstr + 1; - st.termc = *st.cp; - break; - } - goto default_case; + res = ApplyModifier_WordFunc(&p, &st, ModifyWord_Tail); + break; case 'H': - if (st.tstr[1] == st.endc || st.tstr[1] == ':') { - st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarHead, - NULL); - st.cp = st.tstr + 1; - st.termc = *st.cp; - break; - } - goto default_case; + res = ApplyModifier_WordFunc(&p, &st, ModifyWord_Head); + break; case 'E': - if (st.tstr[1] == st.endc || st.tstr[1] == ':') { - st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarSuffix, - NULL); - st.cp = st.tstr + 1; - st.termc = *st.cp; - break; - } - goto default_case; + res = ApplyModifier_WordFunc(&p, &st, ModifyWord_Suffix); + break; case 'R': - if (st.tstr[1] == st.endc || st.tstr[1] == ':') { - st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarRoot, - NULL); - st.cp = st.tstr + 1; - st.termc = *st.cp; - break; - } - goto default_case; + res = ApplyModifier_WordFunc(&p, &st, ModifyWord_Root); + break; case 'r': - if (!ApplyModifier_Range(&st)) - goto default_case; + res = ApplyModifier_Range(&p, &st); break; case 'O': - if (!ApplyModifier_Order(&st)) - goto bad_modifier; + res = ApplyModifier_Order(&p, &st); break; case 'u': - if (st.tstr[1] == st.endc || st.tstr[1] == ':') { - st.newStr = VarUniq(st.nstr); - st.cp = st.tstr + 1; - st.termc = *st.cp; - break; - } - goto default_case; + if (p[1] == st.endc || p[1] == ':') { + st.newVal = VarUniq(st.val); + p++; + res = AMR_OK; + } else + res = AMR_UNKNOWN; + break; #ifdef SUNSHCMD case 's': - if (st.tstr[1] == 'h' && (st.tstr[2] == st.endc || st.tstr[2] == ':')) { - const char *emsg; - if (st.flags & VARF_WANTRES) { - st.newStr = Cmd_Exec(st.nstr, &emsg); - if (emsg) - Error(emsg, st.nstr); + if (p[1] == 'h' && (p[2] == st.endc || p[2] == ':')) { + if (st.eflags & VARE_WANTRES) { + const char *errfmt; + st.newVal = Cmd_Exec(st.val, &errfmt); + if (errfmt) + Error(errfmt, st.val); } else - st.newStr = varNoError; - st.cp = st.tstr + 2; - st.termc = *st.cp; - break; - } - goto default_case; + st.newVal = varNoError; + p += 2; + res = AMR_OK; + } else + res = AMR_UNKNOWN; + break; #endif default: - default_case: - { + res = AMR_UNKNOWN; + } + #ifdef SYSVVARSUB - int res = ApplyModifier_SysV(&st); - if (res == 'c') - goto cleanup; - if (res != '=') + if (res == AMR_UNKNOWN) { + assert(p == mod); + res = ApplyModifier_SysV(&p, &st); + } #endif - { - Error("Unknown modifier '%c'", *st.tstr); - for (st.cp = st.tstr+1; - *st.cp != ':' && *st.cp != st.endc && *st.cp != '\0'; - st.cp++) - continue; - st.termc = *st.cp; - st.newStr = var_Error; - } - } + + if (res == AMR_UNKNOWN) { + Error("Unknown modifier '%c'", *mod); + for (p++; *p != ':' && *p != st.endc && *p != '\0'; p++) + continue; + st.newVal = var_Error; } + if (res == AMR_CLEANUP) + goto cleanup; + if (res == AMR_BAD) + goto bad_modifier; + if (DEBUG(VAR)) { - fprintf(debug_file, "Result[%s] of :%c is \"%s\"\n", - st.v->name, st.modifier, st.newStr); + char eflags_str[VarEvalFlags_ToStringSize]; + char vflags_str[VarFlags_ToStringSize]; + const char *quot = st.newVal == var_Error ? "" : "\""; + const char *newVal = st.newVal == var_Error ? "error" : st.newVal; + + VAR_DEBUG("Result of ${%s:%.*s} is %s%s%s " + "(eflags = %s, vflags = %s)\n", + st.v->name, (int)(p - mod), mod, quot, newVal, quot, + Enum_FlagsToString(eflags_str, sizeof eflags_str, + st.eflags, VarEvalFlags_ToStringSpecs), + Enum_FlagsToString(vflags_str, sizeof vflags_str, + st.v->flags, VarFlags_ToStringSpecs)); } - if (st.newStr != st.nstr) { - if (*st.freePtr) { - free(st.nstr); - *st.freePtr = NULL; + if (st.newVal != st.val) { + if (*freePtr) { + free(st.val); + *freePtr = NULL; } - st.nstr = st.newStr; - if (st.nstr != var_Error && st.nstr != varNoError) { - *st.freePtr = st.nstr; + st.val = st.newVal; + if (st.val != var_Error && st.val != varNoError) { + *freePtr = st.val; } } - if (st.termc == '\0' && st.endc != '\0') { + if (*p == '\0' && st.endc != '\0') { Error("Unclosed variable specification (expecting '%c') " - "for \"%s\" (value \"%s\") modifier %c", - st.endc, st.v->name, st.nstr, st.modifier); - } else if (st.termc == ':') { - st.cp++; + "for \"%s\" (value \"%s\") modifier %c", + st.endc, st.v->name, st.val, *mod); + } else if (*p == ':') { + p++; } - st.tstr = st.cp; + mod = p; } out: - *st.lengthPtr = st.tstr - st.start; - return st.nstr; + *pp = p; + assert(st.val != NULL); /* Use var_Error or varNoError instead. */ + return st.val; bad_modifier: - /* "{(" */ - Error("Bad modifier `:%.*s' for %s", (int)strcspn(st.tstr, ":)}"), st.tstr, - st.v->name); + Error("Bad modifier `:%.*s' for %s", + (int)strcspn(mod, ":)}"), mod, st.v->name); cleanup: - *st.lengthPtr = st.cp - st.start; - if (st.delim != '\0') - Error("Unclosed substitution for %s (%c missing)", - st.v->name, st.delim); - free(*st.freePtr); - *st.freePtr = NULL; + *pp = p; + if (st.missing_delim != '\0') + Error("Unfinished modifier for %s ('%c' missing)", + st.v->name, st.missing_delim); + free(*freePtr); + *freePtr = NULL; return var_Error; } +static Boolean +VarIsDynamic(GNode *ctxt, const char *varname, size_t namelen) +{ + if ((namelen == 1 || + (namelen == 2 && (varname[1] == 'F' || varname[1] == 'D'))) && + (ctxt == VAR_CMD || ctxt == VAR_GLOBAL)) + { + /* + * If substituting a local variable in a non-local context, + * assume it's for dynamic source stuff. We have to handle + * this specially and return the longhand for the variable + * with the dollar sign escaped so it makes it back to the + * caller. Only four of the local variables are treated + * specially as they are the only four that will be set + * when dynamic sources are expanded. + */ + switch (varname[0]) { + case '@': + case '%': + case '*': + case '!': + return TRUE; + } + return FALSE; + } + + if ((namelen == 7 || namelen == 8) && varname[0] == '.' && + isupper((unsigned char)varname[1]) && + (ctxt == VAR_CMD || ctxt == VAR_GLOBAL)) + { + return strcmp(varname, ".TARGET") == 0 || + strcmp(varname, ".ARCHIVE") == 0 || + strcmp(varname, ".PREFIX") == 0 || + strcmp(varname, ".MEMBER") == 0; + } + + return FALSE; +} + /*- *----------------------------------------------------------------------- * Var_Parse -- - * Given the start of a variable invocation, extract the variable - * name and find its value, then modify it according to the - * specification. + * Given the start of a variable invocation (such as $v, $(VAR), + * ${VAR:Mpattern}), extract the variable name, possibly some + * modifiers and find its value by applying the modifiers to the + * original value. * * Input: * str The string to parse * ctxt The context for the variable - * flags VARF_UNDEFERR if undefineds are an error - * VARF_WANTRES if we actually want the result - * VARF_ASSIGN if we are in a := assignment + * flags VARE_UNDEFERR if undefineds are an error + * VARE_WANTRES if we actually want the result + * VARE_ASSIGN if we are in a := assignment * lengthPtr OUT: The length of the specification * freePtr OUT: Non-NULL if caller should free *freePtr * * Results: - * The (possibly-modified) value of the variable or var_Error if the - * specification is invalid. The length of the specification is - * placed in *lengthPtr (for invalid specifications, this is just - * 2...?). - * If *freePtr is non-NULL then it's a pointer that the caller - * should pass to free() to free memory used by the result. + * Returns the value of the variable expression, never NULL. + * var_Error if there was a parse error and VARE_UNDEFERR was set. + * varNoError if there was a parse error and VARE_UNDEFERR was not set. * - * Side Effects: - * None. + * Parsing should continue at str + *lengthPtr. * + * After using the returned value, *freePtr must be freed, preferably + * using bmake_free since it is NULL in most cases. + * + * Side Effects: + * Any effects from the modifiers, such as :!cmd! or ::=value. *----------------------------------------------------------------------- */ /* coverity[+alloc : arg-*4] */ -char * -Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags, +const char * +Var_Parse(const char * const str, GNode *ctxt, VarEvalFlags eflags, int *lengthPtr, void **freePtr) { const char *tstr; /* Pointer into str */ - Var *v; /* Variable in invocation */ Boolean haveModifier; /* TRUE if have modifiers for the variable */ - char endc; /* Ending character when variable in parens + char startc; /* Starting character if variable in parens * or braces */ - char startc; /* Starting character when variable in parens + char endc; /* Ending character if variable in parens * or braces */ - int vlen; /* Length of variable name */ - const char *start; /* Points to original start of str */ - char *nstr; /* New string, used during expansion */ Boolean dynamic; /* TRUE if the variable is local and we're * expanding it in a non-local context. This * is done to support dynamic sources. The * result is just the invocation, unaltered */ - const char *extramodifiers; /* extra modifiers to apply first */ - char name[2]; + const char *extramodifiers; + Var *v; + char *nstr; + char eflags_str[VarEvalFlags_ToStringSize]; + + VAR_DEBUG("%s: %s with %s\n", __func__, str, + Enum_FlagsToString(eflags_str, sizeof eflags_str, eflags, + VarEvalFlags_ToStringSpecs)); *freePtr = NULL; - extramodifiers = NULL; + extramodifiers = NULL; /* extra modifiers to apply first */ dynamic = FALSE; - start = str; + +#ifdef USE_DOUBLE_BOOLEAN + /* Appease GCC 5.5.0, which thinks that the variable might not be + * initialized. */ + endc = '\0'; +#endif startc = str[1]; if (startc != PROPEN && startc != BROPEN) { + char name[2]; + /* * If it's not bounded by braces of some sort, life is much simpler. * We just need to check for the first character and return the @@ -3650,14 +3388,14 @@ Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags, *lengthPtr = 1; return var_Error; } + name[0] = startc; name[1] = '\0'; - v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); if (v == NULL) { *lengthPtr = 2; - if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) { + if (ctxt == VAR_CMD || ctxt == VAR_GLOBAL) { /* * If substituting a local variable in a non-local context, * assume it's for dynamic source stuff. We have to handle @@ -3669,31 +3407,34 @@ Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags, */ switch (str[1]) { case '@': - return UNCONST("$(.TARGET)"); + return "$(.TARGET)"; case '%': - return UNCONST("$(.MEMBER)"); + return "$(.MEMBER)"; case '*': - return UNCONST("$(.PREFIX)"); + return "$(.PREFIX)"; case '!': - return UNCONST("$(.ARCHIVE)"); + return "$(.ARCHIVE)"; } } - return (flags & VARF_UNDEFERR) ? var_Error : varNoError; + return (eflags & VARE_UNDEFERR) ? var_Error : varNoError; } else { haveModifier = FALSE; - tstr = &str[1]; - endc = str[1]; + tstr = str + 1; } } else { - Buffer buf; /* Holds the variable name */ - int depth = 1; + Buffer namebuf; /* Holds the variable name */ + int depth; + size_t namelen; + char *varname; endc = startc == PROPEN ? PRCLOSE : BRCLOSE; - Buf_Init(&buf, 0); + + Buf_Init(&namebuf, 0); /* * Skip to the end character or a colon, whichever comes first. */ + depth = 1; for (tstr = str + 2; *tstr != '\0'; tstr++) { /* Track depth so we can spot parse errors. */ if (*tstr == startc) @@ -3702,39 +3443,42 @@ Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags, if (--depth == 0) break; } - if (depth == 1 && *tstr == ':') + if (*tstr == ':' && depth == 1) break; /* A variable inside a variable, expand. */ if (*tstr == '$') { int rlen; void *freeIt; - char *rval = Var_Parse(tstr, ctxt, flags, &rlen, &freeIt); - if (rval != NULL) - Buf_AddBytes(&buf, strlen(rval), rval); + const char *rval = Var_Parse(tstr, ctxt, eflags, &rlen, + &freeIt); + Buf_AddStr(&namebuf, rval); free(freeIt); tstr += rlen - 1; } else - Buf_AddByte(&buf, *tstr); + Buf_AddByte(&namebuf, *tstr); } if (*tstr == ':') { haveModifier = TRUE; } else if (*tstr == endc) { haveModifier = FALSE; } else { + Parse_Error(PARSE_FATAL, "Unclosed variable \"%s\"", + Buf_GetAll(&namebuf, NULL)); /* * If we never did find the end character, return NULL * right now, setting the length to be the distance to * the end of the string, since that's what make does. */ - *lengthPtr = tstr - str; - Buf_Destroy(&buf, TRUE); + *lengthPtr = (int)(size_t)(tstr - str); + Buf_Destroy(&namebuf, TRUE); return var_Error; } - str = Buf_GetAll(&buf, &vlen); + + varname = Buf_GetAll(&namebuf, &namelen); /* - * At this point, str points into newly allocated memory from - * buf, containing only the name of the variable. + * At this point, varname points into newly allocated memory from + * namebuf, containing only the name of the variable. * * start and tstr point into the const string that was pointed * to by the original value of the str parameter. start points @@ -3743,23 +3487,23 @@ Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags, * will be '\0', ':', PRCLOSE, or BRCLOSE. */ - v = VarFind(str, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); + v = VarFind(varname, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); /* * Check also for bogus D and F forms of local variables since we're * in a local context and the name is the right length. */ - if ((v == NULL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) && - (vlen == 2) && (str[1] == 'F' || str[1] == 'D') && - strchr("@%?*!<>", str[0]) != NULL) { + if (v == NULL && ctxt != VAR_CMD && ctxt != VAR_GLOBAL && + namelen == 2 && (varname[1] == 'F' || varname[1] == 'D') && + strchr("@%?*!<>", varname[0]) != NULL) + { /* * Well, it's local -- go look for it. */ - name[0] = *str; - name[1] = '\0'; + char name[] = { varname[0], '\0' }; v = VarFind(name, ctxt, 0); if (v != NULL) { - if (str[1] == 'D') { + if (varname[1] == 'D') { extramodifiers = "H:"; } else { /* F */ extramodifiers = "T:"; @@ -3768,55 +3512,22 @@ Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags, } if (v == NULL) { - if (((vlen == 1) || - (((vlen == 2) && (str[1] == 'F' || str[1] == 'D')))) && - ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL))) - { - /* - * If substituting a local variable in a non-local context, - * assume it's for dynamic source stuff. We have to handle - * this specially and return the longhand for the variable - * with the dollar sign escaped so it makes it back to the - * caller. Only four of the local variables are treated - * specially as they are the only four that will be set - * when dynamic sources are expanded. - */ - switch (*str) { - case '@': - case '%': - case '*': - case '!': - dynamic = TRUE; - break; - } - } else if (vlen > 2 && *str == '.' && - isupper((unsigned char) str[1]) && - (ctxt == VAR_CMD || ctxt == VAR_GLOBAL)) - { - int len = vlen - 1; - if ((strncmp(str, ".TARGET", len) == 0) || - (strncmp(str, ".ARCHIVE", len) == 0) || - (strncmp(str, ".PREFIX", len) == 0) || - (strncmp(str, ".MEMBER", len) == 0)) - { - dynamic = TRUE; - } - } + dynamic = VarIsDynamic(ctxt, varname, namelen); if (!haveModifier) { /* * No modifiers -- have specification length so we can return * now. */ - *lengthPtr = tstr - start + 1; + *lengthPtr = (int)(size_t)(tstr - str) + 1; if (dynamic) { - char *pstr = bmake_strndup(start, *lengthPtr); + char *pstr = bmake_strldup(str, (size_t)*lengthPtr); *freePtr = pstr; - Buf_Destroy(&buf, TRUE); + Buf_Destroy(&namebuf, TRUE); return pstr; } else { - Buf_Destroy(&buf, TRUE); - return (flags & VARF_UNDEFERR) ? var_Error : varNoError; + Buf_Destroy(&namebuf, TRUE); + return (eflags & VARE_UNDEFERR) ? var_Error : varNoError; } } else { /* @@ -3824,13 +3535,13 @@ Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags, * so kludge up a Var structure for the modifications */ v = bmake_malloc(sizeof(Var)); - v->name = UNCONST(str); + v->name = varname; Buf_Init(&v->val, 1); v->flags = VAR_JUNK; - Buf_Destroy(&buf, FALSE); + Buf_Destroy(&namebuf, FALSE); } } else - Buf_Destroy(&buf, TRUE); + Buf_Destroy(&namebuf, TRUE); } if (v->flags & VAR_IN_USE) { @@ -3839,6 +3550,7 @@ Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags, } else { v->flags |= VAR_IN_USE; } + /* * Before doing any modification, we have to make sure the value * has been fully expanded. If it looks like recursion might be @@ -3849,66 +3561,64 @@ Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags, * return. */ nstr = Buf_GetAll(&v->val, NULL); - if (strchr(nstr, '$') != NULL && (flags & VARF_WANTRES) != 0) { - nstr = Var_Subst(NULL, nstr, ctxt, flags); + if (strchr(nstr, '$') != NULL && (eflags & VARE_WANTRES) != 0) { + nstr = Var_Subst(nstr, ctxt, eflags); *freePtr = nstr; } - v->flags &= ~VAR_IN_USE; + v->flags &= ~(unsigned)VAR_IN_USE; - if (nstr != NULL && (haveModifier || extramodifiers != NULL)) { + if (haveModifier || extramodifiers != NULL) { void *extraFree; - int used; extraFree = NULL; if (extramodifiers != NULL) { - nstr = ApplyModifiers(nstr, extramodifiers, '(', ')', - v, ctxt, flags, &used, &extraFree); + const char *em = extramodifiers; + nstr = ApplyModifiers(&em, nstr, '(', ')', + v, ctxt, eflags, &extraFree); } if (haveModifier) { /* Skip initial colon. */ tstr++; - nstr = ApplyModifiers(nstr, tstr, startc, endc, - v, ctxt, flags, &used, freePtr); - tstr += used; + nstr = ApplyModifiers(&tstr, nstr, startc, endc, + v, ctxt, eflags, freePtr); free(extraFree); } else { *freePtr = extraFree; } } - *lengthPtr = tstr - start + (*tstr ? 1 : 0); - if (v->flags & VAR_FROM_ENV) { - Boolean destroy = FALSE; + /* Skip past endc if possible. */ + *lengthPtr = (int)(size_t)(tstr + (*tstr ? 1 : 0) - str); - if (nstr != Buf_GetAll(&v->val, NULL)) { - destroy = TRUE; - } else { + if (v->flags & VAR_FROM_ENV) { + Boolean destroy = nstr != Buf_GetAll(&v->val, NULL); + if (!destroy) { /* * Returning the value unmodified, so tell the caller to free * the thing. */ *freePtr = nstr; } - VarFreeEnv(v, destroy); + (void)VarFreeEnv(v, destroy); } else if (v->flags & VAR_JUNK) { /* - * Perform any free'ing needed and set *freePtr to NULL so the caller + * Perform any freeing needed and set *freePtr to NULL so the caller * doesn't try to free a static pointer. - * If VAR_KEEP is also set then we want to keep str as is. + * If VAR_KEEP is also set then we want to keep str(?) as is. */ if (!(v->flags & VAR_KEEP)) { - if (*freePtr) { - free(nstr); + if (*freePtr != NULL) { + free(*freePtr); *freePtr = NULL; } if (dynamic) { - nstr = bmake_strndup(start, *lengthPtr); + nstr = bmake_strldup(str, (size_t)*lengthPtr); *freePtr = nstr; } else { - nstr = (flags & VARF_UNDEFERR) ? var_Error : varNoError; + nstr = (eflags & VARE_UNDEFERR) ? var_Error : varNoError; } } if (nstr != Buf_GetAll(&v->val, NULL)) @@ -3919,58 +3629,52 @@ Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags, return nstr; } -/*- - *----------------------------------------------------------------------- - * Var_Subst -- - * Substitute for all variables in the given string in the given context. - * If flags & VARF_UNDEFERR, Parse_Error will be called when an undefined - * variable is encountered. +/* Substitute for all variables in the given string in the given context. + * + * If eflags & VARE_UNDEFERR, Parse_Error will be called when an undefined + * variable is encountered. + * + * If eflags & VARE_WANTRES, any effects from the modifiers, such as ::=, + * :sh or !cmd! take place. * * Input: - * var Named variable || NULL for all * str the string which to substitute * ctxt the context wherein to find variables - * flags VARF_UNDEFERR if undefineds are an error - * VARF_WANTRES if we actually want the result - * VARF_ASSIGN if we are in a := assignment + * eflags VARE_UNDEFERR if undefineds are an error + * VARE_WANTRES if we actually want the result + * VARE_ASSIGN if we are in a := assignment * * Results: * The resulting string. - * - * Side Effects: - * None. - *----------------------------------------------------------------------- */ char * -Var_Subst(const char *var, const char *str, GNode *ctxt, Varf_Flags flags) +Var_Subst(const char *str, GNode *ctxt, VarEvalFlags eflags) { - Buffer buf; /* Buffer for forming things */ - char *val; /* Value to substitute for a variable */ - int length; /* Length of the variable invocation */ - Boolean trailingBslash; /* variable ends in \ */ - void *freeIt = NULL; /* Set if it should be freed */ - static Boolean errorReported; /* Set true if an error has already - * been reported to prevent a plethora - * of messages when recursing */ + Buffer buf; /* Buffer for forming things */ + Boolean trailingBslash; + + /* Set true if an error has already been reported, + * to prevent a plethora of messages when recursing */ + static Boolean errorReported; Buf_Init(&buf, 0); errorReported = FALSE; - trailingBslash = FALSE; + trailingBslash = FALSE; /* variable ends in \ */ while (*str) { if (*str == '\n' && trailingBslash) Buf_AddByte(&buf, ' '); - if (var == NULL && (*str == '$') && (str[1] == '$')) { + + if (*str == '$' && str[1] == '$') { /* - * A dollar sign may be escaped either with another dollar sign. + * A dollar sign may be escaped with another dollar sign. * In such a case, we skip over the escape character and store the * dollar sign into the buffer directly. */ - if (save_dollars && (flags & VARF_ASSIGN)) - Buf_AddByte(&buf, *str); - str++; - Buf_AddByte(&buf, *str); - str++; + if (save_dollars && (eflags & VARE_ASSIGN)) + Buf_AddByte(&buf, '$'); + Buf_AddByte(&buf, '$'); + str += 2; } else if (*str != '$') { /* * Skip as many characters as possible -- either to the end of @@ -3980,71 +3684,12 @@ Var_Subst(const char *var, const char *str, GNode *ctxt, Varf_Flags flags) for (cp = str++; *str != '$' && *str != '\0'; str++) continue; - Buf_AddBytes(&buf, str - cp, cp); + Buf_AddBytesBetween(&buf, cp, str); } else { - if (var != NULL) { - int expand; - for (;;) { - if (str[1] == '\0') { - /* A trailing $ is kind of a special case */ - Buf_AddByte(&buf, str[0]); - str++; - expand = FALSE; - } else if (str[1] != PROPEN && str[1] != BROPEN) { - if (str[1] != *var || strlen(var) > 1) { - Buf_AddBytes(&buf, 2, str); - str += 2; - expand = FALSE; - } else - expand = TRUE; - break; - } else { - const char *p; - - /* Scan up to the end of the variable name. */ - for (p = &str[2]; *p && - *p != ':' && *p != PRCLOSE && *p != BRCLOSE; p++) - if (*p == '$') - break; - /* - * A variable inside the variable. We cannot expand - * the external variable yet, so we try again with - * the nested one - */ - if (*p == '$') { - Buf_AddBytes(&buf, p - str, str); - str = p; - continue; - } - - if (strncmp(var, str + 2, p - str - 2) != 0 || - var[p - str - 2] != '\0') { - /* - * Not the variable we want to expand, scan - * until the next variable - */ - for (; *p != '$' && *p != '\0'; p++) - continue; - Buf_AddBytes(&buf, p - str, str); - str = p; - expand = FALSE; - } else - expand = TRUE; - break; - } - } - if (!expand) - continue; - } - - val = Var_Parse(str, ctxt, flags, &length, &freeIt); + int length; + void *freeIt; + const char *val = Var_Parse(str, ctxt, eflags, &length, &freeIt); - /* - * When we come down here, val should either point to the - * value of this variable, suitably modified, or be NULL. - * Length should be the total length of the potential - * variable invocation (from $ to end character...) - */ if (val == var_Error || val == varNoError) { /* * If performing old-time variable substitution, skip over @@ -4054,7 +3699,7 @@ Var_Subst(const char *var, const char *str, GNode *ctxt, Varf_Flags flags) */ if (oldVars) { str += length; - } else if ((flags & VARF_UNDEFERR) || val == var_Error) { + } else if ((eflags & VARE_UNDEFERR) || val == var_Error) { /* * If variable is undefined, complain and skip the * variable. The complaint will stop us from doing anything @@ -4071,19 +3716,13 @@ Var_Subst(const char *var, const char *str, GNode *ctxt, Varf_Flags flags) str += 1; } } else { - /* - * We've now got a variable structure to store in. But first, - * advance the string pointer. - */ + size_t val_len; + str += length; - /* - * Copy all the characters from the variable value straight - * into the new string. - */ - length = strlen(val); - Buf_AddBytes(&buf, length, val); - trailingBslash = length > 0 && val[length - 1] == '\\'; + val_len = strlen(val); + Buf_AddBytes(&buf, val, val_len); + trailingBslash = val_len > 0 && val[val_len - 1] == '\\'; } free(freeIt); freeIt = NULL; @@ -4106,6 +3745,13 @@ Var_Init(void) void Var_End(void) { + Var_Stats(); +} + +void +Var_Stats(void) +{ + Hash_DebugStats(&VAR_GLOBAL->context, "VAR_GLOBAL"); } @@ -4117,12 +3763,7 @@ VarPrintVar(void *vp, void *data MAKE_ATTR_UNUSED) fprintf(debug_file, "%-16s = %s\n", v->name, Buf_GetAll(&v->val, NULL)); } -/*- - *----------------------------------------------------------------------- - * Var_Dump -- - * print all variables in a context - *----------------------------------------------------------------------- - */ +/* Print all variables in a context, unordered. */ void Var_Dump(GNode *ctxt) { |