* Rework jobserver yet one more time.
authorPaul Smith <psmith@gnu.org>
Fri, 13 Aug 1999 07:36:26 +0000 (07:36 +0000)
committerPaul Smith <psmith@gnu.org>
Fri, 13 Aug 1999 07:36:26 +0000 (07:36 +0000)
* Install the $(if ...) function and document it.  Still need some examples.

ChangeLog
NEWS
configure.in
expand.c
function.c
job.c
main.c
make.h
make.texinfo

index 1d194b5e829a34f014cc1c28c47c240efa87ab2e..e5edfca9d094593c21463d87ab0750154cc3459b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,14 +1,57 @@
+1999-08-13  Paul D. Smith  <psmith@gnu.org>
+
+       * function.c (func_if): New function $(if ...) based on the
+       original by Han-Wen but reworked quite a bit.
+       (function_table): Add it.
+       * NEWS: Introduce it.
+       * make.texinfo (If Function): Document it.
+
+       * job.c (free_job_token): Check for EINTR when writing tokens to
+       the jobserver pipe.
+
+1999-08-12  Paul D. Smith  <psmith@gnu.org>
+
+       Argh.  Another jobserver algorithm change.  We conveniently forgot
+       that the blocking bit is shared by all users of the pipe, it's not
+       a per-process setting.  Since we have many make processes all
+       sharing the pipe we can't use the blocking bit as a signal handler
+       flag.  Instead, we'll dup the pipe's read FD and have the SIGCHLD
+       handler close the dup'd FD.  This will cause the read() to fail
+       with EBADF the next time we invoke it, so we know we need to reap
+       children.  We then re-dup and reap.
+
+       * main.c (main): Define the job_rfd variable to hold the dup'd FD.
+       Actually dup the read side of the pipe.  Don't bother setting the
+       blocking bit on the file descriptor.
+       * make.h: Declare the job_rfd variable.
+       * job.c (child_handler): If the dup'd jobserver pipe is open,
+       close it and assign -1 to job_rfd to notify the main program that
+       we got a SIGCHLD.
+       (start_job_command): Close the dup'd FD before exec'ing children.
+       Since we open and close this thing so often it doesn't seem
+       worth it to use the close-on-exec bit.
+       (new_job): Remove code for testing/setting the blocking bit.
+       Instead of EAGAIN, test for EBADF.  If the dup'd FD has been
+       closed, re-dup it before we reap children.
+
+       * function.c (func_shell): Be a little more accurate about the
+       length of the error string to allocate.
+
+       * expand.c (variable_expand_for_file): If there's no filenm info
+       (say, from a builtin command) then reset reading_file to 0.
+
 1999-08-09  Paul D. Smith  <psmith@gnu.org>
 
        * maintMakefile: Use g in sed (s///g) to replace >1 variable per
        line.
 
-       * Makefile.DOS.template: Fix mostlyclean-aminfo to remove the
-       right stuff.
+       * Makefile.DOS.template [__MSDOS__]: Fix mostlyclean-aminfo to
+       remove the right files.
 
 1999-08-01  Eli Zaretskii  <eliz@is.elta.co.il>
 
-       * function.c (msdos_openpipe): *Really* return a FILE ptr.
+       * function.c (msdos_openpipe) [__MSDOS__]: *Really* return a FILE
+       ptr.
 
 1999-08-01  Paul D. Smith  <psmith@gnu.org>
 
diff --git a/NEWS b/NEWS
index 8f048690fac25f45faf36a723e3b8cd77da98bf9..8d30f3e713912d4e238d41f9f717f46657c6c155 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -12,17 +12,21 @@ Please send GNU make bug reports to bug-make@gnu.org.
 \f
 Version 3.78
 
-* Two new functions, $(error ...) and $(warning ...) are provided.  The
+* Two new functions, $(error ...) and $(warning ...) are available.  The
   former will cause make to fail and exit immediately upon expansion of
   the function, with the text provided as the error message.  The latter
   causes the text provided to be printed as a warning message, but make
   proceeds normally.
 
-* A new function, $(call ...) is provided.  This allows users to create
+* A new function $(call ...) is available.  This allows users to create
   their own parameterized macros and invoke them later.  Original
-  implementation of this feature was provided by Han-Wen Nienhuys
+  implementation of this function was provided by Han-Wen Nienhuys
   <hanwen@cs.uu.nl>.
 
+* A new function $(if ...) is available.  It provides if-then-else
+  capabilities in a builtin function.  Original implementation of this
+  function was provided by Han-Wen Nienhuys <hanwen@cs.uu.nl>.
+
 * Make defines a new variable, .LIBPATTERNS.  This variable controls how
   library dependency expansion (dependencies like ``-lfoo'') is performed.
 
index a864e17c3b9bbe95cfebdb3d3d28916b883c1edb..c320a2a0c8b85450369ee4b6c076c7824be299ed 100644 (file)
@@ -3,7 +3,7 @@ AC_REVISION([$Id$])
 AC_PREREQ(2.13)dnl             dnl Minimum Autoconf version required.
 AC_INIT(vpath.c)dnl            dnl A distinctive file to look for in srcdir.
 
-AM_INIT_AUTOMAKE(make, 3.77.92)
+AM_INIT_AUTOMAKE(make, 3.77.93)
 AM_CONFIG_HEADER(config.h)
 
 dnl Regular configure stuff
index 29e3674414e37ce99cc4eb1bdc3b50b599a7c43f..aebbe3f6bfd41ae3f20ebd0b7bf1089baa70ca8f 100644 (file)
--- a/expand.c
+++ b/expand.c
@@ -442,7 +442,10 @@ variable_expand_for_file (line, file)
 
   save = current_variable_set_list;
   current_variable_set_list = file->variables;
-  reading_file = &file->cmds->fileinfo;
+  if (file->cmds && file->cmds->fileinfo.filenm)
+    reading_file = &file->cmds->fileinfo;
+  else
+    reading_file = 0;
   fnext = file->variables->next;
   /* See if there's a pattern-specific variable struct for this target.  */
   if (!file->pat_searched)
index ca115fe14c0fe322d29ce3c8cb487796b2a795f0..0e619fd2af0229e190539120e8c4c24c4eaa0417 100644 (file)
@@ -699,7 +699,7 @@ int
 is_numeric (p)
      char *p;
 {
-  char *end = p + strlen (p) -1;
+  char *end = p + strlen (p) - 1;
   char *beg = p;
   strip_whitespace (&p, &end);
   while (p <= end)
@@ -817,9 +817,9 @@ func_foreach (o, argv, funcname)
      const char *funcname;
 {
   /* expand only the first two.  */
-  char *varname = expand_argument (argv[0], argv[1] -1);
+  char *varname = expand_argument (argv[0], argv[1] - 1);
   char *list = expand_argument (argv[1], argv[2] -1);
-  char *body = savestring (argv[2], argv[3] - argv[2] -);
+  char *body = savestring (argv[2], argv[3] - argv[2] - 1);
 
   int len =0;
   char *list_iterator = list;
@@ -1075,6 +1075,68 @@ func_sort (o, argv, funcname)
   return o;
 }
 
+/*
+  $(if condition,true-part[,false-part])
+
+  CONDITION is false iff it evaluates to an empty string.  White
+  space before and after condition are stripped before evaluation.
+
+  If CONDITION is true, then TRUE-PART is evaluated, otherwise FALSE-PART is
+  evaluated (if it exists).  Because only one of the two PARTs is evaluated,
+  you can use $(if ...) to create side-effects (with $(shell ...), for
+  example).
+*/
+
+static char *
+func_if (o, argv, funcname)
+     char *o;
+     char **argv;
+     const char *funcname;
+{
+  char *begp = argv[0];
+  char *endp = argv[1]-1;
+  int result = 0;
+
+  /* Find the result of the condition: if we have a value, and it's not
+     empty, the condition is true.  If we don't have a value, or it's the
+     empty string, then it's false.  */
+
+  strip_whitespace (&begp, &endp);
+
+  if (begp < endp)
+    {
+      char *expansion = expand_argument (begp, endp);
+
+      result = strlen (expansion);
+      free (expansion);
+    }
+
+  /* If the result is true (1) we want to eval the first argument, and if
+     it's false (0) we want to eval the second.  If the argument doesn't
+     exist we do nothing, otherwise expand it and add to the buffer.  */
+
+  argv += 1 + !result;
+
+  if (argv[0] != NULL && argv[1] != NULL)
+    {
+      char *expansion;
+      char **endp = argv+1;
+
+      /* If we're doing the else-clause, make sure we concatenate any
+         potential extra arguments into the last argument.  */
+      if (!result)
+        while (*endp && **endp != '\0')
+          ++endp;
+
+      expansion = expand_argument (*argv, *endp-1);
+
+      o = variable_buffer_output (o, expansion, strlen (expansion));
+      free (expansion);
+    }
+
+  return o;
+}
+
 static char *
 func_wildcard(o, argv, funcname)
      char *o;
@@ -1307,7 +1369,7 @@ func_shell (o, argv, funcname)
   /* For error messages.  */
   if (reading_file != 0)
     {
-      error_prefix = (char *) alloca (strlen(reading_file->filenm)+100);
+      error_prefix = (char *) alloca (strlen(reading_file->filenm)+11+4);
       sprintf (error_prefix,
               "%s:%lu: ", reading_file->filenm, reading_file->lineno);
     }
@@ -1546,54 +1608,6 @@ func_not (char* o, char **argv, char *funcname)
   o = variable_buffer_output (o,  result ? "1" : "", result);
   return o;
 }
-
-
-
-/*
- This is an experimental conditional function.
-
- Syntax:
-
-    $(if condition, true-part, false-part)
-
- This is fully not consistent with make's syntax, but more in line
- with `normal' programming languages.
-
- Semantics:
-
- - CONDITION is false iff it evaluates to an empty string.  White
- space before and after condition are stripped before evaluation.
-
- - If CONDITION is true, then TRUE-PART is evaluated, otherwise
- FALSE-PART is evaluated.  Because only one of the two PARTs is
- evaluated, you can use $(if ) to create side-effects with the
- $(shell ) function
-
- */
-static char *
-func_if (char* o, char **argv, char *funcname)
-{
-  char *begp = argv[0];
-  char *endp = argv[1]-2;
-  char *expansion =0;
-  int result = 0;
-
-  strip_whitespace (&begp, &endp);
-  if(begp <= endp)
-    expansion = expand_argument (begp, endp + 1);
-
-  result = expansion
-    ? strlen (expansion)
-    : 0;
-
-  result = !result;
-  free (expansion);
-
-  expansion = expand_argument (argv[1 + result], argv[2+result] -1);
-  o = variable_buffer_output (o, expansion, strlen (expansion));
-
-  return o;
-}
 #endif
 \f
 
@@ -1645,9 +1659,9 @@ static struct function_table_entry function_table[] =
   { STRING_SIZE_TUPLE("call"),         -1,  1,  func_call},
   { STRING_SIZE_TUPLE("error"),         1,  1,  func_error},
   { STRING_SIZE_TUPLE("warning"),       1,  1,  func_error},
+  { STRING_SIZE_TUPLE("if"),           -2,  0,  func_if},
 #ifdef EXPERIMENTAL
   { STRING_SIZE_TUPLE("eq"),            2,  1,  func_eq},
-  { STRING_SIZE_TUPLE("if"),            3,  0,  func_if},
   { STRING_SIZE_TUPLE("not"),           1,  1,  func_not},
 #endif
   { 0 }
diff --git a/job.c b/job.c
index 575a0303fbf81b96585784c6e90f76c742bd1992..7c7d5a0e48c1df07fc58d596ca627ebd8dd98ba9 100644 (file)
--- a/job.c
+++ b/job.c
@@ -246,7 +246,9 @@ free_job_token (child)
 
     default:
       /* Write any other job tokens back to the pipe.  */
-      write (job_fds[1], &child->job_token, 1);
+      while (write (job_fds[1], &child->job_token, 1) != 1)
+        if (!EINTR_SET)
+          pfatal_with_name(_("write jobserver"));
       break;
   }
 
