1*c59c3bf3SSimon J. Gerraty# $NetBSD: var-scope-local.mk,v 1.11 2024/03/05 23:07:58 rillig Exp $ 29f45a3c8SSimon J. Gerraty# 39f45a3c8SSimon J. Gerraty# Tests for target-local variables, such as ${.TARGET} or $@. These variables 49f45a3c8SSimon J. Gerraty# are relatively short-lived as they are created just before making the 59f45a3c8SSimon J. Gerraty# target. In contrast, global variables are typically created when the 69f45a3c8SSimon J. Gerraty# makefiles are read in. 79f45a3c8SSimon J. Gerraty# 89f45a3c8SSimon J. Gerraty# The 7 built-in target-local variables are listed in the manual page. They 99f45a3c8SSimon J. Gerraty# are defined just before the target is actually made. Additional 109f45a3c8SSimon J. Gerraty# target-local variables can be defined in dependency lines like 119f45a3c8SSimon J. Gerraty# 'target: VAR=value', one at a time. 129f45a3c8SSimon J. Gerraty 139f45a3c8SSimon J. Gerraty.MAIN: all 149f45a3c8SSimon J. Gerraty 15c1d01b5fSSimon J. Gerraty# Target-local variables in a target rule 16c1d01b5fSSimon J. Gerraty# 17c1d01b5fSSimon J. Gerraty# In target rules, '$*' only strips the extension off the pathname if the 18c1d01b5fSSimon J. Gerraty# extension is listed in '.SUFFIXES'. 19c1d01b5fSSimon J. Gerraty# 20c1d01b5fSSimon J. Gerraty# expect: target-rule.ext: * = <target-rule.ext> 21c1d01b5fSSimon J. Gerratyall: target-rule.ext dir/subdir/target-rule.ext 22c1d01b5fSSimon J. Gerratytarget-rule.ext dir/subdir/target-rule.ext: .PHONY 23c1d01b5fSSimon J. Gerraty @echo '$@: @ = <${@:Uundefined}>' 24c1d01b5fSSimon J. Gerraty @echo '$@: % = <${%:Uundefined}>' 25c1d01b5fSSimon J. Gerraty @echo '$@: ? = <${?:Uundefined}>' 26c1d01b5fSSimon J. Gerraty @echo '$@: < = <${<:Uundefined}>' 27c1d01b5fSSimon J. Gerraty @echo '$@: * = <${*:Uundefined}>' 28c1d01b5fSSimon J. Gerraty 29c1d01b5fSSimon J. Gerraty.SUFFIXES: .ir-gen-from .ir-from .ir-to 30c1d01b5fSSimon J. Gerraty 31c1d01b5fSSimon J. Gerraty# In target rules, '$*' strips the extension off the pathname of the target 32c1d01b5fSSimon J. Gerraty# if the extension is listed in '.SUFFIXES'. 33c1d01b5fSSimon J. Gerraty# 34c1d01b5fSSimon J. Gerraty# expect: target-rule.ir-gen-from: * = <target-rule> 35c1d01b5fSSimon J. Gerratyall: target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from 36c1d01b5fSSimon J. Gerratytarget-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from: 37c1d01b5fSSimon J. Gerraty @echo '$@: @ = <${@:Uundefined}>' 38c1d01b5fSSimon J. Gerraty @echo '$@: % = <${%:Uundefined}>' 39c1d01b5fSSimon J. Gerraty @echo '$@: ? = <${?:Uundefined}>' 40c1d01b5fSSimon J. Gerraty @echo '$@: < = <${<:Uundefined}>' 41c1d01b5fSSimon J. Gerraty @echo '$@: * = <${*:Uundefined}>' 42c1d01b5fSSimon J. Gerraty 43c1d01b5fSSimon J. Gerraty.ir-from.ir-to: 44c1d01b5fSSimon J. Gerraty @echo '$@: @ = <${@:Uundefined}>' 45c1d01b5fSSimon J. Gerraty @echo '$@: % = <${%:Uundefined}>' 46c1d01b5fSSimon J. Gerraty @echo '$@: ? = <${?:Uundefined}>' 47c1d01b5fSSimon J. Gerraty @echo '$@: < = <${<:Uundefined}>' 48c1d01b5fSSimon J. Gerraty @echo '$@: * = <${*:Uundefined}>' 49c1d01b5fSSimon J. Gerraty.ir-gen-from.ir-from: 50c1d01b5fSSimon J. Gerraty @echo '$@: @ = <${@:Uundefined}>' 51c1d01b5fSSimon J. Gerraty @echo '$@: % = <${%:Uundefined}>' 52c1d01b5fSSimon J. Gerraty @echo '$@: ? = <${?:Uundefined}>' 53c1d01b5fSSimon J. Gerraty @echo '$@: < = <${<:Uundefined}>' 54c1d01b5fSSimon J. Gerraty @echo '$@: * = <${*:Uundefined}>' 55c1d01b5fSSimon J. Gerraty 56c1d01b5fSSimon J. Gerraty# Target-local variables in an inference rule 57c1d01b5fSSimon J. Gerratyall: inference-rule.ir-to dir/subdir/inference-rule.ir-to 58c1d01b5fSSimon J. Gerratyinference-rule.ir-from: .PHONY 59c1d01b5fSSimon J. Gerratydir/subdir/inference-rule.ir-from: .PHONY 60c1d01b5fSSimon J. Gerraty 61c1d01b5fSSimon J. Gerraty# Target-local variables in a chain of inference rules 62c1d01b5fSSimon J. Gerratyall: inference-rule-chain.ir-to dir/subdir/inference-rule-chain.ir-to 63c1d01b5fSSimon J. Gerratyinference-rule-chain.ir-gen-from: .PHONY 64c1d01b5fSSimon J. Gerratydir/subdir/inference-rule-chain.ir-gen-from: .PHONY 65c1d01b5fSSimon J. Gerraty 66c1d01b5fSSimon J. Gerraty# The run-time 'check' directives from above happen after the parse-time 67c1d01b5fSSimon J. Gerraty# 'check' directives from below. 68c1d01b5fSSimon J. Gerraty# 69c1d01b5fSSimon J. Gerraty# expect-reset 70c1d01b5fSSimon J. Gerraty 71c1d01b5fSSimon J. Gerraty# Deferred evaluation during parsing 72c1d01b5fSSimon J. Gerraty# 739f45a3c8SSimon J. Gerraty# The target-local variables can be used in expressions, just like other 749f45a3c8SSimon J. Gerraty# variables. When these expressions are evaluated outside of a target, these 759f45a3c8SSimon J. Gerraty# expressions are not yet expanded, instead their text is preserved, to allow 769f45a3c8SSimon J. Gerraty# these expressions to expand right in time when the target-local variables 779f45a3c8SSimon J. Gerraty# are actually set. 789f45a3c8SSimon J. Gerraty# 791d3f2ddcSSimon J. Gerraty# Conditions from .if directives are evaluated in the scope of the command 809f45a3c8SSimon J. Gerraty# line, which means that variables from the command line, from the global 81c1d01b5fSSimon J. Gerraty# scope and from the environment are resolved, in this precedence order (but 82c1d01b5fSSimon J. Gerraty# see the command line option '-e'). In that phase, expressions involving 839f45a3c8SSimon J. Gerraty# target-local variables need to be preserved, including the exact names of 849f45a3c8SSimon J. Gerraty# the variables. 859f45a3c8SSimon J. Gerraty# 869f45a3c8SSimon J. Gerraty# Each of the built-in target-local variables has two equivalent names, for 879f45a3c8SSimon J. Gerraty# example '@' is equivalent to '.TARGET'. The implementation might 889f45a3c8SSimon J. Gerraty# canonicalize these aliases at some point, and that might be surprising. 899f45a3c8SSimon J. Gerraty# This aliasing happens for single-character variable names like $@ or $< 909f45a3c8SSimon J. Gerraty# (see VarFind, CanonicalVarname), but not for braced or parenthesized 919f45a3c8SSimon J. Gerraty# expressions like ${@}, ${.TARGET} ${VAR:Mpattern} (see Var_Parse, 929f45a3c8SSimon J. Gerraty# ParseVarname). 939f45a3c8SSimon J. Gerraty# 941d3f2ddcSSimon J. Gerraty# In the following condition, make expands '$@' to the long-format alias 951d3f2ddcSSimon J. Gerraty# '$(.TARGET)'; note that the alias is not written with braces, as would be 961d3f2ddcSSimon J. Gerraty# common in BSD makefiles, but with parentheses. This alternative spelling 971d3f2ddcSSimon J. Gerraty# behaves the same though. 989f45a3c8SSimon J. Gerraty.if $@ != "\$\(.TARGET)" 999f45a3c8SSimon J. Gerraty. error 1009f45a3c8SSimon J. Gerraty.endif 1011d3f2ddcSSimon J. Gerraty# In the long form of writing a target-local variable, the text of the 1021d3f2ddcSSimon J. Gerraty# expression is preserved exactly as written, no matter whether it is written 1031d3f2ddcSSimon J. Gerraty# with '{' or '('. 1049f45a3c8SSimon J. Gerraty.if ${@} != "\$\{@}" 1059f45a3c8SSimon J. Gerraty. error 1069f45a3c8SSimon J. Gerraty.endif 1079f45a3c8SSimon J. Gerraty.if $(@) != "\$\(@)" 1089f45a3c8SSimon J. Gerraty. error 1099f45a3c8SSimon J. Gerraty.endif 110d5e0a182SSimon J. Gerraty# If the expression contains modifiers, the behavior depends on the 1119f45a3c8SSimon J. Gerraty# actual modifiers. The modifier ':M' keeps the expression in the state 1129f45a3c8SSimon J. Gerraty# 'undefined'. Since the expression is still undefined after evaluating all 1139f45a3c8SSimon J. Gerraty# the modifiers, the value of the expression is discarded and the expression 1149f45a3c8SSimon J. Gerraty# text is used instead. This preserves the expressions based on target-local 1159f45a3c8SSimon J. Gerraty# variables as long as possible. 1169f45a3c8SSimon J. Gerraty.if ${@:M*} != "\$\{@:M*}" 1179f45a3c8SSimon J. Gerraty. error 1189f45a3c8SSimon J. Gerraty.endif 1199f45a3c8SSimon J. Gerraty# In the following examples, the expressions are based on target-local 1209f45a3c8SSimon J. Gerraty# variables but use the modifier ':L', which turns an undefined expression 1219f45a3c8SSimon J. Gerraty# into a defined one. At the end of evaluating the expression, the state of 1221d3f2ddcSSimon J. Gerraty# the expression is not 'undefined' anymore. The value of the expression 1239f45a3c8SSimon J. Gerraty# is the name of the variable, since that's what the modifier ':L' does. 1249f45a3c8SSimon J. Gerraty.if ${@:L} != "@" 1259f45a3c8SSimon J. Gerraty. error 1269f45a3c8SSimon J. Gerraty.endif 1279f45a3c8SSimon J. Gerraty.if ${.TARGET:L} != ".TARGET" 1289f45a3c8SSimon J. Gerraty. error 1299f45a3c8SSimon J. Gerraty.endif 1309f45a3c8SSimon J. Gerraty.if ${@F:L} != "@F" 1319f45a3c8SSimon J. Gerraty. error 1329f45a3c8SSimon J. Gerraty.endif 1339f45a3c8SSimon J. Gerraty.if ${@D:L} != "@D" 1349f45a3c8SSimon J. Gerraty. error 1359f45a3c8SSimon J. Gerraty.endif 1369f45a3c8SSimon J. Gerraty 1379f45a3c8SSimon J. Gerraty 138c1d01b5fSSimon J. Gerraty# Custom local variables 139c1d01b5fSSimon J. Gerraty# 1409f45a3c8SSimon J. Gerraty# Additional target-local variables may be defined in dependency lines. 1419f45a3c8SSimon J. Gerraty.MAKEFLAGS: -dv 1429f45a3c8SSimon J. Gerraty# In the following line, the ':=' may either be interpreted as an assignment 1439f45a3c8SSimon J. Gerraty# operator or as the dependency operator ':', followed by an empty variable 1449f45a3c8SSimon J. Gerraty# name and the assignment operator '='. It is the latter since in an 145c1d01b5fSSimon J. Gerraty# assignment, the left-hand side must be a single word or empty. 146c1d01b5fSSimon J. Gerraty# 147c1d01b5fSSimon J. Gerraty# The empty variable name is expanded twice, once for 'one' and once for 148c1d01b5fSSimon J. Gerraty# 'two'. 149d5e0a182SSimon J. Gerraty# expect: one: ignoring ' = three' as the variable name '' expands to empty 150d5e0a182SSimon J. Gerraty# expect: two: ignoring ' = three' as the variable name '' expands to empty 1519f45a3c8SSimon J. Gerratyone two:=three 152d5e0a182SSimon J. Gerraty# If the two targets to the left are generated by an expression, the 1539f45a3c8SSimon J. Gerraty# line is parsed as a variable assignment since its left-hand side is a single 1549f45a3c8SSimon J. Gerraty# word. 1559f45a3c8SSimon J. Gerraty# expect: Global: one two = three 1569f45a3c8SSimon J. Gerraty${:Uone two}:=three 1579f45a3c8SSimon J. Gerraty.MAKEFLAGS: -d0 1589f45a3c8SSimon J. Gerraty 1599f45a3c8SSimon J. Gerraty 1609f45a3c8SSimon J. Gerraty.SUFFIXES: .c .o 1619f45a3c8SSimon J. Gerraty 1629f45a3c8SSimon J. Gerraty# One of the dynamic target-local variables is '.TARGET'. Since this is not 1639f45a3c8SSimon J. Gerraty# a suffix transformation rule, the variable '.IMPSRC' is not defined. 1649f45a3c8SSimon J. Gerraty# expect: : Making var-scope-local.c out of nothing. 1659f45a3c8SSimon J. Gerratyvar-scope-local.c: 1669f45a3c8SSimon J. Gerraty : Making ${.TARGET} ${.IMPSRC:Dfrom ${.IMPSRC}:Uout of nothing}. 1679f45a3c8SSimon J. Gerraty 1689f45a3c8SSimon J. Gerraty# This is a suffix transformation rule, so both '.TARGET' and '.IMPSRC' are 1699f45a3c8SSimon J. Gerraty# defined. 1709f45a3c8SSimon J. Gerraty# expect: : Making var-scope-local.o from var-scope-local.c. 1719f45a3c8SSimon J. Gerraty# expect: : Making basename "var-scope-local.o" in "." from "var-scope-local.c" in ".". 1729f45a3c8SSimon J. Gerraty.c.o: 1739f45a3c8SSimon J. Gerraty : Making ${.TARGET} from ${.IMPSRC}. 1749f45a3c8SSimon J. Gerraty 1759f45a3c8SSimon J. Gerraty # The local variables @F, @D, <F, <D are legacy forms. 1769f45a3c8SSimon J. Gerraty # See the manual page for details. 1779f45a3c8SSimon J. Gerraty : Making basename "${@F}" in "${@D}" from "${<F}" in "${<D}". 1789f45a3c8SSimon J. Gerraty 1799f45a3c8SSimon J. Gerraty# expect: : all overwritten 1809f45a3c8SSimon J. Gerratyall: var-scope-local.o 1819f45a3c8SSimon J. Gerraty # The ::= modifier overwrites the .TARGET variable in the node 1829f45a3c8SSimon J. Gerraty # 'all', not in the global scope. This can be seen with the -dv 1839f45a3c8SSimon J. Gerraty # option, looking for "all: @ = overwritten". 1849f45a3c8SSimon J. Gerraty : ${.TARGET} ${.TARGET::=overwritten}${.TARGET} 1859f45a3c8SSimon J. Gerraty 1869f45a3c8SSimon J. Gerraty 1879f45a3c8SSimon J. Gerraty# Begin tests for custom target-local variables, for all 5 variable assignment 1889f45a3c8SSimon J. Gerraty# operators. 1899f45a3c8SSimon J. Gerratyall: var-scope-local-assign.o 1909f45a3c8SSimon J. Gerratyall: var-scope-local-append.o 1919f45a3c8SSimon J. Gerratyall: var-scope-local-append-global.o 1929f45a3c8SSimon J. Gerratyall: var-scope-local-default.o 1939f45a3c8SSimon J. Gerratyall: var-scope-local-subst.o 1949f45a3c8SSimon J. Gerratyall: var-scope-local-shell.o 1959f45a3c8SSimon J. Gerraty 1969f45a3c8SSimon J. Gerratyvar-scope-local-assign.o \ 1979f45a3c8SSimon J. Gerratyvar-scope-local-append.o \ 1989f45a3c8SSimon J. Gerratyvar-scope-local-append-global.o \ 1999f45a3c8SSimon J. Gerratyvar-scope-local-default.o \ 2009f45a3c8SSimon J. Gerratyvar-scope-local-subst.o \ 2019f45a3c8SSimon J. Gerratyvar-scope-local-shell.o: 202*c59c3bf3SSimon J. Gerraty @echo "Making ${.TARGET} with make '"${VAR:Q}"' and env '$$VAR'." 2039f45a3c8SSimon J. Gerraty 2049f45a3c8SSimon J. Gerraty# Target-local variables are enabled by default. Force them to be enabled 2059f45a3c8SSimon J. Gerraty# just in case a test above has disabled them. 2069f45a3c8SSimon J. Gerraty.MAKE.TARGET_LOCAL_VARIABLES= yes 2079f45a3c8SSimon J. Gerraty 2089f45a3c8SSimon J. GerratyVAR= global 209*c59c3bf3SSimon J. Gerraty.export VAR 2109f45a3c8SSimon J. Gerraty 2119f45a3c8SSimon J. Gerraty# If the sources of a dependency line look like a variable assignment, make 2129f45a3c8SSimon J. Gerraty# treats them as such. There is only a single variable assignment per 2139f45a3c8SSimon J. Gerraty# dependency line, which makes whitespace around the assignment operator 2149f45a3c8SSimon J. Gerraty# irrelevant. 2159f45a3c8SSimon J. Gerraty# 2169f45a3c8SSimon J. Gerraty# expect-reset 217*c59c3bf3SSimon J. Gerraty# expect: Making var-scope-local-assign.o with make 'local' and env 'local'. 2189f45a3c8SSimon J. Gerratyvar-scope-local-assign.o: VAR= local 2199f45a3c8SSimon J. Gerraty 2209f45a3c8SSimon J. Gerraty# Assignments using '+=' do *not* look up the global value, instead they only 2219f45a3c8SSimon J. Gerraty# look up the variable in the target's own scope. 2229f45a3c8SSimon J. Gerratyvar-scope-local-append.o: VAR+= local 2239f45a3c8SSimon J. Gerraty# Once a variable is defined in the target-local scope, appending using '+=' 2249f45a3c8SSimon J. Gerraty# behaves as expected. Note that the expression '${.TARGET}' is not resolved 2259f45a3c8SSimon J. Gerraty# when parsing the dependency line, its evaluation is deferred until the 2269f45a3c8SSimon J. Gerraty# target is actually made. 227*c59c3bf3SSimon J. Gerraty# expect: Making var-scope-local-append.o with make 'local to var-scope-local-append.o' and env 'local to var-scope-local-append.o'. 2289f45a3c8SSimon J. Gerratyvar-scope-local-append.o: VAR += to ${.TARGET} 229d5e0a182SSimon J. Gerraty# To access the value of a global variable, use an expression. This 2309f45a3c8SSimon J. Gerraty# expression is expanded before parsing the whole dependency line. Since the 2311d3f2ddcSSimon J. Gerraty# expansion happens to the right of the dependency operator ':', the expanded 2321d3f2ddcSSimon J. Gerraty# text does not influence parsing of the dependency line. Since the expansion 2331d3f2ddcSSimon J. Gerraty# happens to the right of the assignment operator '=', the expanded text does 2341d3f2ddcSSimon J. Gerraty# not influence the parsing of the variable assignment. The effective 2351d3f2ddcSSimon J. Gerraty# variable assignment, after expanding the whole line first, is thus 2369f45a3c8SSimon J. Gerraty# 'VAR= global+local'. 237*c59c3bf3SSimon J. Gerraty# expect: Making var-scope-local-append-global.o with make 'global+local' and env 'global+local'. 2389f45a3c8SSimon J. Gerratyvar-scope-local-append-global.o: VAR= ${VAR}+local 2399f45a3c8SSimon J. Gerraty 2409f45a3c8SSimon J. Gerratyvar-scope-local-default.o: VAR ?= first 2419f45a3c8SSimon J. Gerratyvar-scope-local-default.o: VAR ?= second 2429f45a3c8SSimon J. Gerraty# XXX: '?=' does look at the global variable. That's a long-standing 2439f45a3c8SSimon J. Gerraty# inconsistency between the assignment operators '+=' and '?='. See 2449f45a3c8SSimon J. Gerraty# Var_AppendExpand and VarAssign_Eval. 245*c59c3bf3SSimon J. Gerraty# expect: Making var-scope-local-default.o with make 'global' and env 'global'. 2469f45a3c8SSimon J. Gerraty 2479f45a3c8SSimon J. Gerraty# Using the variable assignment operator ':=' provides another way of 2489f45a3c8SSimon J. Gerraty# accessing a global variable and extending it with local modifications. The 2499f45a3c8SSimon J. Gerraty# '$' has to be written as '$$' though to survive the expansion of the 2501d3f2ddcSSimon J. Gerraty# dependency line as a whole. After that, the parser sees the variable 2511d3f2ddcSSimon J. Gerraty# assignment as 'VAR := ${VAR}+local' and searches for the variable 'VAR' in 2521d3f2ddcSSimon J. Gerraty# the usual scopes, picking up the variable from the global scope. 253*c59c3bf3SSimon J. Gerraty# expect: Making var-scope-local-subst.o with make 'global+local' and env 'global+local'. 2549f45a3c8SSimon J. Gerratyvar-scope-local-subst.o: VAR := $${VAR}+local 2559f45a3c8SSimon J. Gerraty 2569f45a3c8SSimon J. Gerraty# The variable assignment operator '!=' assigns the output of the shell 2571d3f2ddcSSimon J. Gerraty# command, as everywhere else. The shell command is run when the dependency 2581d3f2ddcSSimon J. Gerraty# line is parsed. 2599f45a3c8SSimon J. Gerratyvar-scope-local-shell.o: VAR != echo output 2609f45a3c8SSimon J. Gerraty 2619f45a3c8SSimon J. Gerraty 2629f45a3c8SSimon J. Gerraty# While VAR=use will be set for a .USE node, it will never be seen since only 2639f45a3c8SSimon J. Gerraty# the ultimate target's context is searched; the variable assignments from the 2649f45a3c8SSimon J. Gerraty# .USE target are not copied to the ultimate target's. 265*c59c3bf3SSimon J. Gerraty# expect: Making .USE var-scope-local-use.o with make 'global' and env 'global'. 2669f45a3c8SSimon J. Gerratya_use: .USE VAR=use 267*c59c3bf3SSimon J. Gerraty @echo "Making .USE ${.TARGET} with make '"${VAR:Q}"' and env '$$VAR'." 2689f45a3c8SSimon J. Gerraty 2699f45a3c8SSimon J. Gerratyall: var-scope-local-use.o 2709f45a3c8SSimon J. Gerratyvar-scope-local-use.o: a_use 271