varname-makeflags.mk revision 1.6 1 # $NetBSD: varname-makeflags.mk,v 1.6 2023/02/25 11:59:12 rillig Exp $
2 #
3 # Tests for the special MAKEFLAGS variable, which is basically just a normal
4 # environment variable. It is closely related to .MAKEFLAGS but captures the
5 # state of .MAKEFLAGS at the very beginning of make, before any makefiles are
6 # read.
7
8 all: spaces_stage_0 dollars_stage_0 append_stage_0 override_stage_0
9
10
11 .if !make(*stage*)
12
13 # The unit tests are run with an almost empty environment. In particular,
14 # the variable MAKEFLAGS is not set. The '.MAKEFLAGS:' above also doesn't
15 # influence the environment variable MAKEFLAGS, therefore it is still
16 # undefined at this point.
17 . if ${MAKEFLAGS:Uundefined} != "undefined"
18 . error
19 . endif
20
21 # The special variable .MAKEFLAGS is influenced though.
22 # See varname-dot-makeflags.mk for more details.
23 . if ${.MAKEFLAGS} != " -r -k"
24 . error
25 . endif
26
27
28 # In POSIX mode, the environment variable MAKEFLAGS can contain letters only,
29 # for compatibility. These letters are exploded to form regular options.
30 OUTPUT!= env MAKEFLAGS=ikrs ${MAKE} -f /dev/null -v .MAKEFLAGS
31 . if ${OUTPUT} != " -i -k -r -s -V .MAKEFLAGS"
32 . error
33 . endif
34
35 # As soon as there is a single non-alphabetic character in the environment
36 # variable MAKEFLAGS, it is no longer split. In this example, the word
37 # "d0ikrs" is treated as a target, but the option '-v' prevents any targets
38 # from being built.
39 OUTPUT!= env MAKEFLAGS=d0ikrs ${MAKE} -r -f /dev/null -v .MAKEFLAGS
40 . if ${OUTPUT} != " -r -V .MAKEFLAGS"
41 . error ${OUTPUT}
42 . endif
43
44 .endif
45
46
47 # When options are parsed, the option and its argument are appended as
48 # separate words to the MAKEFLAGS for the child processes. Special characters
49 # in the option arguments are not quoted though.
50 spaces_stage_0:
51 @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
52 @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"
53 @${MAKE} -f ${MAKEFILE} spaces_stage_1 -d00000 -D"VARNAME WITH SPACES"
54
55 # At this point, the 'VARNAME WITH SPACES' is no longer recognizable as a
56 # single command line argument. In practice, variable names don't contain
57 # spaces.
58 spaces_stage_1:
59 @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
60 @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"
61
62
63 # Demonstrate that '$' characters are altered when they are passed on to child
64 # make processes via MAKEFLAGS.
65 dollars_stage_0:
66 @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
67
68 # The '$$$$' becomes a literal '$$' when building the '${MAKE}'
69 # command line, making the actual argument 'DOLLARS=$${varname}'.
70 # At this stage, MAKEFLAGS is not yet involved.
71 @${MAKE} -f ${MAKEFILE} dollars_stage_1 DOLLARS='$$$${varname}'
72
73 .if make(dollars_stage_1)
74 # At this point, the variable 'DOLLARS' contains '$${varname}', which
75 # evaluates to a literal '$' followed by '{varname}'.
76 . if ${DOLLARS} != "\${varname}"
77 . error
78 . endif
79 .endif
80 dollars_stage_1:
81 # At this point, the stage 1 make provides the environment variable
82 # 'MAKEFLAGS' to its child processes, even if the child process is not
83 # another make.
84 #
85 # expect: dollars_stage_1: env MAKEFLAGS=< -r -k DOLLARS=\$\{varname\}>
86 #
87 # The 'DOLLARS=\$\{varname\}' assignment is escaped so that the stage
88 # 2 make will see it as a single word.
89 @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"
90
91 # At this point, evaluating the environment variable 'MAKEFLAGS' leads
92 # to strange side effects as the string '\$\{varname\}' is interpreted
93 # as:
94 #
95 # \ a literal string of a single backslash
96 # $\ the value of the variable named '\'
97 # {varname\} a literal string
98 #
99 # Since the variable name '\' is not defined, the resulting value is
100 # '\{varname\}'. Make doesn't handle isolated '$' characters in
101 # strings well, instead each '$' has to be part of a '$$' or be part
102 # of a subexpression like '${VAR}'.
103 @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
104
105 # The modifier ':q' preserves a '$$' in an expression value instead of
106 # expanding it to a single '$', but it's already too late, as that
107 # modifier applies after the expression has been evaluated. Except
108 # for debug logging, there is no way to process strings that contain
109 # isolated '$'.
110 @echo '$@: MAKEFLAGS:q=<'${MAKEFLAGS:q}'>'
111
112 @${MAKE} -f ${MAKEFILE} dollars_stage_2
113
114 .if make(dollars_stage_2)
115 # At this point, the variable 'DOLLARS' contains '${varname}', and since
116 # 'varname' is undefined, that expression evaluates to an empty string.
117 . if ${DOLLARS} != ""
118 . error
119 . endif
120 varname= varvalue
121 . if ${DOLLARS} != "varvalue"
122 . error
123 . endif
124 . undef varname
125 .endif
126 dollars_stage_2:
127 @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"
128 @echo '$@: dollars=<'${DOLLARS:Q}'>'
129 @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
130 @${MAKE} -f ${MAKEFILE} dollars_stage_3
131
132 dollars_stage_3:
133 @echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"
134 @echo '$@: dollars=<'${DOLLARS:Uundefined:Q}'>'
135 @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
136
137
138 # Demonstrates in which exact order the MAKEFLAGS are built together from the
139 # parent MAKEFLAGS and the flags from the command line, in particular that
140 # variable assignments are passed at the end, after the options.
141 append_stage_0:
142 @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
143 @${MAKE} -Dbefore-0 -f ${MAKEFILE} append_stage_1 VAR0=value -Dafter-0
144
145 append_stage_1:
146 @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
147 @${MAKE} -Dbefore-1 -f ${MAKEFILE} append_stage_2 VAR1=value -Dafter-1
148
149 append_stage_2:
150 @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
151 @${MAKE} -Dbefore-2 -f ${MAKEFILE} append_stage_3 VAR2=value -Dafter-2
152
153 append_stage_3:
154 @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
155
156
157 # Demonstrates the implementation details of 'MAKEFLAGS', in particular that
158 # it is an environment variable rather than a global variable.
159 override_stage_0:
160 @${MAKE} -f ${MAKEFILE} STAGE=1 VAR=value override_stage_1
161
162 .if make(override_stage_1)
163 # While parsing the makefiles, 'MAKEFLAGS' is the value of the environment
164 # variable, in this case provided by stage 0.
165 . if ${MAKEFLAGS:M*} != "-r -k"
166 . error
167 . endif
168 MAKEFLAGS= overridden # temporarily override it
169 . if ${MAKEFLAGS} != "overridden"
170 . error
171 . endif
172 .undef MAKEFLAGS # make the environment variable visible again
173 . if ${MAKEFLAGS:M*} != "-r -k"
174 . error
175 . endif
176 .endif
177 override_stage_1:
178 @echo '$@: run MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
179 @${MAKE} -f ${MAKEFILE} STAGE=2 override_stage_2
180
181 override_stage_2:
182 @echo '$@: STAGE=<${STAGE}> VAR=<${VAR}>'
183