1# $NetBSD: var-scope-local.mk,v 1.5 2022/02/09 21:09:24 rillig Exp $ 2# 3# Tests for target-local variables, such as ${.TARGET} or $@. These variables 4# are relatively short-lived as they are created just before making the 5# target. In contrast, global variables are typically created when the 6# makefiles are read in. 7# 8# The 7 built-in target-local variables are listed in the manual page. They 9# are defined just before the target is actually made. Additional 10# target-local variables can be defined in dependency lines like 11# 'target: VAR=value', one at a time. 12 13.MAIN: all 14 15# The target-local variables can be used in expressions, just like other 16# variables. When these expressions are evaluated outside of a target, these 17# expressions are not yet expanded, instead their text is preserved, to allow 18# these expressions to expand right in time when the target-local variables 19# are actually set. 20# 21# Conditions from .if directives are evaluated in the scope of the command 22# line, which means that variables from the command line, from the global 23# scope and from the environment are resolved, in this order (but see the 24# command line option '-e'). In that phase, expressions involving 25# target-local variables need to be preserved, including the exact names of 26# the variables. 27# 28# Each of the built-in target-local variables has two equivalent names, for 29# example '@' is equivalent to '.TARGET'. The implementation might 30# canonicalize these aliases at some point, and that might be surprising. 31# This aliasing happens for single-character variable names like $@ or $< 32# (see VarFind, CanonicalVarname), but not for braced or parenthesized 33# expressions like ${@}, ${.TARGET} ${VAR:Mpattern} (see Var_Parse, 34# ParseVarname). 35# 36# In the following condition, make expands '$@' to the long-format alias 37# '$(.TARGET)'; note that the alias is not written with braces, as would be 38# common in BSD makefiles, but with parentheses. This alternative spelling 39# behaves the same though. 40.if $@ != "\$\(.TARGET)" 41. error 42.endif 43# In the long form of writing a target-local variable, the text of the 44# expression is preserved exactly as written, no matter whether it is written 45# with '{' or '('. 46.if ${@} != "\$\{@}" 47. error 48.endif 49.if $(@) != "\$\(@)" 50. error 51.endif 52# If the variable expression contains modifiers, the behavior depends on the 53# actual modifiers. The modifier ':M' keeps the expression in the state 54# 'undefined'. Since the expression is still undefined after evaluating all 55# the modifiers, the value of the expression is discarded and the expression 56# text is used instead. This preserves the expressions based on target-local 57# variables as long as possible. 58.if ${@:M*} != "\$\{@:M*}" 59. error 60.endif 61# In the following examples, the expressions are based on target-local 62# variables but use the modifier ':L', which turns an undefined expression 63# into a defined one. At the end of evaluating the expression, the state of 64# the expression is not 'undefined' anymore. The value of the expression 65# is the name of the variable, since that's what the modifier ':L' does. 66.if ${@:L} != "@" 67. error 68.endif 69.if ${.TARGET:L} != ".TARGET" 70. error 71.endif 72.if ${@F:L} != "@F" 73. error 74.endif 75.if ${@D:L} != "@D" 76. error 77.endif 78 79 80# Additional target-local variables may be defined in dependency lines. 81.MAKEFLAGS: -dv 82# In the following line, the ':=' may either be interpreted as an assignment 83# operator or as the dependency operator ':', followed by an empty variable 84# name and the assignment operator '='. It is the latter since in an 85# assignment, the left-hand side must be at most a single word. The empty 86# variable name is expanded twice, once for 'one' and once for 'two'. 87# expect: Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored 88# expect: Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored 89one two:=three 90# If the two targets to the left are generated by a variable expression, the 91# line is parsed as a variable assignment since its left-hand side is a single 92# word. 93# expect: Global: one two = three 94${:Uone two}:=three 95.MAKEFLAGS: -d0 96 97 98.SUFFIXES: .c .o 99 100# One of the dynamic target-local variables is '.TARGET'. Since this is not 101# a suffix transformation rule, the variable '.IMPSRC' is not defined. 102# expect: : Making var-scope-local.c out of nothing. 103var-scope-local.c: 104 : Making ${.TARGET} ${.IMPSRC:Dfrom ${.IMPSRC}:Uout of nothing}. 105 106# This is a suffix transformation rule, so both '.TARGET' and '.IMPSRC' are 107# defined. 108# expect: : Making var-scope-local.o from var-scope-local.c. 109# expect: : Making basename "var-scope-local.o" in "." from "var-scope-local.c" in ".". 110.c.o: 111 : Making ${.TARGET} from ${.IMPSRC}. 112 113 # The local variables @F, @D, <F, <D are legacy forms. 114 # See the manual page for details. 115 : Making basename "${@F}" in "${@D}" from "${<F}" in "${<D}". 116 117# expect: : all overwritten 118all: var-scope-local.o 119 # The ::= modifier overwrites the .TARGET variable in the node 120 # 'all', not in the global scope. This can be seen with the -dv 121 # option, looking for "all: @ = overwritten". 122 : ${.TARGET} ${.TARGET::=overwritten}${.TARGET} 123 124 125# Begin tests for custom target-local variables, for all 5 variable assignment 126# operators. 127all: var-scope-local-assign.o 128all: var-scope-local-append.o 129all: var-scope-local-append-global.o 130all: var-scope-local-default.o 131all: var-scope-local-subst.o 132all: var-scope-local-shell.o 133 134var-scope-local-assign.o \ 135var-scope-local-append.o \ 136var-scope-local-append-global.o \ 137var-scope-local-default.o \ 138var-scope-local-subst.o \ 139var-scope-local-shell.o: 140 : Making ${.TARGET} with VAR="${VAR}". 141 142# Target-local variables are enabled by default. Force them to be enabled 143# just in case a test above has disabled them. 144.MAKE.TARGET_LOCAL_VARIABLES= yes 145 146VAR= global 147 148# If the sources of a dependency line look like a variable assignment, make 149# treats them as such. There is only a single variable assignment per 150# dependency line, which makes whitespace around the assignment operator 151# irrelevant. 152# 153# expect-reset 154# expect: : Making var-scope-local-assign.o with VAR="local". 155var-scope-local-assign.o: VAR= local 156 157# Assignments using '+=' do *not* look up the global value, instead they only 158# look up the variable in the target's own scope. 159var-scope-local-append.o: VAR+= local 160# Once a variable is defined in the target-local scope, appending using '+=' 161# behaves as expected. Note that the expression '${.TARGET}' is not resolved 162# when parsing the dependency line, its evaluation is deferred until the 163# target is actually made. 164# expect: : Making var-scope-local-append.o with VAR="local to var-scope-local-append.o". 165var-scope-local-append.o: VAR += to ${.TARGET} 166# To access the value of a global variable, use a variable expression. This 167# expression is expanded before parsing the whole dependency line. Since the 168# expansion happens to the right of the dependency operator ':', the expanded 169# text does not influence parsing of the dependency line. Since the expansion 170# happens to the right of the assignment operator '=', the expanded text does 171# not influence the parsing of the variable assignment. The effective 172# variable assignment, after expanding the whole line first, is thus 173# 'VAR= global+local'. 174# expect: : Making var-scope-local-append-global.o with VAR="global+local". 175var-scope-local-append-global.o: VAR= ${VAR}+local 176 177var-scope-local-default.o: VAR ?= first 178var-scope-local-default.o: VAR ?= second 179# XXX: '?=' does look at the global variable. That's a long-standing 180# inconsistency between the assignment operators '+=' and '?='. See 181# Var_AppendExpand and VarAssign_Eval. 182# expect: : Making var-scope-local-default.o with VAR="global". 183 184# Using the variable assignment operator ':=' provides another way of 185# accessing a global variable and extending it with local modifications. The 186# '$' has to be written as '$$' though to survive the expansion of the 187# dependency line as a whole. After that, the parser sees the variable 188# assignment as 'VAR := ${VAR}+local' and searches for the variable 'VAR' in 189# the usual scopes, picking up the variable from the global scope. 190# expect: : Making var-scope-local-subst.o with VAR="global+local". 191var-scope-local-subst.o: VAR := $${VAR}+local 192 193# The variable assignment operator '!=' assigns the output of the shell 194# command, as everywhere else. The shell command is run when the dependency 195# line is parsed. 196var-scope-local-shell.o: VAR != echo output 197 198 199# While VAR=use will be set for a .USE node, it will never be seen since only 200# the ultimate target's context is searched; the variable assignments from the 201# .USE target are not copied to the ultimate target's. 202# expect: : var-scope-local-use.o uses .USE VAR="global" 203a_use: .USE VAR=use 204 : ${.TARGET} uses .USE VAR="${VAR}" 205 206all: var-scope-local-use.o 207var-scope-local-use.o: a_use 208 209 210# Since parse.c 1.656 from 2022-01-27 and before parse.c 1.662 from 211# 2022-02-05, there was an out-of-bounds read in Parse_IsVar when looking for 212# a variable assignment in a dependency line with trailing whitespace. Lines 213# without trailing whitespace were not affected. Global variable assignments 214# were guaranteed to have no trailing whitespace and were thus not affected. 215# 216# Try to reproduce some variants that may lead to a crash, depending on the 217# memory allocator. To get a crash, the terminating '\0' of the line must be 218# the last byte of a memory page. The expression '${:U}' forces this trailing 219# whitespace. 220 221# On FreeBSD x86_64, a crash could in some cases be forced using the following 222# line, which has length 47, so the terminating '\0' may end up at an address 223# of the form 0xXXXX_XXXX_XXXX_Xfff: 224Try_to_crash_FreeBSD.xxxxxxxxxxxxxxxxxx: 12345 ${:U} 225 226# The following line has length 4095, so line[4095] == '\0'. If the line is 227# allocated on a page boundary and the following page is not mapped, this line 228# leads to a segmentation fault. 229${:U:range=511:@_@1234567@:ts.}: 12345 ${:U} 230 231# The following line has length 8191, so line[8191] == '\0'. If the line is 232# allocated on a page boundary and the following page is not mapped, this line 233# leads to a segmentation fault. 234${:U:range=1023:@_@1234567@:ts.}: 12345 ${:U} 235 23612345: 237