]> git.ipfire.org Git - thirdparty/make.git/commitdiff
[SV 62706] Only second-expand targets that might be built
authorDmitry Goncharov <dgoncharov@users.sf.net>
Sun, 24 Jul 2022 22:16:50 +0000 (18:16 -0400)
committerPaul Smith <psmith@gnu.org>
Sat, 30 Jul 2022 22:40:28 +0000 (18:40 -0400)
Second-expand only the prerequisites of the targets being built.
Defer second-expanding the prerequisites of targets until we need
to decide if they should be built.

* NEWS: Mention the change in behavior.
* doc/make.texi (Secondary Expansion): Document the new behavior.
* 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.
* tests/scripts/features/se_explicit: Add tests.
* tests/scripts/features/se_implicit: Ditto.
* tests/scripts/features/se_statpat: Ditto.

NEWS
doc/make.texi
src/file.c
src/filedef.h
src/remake.c
tests/scripts/features/se_explicit
tests/scripts/features/se_implicit
tests/scripts/features/se_statpat

diff --git a/NEWS b/NEWS
index 752de7d0ee2061526f55634924f3b7554f0df8ad..3a437a277e17ead115e57e8e278195e7595cd820 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -83,6 +83,11 @@ https://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=109&se
   mentioned in other targets as "ought to exist".
   Implementation provided by Dmitry Goncharov <dgoncharov@users.sf.net>
 
+* GNU make was performing secondary expansion of all targets, even targets
+  which didn't need to be considered during the build.  In this release
+  only targets which are considered will be secondarily expanded.
+  Implementation provided by Dmitry Goncharov <dgoncharov@users.sf.net>
+
 * If the MAKEFLAGS variable is modified in a makefile, it will be re-parsed
   immediately rather than after all makefiles have been read.  Note that
   although all options are parsed immediately, some special effects won't
index 085e9c24f3fade98be0c57c0eb49c4eb636cb8fb..49cdb0b271853084de95ac843fc05430427e3dcb 100644 (file)
@@ -1703,18 +1703,16 @@ this second expansion to occur, the special target
 @code{.SECONDEXPANSION} must be defined before the first prerequisite
 list that makes use of this feature.
 
-If that special target is defined then in between the two phases
-mentioned above, right at the end of the read-in phase, all the
-prerequisites of the targets defined after the special target are
-expanded a @emph{second time}.  In most circumstances this secondary
-expansion will have no effect, since all variable and function
-references will have been expanded during the initial parsing of the
-makefiles.  In order to take advantage of the secondary expansion
-phase of the parser, then, it's necessary to @emph{escape} the
-variable or function reference in the makefile.  In this case the
-first expansion merely un-escapes the reference but doesn't expand it,
-and expansion is left to the secondary expansion phase.  For example,
-consider this makefile:
+If @code{.SECONDEXPANSION} is defined then when GNU @code{make} needs to check
+the prerequisites of a target, the prerequisites are expanded a @emph{second
+time}.  In most circumstances this secondary expansion will have no effect,
+since all variable and function references will have been expanded during the
+initial parsing of the makefiles.  In order to take advantage of the secondary
+expansion phase of the parser, then, it's necessary to @emph{escape} the
+variable or function reference in the makefile.  In this case the first
+expansion merely un-escapes the reference but doesn't expand it, and expansion
+is left to the secondary expansion phase.  For example, consider this
+makefile:
 
 @example
 .SECONDEXPANSION:
index 3960cdabb17ba91391d66dac7f316440205a21b9..7596ff103759897645e0cf17d278cb95473500e5 100644 (file)
@@ -342,6 +342,7 @@ rehash_file (struct file *from_file, const char *to_hname)
   MERGE (secondary);
   MERGE (notintermediate);
   MERGE (ignore_vpath);
+  MERGE (snapped);
 #undef MERGE
 
   to_file->builtin = 0;
@@ -565,8 +566,10 @@ enter_prereqs (struct dep *deps, const char *stem)
   return deps;
 }
 
