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