Sun Jul 26 11:10:29 2020 UTC ()
make(1): add test for appending and exporting a variable


(rillig)
diff -r1.64 -r1.65 src/usr.bin/make/unit-tests/Makefile
diff -r1.9 -r1.10 src/usr.bin/make/unit-tests/varmisc.exp
diff -r1.13 -r1.14 src/usr.bin/make/unit-tests/varmisc.mk

cvs diff -r1.64 -r1.65 src/usr.bin/make/unit-tests/Makefile (switch to unified diff)

--- src/usr.bin/make/unit-tests/Makefile 2020/07/25 21:19:29 1.64
+++ src/usr.bin/make/unit-tests/Makefile 2020/07/26 11:10:29 1.65
@@ -1,182 +1,185 @@ @@ -1,182 +1,185 @@
1# $NetBSD: Makefile,v 1.64 2020/07/25 21:19:29 rillig Exp $ 1# $NetBSD: Makefile,v 1.65 2020/07/26 11:10:29 rillig Exp $
2# 2#
3# Unit tests for make(1) 3# Unit tests for make(1)
4# 4#
5# The main targets are: 5# The main targets are:
6# 6#
7# all: 7# all:
8# run all the tests 8# run all the tests
9# test: 9# test:
10# run 'all', and compare to expected results 10# run 'all', and compare to expected results
11# accept: 11# accept:
12# move generated output to expected results 12# move generated output to expected results
13# 13#
14# Settable variables 14# Settable variables
15# 15#
16# TEST_MAKE 16# TEST_MAKE
17# The make program to be tested. 17# The make program to be tested.
18# 18#
19# 19#
20# Adding a test case 20# Adding a test case
21# 21#
22# Each feature should get its own set of tests in its own suitably 22# Each feature should get its own set of tests in its own suitably
23# named makefile (*.mk), with its own set of expected results (*.exp), 23# named makefile (*.mk), with its own set of expected results (*.exp),
24# and it should be added to the TESTS list. 24# and it should be added to the TESTS list.
25# 25#
26# Any added files must also be added to src/distrib/sets/lists/tests/mi. 26# Any added files must also be added to src/distrib/sets/lists/tests/mi.
27# Makefiles that are not added to TESTS must be ignored in 27# Makefiles that are not added to TESTS must be ignored in
28# src/tests/usr.bin/make/t_make.sh (example: include-sub). 28# src/tests/usr.bin/make/t_make.sh (example: include-sub).
29# 29#
30 30
31# Each test is in a sub-makefile. 31# Each test is in a sub-makefile.
32# Keep the list sorted. 32# Keep the list sorted.
33TESTS+= comment 33TESTS+= comment
34TESTS+= cond-late 34TESTS+= cond-late
35TESTS+= cond-short 35TESTS+= cond-short
36TESTS+= cond1 36TESTS+= cond1
37TESTS+= cond2 37TESTS+= cond2
38TESTS+= dollar 38TESTS+= dollar
39TESTS+= doterror 39TESTS+= doterror
40TESTS+= dotwait 40TESTS+= dotwait
41TESTS+= envfirst 41TESTS+= envfirst
42TESTS+= error 42TESTS+= error
43TESTS+= # escape # broken by reverting POSIX changes 43TESTS+= # escape # broken by reverting POSIX changes
44TESTS+= export 44TESTS+= export
45TESTS+= export-all 45TESTS+= export-all
46TESTS+= export-env 46TESTS+= export-env
47TESTS+= forloop 47TESTS+= forloop
48TESTS+= forsubst 48TESTS+= forsubst
49TESTS+= hash 49TESTS+= hash
50TESTS+= # impsrc # broken by reverting POSIX changes 50TESTS+= # impsrc # broken by reverting POSIX changes
51TESTS+= include-main 51TESTS+= include-main
52TESTS+= misc 52TESTS+= misc
53TESTS+= moderrs 53TESTS+= moderrs
54TESTS+= modmatch 54TESTS+= modmatch
55TESTS+= modmisc 55TESTS+= modmisc
56TESTS+= modorder 56TESTS+= modorder
57TESTS+= modts 57TESTS+= modts
58TESTS+= modword 58TESTS+= modword
59TESTS+= order 59TESTS+= order
60TESTS+= # phony-end # broken by reverting POSIX changes 60TESTS+= # phony-end # broken by reverting POSIX changes
61TESTS+= posix 61TESTS+= posix
62TESTS+= # posix1 # broken by reverting POSIX changes 62TESTS+= # posix1 # broken by reverting POSIX changes
63TESTS+= qequals 63TESTS+= qequals
64TESTS+= # suffixes # broken by reverting POSIX changes 64TESTS+= # suffixes # broken by reverting POSIX changes
65TESTS+= sunshcmd 65TESTS+= sunshcmd
66TESTS+= sysv 66TESTS+= sysv
67TESTS+= ternary 67TESTS+= ternary
68TESTS+= unexport 68TESTS+= unexport
69TESTS+= unexport-env 69TESTS+= unexport-env
70TESTS+= varcmd 70TESTS+= varcmd
71TESTS+= vardebug 71TESTS+= vardebug
72TESTS+= varfind 72TESTS+= varfind
73TESTS+= varmisc 73TESTS+= varmisc
74TESTS+= varmod-edge 74TESTS+= varmod-edge
75TESTS+= varquote 75TESTS+= varquote
76TESTS+= varshell 76TESTS+= varshell
77 77
78# Override environment variables for some of the tests. 78# Override environment variables for some of the tests.
79ENV.envfirst= FROM_ENV=value-from-env 79ENV.envfirst= FROM_ENV=value-from-env
 80ENV.varmisc= FROM_ENV=env
 81ENV.varmisc+= FROM_ENV_BEFORE=env
 82ENV.varmisc+= FROM_ENV_AFTER=env
