Home | History | Annotate | Line # | Download | only in unit-tests
directive-for.mk revision 1.25.2.1
      1  1.25.2.1  perseant # $NetBSD: directive-for.mk,v 1.25.2.1 2025/08/02 05:58:34 perseant Exp $
      2       1.1    rillig #
      3       1.1    rillig # Tests for the .for directive.
      4       1.9    rillig #
      5       1.9    rillig # TODO: Describe naming conventions for the loop variables.
      6       1.9    rillig #	.for f in values
      7       1.9    rillig #	.for file in values
      8       1.9    rillig #	.for _FILE_ in values
      9       1.9    rillig #	.for .FILE. in values
     10       1.9    rillig #	.for _f_ in values
     11      1.19    rillig #
     12      1.19    rillig # See also:
     13      1.19    rillig #	varmod-loop.mk		The ':@var (a] ...@' modifier
     14      1.19    rillig 
     15      1.19    rillig # A typical use case for a .for loop is to populate a variable with a list of
     16      1.19    rillig # values depending on other variables.  In simple cases, the same effect can
     17      1.19    rillig # be achieved using the ':@var@${var}@' modifier.
     18       1.1    rillig .undef NUMBERS
     19       1.1    rillig .for num in 1 2 3
     20       1.1    rillig NUMBERS+=	${num}
     21       1.1    rillig .endfor
     22       1.1    rillig .if ${NUMBERS} != "1 2 3"
     23       1.1    rillig .  error
     24       1.1    rillig .endif
     25       1.1    rillig 
     26      1.19    rillig 
     27       1.1    rillig # The .for loop also works for multiple iteration variables.
     28      1.24    rillig # This is something that the modifier :@ cannot do as easily.
     29       1.1    rillig .for name value in VARNAME value NAME2 value2
     30       1.1    rillig ${name}=	${value}
     31       1.1    rillig .endfor
     32       1.1    rillig .if ${VARNAME} != "value" || ${NAME2} != "value2"
     33       1.1    rillig .  error
     34       1.1    rillig .endif
     35       1.1    rillig 
     36      1.19    rillig 
     37       1.1    rillig # The .for loop splits the items at whitespace, taking quotes into account,
     38      1.19    rillig # just like the :M or :S modifiers.
     39       1.1    rillig #
     40      1.19    rillig # Until 2012-06-03, the .for loop had split the items exactly at whitespace,
     41      1.19    rillig # without taking the quotes into account.  This had resulted in 10 words.
     42       1.1    rillig .undef WORDS
     43       1.1    rillig .for var in one t\ w\ o "three three" 'four four' `five six`
     44       1.1    rillig WORDS+=	counted
     45       1.1    rillig .endfor
     46       1.1    rillig .if ${WORDS:[#]} != 6
     47       1.1    rillig .  error
     48       1.1    rillig .endif
     49       1.1    rillig 
     50      1.19    rillig 
     51       1.1    rillig # In the body of the .for loop, the iteration variables can be accessed
     52       1.1    rillig # like normal variables, even though they are not really variables.
     53       1.1    rillig #
     54      1.19    rillig # Instead, before interpreting the body of the .for loop, the body is
     55      1.19    rillig # generated by replacing each expression ${var} with ${:U1}, ${:U2} and so
     56      1.19    rillig # on.
     57       1.1    rillig #
     58      1.19    rillig # A noticeable effect of this implementation technique is that the .for
     59       1.1    rillig # iteration variables and the normal global variables live in separate
     60      1.19    rillig # namespaces and do not influence each other.  The "scope" of the .for loop
     61      1.20    rillig # variables is restricted to the current makefile, it does not reach over to
     62      1.19    rillig # any included makefiles.
     63       1.1    rillig var=	value before
     64       1.1    rillig var2=	value before
     65       1.1    rillig .for var var2 in 1 2 3 4
     66       1.1    rillig .endfor
     67       1.1    rillig .if ${var} != "value before"
     68       1.2    rillig .  warning After the .for loop, var must still have its original value.
     69       1.1    rillig .endif
     70       1.1    rillig .if ${var2} != "value before"
     71       1.2    rillig .  warning After the .for loop, var2 must still have its original value.
     72       1.1    rillig .endif
     73       1.1    rillig 
     74       1.1    rillig # Everything from the paragraph above also applies if the loop body is
     75      1.19    rillig # empty.  In this particular example, the items to be iterated are empty as
     76      1.19    rillig # well.
     77       1.1    rillig var=	value before
     78       1.1    rillig var2=	value before
     79       1.1    rillig .for var var2 in ${:U}
     80       1.1    rillig .endfor
     81       1.1    rillig .if ${var} != "value before"
     82       1.2    rillig .  warning After the .for loop, var must still have its original value.
     83       1.1    rillig .endif
     84       1.1    rillig .if ${var2} != "value before"
     85       1.2    rillig .  warning After the .for loop, var2 must still have its original value.
     86       1.1    rillig .endif
     87       1.1    rillig 
     88      1.21    rillig # Before for.c 1.39 from 2008-12-21, the values of the iteration variables
     89      1.21    rillig # were simply inserted as plain text and then parsed as usual, which made it
     90      1.21    rillig # possible to achieve all kinds of strange effects, such as generating '.if'
     91      1.19    rillig # directives or inserting '$' characters in random places, thereby changing
     92      1.19    rillig # how following '$' are interpreted.
     93       1.1    rillig #
     94      1.19    rillig # Before that date, the .for loop below expanded to:
     95       1.1    rillig #	EXPANSION+= value
     96      1.19    rillig # Since that date, the .for loop below expands to:
     97       1.1    rillig #	EXPANSION${:U+}= value
     98       1.1    rillig #
     99       1.6    rillig EXPANSION=		before
    100       1.6    rillig EXPANSION+ =		before
    101       1.1    rillig .for plus in +
    102       1.1    rillig EXPANSION${plus}=	value
    103       1.1    rillig .endfor
    104       1.1    rillig .if ${EXPANSION} != "before"
    105       1.1    rillig .  error This must be a make from before 2009.
    106       1.1    rillig .endif
    107       1.1    rillig .if ${EXPANSION+} != "value"
    108       1.1    rillig .  error This must be a make from before 2009.
    109       1.1    rillig .endif
    110       1.1    rillig 
    111       1.3    rillig # When the outer .for loop is expanded, it sees the expression ${i} and
    112      1.19    rillig # expands it.  The inner loop then only sees the expression ${:Uouter} and
    113      1.19    rillig # has nothing more to expand.
    114       1.3    rillig .for i in outer
    115       1.3    rillig .  for i in inner
    116      1.19    rillig # expect+1: outer
    117       1.3    rillig .    info ${i}
    118       1.3    rillig .  endfor
    119       1.3    rillig .endfor
    120       1.3    rillig 
    121      1.19    rillig 
    122       1.4    rillig # From https://gnats.netbsd.org/29985.
    123       1.4    rillig #
    124       1.4    rillig # Until 2008-12-21, the .for loop was expanded by replacing the variable
    125       1.4    rillig # value literally in the body.  This could lead to situations where the
    126       1.4    rillig # characters from the variable value were interpreted as markup rather than
    127       1.4    rillig # plain text.
    128       1.4    rillig #
    129       1.4    rillig # Until 2012-06-03, the .for loop had split the words at whitespace, without
    130       1.4    rillig # taking quotes into account.  This made it possible to have variable values
    131       1.4    rillig # like "a:\ a:\file.txt" that ended in a single backslash.  Since then, the
    132       1.4    rillig # variable values have been replaced with expressions of the form ${:U...},
    133       1.4    rillig # which are not interpreted as code anymore.
    134       1.5    rillig .for path in a:\ a:\file.txt d:\\ d:\\file.txt
    135  1.25.2.1  perseant # expect+3: a:\ a:\file.txt
    136  1.25.2.1  perseant # expect+2: d:\\
    137  1.25.2.1  perseant # expect+1: d:\\file.txt
    138       1.4    rillig .  info ${path}
    139       1.4    rillig .endfor
    140      1.19    rillig 
    141       1.4    rillig 
    142       1.7    rillig # Ensure that braces and parentheses are properly escaped by the .for loop.
    143       1.7    rillig # Each line must print the same word 3 times.
    144      1.11    rillig # See ForLoop_SubstBody.
    145       1.7    rillig .for v in ( [ { ) ] } (()) [[]] {{}} )( ][ }{
    146  1.25.2.1  perseant # expect+12: ( ( (
    147  1.25.2.1  perseant # expect+11: [ [ [
    148  1.25.2.1  perseant # expect+10: { { {
    149  1.25.2.1  perseant # expect+9: ) ) )
    150  1.25.2.1  perseant # expect+8: ] ] ]
    151  1.25.2.1  perseant # expect+7: } } }
    152  1.25.2.1  perseant # expect+6: (()) (()) (())
    153  1.25.2.1  perseant # expect+5: [[]] [[]] [[]]
    154  1.25.2.1  perseant # expect+4: {{}} {{}} {{}}
    155  1.25.2.1  perseant # expect+3: )( )( )(
    156  1.25.2.1  perseant # expect+2: ][ ][ ][
    157  1.25.2.1  perseant # expect+1: }{ }{ }{
    158       1.7    rillig .  info $v ${v} $(v)
    159       1.7    rillig .endfor
    160       1.7    rillig 
    161      1.20    rillig # Before 2023-05-09, the variable names could contain arbitrary characters,
    162      1.20    rillig # except for whitespace, allowing for creative side effects, as usual for
    163      1.20    rillig # arbitrary code injection.
    164       1.8    rillig var=	outer
    165  1.25.2.1  perseant # expect+1: Invalid character ":" in .for loop variable name
    166       1.8    rillig .for var:Q in value "quoted"
    167      1.18    rillig .  info <${var}> <${var:Q}> <${var:Q:Q}>
    168       1.8    rillig .endfor
    169      1.20    rillig 
    170      1.20    rillig # Before 2023-05-09, when variable names could contain '$', the short
    171      1.20    rillig # expression '$$' was preserved, the long expressions were substituted.
    172  1.25.2.1  perseant # expect+1: Invalid character "$" in .for loop variable name
    173      1.17    rillig .for $ in value
    174      1.18    rillig .  info <$$> <${$}> <$($)>
    175      1.17    rillig .endfor
    176      1.20    rillig 
    177      1.20    rillig 
    178      1.20    rillig # https://gnats.netbsd.org/53146 mentions the idea of using a dynamic
    179      1.20    rillig # variable name in .for loops, based on some other variable.  The .for loops
    180      1.20    rillig # are already tricky enough to understand in detail, even without this
    181      1.20    rillig # possibility, therefore the variable names are restricted to using harmless
    182      1.20    rillig # characters only.
    183      1.20    rillig INDIRECT=	direct
    184  1.25.2.1  perseant # expect+1: Invalid character "$" in .for loop variable name
    185      1.20    rillig .for $(INDIRECT) in value
    186      1.20    rillig # If the variable name could be chosen dynamically, the iteration variable
    187      1.20    rillig # might have been 'direct', thereby expanding the expression '${direct}'.
    188      1.20    rillig .  info <$(INDIRECT)> <$(direct)> <$($(INDIRECT))>
    189      1.17    rillig .endfor
    190       1.8    rillig 
    191      1.10    rillig 
    192      1.24    rillig # Regular global variables and the "variables" from the .for loop don't
    193      1.24    rillig # interfere with each other.  In the following snippet, the variable 'DIRECT'
    194      1.24    rillig # is used both as a global variable, as well as an iteration variable in the
    195      1.24    rillig # .for loop.  The expression '${INDIRECT}' refers to the global variable, not
    196      1.24    rillig # to the one from the .for loop.
    197      1.24    rillig DIRECT=		global
    198      1.24    rillig INDIRECT=	${DIRECT}
    199      1.24    rillig .for DIRECT in iteration
    200      1.24    rillig .  if "${DIRECT} ${INDIRECT}" != "iteration global"
    201      1.24    rillig .    error
    202      1.24    rillig .  endif
    203      1.24    rillig .endfor
    204      1.24    rillig 
    205      1.24    rillig 
    206      1.11    rillig # An empty list of variables to the left of the 'in' is a parse error.
    207  1.25.2.1  perseant # expect+1: Missing iteration variables in .for loop
    208  1.25.2.1  perseant .for in value
    209      1.19    rillig .  error
    210      1.19    rillig .endfor
    211      1.11    rillig 
    212      1.11    rillig # An empty list of iteration values to the right of the 'in' is accepted.
    213      1.11    rillig # Unlike in the shell, it is not a parse error.
    214      1.11    rillig .for var in
    215      1.11    rillig .  error
    216      1.11    rillig .endfor
    217      1.11    rillig 
    218      1.11    rillig # If the iteration values become empty after expanding the expressions, the
    219      1.11    rillig # body of the loop is not evaluated.  It is not a parse error.
    220      1.11    rillig .for var in ${:U}
    221      1.11    rillig .  error
    222      1.11    rillig .endfor
    223      1.11    rillig 
    224      1.11    rillig 
    225      1.11    rillig # The loop body can be empty.
    226      1.11    rillig .for var in 1 2 3
    227      1.11    rillig .endfor
    228      1.11    rillig 
    229      1.11    rillig 
    230      1.11    rillig # A mismatched .if inside a .for loop is detected each time when the loop body
    231      1.11    rillig # is processed.
    232      1.11    rillig .for var in value
    233      1.11    rillig .  if 0
    234  1.25.2.1  perseant .endfor
    235  1.25.2.1  perseant # expect-1: 1 open conditional
    236      1.11    rillig 
    237      1.11    rillig # If there are no iteration values, the loop body is not processed, and the
    238      1.11    rillig # check for mismatched conditionals is not performed.
    239      1.11    rillig .for var in ${:U}
    240      1.11    rillig .  if 0
    241      1.11    rillig .endfor
    242      1.11    rillig 
    243      1.11    rillig 
    244      1.11    rillig # When a .for without the corresponding .endfor occurs in an inactive branch
    245      1.11    rillig # of an .if, the .for directive is just skipped, it does not even need a
    246      1.11    rillig # corresponding .endfor.  In other words, the behavior of the parser depends
    247      1.11    rillig # on the actual values of the conditions in the .if clauses.
    248      1.11    rillig .if 0
    249      1.11    rillig .  for var in value		# does not need a corresponding .endfor
    250      1.11    rillig .endif
    251  1.25.2.1  perseant # expect+1: for-less endfor
    252  1.25.2.1  perseant .endfor
    253  1.25.2.1  perseant # expect+1: if-less endif
    254  1.25.2.1  perseant .endif
    255      1.11    rillig 
    256      1.11    rillig 
    257      1.11    rillig # When a .for without the corresponding .endfor occurs in an active branch of
    258      1.11    rillig # an .if, the parser just counts the number of .for and .endfor directives,
    259      1.11    rillig # without looking at any other directives.
    260      1.11    rillig .if 1
    261      1.11    rillig .  for var in value
    262  1.25.2.1  perseant # expect+1: if-less endif
    263  1.25.2.1  perseant .    endif
    264      1.11    rillig .  endfor			# no 'for-less endfor'
    265      1.11    rillig .endif				# no 'if-less endif'
    266      1.12    rillig 
    267      1.12    rillig 
    268      1.16    rillig # Before for.c 1.172 from 2023-05-08, when make parsed a .for loop, it
    269      1.16    rillig # assumed that there was no line continuation between the '.' and the 'for'
    270      1.16    rillig # or 'endfor', as there is no practical reason to break the line at this
    271      1.16    rillig # point.
    272      1.16    rillig #
    273      1.16    rillig # When make scanned the outer .for loop, it did not recognize the inner .for
    274      1.16    rillig # loop as such and instead treated it as an unknown directive.  The body of
    275      1.16    rillig # the outer .for loop thus ended above the '.endfor'.
    276      1.16    rillig #
    277      1.16    rillig # When make scanned the inner .for loop, it did not recognize the inner
    278      1.16    rillig # .endfor as such, which led to a parse error 'Unexpected end of file in .for
    279      1.16    rillig # loop' from the '.endfor' line, followed by a second parse error 'for-less
    280      1.16    rillig # .endfor' from the '.\\n endfor' line.
    281      1.12    rillig .MAKEFLAGS: -df
    282      1.12    rillig .for outer in o
    283      1.12    rillig .\
    284      1.12    rillig    for inner in i
    285      1.12    rillig .\
    286      1.12    rillig    endfor
    287      1.12    rillig .endfor
    288      1.12    rillig .MAKEFLAGS: -d0
    289      1.14    rillig 
    290      1.14    rillig 
    291      1.14    rillig # When there is a variable definition 'scope=cmdline' from the command line
    292      1.14    rillig # (which has higher precedence than global variables) and a .for loop iterates
    293      1.14    rillig # over a variable of the same name, the expression '${scope}' expands to the
    294      1.14    rillig # value from the .for loop.  This is because when the body of the .for loop is
    295      1.14    rillig # expanded, the expression '${scope}' is textually replaced with ${:Uloop}',
    296      1.15    rillig # without resolving any other variable names (ForLoop_SubstBody).  Later, when
    297      1.15    rillig # the body of the .for loop is actually interpreted, the body text doesn't
    298      1.15    rillig # contain the word 'scope' anymore.
    299      1.14    rillig .MAKEFLAGS: scope=cmdline
    300      1.14    rillig .for scope in loop
    301      1.14    rillig .  if ${scope} != "loop"
    302      1.14    rillig .    error
    303      1.14    rillig .  endif
    304      1.14    rillig .endfor
    305      1.20    rillig 
    306      1.20    rillig 
    307      1.20    rillig # Since at least 1993, iteration stops at the first newline.
    308      1.20    rillig # Back then, the .newline variable didn't exist, therefore it was unlikely
    309      1.20    rillig # that a newline ever occurred.
    310      1.20    rillig .for var in a${.newline}b${.newline}c
    311  1.25.2.1  perseant # expect+1: newline-item=(a)
    312      1.20    rillig .  info newline-item=(${var})
    313      1.20    rillig .endfor
    314