-/* Expand and parse each dependency line. */
-static void
+/* Expand and parse each dependency line.
+   For each dependency of the file, make the 'struct dep' point
+   at the appropriate 'struct file' (which may have to be created).  */
+void
 expand_deps (struct file *f)
 {
   struct dep *d;
@@ -574,7 +577,9 @@ expand_deps (struct file *f)
   const char *fstem;
   int initialized = 0;
 
-  f->updating = 0;
+  if (f->snapped)
+    return;
+  f->snapped = 1;
 
   /* Walk through the dependencies.  For any dependency that needs 2nd
      expansion, expand it then insert the result into the list.  */
@@ -646,6 +651,7 @@ expand_deps (struct file *f)
 
       set_file_variables (f, d->stem ? d->stem : f->stem);
 
+      /* Perform second expansion.  */
       p = variable_expand_for_file (d->name, f);
 
       /* Free the un-expanded name.  */
@@ -758,10 +764,7 @@ snap_file (const void *item, void *arg)
     }
 }
 
-/* For each dependency of each file, make the 'struct dep' point
-   at the appropriate 'struct file' (which may have to be created).
-
-   Also mark the files depended on by .PRECIOUS, .PHONY, .SILENT,
+/* Mark the files depended on by .PRECIOUS, .PHONY, .SILENT,
    and various other special targets.  */
 
 void
@@ -775,37 +778,6 @@ snap_deps (void)
      longer define new targets.  */
   snapped_deps = 1;
 
-  /* Perform second expansion and enter each dependency name as a file.  We
-     must use hash_dump() here because within these loops we likely add new
-     files to the table, possibly causing an in-situ table expansion.
-
-     We only need to do this if second_expansion has been defined; if it
-     hasn't then all deps were expanded as the makefile was read in.  If we
-     ever change make to be able to unset .SECONDARY_EXPANSION this will have
-     to change.  */
-
-  if (second_expansion)
-    {
-      struct file **file_slot_0 = (struct file **) hash_dump (&files, 0, 0);
-      struct file **file_end = file_slot_0 + files.ht_fill;
-      struct file **file_slot;
-      const char *suffixes;
-
-      /* Expand .SUFFIXES: its prerequisites are used for $$* calc.  */
-      f = lookup_file (".SUFFIXES");
-      suffixes = f ? f->name : 0;
-      for (; f != 0; f = f->prev)
-        expand_deps (f);
-
-      /* For every target that's not .SUFFIXES, expand its prerequisites.  */
-
-      for (file_slot = file_slot_0; file_slot < file_end; file_slot++)
-        for (f = *file_slot; f != 0; f = f->prev)
-          if (f->name != suffixes)
-            expand_deps (f);
-      free (file_slot_0);
-    }
-
   /* Now manage all the special targets.  */
 
   for (f = lookup_file (".PRECIOUS"); f != 0; f = f->prev)
index 4330d787f10bc323740fefe4d6db86f100813cf0..1eb8f61cb0e2736e53f3ce29b72623352811721e 100644 (file)
@@ -110,6 +110,8 @@ struct file
                                    diagnostics has been issued (dontcare). */
     unsigned int was_shuffled:1; /* Did we already shuffle 'deps'? used when
                                     --shuffle passes through the graph.  */
+    unsigned int snapped:1;     /* True if the deps of this file have been
+                                   secondary expanded.  */
   };
 
 
@@ -120,6 +122,7 @@ struct file *lookup_file (const char *name);
 struct file *enter_file (const char *name);
 struct dep *split_prereqs (char *prereqstr);
 struct dep *enter_prereqs (struct dep *prereqs, const char *stem);
+void expand_deps (struct file *f);
 struct dep *expand_extra_prereqs (const struct variable *extra);
 void remove_intermediates (int sig);
 void snap_deps (void);
