commit a154bff6788e19bbf66275cbbcafcd17fb6ca775
Author: Dmitry Goncharov <dgoncharov@users.sf.net>
Date:   Sun Jul 3 12:44:56 2022 -0400

    [SV 62706] Restrict second expansion.
    
    Second expand only the prerequisites of the targets being built.
    Avoid second expanding the prerequisites of targets which are not being
    built.
    
    * src/filedef.h (struct file): Add flag snapped.
    (expand_deps): Declare a function to second expand the
    prerequisites of a target.
    * src/file.c (rehash_file): Merge flag snapped.
    (expand_deps): Remove qualifier static. Check flag snapped.
    (snap_deps): Remove the loop which performed second expansion for all
    targets.
    * src/remake.c (update_file_1): Second expand the prerequisites of
    the considered target.
    * doc/make.texi (Secondary Expansion): Document the new behavior.
    * tests/scripts/features/se_explicit: Add tests.
    * tests/scripts/features/se_implicit: Ditto.
    * tests/scripts/features/se_statpat: Ditto.

diff --git a/tests/scripts/features/se_explicit b/tests/scripts/features/se_explicit
index d097e1a8..5a74cab3 100644
--- a/tests/scripts/features/se_explicit
+++ b/tests/scripts/features/se_explicit
@@ -299,4 +299,198 @@ all: bye.x
 bye.x: $$(firstword bye.1;
 !, '', "#MAKEFILE#:4: *** unterminated call to function 'firstword': missing ')'.  Stop.", 512);
 