80 83
81# Override make flags for some of the tests; default is -k. 84# Override make flags for some of the tests; default is -k.
82FLAGS.doterror= # none 85FLAGS.doterror= # none
83FLAGS.order= -j1 86FLAGS.order= -j1
84FLAGS.envfirst= -e 87FLAGS.envfirst= -e
85FLAGS.vardebug= -k -dv FROM_CMDLINE= 88FLAGS.vardebug= -k -dv FROM_CMDLINE=
86 89
87# Some tests need extra post-processing. 90# Some tests need extra post-processing.
88SED_CMDS.modmisc+= -e 's,\(substitution error:\).*,\1 (details omitted),' 91SED_CMDS.modmisc+= -e 's,\(substitution error:\).*,\1 (details omitted),'
89SED_CMDS.varshell+= -e 's,^[a-z]*sh: ,,' 92SED_CMDS.varshell+= -e 's,^[a-z]*sh: ,,'
90SED_CMDS.varshell+= -e '/command/s,No such.*,not found,' 93SED_CMDS.varshell+= -e '/command/s,No such.*,not found,'
91 94
92# Some tests need an additional round of postprocessing. 95# Some tests need an additional round of postprocessing.
93POSTPROC.vardebug= ${TOOL_SED} -n -e '/:RELEVANT = yes/,/:RELEVANT = no/p' 96POSTPROC.vardebug= ${TOOL_SED} -n -e '/:RELEVANT = yes/,/:RELEVANT = no/p'
94 97
95# End of the configuration section. 98# End of the configuration section.
96 99
97.MAIN: all 100.MAIN: all
98 101
99UNIT_TESTS:= ${.PARSEDIR} 102UNIT_TESTS:= ${.PARSEDIR}
100.PATH: ${UNIT_TESTS} 103.PATH: ${UNIT_TESTS}
101 104
102OUTFILES= ${TESTS:=.out} 105OUTFILES= ${TESTS:=.out}
103 106
104all: ${OUTFILES} 107all: ${OUTFILES}
105 108
106CLEANFILES+= *.rawout *.out *.status *.tmp *.core *.tmp 109CLEANFILES+= *.rawout *.out *.status *.tmp *.core *.tmp
107CLEANFILES+= obj*.[och] lib*.a # posix1.mk 110CLEANFILES+= obj*.[och] lib*.a # posix1.mk
108CLEANFILES+= issue* .[ab]* # suffixes.mk 111CLEANFILES+= issue* .[ab]* # suffixes.mk
109CLEANRECURSIVE+= dir dummy # posix1.mk 112CLEANRECURSIVE+= dir dummy # posix1.mk
110 113
111clean: 114clean:
112 rm -f ${CLEANFILES} 115 rm -f ${CLEANFILES}
113.if !empty(CLEANRECURSIVE) 116.if !empty(CLEANRECURSIVE)
114 rm -rf ${CLEANRECURSIVE} 117 rm -rf ${CLEANRECURSIVE}
115.endif 118.endif
116 119
117TEST_MAKE?= ${.MAKE} 120TEST_MAKE?= ${.MAKE}
118TOOL_SED?= sed 121TOOL_SED?= sed
119 122
120# ensure consistent results from sort(1) 123# ensure consistent results from sort(1)
121LC_ALL= C 124LC_ALL= C
122LANG= C 125LANG= C
123.export LANG LC_ALL 126.export LANG LC_ALL
124 127
125# the tests are actually done with sub-makes. 128# the tests are actually done with sub-makes.
126.SUFFIXES: .mk .rawout .out 129.SUFFIXES: .mk .rawout .out
127.mk.rawout: 130.mk.rawout:
128 @echo testing ${.IMPSRC} 131 @echo testing ${.IMPSRC}
129 @set -eu; \ 132 @set -eu; \
130 cd ${.OBJDIR}; \ 133 cd ${.OBJDIR}; \
131 ${ENV.${.TARGET:R}} ${TEST_MAKE} \ 134 ${ENV.${.TARGET:R}} ${TEST_MAKE} \
132 ${FLAGS.${.TARGET:R}:U-k} -f ${.IMPSRC} \ 135 ${FLAGS.${.TARGET:R}:U-k} -f ${.IMPSRC} \
133 > ${.TARGET}.tmp 2>&1 \ 136 > ${.TARGET}.tmp 2>&1 \
134 && status=$$? || status=$$?; \ 137 && status=$$? || status=$$?; \
135 echo $$status > ${.TARGET:R}.status 138 echo $$status > ${.TARGET:R}.status
136 @mv ${.TARGET}.tmp ${.TARGET} 139 @mv ${.TARGET}.tmp ${.TARGET}
137 140
138# Post-process the test output so that the results can be compared. 141# Post-process the test output so that the results can be compared.
139# 142#
140# always pretend .MAKE was called 'make' 143# always pretend .MAKE was called 'make'
141_SED_CMDS+= -e 's,^${TEST_MAKE:T:S,.,\\.,g}[][0-9]*:,make:,' 144_SED_CMDS+= -e 's,^${TEST_MAKE:T:S,.,\\.,g}[][0-9]*:,make:,'
142_SED_CMDS+= -e 's,${TEST_MAKE:S,.,\\.,g},make,' 145_SED_CMDS+= -e 's,${TEST_MAKE:S,.,\\.,g},make,'
143# replace anything after 'stopped in' with unit-tests 146# replace anything after 'stopped in' with unit-tests
144_SED_CMDS+= -e '/stopped/s, /.*, unit-tests,' 147_SED_CMDS+= -e '/stopped/s, /.*, unit-tests,'
145# strip ${.CURDIR}/ from the output 148# strip ${.CURDIR}/ from the output
146_SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g' 149_SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g'
147_SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g' 150_SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'
148 151
149.rawout.out: 152.rawout.out:
150 @echo postprocess ${.TARGET} 153 @echo postprocess ${.TARGET}
151 @${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.TARGET:R}} \ 154 @${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.TARGET:R}} \
152 < ${.IMPSRC} > ${.TARGET}.tmp1 155 < ${.IMPSRC} > ${.TARGET}.tmp1
153 @${POSTPROC.${.TARGET:R}:U${TOOL_SED}} < ${.TARGET}.tmp1 > ${.TARGET}.tmp2 156 @${POSTPROC.${.TARGET:R}:U${TOOL_SED}} < ${.TARGET}.tmp1 > ${.TARGET}.tmp2
154 @rm ${.TARGET}.tmp1 157 @rm ${.TARGET}.tmp1
155 @echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp2 158 @echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp2
156 @mv ${.TARGET}.tmp2 ${.TARGET} 159 @mv ${.TARGET}.tmp2 ${.TARGET}
157 160
158# Compare all output files 161# Compare all output files
159test: ${OUTFILES} .PHONY 162test: ${OUTFILES} .PHONY
160 @failed= ; \ 163 @failed= ; \
161 for test in ${TESTS}; do \ 164 for test in ${TESTS}; do \
162 diff -u ${UNIT_TESTS}/$${test}.exp $${test}.out \ 165 diff -u ${UNIT_TESTS}/$${test}.exp $${test}.out \
163 || failed="$${failed}$${failed:+ }$${test}" ; \ 166 || failed="$${failed}$${failed:+ }$${test}" ; \
164 done ; \ 167 done ; \
165 if [ -n "$${failed}" ]; then \ 168 if [ -n "$${failed}" ]; then \
166 echo "Failed tests: $${failed}" ; false ; \ 169 echo "Failed tests: $${failed}" ; false ; \
167 else \ 170 else \
168 echo "All tests passed" ; \ 171 echo "All tests passed" ; \
169 fi 172 fi
170 173
171accept: 174accept:
172 @for test in ${TESTS}; do \ 175 @for test in ${TESTS}; do \
173 cmp -s ${UNIT_TESTS}/$${test}.exp $${test}.out \ 176 cmp -s ${UNIT_TESTS}/$${test}.exp $${test}.out \
174 || { echo "Replacing $${test}.exp" ; \ 177 || { echo "Replacing $${test}.exp" ; \
175 cp $${test}.out ${UNIT_TESTS}/$${test}.exp ; } \ 178 cp $${test}.out ${UNIT_TESTS}/$${test}.exp ; } \
176 done 179 done
177 180
178.if exists(${TEST_MAKE}) 181.if exists(${TEST_MAKE})
179${TESTS:=.rawout}: ${TEST_MAKE} 182${TESTS:=.rawout}: ${TEST_MAKE}
180.endif 183.endif
181 184
182.-include <bsd.obj.mk> 185.-include <bsd.obj.mk>