@@ -308,11 +310,10 @@ vmsWaitForChildren(int *status)
 
 /* Handle a dead child.  This handler may or may not ever be installed.
 
-   If we're using the jobserver blocking read, we need it.  First, installing
-   it ensures the read will interrupt on SIGCHLD.  Second, we reset the
-   blocking bit on the read side of the pipe to ensure we don't enter another
-   blocking read without reaping all the dead children.  In this case we
-   don't need the dead_children count.
+   If we're using the jobserver feature, we need it.  First, installing it
+   ensures the read will interrupt on SIGCHLD.  Second, we close the dup'd
+   read FD to ensure we don't enter another blocking read without reaping all
+   the dead children.  In this case we don't need the dead_children count.
 
    If we don't have either waitpid or wait3, then make is unreliable, but we
    use the dead_children count to reap children as best we can.  */
@@ -326,12 +327,10 @@ child_handler (sig)
   ++dead_children;
 
 #ifdef HAVE_JOBSERVER
-  if (job_fds[0] >= 0)
+  if (job_rfd >= 0)
     {
-      int fl = fcntl(job_fds[0], F_GETFL, 0);
-
-      if (fl >= 0)
-        fcntl(job_fds[0], F_SETFL, fl | O_NONBLOCK);
+      close (job_rfd);
+      job_rfd = -1;
     }
 #endif
 
