Module Name:    src
Committed By:   rillig
Date:           Tue Dec 29 01:45:06 UTC 2020

Modified Files:
        src/distrib/sets/lists/tests: mi
        src/usr.bin/make/unit-tests: Makefile
Added Files:
        src/usr.bin/make/unit-tests: directive-export-impl.exp
            directive-export-impl.mk

Log Message:
make(1): add test that explains how variables are exported

Exporting the variables at the right time and with the correct values is
a subtle issue.  The current implementation carefully marks variables as
ready to be exported, then exports them and at the same time tries to
export as few variables as possible, to avoid memory leaks.  This test
describes and explains how all this works in detail.

This test also justifies that the call to Var_ReexportVars happens in
the make process itself, not in the child processes, no matter whether
these are created with vfork or (only theoretically) with plain fork.
This has changed in compat.c 1.217, job.c 1.390 and main.c 1.504 from
2020-12-27.


To generate a diff of this commit:
cvs rdiff -u -r1.999 -r1.1000 src/distrib/sets/lists/tests/mi
cvs rdiff -u -r1.257 -r1.258 src/usr.bin/make/unit-tests/Makefile
cvs rdiff -u -r0 -r1.1 src/usr.bin/make/unit-tests/directive-export-impl.exp \
    src/usr.bin/make/unit-tests/directive-export-impl.mk

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/distrib/sets/lists/tests/mi
diff -u src/distrib/sets/lists/tests/mi:1.999 src/distrib/sets/lists/tests/mi:1.1000
--- src/distrib/sets/lists/tests/mi:1.999	Mon Dec 28 12:47:39 2020
+++ src/distrib/sets/lists/tests/mi	Tue Dec 29 01:45:06 2020
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.999 2020/12/28 12:47:39 rillig Exp $
+# $NetBSD: mi,v 1.1000 2020/12/29 01:45:06 rillig Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -5050,6 +5050,8 @@
 ./usr/tests/usr.bin/make/unit-tests/directive-export-env.mk			tests-usr.bin-tests	compattestfile,atf
 ./usr/tests/usr.bin/make/unit-tests/directive-export-gmake.exp			tests-usr.bin-tests	compattestfile,atf
 ./usr/tests/usr.bin/make/unit-tests/directive-export-gmake.mk			tests-usr.bin-tests	compattestfile,atf
+./usr/tests/usr.bin/make/unit-tests/directive-export-impl.exp			tests-usr.bin-tests	compattestfile,atf
+./usr/tests/usr.bin/make/unit-tests/directive-export-impl.mk			tests-usr.bin-tests	compattestfile,atf
 ./usr/tests/usr.bin/make/unit-tests/directive-export-literal.exp		tests-usr.bin-tests	compattestfile,atf
 ./usr/tests/usr.bin/make/unit-tests/directive-export-literal.mk			tests-usr.bin-tests	compattestfile,atf
 ./usr/tests/usr.bin/make/unit-tests/directive-export.exp			tests-usr.bin-tests	compattestfile,atf

Index: src/usr.bin/make/unit-tests/Makefile
diff -u src/usr.bin/make/unit-tests/Makefile:1.257 src/usr.bin/make/unit-tests/Makefile:1.258
--- src/usr.bin/make/unit-tests/Makefile:1.257	Sun Dec 27 05:11:40 2020
+++ src/usr.bin/make/unit-tests/Makefile	Tue Dec 29 01:45:06 2020
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.257 2020/12/27 05:11:40 rillig Exp $
+# $NetBSD: Makefile,v 1.258 2020/12/29 01:45:06 rillig Exp $
 #
 # Unit tests for make(1)
 #
@@ -157,6 +157,7 @@ TESTS+=		directive-endif
 TESTS+=		directive-error
 TESTS+=		directive-export
 TESTS+=		directive-export-env
+TESTS+=		directive-export-impl
 TESTS+=		directive-export-gmake
 TESTS+=		directive-export-literal
 TESTS+=		directive-for

Added files:

