freebsd-dev/contrib/bmake/unit-tests/varname-makeflags.mk
Simon J. Gerraty 8c973ee23d Merge bmake-20230414
Merge commit '51d8a8b4ac1dd7265e891149e470a803906de2a7'
2023-04-24 16:50:16 -07:00

183 lines
6.1 KiB
Makefile

# $NetBSD: varname-makeflags.mk,v 1.7 2023/02/25 19:24:07 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 name '\' 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 together 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}>'