cvs diff -r1.9 -r1.10 src/usr.bin/make/unit-tests/varmisc.exp (switch to unified diff)

--- src/usr.bin/make/unit-tests/varmisc.exp 2020/07/26 10:48:21 1.9
+++ src/usr.bin/make/unit-tests/varmisc.exp 2020/07/26 11:10:29 1.10
@@ -1,43 +1,46 @@ @@ -1,43 +1,46 @@
1 1
2:D expanded when var set 2:D expanded when var set
3true 3true
4TRUE 4TRUE
5:U expanded when var undef 5:U expanded when var undef
6true 6true
7TRUE 7TRUE
8:D skipped if var undef 8:D skipped if var undef
9 9
10:U skipped when var set 10:U skipped when var set
11is set 11is set
12:? only lhs when value true 12:? only lhs when value true
13true 13true
14TRUE 14TRUE
15:? only rhs when value false 15:? only rhs when value false
16false 16false
17FALSE 17FALSE
18do not evaluate or expand :? if discarding 18do not evaluate or expand :? if discarding
19is set 19is set
20year=2016 month=04 day=01 20year=2016 month=04 day=01
21date=20160401 21date=20160401
22Version=123.456.789 == 123456789 22Version=123.456.789 == 123456789
23Literal=3.4.5 == 3004005 23Literal=3.4.5 == 3004005
24We have target specific vars 24We have target specific vars
25MAN= make.1 25MAN= make.1
26save-dollars: 0 = $ 26save-dollars: 0 = $
27save-dollars: 1 = $$ 27save-dollars: 1 = $$
28save-dollars: 2 = $$ 28save-dollars: 2 = $$
29save-dollars: False = $ 29save-dollars: False = $
30save-dollars: True = $$ 30save-dollars: True = $$
31save-dollars: false = $ 31save-dollars: false = $
32save-dollars: true = $$ 32save-dollars: true = $$
33save-dollars: Yes = $$ 33save-dollars: Yes = $$
34save-dollars: No = $ 34save-dollars: No = $
35save-dollars: yes = $$ 35save-dollars: yes = $$
36save-dollars: no = $ 36save-dollars: no = $
37save-dollars: On = $$ 37save-dollars: On = $$
38save-dollars: Off = $ 38save-dollars: Off = $
39save-dollars: ON = $$ 39save-dollars: ON = $$
40save-dollars: OFF = $ 40save-dollars: OFF = $
41save-dollars: on = $$ 41save-dollars: on = $$
42save-dollars: off = $ 42save-dollars: off = $
 43export-appended: env
 44export-appended: env
 45export-appended: env mk
