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