Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

# $NetBSD: varname-makeflags.mk,v 1.8 2023/06/01 07:27:30 rillig Exp $
#
# Tests for the environment variable 'MAKEFLAGS', from which additional
# command line arguments are read before the actual command line arguments.
#
# After reading the makefiles and before making the targets, the arguments
# that were collected in '.MAKEFLAGS' and '.MAKEOVERRIDES' are written back to
# the environment variable 'MAKEFLAGS'.

all: spaces_stage_0 dollars_stage_0 append_stage_0 override_stage_0


.if !make(*stage*)

# The unit tests are run with an almost empty environment.  In particular,
# the variable MAKEFLAGS is not set.
.  if ${MAKEFLAGS:Uundefined} != "undefined"
.    error
.  endif

# The special variable .MAKEFLAGS is influenced though.
# See varname-dot-makeflags.mk for more details.
.  if ${.MAKEFLAGS} != " -r -k"
.    error
.  endif


# In POSIX mode, the environment variable MAKEFLAGS can contain letters only,
# for compatibility.  These letters are exploded to form regular options.
OUTPUT!=	env MAKEFLAGS=ikrs ${MAKE} -f /dev/null -v .MAKEFLAGS
.  if ${OUTPUT} != " -i -k -r -s -V .MAKEFLAGS"
.    error
.  endif

# As soon as there is a single non-alphabetic character in the environment
# variable MAKEFLAGS, it is no longer split.  In this example, the word
# "d0ikrs" is treated as a target, but the option '-v' prevents any targets
# from being built.
OUTPUT!=	env MAKEFLAGS=d0ikrs ${MAKE} -r -f /dev/null -v .MAKEFLAGS
.  if ${OUTPUT} != " -r -V .MAKEFLAGS"
.    error ${OUTPUT}
.  endif

.endif


# When options are parsed, the option and its argument are appended as
# separate words to the MAKEFLAGS for the child processes.  Special characters
# in the option arguments are not quoted though.
spaces_stage_0:
	@echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
	@echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"
	@${MAKE} -f ${MAKEFILE} spaces_stage_1 -d00000 -D"VARNAME WITH SPACES"

# At this point, the 'VARNAME WITH SPACES' is no longer recognizable as a
# single command line argument.  In practice, variable names don't contain
# spaces.
spaces_stage_1:
	@echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
	@echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"


# Demonstrate that '$' characters are altered when they are passed on to child
# make processes via MAKEFLAGS.
dollars_stage_0:
	@echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'

	# The '$$$$' becomes a literal '$$' when building the '${MAKE}'
	# command line, making the actual argument 'DOLLARS=$${varname}'.
	# At this stage, MAKEFLAGS is not yet involved.
	@${MAKE} -f ${MAKEFILE} dollars_stage_1 DOLLARS='$$$${varname}'

.if make(dollars_stage_1)
# At this point, the variable 'DOLLARS' contains '$${varname}', which
# evaluates to a literal '$' followed by '{varname}'.
.  if ${DOLLARS} != "\${varname}"
.    error
.  endif
.endif
dollars_stage_1:
	# At this point, the stage 1 make provides the environment variable
	# 'MAKEFLAGS' to its child processes, even if the child process is not
	# another make.
	#
	# expect: dollars_stage_1: env MAKEFLAGS=< -r -k DOLLARS=\$\{varname\}>
	#
	# The 'DOLLARS=\$\{varname\}' assignment is escaped so that the stage
	# 2 make will see it as a single word.
	@echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"

	# At this point, evaluating the environment variable 'MAKEFLAGS' leads
	# to strange side effects as the string '\$\{varname\}' is interpreted
	# as:
	#
	#	\		a literal string of a single backslash
	#	$\		the value of the variable named '\'
	#	{varname\}	a literal string
	#
	# Since the variable named '\' is not defined, the resulting value is
	# '\{varname\}'.  Make doesn't handle isolated '$' characters in
	# strings well, instead each '$' has to be part of a '$$' or be part
	# of a subexpression like '${VAR}'.
	@echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'

	# The modifier ':q' preserves a '$$' in an expression value instead of
	# expanding it to a single '$', but it's already too late, as that
	# modifier applies after the expression has been evaluated.  Except
	# for debug logging, there is no way to process strings that contain
	# isolated '$'.
	@echo '$@: MAKEFLAGS:q=<'${MAKEFLAGS:q}'>'

	@${MAKE} -f ${MAKEFILE} dollars_stage_2

.if make(dollars_stage_2)
# At this point, the variable 'DOLLARS' contains '${varname}', and since
# 'varname' is undefined, that expression evaluates to an empty string.
.  if ${DOLLARS} != ""
.    error
.  endif
varname=	varvalue
.  if ${DOLLARS} != "varvalue"
.    error
.  endif
.  undef varname
.endif
dollars_stage_2:
	@echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"
	@echo '$@: dollars=<'${DOLLARS:Q}'>'
	@echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
	@${MAKE} -f ${MAKEFILE} dollars_stage_3

dollars_stage_3:
	@echo "$@: env MAKEFLAGS=<$$MAKEFLAGS>"
	@echo '$@: dollars=<'${DOLLARS:Uundefined:Q}'>'
	@echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'


# Demonstrates in which exact order the MAKEFLAGS are built from the parent
# MAKEFLAGS and the flags from the command line, in particular that variable
# assignments are passed at the end, after the options.
append_stage_0:
	@echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
	@${MAKE} -Dbefore-0 -f ${MAKEFILE} append_stage_1 VAR0=value -Dafter-0

append_stage_1:
	@echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
	@${MAKE} -Dbefore-1 -f ${MAKEFILE} append_stage_2 VAR1=value -Dafter-1

append_stage_2:
	@echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
	@${MAKE} -Dbefore-2 -f ${MAKEFILE} append_stage_3 VAR2=value -Dafter-2

append_stage_3:
	@echo '$@: MAKEFLAGS=<'${MAKEFLAGS:Q}'>'


# Demonstrates the implementation details of 'MAKEFLAGS', in particular that
# it is an environment variable rather than a global variable.
override_stage_0:
	@${MAKE} -f ${MAKEFILE} STAGE=1 VAR=value override_stage_1

.if make(override_stage_1)
# While parsing the makefiles, 'MAKEFLAGS' is the value of the environment
# variable, in this case provided by stage 0.
.  if ${MAKEFLAGS:M*} != "-r -k"
.    error
.  endif
MAKEFLAGS=	overridden	# temporarily override it
.  if ${MAKEFLAGS} != "overridden"
.    error
.  endif
.undef MAKEFLAGS		# make the environment variable visible again
.  if ${MAKEFLAGS:M*} != "-r -k"
.    error
.  endif
.endif
override_stage_1:
	@echo '$@: run MAKEFLAGS=<'${MAKEFLAGS:Q}'>'
	@${MAKE} -f ${MAKEFILE} STAGE=2 override_stage_2

override_stage_2:
	@echo '$@: STAGE=<${STAGE}> VAR=<${VAR}>'