1# $NetBSD: varname-makeflags.mk,v 1.8 2023/06/01 07:27:30 rillig Exp $
2#
3# Tests for the environment variable 'MAKEFLAGS', from which additional
4# command line arguments are read before the actual command line arguments.
5#
6# After reading the makefiles and before making the targets, the arguments
7# that were collected in '.MAKEFLAGS' and '.MAKEOVERRIDES' are written back to
8# the environment variable 'MAKEFLAGS'.
9
10all: spaces_stage_0 dollars_stage_0 append_stage_0 override_stage_0
11
12
13.if !make(*stage*)
14
15# The unit tests are run with an almost empty environment.  In particular,
16# the variable MAKEFLAGS is not set.
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.
30OUTPUT!=  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.
39OUTPUT!=  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.
50spaces_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.
58spaces_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.
65dollars_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
80dollars_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 named '\' 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
120varname=  varvalue
121.  if ${DOLLARS} != "varvalue"
122.    error
123.  endif
124.  undef varname
125.endif
126dollars_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
132dollars_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 from the parent
139# MAKEFLAGS and the flags from the command line, in particular that variable
140# assignments are passed at the end, after the options.
141append_stage_0:
142          @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
143          @${MAKE} -Dbefore-0 -f ${MAKEFILE} append_stage_1 VAR0=value -Dafter-0
144
145append_stage_1:
146          @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
147          @${MAKE} -Dbefore-1 -f ${MAKEFILE} append_stage_2 VAR1=value -Dafter-1
148
149append_stage_2:
150          @echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
151          @${MAKE} -Dbefore-2 -f ${MAKEFILE} append_stage_3 VAR2=value -Dafter-2
152
153append_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.
159override_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
168MAKEFLAGS=          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
177override_stage_1:
178          @echo '$@: run MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
179          @${MAKE} -f ${MAKEFILE} STAGE=2 override_stage_2
180
181override_stage_2:
182          @echo '$@: STAGE=<${STAGE}> VAR=<${VAR}>'
183