varname.mk revision 1.17 1 1.17 rillig # $NetBSD: varname.mk,v 1.17 2025/06/12 04:33:00 rillig Exp $
2 1.1 rillig #
3 1.17 rillig # Tests for variable names.
4 1.1 rillig
5 1.5 rillig .MAKEFLAGS: -dv
6 1.5 rillig
7 1.17 rillig # In a variable assignment, braces are allowed in the variable name, but they
8 1.17 rillig # must be balanced. Parentheses and braces may be mixed.
9 1.5 rillig VAR{{{}}}= 3 braces
10 1.5 rillig .if "${VAR{{{}}}}" != "3 braces"
11 1.5 rillig . error
12 1.5 rillig .endif
13 1.5 rillig
14 1.14 rillig # In expressions, the parser works differently. It doesn't treat
15 1.5 rillig # braces and parentheses equally, therefore the first closing brace already
16 1.5 rillig # marks the end of the variable name.
17 1.5 rillig VARNAME= VAR(((
18 1.5 rillig ${VARNAME}= 3 open parentheses
19 1.5 rillig .if "${VAR(((}}}}" != "3 open parentheses}}}"
20 1.5 rillig . error
21 1.5 rillig .endif
22 1.5 rillig
23 1.5 rillig # In the above test, the variable name is constructed indirectly. Neither
24 1.5 rillig # of the following expressions produces the intended effect.
25 1.7 rillig #
26 1.7 rillig # This is not a variable assignment since the parentheses and braces are not
27 1.7 rillig # balanced. At the end of the line, there are still 3 levels open, which
28 1.7 rillig # means the variable name is not finished.
29 1.16 rillig # expect+2: Missing ')' in archive specification
30 1.16 rillig # expect+1: Error in archive specification: "VAR"
31 1.6 rillig ${:UVAR(((}= try1
32 1.8 rillig # On the left-hand side of a variable assignments, the backslash is not parsed
33 1.8 rillig # as an escape character, therefore the parentheses still count to the nesting
34 1.8 rillig # level, which at the end of the line is still 3. Therefore this is not a
35 1.8 rillig # variable assignment as well.
36 1.13 rillig # expect+1: Invalid line '${:UVAR\(\(\(}= try2', expanded to 'VAR\(\(\(= try2'
37 1.6 rillig ${:UVAR\(\(\(}= try2
38 1.8 rillig # To assign to a variable with an arbitrary name, the variable name has to
39 1.8 rillig # come from an external source, not the text that is parsed in the assignment
40 1.8 rillig # itself. This is exactly the reason why further above, the indirect
41 1.8 rillig # ${VARNAME} works, while all other attempts fail.
42 1.8 rillig ${VARNAME}= try3
43 1.5 rillig
44 1.5 rillig .MAKEFLAGS: -d0
45 1.1 rillig
46 1.9 rillig # All variable names of a scope are stored in the same hash table, using a
47 1.10 rillig # simple hash function. Ensure that HashTable_Find handles collisions
48 1.9 rillig # correctly and that the correct variable is looked up. The strings "0x" and
49 1.9 rillig # "1Y" have the same hash code, as 31 * '0' + 'x' == 31 * '1' + 'Y'.
50 1.9 rillig V.0x= 0x
51 1.9 rillig V.1Y= 1Y
52 1.9 rillig .if ${V.0x} != "0x" || ${V.1Y} != "1Y"
53 1.9 rillig . error
54 1.9 rillig .endif
55 1.9 rillig
56 1.9 rillig # The string "ASDZguv", when used as a prefix of a variable name, keeps the
57 1.9 rillig # hash code unchanged, its own hash code is 0.
58 1.9 rillig ASDZguvV.0x= 0x
59 1.9 rillig ASDZguvV.1Y= 1Y
60 1.9 rillig .if ${ASDZguvV.0x} != "0x"
61 1.9 rillig . error
62 1.9 rillig .elif ${ASDZguvV.1Y} != "1Y"
63 1.9 rillig . error
64 1.9 rillig .endif
65 1.9 rillig
66 1.9 rillig # Ensure that variables with the same hash code whose name is a prefix of the
67 1.9 rillig # other can be accessed. In this case, the shorter variable name is defined
68 1.9 rillig # first to make it appear later in the bucket of the hash table.
69 1.9 rillig ASDZguv= once
70 1.9 rillig ASDZguvASDZguv= twice
71 1.9 rillig .if ${ASDZguv} != "once"
72 1.9 rillig . error
73 1.9 rillig .elif ${ASDZguvASDZguv} != "twice"
74 1.9 rillig . error
75 1.9 rillig .endif
76 1.9 rillig
77 1.9 rillig # Ensure that variables with the same hash code whose name is a prefix of the
78 1.9 rillig # other can be accessed. In this case, the longer variable name is defined
79 1.9 rillig # first to make it appear later in the bucket of the hash table.
80 1.9 rillig ASDZguvASDZguv.param= twice
81 1.9 rillig ASDZguv.param= once
82 1.9 rillig .if ${ASDZguv.param} != "once"
83 1.9 rillig . error
84 1.9 rillig .elif ${ASDZguvASDZguv.param} != "twice"
85 1.9 rillig . error
86 1.9 rillig .endif
87 1.9 rillig
88 1.17 rillig
89 1.17 rillig # Warn about expressions in the style of GNU make, as these would silently
90 1.17 rillig # expand to an empty string instead.
91 1.17 rillig #
92 1.17 rillig # https://pubs.opengroup.org/onlinepubs/9799919799/utilities/make.html says:
93 1.17 rillig # a macro name shall not contain an <equals-sign>, <blank>, or control
94 1.17 rillig # character.
95 1.17 rillig #
96 1.17 rillig GNU_MAKE_IF= $(if ${HAVE_STRLEN},yes,no)
97 1.17 rillig # expect+1: warning: Invalid character " " in variable name "if ,yes,no"
98 1.17 rillig .if ${GNU_MAKE_IF} != ""
99 1.17 rillig . error
100 1.17 rillig .endif
101 1.17 rillig #
102 1.17 rillig # This requirement needs to be ignored for expressions with a ":L" or ":?:"
103 1.17 rillig # modifier, as these modifiers rely on arbitrary characters in the expression
104 1.17 rillig # name.
105 1.17 rillig .if ${"left" == "right":?equal:unequal} != "unequal"
106 1.17 rillig . error
107 1.17 rillig .endif
108 1.17 rillig #
109 1.17 rillig # In fact, this requirement is ignored for any expression that has a modifier.
110 1.17 rillig # In this indirect case, though, the expression with the space in the name is
111 1.17 rillig # a nested expression, so the ":U" modifier doesn't affect the warning.
112 1.17 rillig # expect+1: warning: Invalid character " " in variable name "if ,yes,no"
113 1.17 rillig .if ${GNU_MAKE_IF:Ufallback} != ""
114 1.17 rillig . error
115 1.17 rillig .endif
116 1.17 rillig #
117 1.17 rillig # A modifier in a nested expression does not affect the warning.
118 1.17 rillig GNU_MAKE_IF_EXPR= $(if ${HAVE_STRLEN},${HEADERS:.h=.c},)
119 1.17 rillig # expect+1: warning: Invalid character " " in variable name "if ,,"
120 1.17 rillig .if ${GNU_MAKE_IF_EXPR} != ""
121 1.17 rillig . error
122 1.17 rillig .endif
123 1.17 rillig #
124 1.17 rillig # When the GNU make expression contains a colon, chances are good that the
125 1.17 rillig # colon is interpreted as an unknown modifier.
126 1.17 rillig GNU_MAKE_IF_MODIFIER= $(if ${HAVE_STRLEN},answer:yes,answer:no)
127 1.17 rillig # expect+1: Unknown modifier ":yes,answer"
128 1.17 rillig .if ${GNU_MAKE_IF_MODIFIER} != "no)"
129 1.17 rillig . error
130 1.17 rillig .endif
131 1.17 rillig #
132 1.17 rillig # If the variable name contains a non-printable character, the warning
133 1.17 rillig # contains the numeric character value instead, to prevent control sequences
134 1.17 rillig # in the output.
135 1.17 rillig CONTROL_CHARACTER= ${:U a b:ts\t}
136 1.17 rillig # expect+2: warning: Invalid character "\x09" in variable name "a b"
137 1.17 rillig # expect+1: Variable "a b" is undefined
138 1.17 rillig .if ${${CONTROL_CHARACTER}} != ""
139 1.17 rillig .endif
140 1.17 rillig #
141 1.17 rillig # For now, only whitespace generates a warning, non-ASCII characters don't.
142 1.17 rillig UMLAUT=
143 1.17 rillig # expect+1: Variable "" is undefined
144 1.17 rillig .if ${${UMLAUT}} != ""
145 1.17 rillig .endif
146