index 2930a5bb0d81e146a7ba691f314af7dda4ece8b8..228b8af6daf42f8e3989a66073cb8c815b4c28fa 100644 (file)
@@ -532,6 +532,12 @@ update_file_1 (struct file *file, unsigned int depth)
     {
       struct dep *lastd = 0;
 
+      /* Perform second expansion and enter each dependency name as a file.
+         We only need to do this if second_expansion has been defined; if it
+         hasn't then all deps were expanded as the makefile was read in.  */
+      if (second_expansion)
+        expand_deps (ad->file);
+
       /* Find the deps we're scanning */
       du = ad->file->deps;
       ad = ad->next;
@@ -1089,6 +1095,12 @@ check_dep (struct file *file, unsigned int depth,
             }
 
           ld = 0;
+          /* Perform second expansion and enter each dependency name as a file.
+             We only need to do this if second_expansion has been defined; if it
+             hasn't then all deps were expanded as the makefile was read in.  */
+          if (second_expansion)
+            expand_deps (file);
+
           d = file->deps;
           while (d != 0)
             {
index 354182747c3ab36810dd1643370ea5d70dcee4ee..ee5a89de5946016393c129293f3b368548bdd70d 100644 (file)
@@ -299,4 +299,207 @@ 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.
+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#:5,
+#MAKEFILE#:5: but 'hello.c' is now considered the same file as 'src/hello.c'.
+#MAKEFILE#:5: Recipe for 'hello.c' will be ignored in favor of the one for 'src/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);
+
+# sv 62706.
+# Second expansion of intermediate prerequisites.
+# The rule to build hello.x is explicit.
+# .SECONDARY marks hello.x as intermediate.
+# Test that $$(deps) is secondary expanded.
+run_make_test(q!
+deps:=hello.h
+.SECONDEXPANSION:
+.SECONDARY: hello.x
+all: hello.x
+hello.x: $$(deps); $(info $@)
+hello.h:; $(info $@)
+!, '', "hello.h\nhello.x\n#MAKE#: Nothing to be done for 'all'.\n");
+
+
 1;
index e7d884687f004a348799541915e1505b84871e06..58775631c650aec8bedcb9056abae6cfa6519776 100644 (file)
@@ -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,51 @@ 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);
+
+# sv 62706.
+# Second expansion of intermediate prerequisites.
+# The rule to build hello.x is implicit.
+# Test that $$(deps) is secondary expanded.
+run_make_test(q!
+deps:=hello.h
+.SECONDEXPANSION:
+all: hello.tsk
+%.tsk: %.x; $(info $@)
+%.x: $$(deps); $(info $@)
+hello.h:; $(info $@)
+!, '', "hello.h\nhello.x\nhello.tsk\n#MAKE#: Nothing to be done for 'all'.\n");
+
 # This tells the test driver that the perl test script executed properly.
 1;
index 17f9c3484e8f3d74231643ccb3ed7a505d57f24c..616a1a8581fb52f1f2ec46ee2e5fda5526df5256 100644 (file)
@@ -91,11 +91,9 @@ baz.a.2
 #
 run_make_test(q!
 .SECONDEXPANSION:
-foo$$bar: f%r: % $$*.1
-       @echo '$*'
+foo$$bar: f%r: % $$*.1 ; @echo '$*'
 
-oo$$ba oo$$ba.1:
-       @echo '$@'
+oo$$ba oo$$ba.1: ; @echo '$@'
 !,
               '', 'oo$ba
 oo$ba.1
@@ -110,5 +108,126 @@ 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);
+
+# sv 62706.
+# Second expansion of intermediate prerequisites.
+# The rule to build hello.x is static pattern.
+# .SECONDARY marks hello.x as intermediate.
+# Test that $$(deps) is secondary expanded.
+run_make_test(q!
+deps:=hello.h
+.SECONDEXPANSION:
+.SECONDARY: hello.x
+all: hello.x
+hello.x: %.x: $$(deps); $(info $@)
+hello.h:; $(info $@)
+!, '', "hello.h\nhello.x\n#MAKE#: Nothing to be done for 'all'.\n");
+
 # This tells the test driver that the perl test script executed properly.
 1;