+unlink('hello.tsk', 'test.o', 'bye.tsk', 'hello.o', 'hello.h', 'hello.q', 'bye.c', 'bye.o');
+
+# sv 62706.
+# Test that makes avoids second expanding prerequisites of the targets which
+# are not built.
+# Here, hello.tsk is built and its prerequisites are second expanded.
+# bye.tsk is not built and its prerequisites are not second expanded.
+run_make_test(q!
+.SECONDEXPANSION:
+hello.tsk: hello.o $$(info second expansion of $$@ prereqs); $(info $@ from $<)
+bye.tsk: bye.o $$(info second expansion of $$@ prereqs); $(info $@ from $<)
+hello.o: $$(info second expansion of $$@ prereqs); $(info $@)
+bye.o: $$(info second expansion of $$@ prereqs); $(info $@)
+!, 'hello.tsk',
+"second expansion of hello.tsk prereqs
+second expansion of hello.o prereqs
+hello.o
+hello.tsk from hello.o
+#MAKE#: 'hello.tsk' is up to date.\n");
+
+# sv 62706.
+# Multipe rules per target.
+run_make_test(q!
+.SECONDEXPANSION:
+
+all: hello.tsk
+dep1:=hello.o
+dep2:=hello.h
+hello.tsk: $$(dep1)
+hello.tsk: $$(dep2); $(info $@ from $^)
+hello.o:; $(info $@)
+hello.h:; $(info $@)
+!, 'hello.tsk',
+"hello.h
+hello.o
+hello.tsk from hello.h hello.o
+#MAKE#: 'hello.tsk' is up to date.\n");
+
+# sv 62706.
+# Multiple targets per rule.
+run_make_test(q!
+.SECONDEXPANSION:
+hello.tsk bye.tsk: hello.o $$(info second expansion of $$@ prereqs); $(info $@ from $<)
+bye.tsk: bye.o $$(info second expansion of $$@ prereqs)
+hello.o: $$(info second expansion of $$@ prereqs); $(info $@)
+bye.o: $$(info second expansion of $$@ prereqs); $(info $@)
+!, 'hello.tsk',
+"second expansion of hello.tsk prereqs
+second expansion of hello.o prereqs
+hello.o
+hello.tsk from hello.o
+#MAKE#: 'hello.tsk' is up to date.\n");
+
+# sv 62706.
+# Grouped targets.
+run_make_test(q!
+.SECONDEXPANSION:
+world.tsk: world.o $$(info 1 second expansion of $$@ prereqs)
+hello.tsk world.tsk &: hello.o $$(info 2 second expansion of $$@ prereqs); $(info $@ from $<)
+bye.tsk: bye.o $$(info second expansion of $$@ prereqs); $(info $@ from $<)
+hello.o: $$(info second expansion of $$@ prereqs); $(info $@)
+world.o: $$(info second expansion of $$@ prereqs); $(info $@)
+bye.o: $$(info second expansion of $$@ prereqs); $(info $@)
+!, 'hello.tsk',
+"2 second expansion of hello.tsk prereqs
+second expansion of hello.o prereqs
+hello.o
+2 second expansion of world.tsk prereqs
+1 second expansion of world.tsk prereqs
+second expansion of world.o prereqs
+world.o
+hello.tsk from hello.o
+#MAKE#: 'hello.tsk' is up to date.\n");
+
+# sv 62706.
+# Order only.
+run_make_test(q!
+.SECONDEXPANSION:
+hello.tsk:| hello.o $$(info second expansion of $$@ prereqs); $(info $@ from $|)
+bye.tsk:| bye.o $$(info second expansion of $$@ prereqs); $(info $@ from $|)
+hello.o:| $$(info second expansion of $$@ prereqs); $(info $@)
+bye.o:| $$(info second expansion of $$@ prereqs); $(info $@)
+!, 'hello.tsk',
+"second expansion of hello.tsk prereqs
+second expansion of hello.o prereqs
+hello.o
+hello.tsk from hello.o
+#MAKE#: 'hello.tsk' is up to date.\n");
+
+# sv 62706.
+# Double colon. 1 rule per target.
+run_make_test(q!
+.SECONDEXPANSION:
+hello.tsk:: hello.o $$(info second expansion of $$@ prereqs); $(info $@ from $<)
+bye.tsk:: bye.o $$(info second expansion of $$@ prereqs); $(info $@ from $<)
+hello.o:: $$(info second expansion of $$@ prereqs); $(info $@)
+bye.o:: $$(info second expansion of $$@ prereqs); $(info $@)
+!, 'hello.tsk',
+"second expansion of hello.tsk prereqs
+second expansion of hello.o prereqs
+hello.o
+hello.tsk from hello.o
+#MAKE#: 'hello.tsk' is up to date.\n");
+
+# sv 62706.
+# Double colon. 2 rules per targets.
+run_make_test(q!
+.SECONDEXPANSION:
+hello.tsk:: hello.o $$(info 1 second expansion of $$@ prereqs); $(info 1 $@ from $<)
+hello.tsk:: hello.o $$(info 2 second expansion of $$@ prereqs); $(info 2 $@ from $<)
+bye.tsk:: bye.o $$(info 1 second expansion of $$@ prereqs); $(info 1 $@ from $<)
+bye.tsk:: bye.o $$(info 2 second expansion of $$@ prereqs); $(info 2 $@ from $<)
+hello.o:: $$(info 1 second expansion of $$@ prereqs); $(info 1 $@)
+hello.o:: $$(info 2 second expansion of $$@ prereqs); $(info 2 $@)
+bye.o:: $$(info 1 second expansion of $$@ prereqs); $(info 1 $@)
+bye.o:: $$(info 2 second expansion of $$@ prereqs); $(info 2 $@)
+!, 'hello.tsk',
+"1 second expansion of hello.tsk prereqs
+1 second expansion of hello.o prereqs
+1 hello.o
+2 second expansion of hello.o prereqs
+2 hello.o
+1 hello.tsk from hello.o
+2 second expansion of hello.tsk prereqs
+2 hello.tsk from hello.o
+#MAKE#: 'hello.tsk' is up to date.\n");
+
+# sv 62706.
+# Test that the prerequisites of 'hello.tsk' are second expanded once.
+run_make_test(q!
+.SECONDEXPANSION:
+all: hello.tsk hello.q
+hello.tsk: hello.o $$(info second expansion of $$@ prereqs); $(info $@ from $^)
+hello.o: $$(info second expansion of $$@ prereqs); $(info $@)
+hello.q: hello.tsk $$(info second expansion of $$@ prereqs); $(info $@ from $^)
+!, '',
+"second expansion of hello.tsk prereqs
+second expansion of hello.o prereqs
+hello.o
+hello.tsk from hello.o
+second expansion of hello.q prereqs
+hello.q from hello.tsk
+#MAKE#: Nothing to be done for 'all'.\n");
+
+# sv 62706.
+# Merge vpath file and local file.
+# Test that both sets of prerequisites from 'hello.c' rule and from
+# 'src/hello.c' rule are second expanded.
+#TODO: there is sv 62650 which modified this user message.
+# 1. remove this test?
+# 2. use the current message?
+# 3. use the new message?
+# 4. wait until sv 62650 is closed?
+run_make_test(q!
+.SECONDEXPANSION:
+vpath hello.c src
+all: hello.c; $(info $@ from $^)
+hello.c: $$(info second expansion of hello.c prereqs); $(info 1 $@)
+src/hello.c: $$(info second expansion of src/hello.c prereqs); $(info 2 $@)
+!, '',
+"#MAKEFILE#:5: Recipe was specified for file 'hello.c' at #MAKEFILE#:6,
+#MAKEFILE#:5: but 'hello.c' is now considered the same file as 'src/hello.c'.
+#MAKEFILE#:5: Recipe for 'src/hello.c' will be ignored in favor of the one for 'hello.c'.
+second expansion of src/hello.c prereqs
+second expansion of hello.c prereqs
+2 src/hello.c
+all from src/hello.c
+#MAKE#: 'all' is up to date.\n");
+
+# sv 62706.
+# .DEFAULT.
+run_make_test(q!
+.SECONDEXPANSION:
+bye:=bye.c
+all: hello.o
+.DEFAULT: $$(info second expansion of prereqs of default recipe @ = $$@) ; $(info default recipe $@)
+!, '',
+"default recipe hello.o
+#MAKE#: Nothing to be done for 'all'.\n");
+
+unlink('hello.1');
+
+# sv 62706.
+# No side effects from second expansion of unrelated rules.
+run_make_test(q!
+.SECONDEXPANSION:
+hello.tsk:; cp hello.1 $@
+unrelated: $$(shell touch hello.1);
+!, '',
+"cp hello.1 hello.tsk
+cp: cannot stat 'hello.1': $ERR_no_such_file
+#MAKE#: *** [#MAKEFILE#:3: hello.tsk] Error 1\n", 512);
+
+
 1;
diff --git a/tests/scripts/features/se_implicit b/tests/scripts/features/se_implicit
index e7d88468..7fd562bd 100644
--- a/tests/scripts/features/se_implicit
+++ b/tests/scripts/features/se_implicit
@@ -292,8 +292,11 @@ all: hello.z
 !, '', "hello.z\n");
 
 # subtest 3.
-# hello.x is explicitly mentioned on an unrelated rule and thus is not an
-# intermediate file.
+# make is building hello.z and does not second expand the prerequisites of rule
+# 'unrelated: $$(dep)'. '$$(dep)' stays not expanded and 'hello.x' is never
+# entered to the database. Make considers 'hello.x' intermediate while building
+# 'hello.z'. Because 'hello.z' is present and 'hello.x' is missing and
+# 'hello.x' is intermediate, there is no need to rebuild 'hello.z'.
 run_make_test(q!
 .SECONDEXPANSION:
 dep:=hello.x
@@ -301,7 +304,19 @@ all: hello.z
 %.z: %.x; @echo $@
 %.x: ;
 unrelated: $$(dep)
-!, '', "hello.z\n");
+!, '', "#MAKE#: Nothing to be done for 'all'.\n");
+
+# subtest 4.
+# Just like subtest 3. $$(dep) is not second expanded. 'hello.x' is
+# intermediate.
+run_make_test(q!
+.SECONDEXPANSION:
+dep:=hello.x
+all: hello.z
+%.z: %.x; @echo $@
+%.x: ;
+%.q: $$(dep)
+!, '', "#MAKE#: Nothing to be done for 'all'.\n");
 
 unlink('hello.z');
 
@@ -442,5 +457,38 @@ all: bye.x
 %.x: $$(firstword %.1;
 !, '', "#MAKE#: *** unterminated call to function 'firstword': missing ')'.  Stop.", 512);
 
+# sv 62706.
+# Test that makes avoids second expanding prerequisites of the rules which are
+# not tried during implicit search.
+# Here, make tries rules '%.tsk: %.o' and '%.o' and their prerequisites are
+# second expanded.
+# Rules '%.bin: %.x' and '%.x:' are not used in implicit search for 'hello.tsk'
+# and 'hello.o' and their prerequisites are not expanded.
+run_make_test(q!
+.SECONDEXPANSION:
+%.bin: %.x $$(info second expansion of $$@ prereqs); $(info $@ from $<)
+%.tsk: %.o $$(info second expansion of $$@ prereqs); $(info $@ from $<)
+%.x: $$(info second expansion of $$@ prereqs); $(info $@)
+%.o: $$(info second expansion of $$@ prereqs); $(info $@)
+!, '-R hello.tsk',
+"second expansion of hello.o prereqs
+second expansion of hello.tsk prereqs
+hello.o
+hello.tsk from hello.o
+#MAKE#: 'hello.tsk' is up to date.\n");
+
+# sv 62706.
+# No side effects from second expansion of unrelated rules.
+run_make_test(q!
+.SECONDEXPANSION:
+all: hello.tsk
+%.tsk: %.o; cp hello.1 $@
+hello.o:;
+%.q: $$(shell touch hello.1);
+!, '',
+"cp hello.1 hello.tsk
+cp: cannot stat 'hello.1': $ERR_no_such_file
+#MAKE#: *** [#MAKEFILE#:4: hello.tsk] Error 1\n", 512);
+
 # This tells the test driver that the perl test script executed properly.
 1;
diff --git a/tests/scripts/features/se_statpat b/tests/scripts/features/se_statpat
index 17f9c348..decd3d47 100644
--- a/tests/scripts/features/se_statpat
+++ b/tests/scripts/features/se_statpat
@@ -110,5 +110,112 @@ all: bye.x
 bye.x: %.x: $$(firstword %.1;
 !, '', "#MAKEFILE#:4: *** unterminated call to function 'firstword': missing ')'.  Stop.", 512);
 
+unlink('hello.tsk', 'bye.tsk', 'hello.o', 'hello.q', 'bye.o');
+
+# sv 62706.
+# Test that makes avoids second expanding prerequisites of the targets which
+# are not built.
+# Here, hello.tsk is built and its prerequisites are second expanded.
+# bye.tsk is not built and its prerequisites are not second expanded.
+
+# Static pattern rules.
+run_make_test(q!
+.SECONDEXPANSION:
+hello.tsk: %.tsk: %.o $$(info second expansion of $$@ prereqs); $(info $@ from $<)
+bye.tsk: %.tsk: %.o $$(info second expansion of $$@ prereqs); $(info $@ from $<)
+hello.o: $$(info second expansion of $$@ prereqs); $(info $@)
+bye.o: $$(info second expansion of $$@ prereqs); $(info $@)
+!, 'hello.tsk',
+"second expansion of hello.tsk prereqs
+second expansion of hello.o prereqs
+hello.o
+hello.tsk from hello.o
+#MAKE#: 'hello.tsk' is up to date.\n");
+
+# sv 62706.
+# Order only prereqs.
+run_make_test(q!
+.SECONDEXPANSION:
+hello.tsk: %.tsk:| %.o $$(info second expansion of $$@ prereqs); $(info $@ from $|)
+bye.tsk: %.tsk:| %.o $$(info second expansion of $$@ prereqs); $(info $@ from $|)
+hello.o:| $$(info second expansion of $$@ prereqs); $(info $@)
+bye.o:| $$(info second expansion of $$@ prereqs); $(info $@)
+!, 'hello.tsk',
+"second expansion of hello.tsk prereqs
+second expansion of hello.o prereqs
+hello.o
+hello.tsk from hello.o
+#MAKE#: 'hello.tsk' is up to date.\n");
+
+# sv 62706.
+# Double colon. 1 rule per target.
+run_make_test(q!
+.SECONDEXPANSION:
+hello.tsk:: %.tsk: %.o $$(info second expansion of $$@ prereqs); $(info $@ from $<)
+bye.tsk:: %.tsk: %.o $$(info second expansion of $$@ prereqs); $(info $@ from $<)
+hello.o:: $$(info second expansion of $$@ prereqs); $(info $@)
+bye.o:: $$(info second expansion of $$@ prereqs); $(info $@)
+!, 'hello.tsk',
+"second expansion of hello.tsk prereqs
+second expansion of hello.o prereqs
+hello.o
+hello.tsk from hello.o
+#MAKE#: 'hello.tsk' is up to date.\n");
+
+# sv 62706.
+# Double colon. 2 rules per target.
+run_make_test(q!
+.SECONDEXPANSION:
+hello.tsk:: %.tsk: %.o $$(info 1 second expansion of $$@ prereqs); $(info 1 $@ from $<)
+hello.tsk:: %.tsk: %.o $$(info 2 second expansion of $$@ prereqs); $(info 2 $@ from $<)
+bye.tsk:: %.tsk: %.o $$(info 1 second expansion of $$@ prereqs); $(info 1 $@ from $<)
+bye.tsk:: %.tsk: %.o $$(info 2 second expansion of $$@ prereqs); $(info 2 $@ from $<)
+hello.o:: $$(info 1 second expansion of $$@ prereqs); $(info 1 $@)
+hello.o:: $$(info 2 second expansion of $$@ prereqs); $(info 2 $@)
+bye.o:: $$(info 1 second expansion of $$@ prereqs); $(info 1 $@)
+bye.o:: $$(info 2 second expansion of $$@ prereqs); $(info 2 $@)
+!, 'hello.tsk',
+"1 second expansion of hello.tsk prereqs
+1 second expansion of hello.o prereqs
+1 hello.o
+2 second expansion of hello.o prereqs
+2 hello.o
+1 hello.tsk from hello.o
+2 second expansion of hello.tsk prereqs
+2 hello.tsk from hello.o
+#MAKE#: 'hello.tsk' is up to date.\n");
+
+# sv 62706.
+# Test that the prerequisites of 'hello.tsk' are second expanded once.
+run_make_test(q!
+.SECONDEXPANSION:
+all: hello.tsk hello.q
+hello.tsk: %.tsk: %.o $$(info second expansion of $$@ prereqs); $(info $@ from $^)
+hello.o: $$(info second expansion of $$@ prereqs); $(info $@)
+hello.q: %.q: %.tsk $$(info second expansion of $$@ prereqs); $(info $@ from $^)
+!, '',
+"second expansion of hello.tsk prereqs
+second expansion of hello.o prereqs
+hello.o
+hello.tsk from hello.o
+second expansion of hello.q prereqs
+hello.q from hello.tsk
+#MAKE#: Nothing to be done for 'all'.\n");
+
+unlink('hello.1');
+
+# sv 62706.
+# No side effects from second expansion of unrelated rules.
+run_make_test(q!
+.SECONDEXPANSION:
+all: hello.tsk
+hello.tsk: %.tsk: %.o; cp hello.1 $@
+hello.o:;
+bye.tsk: %.tsk: $$(shell touch hello.1);
+!, '',
+"cp hello.1 hello.tsk
+cp: cannot stat 'hello.1': $ERR_no_such_file
+#MAKE#: *** [#MAKEFILE#:4: hello.tsk] Error 1\n", 512);
+
 # This tells the test driver that the perl test script executed properly.
 1;
