Home | History | Annotate | Line # | Download | only in unit-tests
var-scope-local.mk revision 1.10
      1 # $NetBSD: var-scope-local.mk,v 1.10 2024/03/01 20:15:59 sjg 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>
     21 all: target-rule.ext dir/subdir/target-rule.ext
     22 target-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>
     35 all: target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from
     36 target-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
     57 all: inference-rule.ir-to dir/subdir/inference-rule.ir-to
     58 inference-rule.ir-from: .PHONY
     59 dir/subdir/inference-rule.ir-from: .PHONY
     60 
     61 # Target-local variables in a chain of inference rules
     62 all: inference-rule-chain.ir-to dir/subdir/inference-rule-chain.ir-to
     63 inference-rule-chain.ir-gen-from: .PHONY
     64 dir/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
    151 one 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.
    165 var-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
    180 all: 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.
    189 all: var-scope-local-assign.o
    190 all: var-scope-local-append.o
    191 all: var-scope-local-append-global.o
    192 all: var-scope-local-default.o
    193 all: var-scope-local-subst.o
    194 all: var-scope-local-shell.o
    195 
    196 var-scope-local-assign.o \
    197 var-scope-local-append.o \
    198 var-scope-local-append-global.o \
    199 var-scope-local-default.o \
    200 var-scope-local-subst.o \
    201 var-scope-local-shell.o:
    202 	: Making ${.TARGET} with VAR="${VAR}".
    203 	@echo "${.TARGET} env has VAR='$$VAR'"
    204 
    205 # Target-local variables are enabled by default.  Force them to be enabled
    206 # just in case a test above has disabled them.
    207 .MAKE.TARGET_LOCAL_VARIABLES= yes
    208 
    209 VAR=	global
    210 .export VAR
    211 
    212 # If the sources of a dependency line look like a variable assignment, make
    213 # treats them as such.  There is only a single variable assignment per
    214 # dependency line, which makes whitespace around the assignment operator
    215 # irrelevant.
    216 #
    217 # expect-reset
    218 # expect: : Making var-scope-local-assign.o with VAR="local".
    219 var-scope-local-assign.o: VAR= local
    220 
    221 # Assignments using '+=' do *not* look up the global value, instead they only
    222 # look up the variable in the target's own scope.
    223 var-scope-local-append.o: VAR+= local
    224 # Once a variable is defined in the target-local scope, appending using '+='
    225 # behaves as expected.  Note that the expression '${.TARGET}' is not resolved
    226 # when parsing the dependency line, its evaluation is deferred until the
    227 # target is actually made.
    228 # expect: : Making var-scope-local-append.o with VAR="local to var-scope-local-append.o".
    229 var-scope-local-append.o: VAR += to ${.TARGET}
    230 # To access the value of a global variable, use an expression.  This
    231 # expression is expanded before parsing the whole dependency line.  Since the
    232 # expansion happens to the right of the dependency operator ':', the expanded
    233 # text does not influence parsing of the dependency line.  Since the expansion
    234 # happens to the right of the assignment operator '=', the expanded text does
    235 # not influence the parsing of the variable assignment.  The effective
    236 # variable assignment, after expanding the whole line first, is thus
    237 # 'VAR= global+local'.
    238 # expect: : Making var-scope-local-append-global.o with VAR="global+local".
    239 var-scope-local-append-global.o: VAR= ${VAR}+local
    240 
    241 var-scope-local-default.o: VAR ?= first
    242 var-scope-local-default.o: VAR ?= second
    243 # XXX: '?=' does look at the global variable.  That's a long-standing
    244 # inconsistency between the assignment operators '+=' and '?='.  See
    245 # Var_AppendExpand and VarAssign_Eval.
    246 # expect: : Making var-scope-local-default.o with VAR="global".
    247 
    248 # Using the variable assignment operator ':=' provides another way of
    249 # accessing a global variable and extending it with local modifications.  The
    250 # '$' has to be written as '$$' though to survive the expansion of the
    251 # dependency line as a whole.  After that, the parser sees the variable
    252 # assignment as 'VAR := ${VAR}+local' and searches for the variable 'VAR' in
    253 # the usual scopes, picking up the variable from the global scope.
    254 # expect: : Making var-scope-local-subst.o with VAR="global+local".
    255 var-scope-local-subst.o: VAR := $${VAR}+local
    256 
    257 # The variable assignment operator '!=' assigns the output of the shell
    258 # command, as everywhere else.  The shell command is run when the dependency
    259 # line is parsed.
    260 var-scope-local-shell.o: VAR != echo output
    261 
    262 
    263 # While VAR=use will be set for a .USE node, it will never be seen since only
    264 # the ultimate target's context is searched; the variable assignments from the
    265 # .USE target are not copied to the ultimate target's.
    266 # expect: : var-scope-local-use.o uses .USE VAR="global"
    267 a_use: .USE VAR=use
    268 	: ${.TARGET} uses .USE VAR="${VAR}"
    269 	@echo "${.TARGET} env has VAR='$$VAR'"
    270 
    271 all: var-scope-local-use.o
    272 var-scope-local-use.o: a_use
    273