]> git.ipfire.org Git - thirdparty/make.git/commitdiff
[SV 63856] Implement .WAIT on the command line
authorDmitry Goncharov <dgoncharov@users.sf.net>
Sun, 2 Apr 2023 14:50:17 +0000 (10:50 -0400)
committerPaul Smith <psmith@gnu.org>
Sun, 2 Apr 2023 15:12:11 +0000 (11:12 -0400)
* src/main.c (handle_non_switch_argument): Return 1 if arg is .WAIT.
(decode_switches): Set wait_here for a goal that follows .WAIT.
* src/remake.c (update_goal_chain): Honor wait_here for a command
line goal.  Don't allow double-colon targets to continue if .WAIT is
given for one of them.
* tests/scripts/targets/WAIT: Add .WAIT tests.

src/main.c
src/remake.c
tests/scripts/targets/WAIT

index 8587de61427d7354b7faf843ee7cf42f2d7482bd..7b8e91cfc0baef3c21f26231b1460656543c5230 100644 (file)
@@ -2975,15 +2975,17 @@ init_switches (void)
 }
 
 
-/* Non-option argument.  It might be a variable definition.  */
-static void
+/* Non-option argument.  It might be a variable definition.
+   Returns 1 if the argument we read was .WAIT, else 0.
+ */
+static unsigned int
 handle_non_switch_argument (const char *arg, enum variable_origin origin)
 {
   struct variable *v;
 
   if (arg[0] == '-' && arg[1] == '\0')
     /* Ignore plain '-' for compatibility.  */
-    return;
+    return 0;
 
 #if MK_OS_VMS
   {
@@ -3036,7 +3038,12 @@ handle_non_switch_argument (const char *arg, enum variable_origin origin)
          Enter it as a file and add it to the dep chain of goals.
          Check ARG[0] because if the top makefile resets MAKEOVERRIDES
          then ARG points to an empty string in the submake.  */
-      struct file *f = enter_file (strcache_add (expand_command_line_file (arg)));
+      struct file *f;
+
+      if (strcmp (arg, ".WAIT") == 0)
+        return 1;
+
+      f = enter_file (strcache_add (expand_command_line_file (arg)));
       f->cmd_target = 1;
 
       if (goals == 0)
@@ -3077,6 +3084,7 @@ handle_non_switch_argument (const char *arg, enum variable_origin origin)
         define_variable_cname ("MAKECMDGOALS", value, o_default, 0);
       }
     }
+  return 0;
 }
 
 /* Called if the makefile resets the MAKEFLAGS variable.  */
@@ -3098,6 +3106,7 @@ decode_switches (int argc, const char **argv, enum variable_origin origin)
   struct command_switch *cs;
   struct stringlist *sl;
   int c;
+  unsigned int found_wait = 0;
 
   /* getopt does most of the parsing for us.
      First, get its vectors set up.  */
@@ -3120,15 +3129,22 @@ decode_switches (int argc, const char **argv, enum variable_origin origin)
       if (c == EOF)
         /* End of arguments, or "--" marker seen.  */
         break;
-      else if (c == 1)
-        /* An argument not starting with a dash.  */
-        handle_non_switch_argument (coptarg, origin);
       else if (c == '?')
         /* Bad option.  We will print a usage message and die later.
            But continue to parse the other options so the user can
            see all he did wrong.  */
         bad = 1;
+      else if (c == 1)
+        {
+          /* An argument not starting with a dash.  */
+          const unsigned int prior_found_wait = found_wait;
+          found_wait = handle_non_switch_argument (coptarg, origin);
+          if (prior_found_wait && lastgoal)
+            /* If the argument before this was .WAIT, wait here.  */
+            lastgoal->wait_here = 1;
+        }
       else
