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