make(1): add test for the .for directive
For a long time, I had assumed that the iteration variables of a .for loop are just normal global variables. This assumption was wrong but didn't have any consequences. The iteration variables of a .for loop can just be accessed like global variables, therefore it is not obvious that they are implemented in a completely different way. There are some edge cases in conditions used inside .for loops, in which the iteration variables cannot be used like normal variables. An example is brought up in https://gnats.netbsd.org/47888, which observes that the defined() and empty() functions in conditions only work with variables but ignore the iteration "variables", simply because these are not variables but only expressions.
This commit is contained in:
parent
34a9f9ee4a
commit
640eab791b
@ -1,4 +1,4 @@
|
||||
# $NetBSD: mi,v 1.914 2020/08/29 18:50:25 rillig Exp $
|
||||
# $NetBSD: mi,v 1.915 2020/09/02 05:33:57 rillig Exp $
|
||||
#
|
||||
# Note: don't delete entries from here - mark them as "obsolete" instead.
|
||||
#
|
||||
@ -4730,6 +4730,8 @@
|
||||
./usr/tests/usr.bin/make/unit-tests/directive-export-literal.mk tests-usr.bin-tests compattestfile,atf
|
||||
./usr/tests/usr.bin/make/unit-tests/directive-export.exp tests-usr.bin-tests compattestfile,atf
|
||||
./usr/tests/usr.bin/make/unit-tests/directive-export.mk tests-usr.bin-tests compattestfile,atf
|
||||
./usr/tests/usr.bin/make/unit-tests/directive-for.exp tests-usr.bin-tests compattestfile,atf
|
||||
./usr/tests/usr.bin/make/unit-tests/directive-for.mk tests-usr.bin-tests compattestfile,atf
|
||||
./usr/tests/usr.bin/make/unit-tests/directive-for-generating-endif.exp tests-usr.bin-tests compattestfile,atf
|
||||
./usr/tests/usr.bin/make/unit-tests/directive-for-generating-endif.mk tests-usr.bin-tests compattestfile,atf
|
||||
./usr/tests/usr.bin/make/unit-tests/directive-if.exp tests-usr.bin-tests compattestfile,atf
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: Makefile,v 1.129 2020/08/29 19:35:38 rillig Exp $
|
||||
# $NetBSD: Makefile,v 1.130 2020/09/02 05:33:57 rillig Exp $
|
||||
#
|
||||
# Unit tests for make(1)
|
||||
#
|
||||
@ -132,6 +132,7 @@ 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
|
||||
|
1
usr.bin/make/unit-tests/directive-for.exp
Normal file
1
usr.bin/make/unit-tests/directive-for.exp
Normal file
@ -0,0 +1 @@
|
||||
exit status 0
|
97
usr.bin/make/unit-tests/directive-for.mk
Normal file
97
usr.bin/make/unit-tests/directive-for.mk
Normal file
@ -0,0 +1,97 @@
|
||||
# $NetBSD: directive-for.mk,v 1.1 2020/09/02 05:33:57 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 var must be undefined.
|
||||
.endif
|
||||
.if ${var2} != "value before"
|
||||
. warning var2 must be undefined.
|
||||
.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 var must be undefined.
|
||||
.endif
|
||||
.if ${var2} != "value before"
|
||||
. warning var2 must be undefined.
|
||||
.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:
|
||||
@:;
|
Loading…
Reference in New Issue
Block a user