commit 86e65718e1aa498962d33f14341097d124ac33b2
Author: Dmitry Goncharov <dgoncharov@users.sf.net>
Date:   Sun Mar 19 14:08:14 2023 -0400

    [SV 63856] Fix pruning of double colon rules.
    
    * src/remake.c (update_file): Fix premature pruning of double colon rules.
    
    Given this makefile
    
    A::; @echo A-1 && sleep 1 && echo A-1 done
    A::; @echo A-2 && sleep 1 && echo A-2 done
    A::; @echo A-3 && sleep 1 && echo A-3 done
    B::; @echo B-1 && sleep 1 && echo B-1 done
    B::; @echo B-2 && sleep 1 && echo B-2 done
    B::; @echo B-3 && sleep 1 && echo B-3 done
    
    And running
    $ make -j8 A .WAIT B
    all 3 recipes for A should get started and complete sequentially before the 1st
    recipe for B gets started and then all 3 recipes for B should get started and
    complete sequentially.
    The change in update_file is needed, because update_file prunes A even
    when the current A recipe is already done and the recipe of the next double
    colon rule of the same target should be started. This incorrect pruning
    causes update_goal_chain to run a recipe of the next goal, even though there
    are still not started recipes of this goal.
    This patch modifies the pruning criteria such that for double colon rules
    pruning only takes place if the current recipe is still in progress.
    
    * tests/scripts/targets/WAIT: Test .WAIT with double colon rules.

diff --git a/src/remake.c b/src/remake.c
index 42a672ac..183e27e6 100644
--- a/src/remake.c
+++ b/src/remake.c
@@ -366,9 +366,15 @@ update_file (struct file *file, unsigned int depth)
     {
       /* Check for the case where a target has been tried and failed but
          the diagnostics haven't been issued. If we need the diagnostics
-         then we will have to continue. */
+         then we will have to continue.
+         In the case of double colon rules, this file cannot be pruned if
+         this recipe finished (file->command_state == cs_finished) and there
+         are more double colon rules for this file. Instead the recipe of the
+         next double colon rule of this file should be run.  */
       if (!(f->updated && f->update_status > us_none
-            && !f->dontcare && f->no_diag))
+            && !f->dontcare && f->no_diag)
+            && (!file->double_colon || file->command_state != cs_finished ||
+                !f->prev))
         {
           DBF (DB_VERBOSE, _("Pruning file '%s'.\n"));
           return f->command_state == cs_finished ? f->update_status : us_success;
diff --git a/tests/scripts/targets/WAIT b/tests/scripts/targets/WAIT
index 343b87ac..4d56f6e7 100644
--- a/tests/scripts/targets/WAIT
+++ b/tests/scripts/targets/WAIT
@@ -298,5 +298,344 @@ all:;@:
 !,
               '', "#MAKEFILE#:2: .WAIT should not have prerequisites\n#MAKEFILE#:3: .WAIT should not have commands\n");
 