@@ -1032,6 +1031,8 @@ start_job_command (child)
               close (job_fds[0]);
               close (job_fds[1]);
             }
+          if (job_rfd >= 0)
+            close (job_rfd);
 
          child_execute_job (child->good_stdin ? 0 : bad_stdin, 1,
                              argv, child->environment);
@@ -1386,22 +1387,17 @@ new_job (file)
           }
         /* Read a token.  As long as there's no token available we'll block.
            If we get a SIGCHLD we'll return with EINTR.  If one happened
-           before we got here we'll return immediately with EAGAIN because
-           the signal handler unsets the blocking bit.  */
-        else if (read (job_fds[0], &c->job_token, 1) < 1)
+           before we got here we'll return immediately with EBADF because
+           the signal handler closes the dup'd file descriptor.  */
+        else if (read (job_rfd, &c->job_token, 1) < 1)
           {
-            int fl;
-
-#if !defined(EAGAIN)
-# define EAGAIN EWOULDBLOCK
-#endif
-            if (errno != EINTR && errno != EAGAIN)
+            if (errno != EINTR && errno != EBADF)
               pfatal_with_name (_("read jobs pipe"));
 
-            /* Set the blocking bit on the read FD again, just in case.  */
-            fl = fcntl(job_fds[0], F_GETFL, 0);
-            if (fl >= 0)
-              fcntl(job_fds[0], F_SETFL, fl & ~O_NONBLOCK);
+            /* Re-dup the read side of the pipe, so the signal handler can
+               notify us if we miss a child.  */
+            if (job_rfd < 0)
+              job_rfd = dup (job_fds[0]);
 
             /* Something's done.  We don't want to block for a whole child,
                just reap whatever's there.  */
diff --git a/main.c b/main.c
index c51fe7c709987f5a2716a872043f8b959f49f894..72a8aa9900bf550cdae371e49a1873c51e6bfa0d 100644 (file)
--- a/main.c
+++ b/main.c
@@ -205,6 +205,7 @@ static unsigned int inf_jobs = 0;
 /* File descriptors for the jobs pipe.  */
 
 int job_fds[2] = { -1, -1 };
+int job_rfd = -1;
 
 /* Maximum load average at which multiple jobs will be run.
    Negative values mean unlimited, while zero means limit to
@@ -1305,12 +1306,15 @@ int main (int argc, char ** argv)
         job_fds[0] = job_slots;
         job_slots = 0;
 
-        /* Make sure the pipe is open!  The parent might have closed it
-           because it didn't think we were a submake.  If so, print a warning
-           then default to -j1.  */
-        if (fcntl (job_fds[0], F_GETFL, 0) < 0
-            || fcntl (job_fds[1], F_GETFL, 0) < 0)
+        /* Create a duplicate pipe, that will be closed in the SIGCHLD
+           handler.  If this fails with EBADF, the parent has closed the pipe
+           on us because it didn't think we were a submake.  If so, print a
+           warning then default to -j1.  */
+        if ((job_rfd = dup (job_fds[0])) < 0)
           {
+            if (errno != EBADF)
+              pfatal_with_name (_("dup jobserver"));
+
             error (NILF,
                    _("warning: jobserver unavailable (using -j1).  Add `+' to parent make rule."));
             job_slots = 1;
@@ -1328,7 +1332,7 @@ int main (int argc, char ** argv)
       char buf[(sizeof("1024")*2)+1];
       char c = '0';
 
-      if (pipe (job_fds) < 0)
+      if (pipe (job_fds) < 0 || (job_rfd = dup (job_fds[0])) < 0)
        pfatal_with_name (_("creating jobs pipe"));
 
       /* Every make assumes that it always has one job it can run.  For the
@@ -1351,14 +1355,6 @@ int main (int argc, char ** argv)
       sprintf(buf, "%d,%d", job_fds[0], job_fds[1]);
       job_slots_str = xstrdup(buf);
     }
-
-  /* Be sure the blocking bit on the read FD is set to start with.  */
-  if (job_fds[0] >= 0)
-    {
-      int fl = fcntl(job_fds[0], F_GETFL, 0);
-      if (fl >= 0)
-        fcntl(job_fds[0], F_SETFL, fl & ~O_NONBLOCK);
-    }
 #endif
 
   /* Set up MAKEFLAGS and MFLAGS again, so they will be right.  */
diff --git a/make.h b/make.h
index 26eaec909c31b8deaa4e077b40bbd6fbde2c825b..b25318b74a233770cf8800d0417d0d6386639a99 100644 (file)
--- a/make.h
+++ b/make.h
@@ -479,6 +479,7 @@ extern int batch_mode_shell;
 
 extern unsigned int job_slots;
 extern int job_fds[2];
+extern int job_rfd;
 #ifndef NO_FLOAT
 extern double max_load_average;
 #else
index 51330d0cdf8934f1aa191f3e4b83d70b43922e64..8706c4a8b113a4dda498acb8f8f9759934aa6409 100644 (file)
@@ -5200,6 +5200,7 @@ call, just as a variable might be substituted.
 * Text Functions::              General-purpose text manipulation functions.
 * File Name Functions::         Functions for manipulating file names.
 * Foreach Function::            Repeat some text with controlled variation.
+* If Function::                 Conditionally expand a value.
 * Call Function::               Expand a user-defined function.
 * Origin Function::             Find where a variable got its value.
 * Shell Function::              Substitute the output of a shell command.
@@ -5749,7 +5750,7 @@ that match the pattern.
 @xref{Wildcards, ,Using Wildcard Characters in File Names}.
 @end table
 
-@node Foreach Function, Call Function, File Name Functions, Functions
+@node Foreach Function, If Function, File Name Functions, Functions
 @section The @code{foreach} Function
 @findex foreach
 @cindex words, iterating over
@@ -5837,7 +5838,41 @@ might be useful if the value of @code{find_files} references the variable
 whose name is @samp{Esta escrito en espanol!} (es un nombre bastante largo,
 no?), but it is more likely to be a mistake.
 
-@node Call Function, Origin Function, Foreach Function, Functions
+@node If Function, Call Function, Foreach Function, Functions
+@section The @code{if} Function
+@findex if
+@cindex conditional expansion
+
+The @code{if} function provides support for conditional expansion in a
+functional context (as opposed to the GNU @code{make} makefile
+conditionals such as @code{ifeq} (@pxref{Conditional Syntax, ,Syntax of
+Conditionals}).
+
+An @code{if} function call can contain either two or three arguments:
+
+@example
+$(if @var{condition},@var{then-part}[,@var{else-part}])
+@end example
+
+The first argument, @var{condition}, first has all preceding and
+trailing whitespace stripped, then is expanded.  If it expands to any
+non-empty string, then the condition is considered to be true.  If it
+expands to an empty string, the condition is considered to be false.
+
+If the condition is true then the second argument, @var{then-part}, is
+evaluated and this is used as the result of the evaluation of the entire
+@code{if} function.
+
+If the condition is false then the third argument, @var{else-part}, is
+evaluated and this is the result of the @code{if} function.  If there is
+no third argument, the @code{if} function evaluates to nothing (the
+empty string).
+
+Note that only one of the @var{then-part} or the @var{else-part} will be
+evaluated, never both.  Thus, either can contain side-effects (such as
+@code{shell} function calls, etc.)
+
+@node Call Function, Origin Function, If Function, Functions
 @section The @code{call} Function
 @findex call
 @cindex functions, user defined
@@ -5851,7 +5886,7 @@ values.
 The syntax of the @code{call} function is:
 
 @example
-$(call @var{variable}, @var{param}, @var{param}, @dots{})
+$(call @var{variable},@var{param},@var{param},@dots{})
 @end example
 
 When @code{make} expands this function, it assigns each @var{param} to