+        /* An option starting with a dash.  */
         for (cs = switches; cs->c != '\0'; ++cs)
           if (cs->c == c)
             {
@@ -3320,7 +3336,12 @@ decode_switches (int argc, const char **argv, enum variable_origin origin)
      to be returned in order, this only happens when there is a "--"
      argument to prevent later arguments from being options.  */
   while (optind < argc)
-    handle_non_switch_argument (argv[optind++], origin);
+    {
+      const int prior_found_wait = found_wait;
+      found_wait = handle_non_switch_argument (argv[optind++], origin);
+      if (prior_found_wait && lastgoal)
+        lastgoal->wait_here = 1;
+    }
 
   if (bad && origin == o_command)
     print_usage (bad);
index 04daf49c4f8911dec1877fa90af785963fe4dc2e..dec4667c821e49ffd2ba6b50240617b339be5c60 100644 (file)
@@ -119,6 +119,7 @@ update_goal_chain (struct goaldep *goaldeps)
   unsigned long last_cmd_count = 0;
   int t = touch_flag, q = question_flag, n = just_print_flag;
   enum update_status status = us_none;
+  const unsigned int depth = rebuilding_makefiles ? 1 : 0;
 
   /* Duplicate the chain so we can remove things from it.  */
   struct dep *goals_orig = copy_dep_chain ((struct dep *)goaldeps);
@@ -137,6 +138,7 @@ update_goal_chain (struct goaldep *goaldeps)
   while (goals != 0)
     {
       struct dep *gu, *g, *lastgoal;
+      int running = 0, wait = 0;
 
       /* Start jobs that are waiting for the load to go down.  */
 
@@ -154,16 +156,14 @@ update_goal_chain (struct goaldep *goaldeps)
       while (gu != 0)
         {
           /* Iterate over all double-colon entries for this file.  */
-          struct file *file;
+          struct file *file, *dchead;
           int stop = 0, any_not_updated = 0;
 
           g = gu->shuf ? gu->shuf : gu;
 
           goal_dep = g;
-
-          for (file = g->file->double_colon ? g->file->double_colon : g->file;
-               file != NULL;
-               file = file->prev)
+          dchead = g->file->double_colon ? g->file->double_colon : g->file;
+          for (file = dchead; file != NULL; file = file->prev)
             {
               unsigned int ocommands_started;
               enum update_status fail;
@@ -188,8 +188,24 @@ update_goal_chain (struct goaldep *goaldeps)
                  actually run.  */
               ocommands_started = commands_started;
 
-              fail = update_file (file, rebuilding_makefiles ? 1 : 0);
+              stop = 0;
+
+              /* In the case of double colon rules, only the recipe of the 1st
+                 rule should be blocked by .WAIT. The recipes of all subsequent
+                 rules for the same file will execute sequentially in order
+                 after the 1st.  */
+              wait = file == dchead && g->wait_here && running;
+              if (wait)
+                {
+                  DBF (DB_VERBOSE, _(".WAIT is blocking '%s'.\n"));
+                  break;
+                }
+
+              fail = update_file (file, depth);
               check_renamed (file);
+              running |= (file->command_state == cs_running
+                          || file->command_state == cs_deps_running);
+
 
               /* Set the goal's 'changed' flag if any commands were started
                  by calling update_file above.  We check this flag below to
@@ -197,7 +213,6 @@ update_goal_chain (struct goaldep *goaldeps)
               if (commands_started > ocommands_started)
                 g->changed = 1;
 
-              stop = 0;
               if ((fail || file->updated) && status < us_question)
                 {
                   /* We updated this goal.  Update STATUS and decide whether
@@ -249,6 +264,9 @@ update_goal_chain (struct goaldep *goaldeps)
           /* Reset FILE since it is null at the end of the loop.  */
           file = g->file;
 
+          if (wait)
+            break;
+
           if (stop || !any_not_updated)
             {
               /* If we have found nothing whatever to do for the goal,
@@ -285,8 +303,9 @@ update_goal_chain (struct goaldep *goaldeps)
         }
 
       /* If we reached the end of the dependency graph update CONSIDERED
-         for the next pass.  */
-      if (gu == 0)
+         for the next pass.  In the case of waiting, increment CONSIDERED to
+         prevent the same file from getting pruned over and over again.  */
+      if (gu == 0 || wait)
         ++considered;
     }
 
@@ -384,7 +403,8 @@ update_file (struct file *file, unsigned int depth)
       if (f->command_state == cs_running
           || f->command_state == cs_deps_running)
         /* Don't run other :: rules for this target until
-           this rule is finished.  */
+           this rule is finished.  Multiple recipes running in parallel and
+           updating the same target will corrupt the target.  */
         return us_success;
 
       if (new > status)
index b981023f8f7c69830554d90b23b74055ed934b78..343b87ac46c71ff8fb21c1e2e5d3452e00a8e7d9 100644 (file)
@@ -56,6 +56,73 @@ pre2: ; @#HELPER# -q out $@
 
 run_make_test(undef, '-j10 pre2', "pre2\n");
 
+# sv 63856.
+# .WAIT on the command line.
+
+run_make_test(q!
+pre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@
+pre2: ; @#HELPER# -q out $@
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10 pre1 .WAIT pre2', "start-pre1\nend-pre1\npre2\n");
+
+# Multiple consecutive .WAITs.
+
+run_make_test(q!
+all : pre1 .WAIT .WAIT .WAIT pre2
+pre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@
+pre2: ; @#HELPER# -q out $@
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10', "start-pre1\nend-pre1\npre2\n");
+
+# First and last prerequsites are .WAIT.
+
+run_make_test(q!
+all : .WAIT pre1 .WAIT pre2 .WAIT
+pre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@
+pre2: ; @#HELPER# -q out $@
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10', "start-pre1\nend-pre1\npre2\n");
+
+# All prerequisites are .WAITs.
+
+run_make_test(q!
+all : .WAIT .WAIT .WAIT
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10', "#MAKE#: Nothing to be done for 'all'.\n");
+
+run_make_test(q!
+all:
+!,
+              '-j10 .WAIT', "#MAKE#: Nothing to be done for 'all'.\n");
+
+# Wait between the duplicate goals.
+
+run_make_test(q!
+all: hello.tsk .WAIT hello.tsk
+hello.tsk:; $(info $@)
+!,
+              '-j10', "hello.tsk\n#MAKE#: Nothing to be done for 'all'.\n");
+
+# Wait between the duplicate command line goals.
+
+run_make_test(q!
+hello.tsk:; $(info $@)
+!,
+              '-j10 hello.tsk .WAIT hello.tsk', "hello.tsk\n#MAKE#: 'hello.tsk' is up to date.\n#MAKE#: 'hello.tsk' is up to date.\n");
+
+
 # Ensure .WAIT doesn't wait between all targets
 
 run_make_test(q!
@@ -71,6 +138,20 @@ pre3: ; @#HELPER# -q wait TWO out $@ file THREE
 
 unlink(qw(TWO THREE));
 
+# Ensure .WAIT on the command line doesn't wait between all targets.
+
+run_make_test(q!
+pre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@
+pre2: ; @#HELPER# -q out start-$@ file TWO wait THREE out end-$@
+pre3: ; @#HELPER# -q wait TWO out $@ file THREE
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10 pre1 .WAIT pre2 pre3', "start-pre1\nend-pre1\nstart-pre2\npre3\nend-pre2\n");
+
+unlink(qw(TWO THREE));
+
 # Ensure .WAIT waits for ALL targets on the left before ANY targets on the right
 
 run_make_test(q!
@@ -88,6 +169,23 @@ post2: ; @#HELPER# -q file POST2 wait POST1 out $@
 
 unlink(qw(PRE1 PRE2 POST1 POST2));
 
+# Ensure .WAIT on the command line waits for ALL targets on the left before ANY
+# targets on the right.
+
+run_make_test(q!
+pre1: ; @#HELPER# -q out start-$@ file PRE1 wait PRE2 sleep 1 out end-$@
+pre2: ; @#HELPER# -q wait PRE1 out $@ file PRE2
+
+post1: ; @#HELPER# -q wait POST2 out $@ file POST1
+post2: ; @#HELPER# -q file POST2 wait POST1 out $@
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10 pre1 pre2 .WAIT post1 post2', "start-pre1\npre2\nend-pre1\npost1\npost2\n");
+
+unlink(qw(PRE1 PRE2 POST1 POST2));
+
 # See if .WAIT takes effect between different lists of prereqs
 # In the current implementation, .WAIT waits only between two prerequisites
 # in a given target.  These same two targets might be run in a different
@@ -180,6 +278,17 @@ pre2: ; @#HELPER# -q out $@
 !,
               '-j10 --shuffle=reverse', "start-pre1\nend-pre1\npre2\n");
 
+# Ensure we don't shuffle if .WAIT is set on the command line.
+
+run_make_test(q!
+pre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@
+pre2: ; @#HELPER# -q out $@
+
+# This is just here so we don't fail with older versions of make
+.WAIT:
+!,
+              '-j10 --shuffle=reverse pre1 .WAIT pre2', "start-pre1\nend-pre1\npre2\n");
+
 # Warn about invalid .WAIT definitions
 
 run_make_test(q!