1# $NetBSD: var-scope-local.mk,v 1.9 2023/12/20 09:03:09 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# Target-local variables in a target rule 16# 17# In target rules, '$*' only strips the extension off the pathname if the 18# extension is listed in '.SUFFIXES'. 19# 20# expect: target-rule.ext: * = <target-rule.ext> 21all: target-rule.ext dir/subdir/target-rule.ext 22target-rule.ext dir/subdir/target-rule.ext: .PHONY 23 @echo '$@: @ = <${@:Uundefined}>' 24 @echo '$@: % = <${%:Uundefined}>' 25 @echo '$@: ? = <${?:Uundefined}>' 26 @echo '$@: < = <${<:Uundefined}>' 27 @echo '$@: * = <${*:Uundefined}>' 28 29.SUFFIXES: .ir-gen-from .ir-from .ir-to 30 31# In target rules, '$*' strips the extension off the pathname of the target 32# if the extension is listed in '.SUFFIXES'. 33# 34# expect: target-rule.ir-gen-from: * = <target-rule> 35all: target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from 36target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from: 37 @echo '$@: @ = <${@:Uundefined}>' 38 @echo '$@: % = <${%:Uundefined}>' 39 @echo '$@: ? = <${?:Uundefined}>' 40 @echo '$@: < = <${<:Uundefined}>' 41 @echo '$@: * = <${*:Uundefined}>' 42 43.ir-from.ir-to: 44 @echo '$@: @ = <${@:Uundefined}>' 45 @echo '$@: % = <${%:Uundefined}>' 46 @echo '$@: ? = <${?:Uundefined}>' 47 @echo '$@: < = <${<:Uundefined}>' 48 @echo '$@: * = <${*:Uundefined}>' 49.ir-gen-from.ir-from: 50 @echo '$@: @ = <${@:Uundefined}>' 51 @echo '$@: % = <${%:Uundefined}>' 52 @echo '$@: ? = <${?:Uundefined}>' 53 @echo '$@: < = <${<:Uundefined}>' 54 @echo '$@: * = <${*:Uundefined}>' 55 56# Target-local variables in an inference rule 57all: inference-rule.ir-to dir/subdir/inference-rule.ir-to 58inference-rule.ir-from: .PHONY 59dir/subdir/inference-rule.ir-from: .PHONY 60 61# Target-local variables in a chain of inference rules 62all: inference-rule-chain.ir-to dir/subdir/inference-rule-chain.ir-to 63inference-rule-chain.ir-gen-from: .PHONY 64dir/subdir/inference-rule-chain.ir-gen-from: .PHONY 65 66# The run-time 'check' directives from above happen after the parse-time 67# 'check' directives from below. 68# 69# expect-reset 70 71# Deferred evaluation during parsing 72# 73# The target-local variables can be used in expressions, just like other 74# variables. When these expressions are evaluated outside of a target, these 75# expressions are not yet expanded, instead their text is preserved, to allow 76# these expressions to expand right in time when the target-local variables 77# are actually set. 78# 79# Conditions from .if directives are evaluated in the scope of the command 80# line, which means that variables from the command line, from the global 81# scope and from the environment are resolved, in this precedence order (but 82# see the command line option '-e'). In that phase, expressions involving 83# target-local variables need to be preserved, including the exact names of 84# the variables. 85# 86# Each of the built-in target-local variables has two equivalent names, for 87# example '@' is equivalent to '.TARGET'. The implementation might 88# canonicalize these aliases at some point, and that might be surprising. 89# This aliasing happens for single-character variable names like $@ or $< 90# (see VarFind, CanonicalVarname), but not for braced or parenthesized 91# expressions like ${@}, ${.TARGET} ${VAR:Mpattern} (see Var_Parse, 92# ParseVarname). 93# 94# In the following condition, make expands '$@' to the long-format alias 95# '$(.TARGET)'; note that the alias is not written with braces, as would be 96# common in BSD makefiles, but with parentheses. This alternative spelling 97# behaves the same though. 98.if $@ != "\$\(.TARGET)" 99. error 100.endif 101# In the long form of writing a target-local variable, the text of the 102# expression is preserved exactly as written, no matter whether it is written 103# with '{' or '('. 104.if ${@} != "\$\{@}" 105. error 106.endif 107.if $(@) != "\$\(@)" 108. error 109.endif 110# If the expression contains modifiers, the behavior depends on the 111# actual modifiers. The modifier ':M' keeps the expression in the state 112# 'undefined'. Since the expression is still undefined after evaluating all 113# the modifiers, the value of the expression is discarded and the expression 114# text is used instead. This preserves the expressions based on target-local 115# variables as long as possible. 116.if ${@:M*} != "\$\{@:M*}" 117. error 118.endif 119# In the following examples, the expressions are based on target-local 120# variables but use the modifier ':L', which turns an undefined expression 121# into a defined one. At the end of evaluating the expression, the state of 122# the expression is not 'undefined' anymore. The value of the expression 123# is the name of the variable, since that's what the modifier ':L' does. 124.if ${@:L} != "@" 125. error 126.endif 127.if ${.TARGET:L} != ".TARGET" 128. error 129.endif 130.if ${@F:L} != "@F" 131. error 132.endif 133.if ${@D:L} != "@D" 134. error 135.endif 136 137 138# Custom local variables 139# 140# Additional target-local variables may be defined in dependency lines. 141.MAKEFLAGS: -dv 142# In the following line, the ':=' may either be interpreted as an assignment 143# operator or as the dependency operator ':', followed by an empty variable 144# name and the assignment operator '='. It is the latter since in an 145# assignment, the left-hand side must be a single word or empty. 146# 147# The empty variable name is expanded twice, once for 'one' and once for 148# 'two'. 149# expect: one: ignoring ' = three' as the variable name '' expands to empty 150# expect: two: ignoring ' = three' as the variable name '' expands to empty 151one two:=three 152# If the two targets to the left are generated by an expression, the 153# line is parsed as a variable assignment since its left-hand side is a single 154# word. 155# expect: Global: one two = three 156${:Uone two}:=three 157.MAKEFLAGS: -d0 158 159 160.SUFFIXES: .c .o 161 162# One of the dynamic target-local variables is '.TARGET'. Since this is not 163# a suffix transformation rule, the variable '.IMPSRC' is not defined. 164# expect: : Making var-scope-local.c out of nothing. 165var-scope-local.c: 166 : Making ${.TARGET} ${.IMPSRC:Dfrom ${.IMPSRC}:Uout of nothing}. 167 168# This is a suffix transformation rule, so both '.TARGET' and '.IMPSRC' are 169# defined. 170# expect: : Making var-scope-local.o from var-scope-local.c. 171# expect: : Making basename "var-scope-local.o" in "." from "var-scope-local.c" in ".". 172.c.o: 173 : Making ${.TARGET} from ${.IMPSRC}. 174 175 # The local variables @F, @D, <F, <D are legacy forms. 176 # See the manual page for details. 177 : Making basename "${@F}" in "${@D}" from "${<F}" in "${<D}". 178 179# expect: : all overwritten 180all: var-scope-local.o 181 # The ::= modifier overwrites the .TARGET variable in the node 182 # 'all', not in the global scope. This can be seen with the -dv 183 # option, looking for "all: @ = overwritten". 184 : ${.TARGET} ${.TARGET::=overwritten}${.TARGET} 185 186 187# Begin tests for custom target-local variables, for all 5 variable assignment 188# operators. 189all: var-scope-local-assign.o 190all: var-scope-local-append.o 191all: var-scope-local-append-global.o 192all: var-scope-local-default.o 193all: var-scope-local-subst.o 194all: var-scope-local-shell.o 195 196var-scope-local-assign.o \ 197var-scope-local-append.o \ 198var-scope-local-append-global.o \ 199var-scope-local-default.o \ 200var-scope-local-subst.o \ 201var-scope-local-shell.o: 202 : Making ${.TARGET} with VAR="${VAR}". 203 204# Target-local variables are enabled by default. Force them to be enabled 205# just in case a test above has disabled them. 206.MAKE.TARGET_LOCAL_VARIABLES= yes 207 208VAR= global 209 210# If the sources of a dependency line look like a variable assignment, make 211# treats them as such. There is only a single variable assignment per 212# dependency line, which makes whitespace around the assignment operator 213# irrelevant. 214# 215# expect-reset 216# expect: : Making var-scope-local-assign.o with VAR="local". 217var-scope-local-assign.o: VAR= local 218 219# Assignments using '+=' do *not* look up the global value, instead they only 220# look up the variable in the target's own scope. 221var-scope-local-append.o: VAR+= local 222# Once a variable is defined in the target-local scope, appending using '+=' 223# behaves as expected. Note that the expression '${.TARGET}' is not resolved 224# when parsing the dependency line, its evaluation is deferred until the 225# target is actually made. 226# expect: : Making var-scope-local-append.o with VAR="local to var-scope-local-append.o". 227var-scope-local-append.o: VAR += to ${.TARGET} 228# To access the value of a global variable, use an expression. This 229# expression is expanded before parsing the whole dependency line. Since the 230# expansion happens to the right of the dependency operator ':', the expanded 231# text does not influence parsing of the dependency line. Since the expansion 232# happens to the right of the assignment operator '=', the expanded text does 233# not influence the parsing of the variable assignment. The effective 234# variable assignment, after expanding the whole line first, is thus 235# 'VAR= global+local'. 236# expect: : Making var-scope-local-append-global.o with VAR="global+local". 237var-scope-local-append-global.o: VAR= ${VAR}+local 238 239var-scope-local-default.o: VAR ?= first 240var-scope-local-default.o: VAR ?= second 241# XXX: '?=' does look at the global variable. That's a long-standing 242# inconsistency between the assignment operators '+=' and '?='. See 243# Var_AppendExpand and VarAssign_Eval. 244# expect: : Making var-scope-local-default.o with VAR="global". 245 246# Using the variable assignment operator ':=' provides another way of 247# accessing a global variable and extending it with local modifications. The 248# '$' has to be written as '$$' though to survive the expansion of the 249# dependency line as a whole. After that, the parser sees the variable 250# assignment as 'VAR := ${VAR}+local' and searches for the variable 'VAR' in 251# the usual scopes, picking up the variable from the global scope. 252# expect: : Making var-scope-local-subst.o with VAR="global+local". 253var-scope-local-subst.o: VAR := $${VAR}+local 254 255# The variable assignment operator '!=' assigns the output of the shell 256# command, as everywhere else. The shell command is run when the dependency 257# line is parsed. 258var-scope-local-shell.o: VAR != echo output 259 260 261# While VAR=use will be set for a .USE node, it will never be seen since only 262# the ultimate target's context is searched; the variable assignments from the 263# .USE target are not copied to the ultimate target's. 264# expect: : var-scope-local-use.o uses .USE VAR="global" 265a_use: .USE VAR=use 266 : ${.TARGET} uses .USE VAR="${VAR}" 267 268all: var-scope-local-use.o 269var-scope-local-use.o: a_use 270