43exit status 0 46exit status 0

cvs diff -r1.13 -r1.14 src/usr.bin/make/unit-tests/varmisc.mk (switch to unified diff)

--- src/usr.bin/make/unit-tests/varmisc.mk 2020/07/26 10:59:56 1.13
+++ src/usr.bin/make/unit-tests/varmisc.mk 2020/07/26 11:10:29 1.14
@@ -1,141 +1,158 @@ @@ -1,141 +1,158 @@
1# $Id: varmisc.mk,v 1.13 2020/07/26 10:59:56 rillig Exp $ 1# $Id: varmisc.mk,v 1.14 2020/07/26 11:10:29 rillig Exp $
2# 2#
3# Miscellaneous variable tests. 3# Miscellaneous variable tests.
4 4
5all: unmatched_var_paren D_true U_true D_false U_false Q_lhs Q_rhs NQ_none \ 5all: unmatched_var_paren D_true U_true D_false U_false Q_lhs Q_rhs NQ_none \
6 strftime cmpv manok 6 strftime cmpv manok
7all: save-dollars 7all: save-dollars
 8all: export-appended
8 9
9unmatched_var_paren: 10unmatched_var_paren:
10 @echo ${foo::=foo-text} 11 @echo ${foo::=foo-text}
11 12
12True = ${echo true >&2:L:sh}TRUE 13True = ${echo true >&2:L:sh}TRUE
13False= ${echo false >&2:L:sh}FALSE 14False= ${echo false >&2:L:sh}FALSE
14 15
15VSET= is set 16VSET= is set
16.undef UNDEF 17.undef UNDEF
17 18
18U_false: 19U_false:
19 @echo :U skipped when var set 20 @echo :U skipped when var set
20 @echo ${VSET:U${False}} 21 @echo ${VSET:U${False}}
21 22
22D_false: 23D_false:
23 @echo :D skipped if var undef 24 @echo :D skipped if var undef
24 @echo ${UNDEF:D${False}} 25 @echo ${UNDEF:D${False}}
25 26
26U_true: 27U_true:
27 @echo :U expanded when var undef 28 @echo :U expanded when var undef
28 @echo ${UNDEF:U${True}} 29 @echo ${UNDEF:U${True}}
29 30
30D_true: 31D_true:
31 @echo :D expanded when var set 32 @echo :D expanded when var set
32 @echo ${VSET:D${True}} 33 @echo ${VSET:D${True}}
33 34
34Q_lhs: 35Q_lhs:
35 @echo :? only lhs when value true 36 @echo :? only lhs when value true
36 @echo ${1:L:?${True}:${False}} 37 @echo ${1:L:?${True}:${False}}
37 38
38Q_rhs: 39Q_rhs:
39 @echo :? only rhs when value false 40 @echo :? only rhs when value false
40 @echo ${0:L:?${True}:${False}} 41 @echo ${0:L:?${True}:${False}}
41 42
42NQ_none: 43NQ_none:
43 @echo do not evaluate or expand :? if discarding 44 @echo do not evaluate or expand :? if discarding
44 @echo ${VSET:U${1:L:?${True}:${False}}} 45 @echo ${VSET:U${1:L:?${True}:${False}}}
45 46
46April1= 1459494000 47April1= 1459494000
47 48
48# slightly contorted syntax to use utc via variable 49# slightly contorted syntax to use utc via variable
49strftime: 50strftime:
50 @echo ${year=%Y month=%m day=%d:L:gmtime=1459494000} 51 @echo ${year=%Y month=%m day=%d:L:gmtime=1459494000}
51 @echo date=${%Y%m%d:L:${gmtime=${April1}:L}} 52 @echo date=${%Y%m%d:L:${gmtime=${April1}:L}}
52 53
53# big jumps to handle 3 digits per step 54# big jumps to handle 3 digits per step
54M_cmpv.units = 1 1000 1000000 55M_cmpv.units = 1 1000 1000000
55M_cmpv = S,., ,g:_:range:@i@+ $${_:[-$$i]} \* $${M_cmpv.units:[$$i]}@:S,^,expr 0 ,1:sh 56M_cmpv = S,., ,g:_:range:@i@+ $${_:[-$$i]} \* $${M_cmpv.units:[$$i]}@:S,^,expr 0 ,1:sh
56 57
57Version = 123.456.789 58Version = 123.456.789
58cmpv.only = target specific vars 59cmpv.only = target specific vars
59 60
60cmpv: 61cmpv:
61 @echo Version=${Version} == ${Version:${M_cmpv}} 62 @echo Version=${Version} == ${Version:${M_cmpv}}
62 @echo Literal=3.4.5 == ${3.4.5:L:${M_cmpv}} 63 @echo Literal=3.4.5 == ${3.4.5:L:${M_cmpv}}
63 @echo We have ${${.TARGET:T}.only} 64 @echo We have ${${.TARGET:T}.only}
64 65
65# catch misshandling of nested vars in .for loop 66# catch misshandling of nested vars in .for loop
66MAN= 67MAN=
67MAN1= make.1 68MAN1= make.1
68.for s in 1 2 69.for s in 1 2
69.if defined(MAN$s) && !empty(MAN$s) 70.if defined(MAN$s) && !empty(MAN$s)
70MAN+= ${MAN$s} 71MAN+= ${MAN$s}
71.endif 72.endif
72.endfor 73.endfor
73 74
74manok: 75manok:
75 @echo MAN=${MAN} 76 @echo MAN=${MAN}
76 77
77# This is an expanded variant of the above .for loop. 78# This is an expanded variant of the above .for loop.
78# Between 2020-06-28 and 2020-07-02 this paragraph generated a wrong 79# Between 2020-06-28 and 2020-07-02 this paragraph generated a wrong
79# error message "Variable VARNAME is recursive". 80# error message "Variable VARNAME is recursive".
80# When evaluating the !empty expression, the ${:U1} was not expanded and 81# When evaluating the !empty expression, the ${:U1} was not expanded and
81# thus resulted in the seeming definition VARNAME=${VARNAME}, which is 82# thus resulted in the seeming definition VARNAME=${VARNAME}, which is
82# obviously recursive. 83# obviously recursive.
83VARNAME= ${VARNAME${:U1}} 84VARNAME= ${VARNAME${:U1}}
84.if defined(VARNAME${:U2}) && !empty(VARNAME${:U2}) 85.if defined(VARNAME${:U2}) && !empty(VARNAME${:U2})
85.endif 86.endif
86 87
87# begin .MAKE.SAVE_DOLLARS; see Var_Set_with_flags and s2Boolean. 88# begin .MAKE.SAVE_DOLLARS; see Var_Set_with_flags and s2Boolean.
88SD_VALUES= 0 1 2 False True false true Yes No yes no On Off ON OFF on off 89SD_VALUES= 0 1 2 False True false true Yes No yes no On Off ON OFF on off
89SD_4_DOLLARS= $$$$ 90SD_4_DOLLARS= $$$$
90 91
91.for val in ${SD_VALUES} 92.for val in ${SD_VALUES}
92.MAKE.SAVE_DOLLARS:= ${val} # Must be := since a simple = has no effect. 93.MAKE.SAVE_DOLLARS:= ${val} # Must be := since a simple = has no effect.
93SD.${val}:= ${SD_4_DOLLARS} 94SD.${val}:= ${SD_4_DOLLARS}
94.endfor 95.endfor
95.MAKE.SAVE_DOLLARS:= yes 96.MAKE.SAVE_DOLLARS:= yes
96 97
97save-dollars: 98save-dollars:
98.for val in ${SD_VALUES} 99.for val in ${SD_VALUES}
99 @printf '%s: %-8s = %s\n' $@ ${val} ${SD.${val}:Q} 100 @printf '%s: %-8s = %s\n' $@ ${val} ${SD.${val}:Q}
100.endfor 101.endfor
101 102
102# Appending to an undefined variable does not add a space in front. 103# Appending to an undefined variable does not add a space in front.
103.undef APPENDED 104.undef APPENDED
104APPENDED+= value 105APPENDED+= value
105.if ${APPENDED} != "value" 106.if ${APPENDED} != "value"
106.error "${APPENDED}" 107.error "${APPENDED}"
107.endif 108.endif
108 109
109# Appending to an empty variable adds a space between the old value 110# Appending to an empty variable adds a space between the old value
110# and the additional value. 111# and the additional value.
111APPENDED= # empty 112APPENDED= # empty
112APPENDED+= value 113APPENDED+= value
113.if ${APPENDED} != " value" 114.if ${APPENDED} != " value"
114.error "${APPENDED}" 115.error "${APPENDED}"
115.endif 116.endif
116 117
117# Appending to parameterized variables works as well. 118# Appending to parameterized variables works as well.
118PARAM= param 119PARAM= param
119VAR.${PARAM}= 1 120VAR.${PARAM}= 1
120VAR.${PARAM}+= 2 121VAR.${PARAM}+= 2
121.if ${VAR.param} != "1 2" 122.if ${VAR.param} != "1 2"
122.error "${VAR.param}" 123.error "${VAR.param}"
123.endif 124.endif
124 125
125# The variable name can contain arbitrary characters. 126# The variable name can contain arbitrary characters.
126# If the expanded variable name ends in a +, this still does not influence 127# If the expanded variable name ends in a +, this still does not influence
127# the parser. The assignment operator is still a simple assignment. 128# the parser. The assignment operator is still a simple assignment.
128# Therefore, there is no need to add a space between the variable name 129# Therefore, there is no need to add a space between the variable name
129# and the assignment operator. 130# and the assignment operator.
130PARAM= + 131PARAM= +
131VAR.${PARAM}= 1 132VAR.${PARAM}= 1
132VAR.${PARAM}+= 2 133VAR.${PARAM}+= 2
133.if ${VAR.+} != "1 2" 134.if ${VAR.+} != "1 2"
134.error "${VAR.+}" 135.error "${VAR.+}"
135.endif 136.endif
136.for param in + ! ? 137.for param in + ! ?
137VAR.${param}= ${param} 138VAR.${param}= ${param}
138.endfor 139.endfor
139.if ${VAR.+} != "+" || ${VAR.!} != "!" || ${VAR.?} != "?" 140.if ${VAR.+} != "+" || ${VAR.!} != "!" || ${VAR.?} != "?"
140.error "${VAR.+}" "${VAR.!}" "${VAR.?}" 141.error "${VAR.+}" "${VAR.!}" "${VAR.?}"
141.endif 142.endif
 143
 144# Appending to a variable from the environment creates a copy of that variable
 145# in the global context.
 146# The appended value is not exported automatically.
 147# When a variable is exported, the exported value is taken at the time of the
 148# .export directive. Later changes to the variable have no effect.
 149.export FROM_ENV_BEFORE
 150FROM_ENV+= mk
 151FROM_ENV_BEFORE+= mk
 152FROM_ENV_AFTER+= mk
 153.export FROM_ENV_AFTER
 154
 155export-appended:
 156 @echo $@: "$$FROM_ENV"
 157 @echo $@: "$$FROM_ENV_BEFORE"
 158 @echo $@: "$$FROM_ENV_AFTER"