+# Wait for all double colon targets on the left, before building targets on the
+# right.
+# Subtest 1. First run this test without .WAIT and observe and A recipes
+# interleave with B recipes.
+# Then run the same test with .WAIT and observe that all A recipes get started
+# sequentially before the 1st B recipe gets started and then all B recipes get
+# started sequentially.
+
+unlink('A-1-done', 'A-2-done', 'A-3-done', 'B-1-done', 'B-2-done', 'B-3-done');
+
+run_make_test(q!
+all: A B
+A::; @#HELPER# -q out A-1 file A-1-done wait B-1-done out A-1-done
+A::; @#HELPER# -q out A-2 file A-2-done wait B-2-done out A-2-done
+A::; @#HELPER# -q out A-3 file A-3-done wait B-3-done out A-3-done
+B::; @#HELPER# -q wait A-1-done out B-1 out B-1-done file B-1-done
+B::; @#HELPER# -q wait A-2-done out B-2 out B-2-done file B-2-done
+B::; @#HELPER# -q wait A-3-done out B-3 out B-3-done file B-3-done
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10', "A-1\nB-1\nB-1-done\nA-1-done\nA-2\nB-2\nB-2-done\nA-2-done\nA-3\nB-3\nB-3-done\nA-3-done\n");
+
+unlink('A-1-done', 'A-2-done', 'A-3-done', 'B-1-done', 'B-2-done', 'B-3-done');
+
+# Subtest 2.
+# Wait for all double colon targets on the left, before building targets on the
+# right.
+
+run_make_test(q!
+all: A .WAIT B
+A::; @#HELPER# -q out $@-1 out $@-1-done
+A::; @#HELPER# -q out $@-2 out $@-2-done
+A::; @#HELPER# -q out $@-3 out $@-3-done
+B::; @#HELPER# -q out $@-1 out $@-1-done
+B::; @#HELPER# -q out $@-2 out $@-2-done
+B::; @#HELPER# -q out $@-3 out $@-3-done
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10', "A-1\nA-1-done\nA-2\nA-2-done\nA-3\nA-3-done\nB-1\nB-1-done\nB-2\nB-2-done\nB-3\nB-3-done\n");
+
+# sv 63856.
+# Wait for all double colon targets on the left, before building targets on the
+# right.
+# Subtest 1. First run this test without .WAIT and observe and A recipes
+# interleave with B recipes.
+# Then run the same test with .WAIT and observe that all A recipes get started
+# sequentially before the 1st B recipe gets started and then all B recipes get
+# started sequentially.
+# The targets are specified on the command line.
+
+unlink('A-1-done', 'A-2-done', 'A-3-done', 'B-1-done', 'B-2-done', 'B-3-done');
+
+run_make_test(q!
+A::; @#HELPER# -q out A-1 file A-1-done wait B-1-done out A-1-done
+A::; @#HELPER# -q out A-2 file A-2-done wait B-2-done out A-2-done
+A::; @#HELPER# -q out A-3 file A-3-done wait B-3-done out A-3-done
+B::; @#HELPER# -q wait A-1-done out B-1 out B-1-done file B-1-done
+B::; @#HELPER# -q wait A-2-done out B-2 out B-2-done file B-2-done
+B::; @#HELPER# -q wait A-3-done out B-3 out B-3-done file B-3-done
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10 A B', "A-1\nB-1\nB-1-done\nA-1-done\nA-2\nB-2\nB-2-done\nA-2-done\nA-3\nB-3\nB-3-done\nA-3-done\n");
+
+unlink('A-1-done', 'A-2-done', 'A-3-done', 'B-1-done', 'B-2-done', 'B-3-done');
+
+# Subtest 2.
+# sv 63856.
+# Wait for all double colon targets on the left, before building targets on the
+# right. The targets are specified on the command line.
+
+run_make_test(q!
+A::; @#HELPER# -q out $@-1 out $@-1-done
+A::; @#HELPER# -q out $@-2 out $@-2-done
+A::; @#HELPER# -q out $@-3 out $@-3-done
+B::; @#HELPER# -q out $@-1 out $@-1-done
+B::; @#HELPER# -q out $@-2 out $@-2-done
+B::; @#HELPER# -q out $@-3 out $@-3-done
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10 A .WAIT B', "A-1\nA-1-done\nA-2\nA-2-done\nA-3\nA-3-done\nB-1\nB-1-done\nB-2\nB-2-done\nB-3\nB-3-done\n");
+
+
+# Wait for all double colon targets on the left, before building targets on the
+# right.
+# Regular targets which have double colon targets as prerequisites.
+# Subtest 1. First run this test without .WAIT and observe and A recipes
+# interleave with B recipes.
+# The targets are specified in the makefile.
+
+unlink('D', 'D-done', 'E', 'E-done');
+unlink('A-1-done', 'A-2-done', 'A-3-done', 'B-1-done', 'B-2-done', 'B-3-done');
+
+run_make_test(q!
+all: C .WAIT A B
+A::; @#HELPER# -q out A-1 file A-1-done wait B-1-done out A-1-done
+A::; @#HELPER# -q out A-2 file A-2-done wait B-2-done out A-2-done
+A::; @#HELPER# -q out A-3 file A-3-done wait B-3-done out A-3-done
+B::; @#HELPER# -q wait A-1-done out B-1 out B-1-done file B-1-done
+B::; @#HELPER# -q wait A-2-done out B-2 out B-2-done file B-2-done
+B::; @#HELPER# -q wait A-3-done out B-3 out B-3-done file B-3-done
+
+C: D E
+D:; @#HELPER# -q out D file D wait E-done out D-done
+E:; @#HELPER# -q wait D out E out E-done file E-done
+
+.PHONY: C D E
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10', "D\nE\nE-done\nD-done\nA-1\nB-1\nB-1-done\nA-1-done\nA-2\nB-2\nB-2-done\nA-2-done\nA-3\nB-3\nB-3-done\nA-3-done\n");
+
+# Wait for all double colon targets on the left, before building targets on the
+# right.
+# Regular targets which have double colon targets as prerequisites.
+# Subtest 2.
+# Run the same test with .WAIT and observe that all A recipes get started
+# sequentially before the 1st B recipe gets started and then all B recipes get
+# started sequentially.
+# The targets are specified in the makefile.
+#
+
+unlink('D', 'D-done', 'E', 'E-done');
+unlink('A-1-done', 'A-2-done', 'A-3-done', 'B-1-done', 'B-2-done', 'B-3-done');
+
+run_make_test(q!
+all: C .WAIT A .WAIT B
+A::; @#HELPER# -q out $@-1 out $@-1-done
+A::; @#HELPER# -q out $@-2 out $@-2-done
+A::; @#HELPER# -q out $@-3 out $@-3-done
+B::; @#HELPER# -q out $@-1 out $@-1-done
+B::; @#HELPER# -q out $@-2 out $@-2-done
+B::; @#HELPER# -q out $@-3 out $@-3-done
+
+C: D E
+D:; @#HELPER# -q out D file D wait E-done out D-done
+E:; @#HELPER# -q wait D out E out E-done file E-done
+
+.PHONY: C D E
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10', "D\nE\nE-done\nD-done\nA-1\nA-1-done\nA-2\nA-2-done\nA-3\nA-3-done\nB-1\nB-1-done\nB-2\nB-2-done\nB-3\nB-3-done\n");
+
+
+# Wait for all double colon targets on the left, before building targets on the
+# right.
+# Regular targets which have double colon targets as prerequisites.
+# Subtest 1. First run this test without .WAIT and observe and A recipes
+# interleave with B recipes.
+# The targets are specified on the command line.
+
+unlink('D', 'D-done', 'E', 'E-done');
+unlink('A-1-done', 'A-2-done', 'A-3-done', 'B-1-done', 'B-2-done', 'B-3-done');
+
+run_make_test(q!
+A::; @#HELPER# -q out A-1 file A-1-done wait B-1-done out A-1-done
+A::; @#HELPER# -q out A-2 file A-2-done wait B-2-done out A-2-done
+A::; @#HELPER# -q out A-3 file A-3-done wait B-3-done out A-3-done
+B::; @#HELPER# -q wait A-1-done out B-1 out B-1-done file B-1-done
+B::; @#HELPER# -q wait A-2-done out B-2 out B-2-done file B-2-done
+B::; @#HELPER# -q wait A-3-done out B-3 out B-3-done file B-3-done
+
+C: D E
+D:; @#HELPER# -q out D file D wait E-done out D-done
+E:; @#HELPER# -q wait D out E out E-done file E-done
+
+.PHONY: C D E
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10 C .WAIT A B', "D\nE\nE-done\nD-done\nA-1\nB-1\nB-1-done\nA-1-done\nA-2\nB-2\nB-2-done\nA-2-done\nA-3\nB-3\nB-3-done\nA-3-done\n");
+
+# Wait for all double colon targets on the left, before building targets on the
+# right.
+# Regular targets which have double colon targets as prerequisites.
+# Subtest 2.
+# Run the same test with .WAIT and observe that all A recipes get started
+# sequentially before the 1st B recipe gets started and then all B recipes get
+# started sequentially.
+# The targets are specified on the command line.
+
+unlink('D', 'D-done', 'E', 'E-done');
+unlink('A-1-done', 'A-2-done', 'A-3-done', 'B-1-done', 'B-2-done', 'B-3-done');
+
+run_make_test(q!
+A::; @#HELPER# -q out $@-1 out $@-1-done
+A::; @#HELPER# -q out $@-2 out $@-2-done
+A::; @#HELPER# -q out $@-3 out $@-3-done
+B::; @#HELPER# -q out $@-1 out $@-1-done
+B::; @#HELPER# -q out $@-2 out $@-2-done
+B::; @#HELPER# -q out $@-3 out $@-3-done
+
+C: D E
+D:; @#HELPER# -q out D file D wait E-done out D-done
+E:; @#HELPER# -q wait D out E out E-done file E-done
+
+.PHONY: C D E
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10 C .WAIT A .WAIT B', "D\nE\nE-done\nD-done\nA-1\nA-1-done\nA-2\nA-2-done\nA-3\nA-3-done\nB-1\nB-1-done\nB-2\nB-2-done\nB-3\nB-3-done\n");
+
+unlink('D', 'D-done', 'E', 'E-done');
+unlink('A-1-done', 'A-2-done', 'A-3-done', 'B-1-done', 'B-2-done', 'B-3-done');
+
+# Wait for all double colon targets on the left, before building targets on the
+# right.
+# Double colon targets have reqular targets as prerequisites.
+# Subtest 1. First run this test without .WAIT and observe and A recipes
+# interleave with B recipes.
+# The targets are specified in the makefile.
+
+unlink('D', 'D-done', 'E', 'E-done');
+unlink('A-1-done', 'A-2-done', 'A-3-done', 'B-1-done', 'B-2-done', 'B-3-done');
+
+run_make_test(q!
+all: A B
+A:: D; @#HELPER# -q out A-1 file A-1-done wait B-1-done out A-1-done
+A::; @#HELPER# -q out A-2 file A-2-done wait B-2-done out A-2-done
+A::; @#HELPER# -q out A-3 file A-3-done wait B-3-done out A-3-done
+B:: E; @#HELPER# -q wait A-1-done out B-1 out B-1-done file B-1-done
+B::; @#HELPER# -q wait A-2-done out B-2 out B-2-done file B-2-done
+B::; @#HELPER# -q wait A-3-done out B-3 out B-3-done file B-3-done
+
+D:; @#HELPER# -q out D file D wait E-done out D-done
+E:; @#HELPER# -q wait D out E out E-done file E-done
+
+.PHONY: D E
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10', "D\nE\nE-done\nD-done\nA-1\nB-1\nB-1-done\nA-1-done\nA-2\nB-2\nB-2-done\nA-2-done\nA-3\nB-3\nB-3-done\nA-3-done\n");
+
+# Wait for all double colon targets on the left, before building targets on the
+# right.
+# Double colon targets have reqular targets as prerequisites.
+# Subtest 2.
+# Run the same test with .WAIT and observe that all A recipes get started
+# sequentially before the 1st B recipe gets started and then all B recipes get
+# started sequentially.
+# The targets are specified in the makefile.
+
+unlink('D', 'D-done', 'E', 'E-done');
+unlink('A-1-done', 'A-2-done', 'A-3-done', 'B-1-done', 'B-2-done', 'B-3-done');
+
+run_make_test(q!
+all: A .WAIT B
+A:: D; @#HELPER# -q out $@-1 out $@-1-done
+A::  ; @#HELPER# -q out $@-2 out $@-2-done
+A::  ; @#HELPER# -q out $@-3 out $@-3-done
+B:: E; @#HELPER# -q out $@-1 out $@-1-done
+B::  ; @#HELPER# -q out $@-2 out $@-2-done
+B::  ; @#HELPER# -q out $@-3 out $@-3-done
+
+D:; @#HELPER# -q out D out D-done
+E:; @#HELPER# -q out E out E-done
+
+.PHONY: D E
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10', "D\nD-done\nA-1\nA-1-done\nA-2\nA-2-done\nA-3\nA-3-done\nE\nE-done\nB-1\nB-1-done\nB-2\nB-2-done\nB-3\nB-3-done\n");
+
+
+# Wait for all double colon targets on the left, before building targets on the
+# right.
+# Double colon targets have reqular targets as prerequisites.
+# Subtest 1. First run this test without .WAIT and observe and A recipes
+# interleave with B recipes.
+# The targets are specified on the command line.
+
+unlink('D', 'D-done', 'E', 'E-done');
+unlink('A-1-done', 'A-2-done', 'A-3-done', 'B-1-done', 'B-2-done', 'B-3-done');
+
+run_make_test(q!
+A:: D; @#HELPER# -q out A-1 file A-1-done wait B-1-done out A-1-done
+A::; @#HELPER# -q out A-2 file A-2-done wait B-2-done out A-2-done
+A::; @#HELPER# -q out A-3 file A-3-done wait B-3-done out A-3-done
+B:: E; @#HELPER# -q wait A-1-done out B-1 out B-1-done file B-1-done
+B::; @#HELPER# -q wait A-2-done out B-2 out B-2-done file B-2-done
+B::; @#HELPER# -q wait A-3-done out B-3 out B-3-done file B-3-done
+
+D:; @#HELPER# -q out D file D wait E-done out D-done
+E:; @#HELPER# -q wait D out E out E-done file E-done
+
+.PHONY: D E
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10 A B', "D\nE\nE-done\nD-done\nA-1\nB-1\nB-1-done\nA-1-done\nA-2\nB-2\nB-2-done\nA-2-done\nA-3\nB-3\nB-3-done\nA-3-done\n");
+
+# Wait for all double colon targets on the left, before building targets on the
+# right.
+# Double colon targets have reqular targets as prerequisites.
+# Subtest 2.
+# Run the same test with .WAIT and observe that all A recipes get started
+# sequentially before the 1st B recipe gets started and then all B recipes get
+# started sequentially.
+# The targets are specified on the command line.
+
+unlink('D', 'D-done', 'E', 'E-done');
+unlink('A-1-done', 'A-2-done', 'A-3-done', 'B-1-done', 'B-2-done', 'B-3-done');
+
+run_make_test(q!
+A:: D; @#HELPER# -q out $@-1 out $@-1-done
+A::  ; @#HELPER# -q out $@-2 out $@-2-done
+A::  ; @#HELPER# -q out $@-3 out $@-3-done
+B:: E; @#HELPER# -q out $@-1 out $@-1-done
+B::  ; @#HELPER# -q out $@-2 out $@-2-done
+B::  ; @#HELPER# -q out $@-3 out $@-3-done
+
+D:; @#HELPER# -q out D out D-done
+E:; @#HELPER# -q out E out E-done
+
+.PHONY: D E
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10 A .WAIT B', "D\nD-done\nA-1\nA-1-done\nA-2\nA-2-done\nA-3\nA-3-done\nE\nE-done\nB-1\nB-1-done\nB-2\nB-2-done\nB-3\nB-3-done\n");
+
+unlink('D', 'D-done', 'E', 'E-done');
+unlink('A-1-done', 'A-2-done', 'A-3-done', 'B-1-done', 'B-2-done', 'B-3-done');
+
 # This tells the test driver that the perl test script executed properly.
 1;