Index: src/usr.bin/make/unit-tests/directive-export-impl.exp
diff -u /dev/null src/usr.bin/make/unit-tests/directive-export-impl.exp:1.1
--- /dev/null	Tue Dec 29 01:45:06 2020
+++ src/usr.bin/make/unit-tests/directive-export-impl.exp	Tue Dec 29 01:45:06 2020
@@ -0,0 +1,56 @@
+ParseReadLine (21): 'UT_VAR=		<${REF}>'
+Global:UT_VAR = <${REF}>
+ParseReadLine (28): '.export UT_VAR'
+Global:.MAKE.EXPORTED = UT_VAR
+ParseReadLine (32): ': ${UT_VAR:N*}'
+Var_Parse: ${UT_VAR:N*} with VARE_UNDEFERR|VARE_WANTRES
+Var_Parse: ${REF}> with VARE_UNDEFERR|VARE_WANTRES
+Applying ${UT_VAR:N...} to "<>" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
+Pattern[UT_VAR] for [<>] is [*]
+ModifyWords: split "<>" into 1 words
+Result of ${UT_VAR:N*} is "" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
+ParseDoDependency(: )
+CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<>"
+Var_Parse: ${:!echo "\$UT_VAR"!} != "<>" with VARE_UNDEFERR|VARE_WANTRES
+Applying ${:!...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF)
+Modifier part: "echo "$UT_VAR""
+Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES
+Applying ${.MAKE.EXPORTED:O} to "UT_VAR" (VARE_WANTRES, none, none)
+Result of ${.MAKE.EXPORTED:O} is "UT_VAR" (VARE_WANTRES, none, none)
+Applying ${.MAKE.EXPORTED:u} to "UT_VAR" (VARE_WANTRES, none, none)
+Result of ${.MAKE.EXPORTED:u} is "UT_VAR" (VARE_WANTRES, none, none)
+Var_Parse: ${UT_VAR} with VARE_WANTRES
+Var_Parse: ${REF}> with VARE_WANTRES
+Result of ${:!echo "\$UT_VAR"!} is "<>" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF)
+lhs = "<>", rhs = "<>", op = !=
+ParseReadLine (49): ': ${UT_VAR:N*}'
+Var_Parse: ${UT_VAR:N*} with VARE_UNDEFERR|VARE_WANTRES
+Var_Parse: ${REF}> with VARE_UNDEFERR|VARE_WANTRES
+Applying ${UT_VAR:N...} to "<>" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
+Pattern[UT_VAR] for [<>] is [*]
+ModifyWords: split "<>" into 1 words
+Result of ${UT_VAR:N*} is "" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
+ParseDoDependency(: )
+ParseReadLine (53): 'REF=		defined'
+Global:REF = defined
+CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<defined>"
+Var_Parse: ${:!echo "\$UT_VAR"!} != "<defined>" with VARE_UNDEFERR|VARE_WANTRES
+Applying ${:!...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF)
+Modifier part: "echo "$UT_VAR""
+Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES
+Applying ${.MAKE.EXPORTED:O} to "UT_VAR" (VARE_WANTRES, none, none)
+Result of ${.MAKE.EXPORTED:O} is "UT_VAR" (VARE_WANTRES, none, none)
+Applying ${.MAKE.EXPORTED:u} to "UT_VAR" (VARE_WANTRES, none, none)
+Result of ${.MAKE.EXPORTED:u} is "UT_VAR" (VARE_WANTRES, none, none)
+Var_Parse: ${UT_VAR} with VARE_WANTRES
+Var_Parse: ${REF}> with VARE_WANTRES
+Result of ${:!echo "\$UT_VAR"!} is "<defined>" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF)
+lhs = "<defined>", rhs = "<defined>", op = !=
+ParseReadLine (61): 'all:'
+ParseDoDependency(all:)
+Global:.ALLTARGETS =  all
+ParseReadLine (62): '.MAKEFLAGS: -d0'
+ParseDoDependency(.MAKEFLAGS: -d0)
+Global:.MAKEFLAGS =  -r -k -d cpv -d
+Global:.MAKEFLAGS =  -r -k -d cpv -d 0
+exit status 0
Index: src/usr.bin/make/unit-tests/directive-export-impl.mk
diff -u /dev/null src/usr.bin/make/unit-tests/directive-export-impl.mk:1.1
--- /dev/null	Tue Dec 29 01:45:06 2020
+++ src/usr.bin/make/unit-tests/directive-export-impl.mk	Tue Dec 29 01:45:06 2020
@@ -0,0 +1,62 @@
+# $NetBSD: directive-export-impl.mk,v 1.1 2020/12/29 01:45:06 rillig Exp $
+#
+# Test for the implementation of exporting variables to child processes.
+# This involves marking variables for export, actually exporting them,
+# or marking them for being re-exported.
+#
+# See also:
+#	Var_Export
+#	ExportVar
+#	VarExportedMode (global)
+#	VAR_EXPORTED (per variable)
+#	VAR_REEXPORT (per variable)
+#	VarExportMode (per call of Var_Export and ExportVar)
+
+: ${:U:sh}			# side effect: initialize .SHELL
+
+.MAKEFLAGS: -dcpv
+
+# This is a variable that references another variable.  At this point, the
+# other variable is still undefined.
+UT_VAR=		<${REF}>
+
+# At this point, ExportVar("UT_VAR", VEM_PLAIN) is called.  Since the
+# variable value refers to another variable, ExportVar does not actually
+# export the variable but only marks it as VAR_EXPORTED and VAR_REEXPORT.
+# After that, ExportVars registers the variable name in .MAKE.EXPORTED.
+# That's all for now.
+.export UT_VAR
+
+# Evaluating this expression shows the variable flags in the debug log,
+# which are VAR_EXPORTED|VAR_REEXPORT.
+: ${UT_VAR:N*}
+
+# At the last moment before actually forking off the child process for the
+# :!...! modifier, Cmd_Exec calls Var_ReexportVars to have all relevant
+# variables exported.  Since this variable has both of the above-mentioned
+# flags set, it is actually exported to the environment.  The variable flags
+# are not modified though, since the next time the :!...! modifier is
+# evaluated, the referenced variables could have changed, therefore the
+# variable will be exported anew for each ':sh' modifier, ':!...!' modifier,
+# '!=' variable assignment.
+.if ${:!echo "\$UT_VAR"!} != "<>"
+.  error
+.endif
+
+# Evaluating this expression shows the variable flags in the debug log,
+# which are still VAR_EXPORTED|VAR_REEXPORT, which means that the variable
+# is still marked as being re-exported for each child process.
+: ${UT_VAR:N*}
+
+# Now the referenced variable gets defined.  This does not influence anything
+# in the process of exporting the variable value, though.
+REF=		defined
+
+# Nothing surprising here.  The variable UT_VAR gets exported, and this time,
+# REF is defined and gets expanded into the exported environment variable.
+.if ${:!echo "\$UT_VAR"!} != "<defined>"
+.  error
+.endif
+
+all:
+.MAKEFLAGS: -d0

Reply via email to