Home | History | Annotate | Line # | Download | only in unit-tests
directive-for.mk revision 1.24
      1  1.24  rillig # $NetBSD: directive-for.mk,v 1.24 2023/12/06 22:28: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.4  rillig .  info ${path}
    136   1.4  rillig .endfor
    137  1.19  rillig # expect-2: a:\ a:\file.txt
    138  1.19  rillig # expect-3: d:\\
    139  1.19  rillig # expect-4: d:\\file.txt
    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.7  rillig .  info $v ${v} $(v)
    147   1.7  rillig .endfor
    148  1.19  rillig # expect-02: ( ( (
    149  1.19  rillig # expect-03: [ [ [
    150  1.19  rillig # expect-04: { { {
    151  1.19  rillig # expect-05: ) ) )
    152  1.19  rillig # expect-06: ] ] ]
    153  1.19  rillig # expect-07: } } }
    154  1.19  rillig # expect-08: (()) (()) (())
    155  1.19  rillig # expect-09: [[]] [[]] [[]]
    156  1.19  rillig # expect-10: {{}} {{}} {{}}
    157  1.19  rillig # expect-11: )( )( )(
    158  1.19  rillig # expect-12: ][ ][ ][
    159  1.19  rillig # expect-13: }{ }{ }{
    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.18  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.18  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.18  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.10  rillig # XXX: A parse error or evaluation error in the items of the .for loop
    207  1.19  rillig # should skip the whole loop.  As of 2023-05-09, the loop is expanded as
    208  1.19  rillig # usual.
    209  1.19  rillig # expect+1: Unknown modifier "Z"
    210  1.23  rillig .for var in word1 before-${:Uword2:Z}-after word3
    211  1.23  rillig .  info XXX: Should not reach ${var}
    212  1.10  rillig .endfor
    213  1.23  rillig # expect-2: XXX: Should not reach word1
    214  1.23  rillig # expect-3: XXX: Should not reach before--after
    215  1.23  rillig # expect-4: XXX: Should not reach word3
    216  1.10  rillig 
    217  1.11  rillig 
    218  1.11  rillig # An empty list of variables to the left of the 'in' is a parse error.
    219  1.13  rillig .for in value			# expect+0: no iteration variables in for
    220  1.19  rillig .  error
    221  1.19  rillig .endfor
    222  1.11  rillig 
    223  1.11  rillig # An empty list of iteration values to the right of the 'in' is accepted.
    224  1.11  rillig # Unlike in the shell, it is not a parse error.
    225  1.11  rillig .for var in
    226  1.11  rillig .  error
    227  1.11  rillig .endfor
    228  1.11  rillig 
    229  1.11  rillig # If the iteration values become empty after expanding the expressions, the
    230  1.11  rillig # body of the loop is not evaluated.  It is not a parse error.
    231  1.11  rillig .for var in ${:U}
    232  1.11  rillig .  error
    233  1.11  rillig .endfor
    234  1.11  rillig 
    235  1.11  rillig 
    236  1.11  rillig # The loop body can be empty.
    237  1.11  rillig .for var in 1 2 3
    238  1.11  rillig .endfor
    239  1.11  rillig 
    240  1.11  rillig 
    241  1.11  rillig # A mismatched .if inside a .for loop is detected each time when the loop body
    242  1.11  rillig # is processed.
    243  1.11  rillig .for var in value
    244  1.11  rillig .  if 0
    245  1.13  rillig .endfor				# expect+0: 1 open conditional
    246  1.11  rillig 
    247  1.11  rillig # If there are no iteration values, the loop body is not processed, and the
    248  1.11  rillig # check for mismatched conditionals is not performed.
    249  1.11  rillig .for var in ${:U}
    250  1.11  rillig .  if 0
    251  1.11  rillig .endfor
    252  1.11  rillig 
    253  1.11  rillig 
    254  1.11  rillig # When a .for without the corresponding .endfor occurs in an inactive branch
    255  1.11  rillig # of an .if, the .for directive is just skipped, it does not even need a
    256  1.11  rillig # corresponding .endfor.  In other words, the behavior of the parser depends
    257  1.11  rillig # on the actual values of the conditions in the .if clauses.
    258  1.11  rillig .if 0
    259  1.11  rillig .  for var in value		# does not need a corresponding .endfor
    260  1.11  rillig .endif
    261  1.13  rillig .endfor				# expect+0: for-less endfor
    262  1.13  rillig .endif				# expect+0: if-less endif
    263  1.11  rillig 
    264  1.11  rillig 
    265  1.11  rillig # When a .for without the corresponding .endfor occurs in an active branch of
    266  1.11  rillig # an .if, the parser just counts the number of .for and .endfor directives,
    267  1.11  rillig # without looking at any other directives.
    268  1.11  rillig .if 1
    269  1.11  rillig .  for var in value
    270  1.13  rillig .    endif			# expect+0: if-less endif
    271  1.11  rillig .  endfor			# no 'for-less endfor'
    272  1.11  rillig .endif				# no 'if-less endif'
    273  1.12  rillig 
    274  1.12  rillig 
    275  1.16  rillig # Before for.c 1.172 from 2023-05-08, when make parsed a .for loop, it
    276  1.16  rillig # assumed that there was no line continuation between the '.' and the 'for'
    277  1.16  rillig # or 'endfor', as there is no practical reason to break the line at this
    278  1.16  rillig # point.
    279  1.16  rillig #
    280  1.16  rillig # When make scanned the outer .for loop, it did not recognize the inner .for
    281  1.16  rillig # loop as such and instead treated it as an unknown directive.  The body of
    282  1.16  rillig # the outer .for loop thus ended above the '.endfor'.
    283  1.16  rillig #
    284  1.16  rillig # When make scanned the inner .for loop, it did not recognize the inner
    285  1.16  rillig # .endfor as such, which led to a parse error 'Unexpected end of file in .for
    286  1.16  rillig # loop' from the '.endfor' line, followed by a second parse error 'for-less
    287  1.16  rillig # .endfor' from the '.\\n endfor' line.
    288  1.12  rillig .MAKEFLAGS: -df
    289  1.12  rillig .for outer in o
    290  1.12  rillig .\
    291  1.12  rillig    for inner in i
    292  1.12  rillig .\
    293  1.12  rillig    endfor
    294  1.12  rillig .endfor
    295  1.12  rillig .MAKEFLAGS: -d0
    296  1.14  rillig 
    297  1.14  rillig 
    298  1.14  rillig # When there is a variable definition 'scope=cmdline' from the command line
    299  1.14  rillig # (which has higher precedence than global variables) and a .for loop iterates
    300  1.14  rillig # over a variable of the same name, the expression '${scope}' expands to the
    301  1.14  rillig # value from the .for loop.  This is because when the body of the .for loop is
    302  1.14  rillig # expanded, the expression '${scope}' is textually replaced with ${:Uloop}',
    303  1.15  rillig # without resolving any other variable names (ForLoop_SubstBody).  Later, when
    304  1.15  rillig # the body of the .for loop is actually interpreted, the body text doesn't
    305  1.15  rillig # contain the word 'scope' anymore.
    306  1.14  rillig .MAKEFLAGS: scope=cmdline
    307  1.14  rillig .for scope in loop
    308  1.14  rillig .  if ${scope} != "loop"
    309  1.14  rillig .    error
    310  1.14  rillig .  endif
    311  1.14  rillig .endfor
    312  1.20  rillig 
    313  1.20  rillig 
    314  1.20  rillig # Since at least 1993, iteration stops at the first newline.
    315  1.20  rillig # Back then, the .newline variable didn't exist, therefore it was unlikely
    316  1.20  rillig # that a newline ever occurred.
    317  1.20  rillig .for var in a${.newline}b${.newline}c
    318  1.20  rillig .  info newline-item=(${var})
    319  1.20  rillig .endfor
    320  1.20  rillig # expect-2: newline-item=(a)
    321