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