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