Home | History | Annotate | Line # | Download | only in unit-tests
directive-for.mk revision 1.32
      1  1.32  rillig # $NetBSD: directive-for.mk,v 1.32 2025/07/01 04:24:20 rillig 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.27  rillig # expect+3: a:\ a:\file.txt
    136  1.27  rillig # expect+2: d:\\
    137  1.27  rillig # 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.27  rillig # expect+12: ( ( (
    147  1.27  rillig # expect+11: [ [ [
    148  1.27  rillig # expect+10: { { {
    149  1.27  rillig # expect+9: ) ) )
    150  1.27  rillig # expect+8: ] ] ]
    151  1.27  rillig # expect+7: } } }
    152  1.27  rillig # expect+6: (()) (()) (())
    153  1.27  rillig # expect+5: [[]] [[]] [[]]
    154  1.27  rillig # expect+4: {{}} {{}} {{}}
    155  1.27  rillig # expect+3: )( )( )(
    156  1.27  rillig # expect+2: ][ ][ ][
    157  1.27  rillig # 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.31  rillig # 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.31  rillig # 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.31  rillig # 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.31  rillig # expect+1: Missing iteration variables in .for loop
    208  1.31  rillig .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.32  rillig .endfor
    235  1.32  rillig # 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.32  rillig # expect+1: for-less endfor
    252  1.32  rillig .endfor
    253  1.32  rillig # expect+1: if-less endif
    254  1.32  rillig .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.32  rillig # expect+1: if-less endif
    263  1.32  rillig .    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.27  rillig # expect+1: newline-item=(a)
    312  1.20  rillig .  info newline-item=(${var})
    313  1.20  rillig .endfor
    314