* Add new jobserver feature.
authorPaul Smith <psmith@gnu.org>
Sun, 25 Apr 1999 04:30:55 +0000 (04:30 +0000)
committerPaul Smith <psmith@gnu.org>
Sun, 25 Apr 1999 04:30:55 +0000 (04:30 +0000)
* Small updates to the manual
* E.Zaretskii fix for new DJGPP version.

14 files changed:
ChangeLog
NEWS
acconfig.h
acinclude.m4
config.ami.template
config.h-vms.template
config.h.W32.template
configh.dos.template
configure.in
job.c
job.h
main.c
make.h
make.texinfo

index e951eaf..cb8832a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,68 @@
+1999-04-25  Paul D. Smith  <psmith@gnu.org>
+
+       * make.texinfo: Updates to @dircategory and @direntry suggested by
+       Karl Berry <karl@cs.umb.edu>.
+
+1999-04-18  Eli Zaretskii  <eliz@is.elta.co.il>
+
+       * configh.dos.template: Update to recognize that version 2.02 of
+       DJGPP contains sys_siglist stuff.
+
+1999-04-14  Paul D. Smith  <psmith@gnu.org>
+
+       * make.texinfo (Options/Recursion): Document the job server.
+       (Parallel): Tweaks.
+
+1999-04-13  Paul D. Smith  <psmith@gnu.org>
+
+       Implement a new "job server" feature; the implementation was
+       suggested by Howard Chu <hyc@highlandsun.com>.
+
+       * configure.in (job-server): New disable option for job server
+       support--it's enabled by default.
+
+       * NEWS: Summarize the new feature.
+
+       * acconfig.h: New definition MAKE_JOBSERVER if job server support
+       is enabled.
+       * config.h-vms.template: Undef MAKE_JOBSERVER for this port.
+       * config.h.W32.template: Ditto.
+       * config.ami.template: Ditto.
+
+       * main.c (struct command_switch): Add a new type: int_string.
+       (switches[]) Use int_string for -j if MAKE_JOBSERVER.
+       (init_switches): Initialize the new int_string switch type.
+       (print_usage): New function, extracted from decode_switches().
+       (decode_switches): Call it.  Decode the new int_string switch type.
+       (define_makeflags): Add new int_string switch data to MAKEFLAGS.
+       (job_fds[]) Array to contain the pipe file descriptors.
+       (main): Parse the job_slots_str option results.  If necessary,
+       create the pipe and seed it with tokens.  Set the non-blocking bit
+       for the read fd.  Enable the signal handler for SIGCHLD even if we
+       have a non-hanging wait; it's needed to interrupt the select() in
+       job.c:start_waiting_job().
+
+       * make.h: Declare job_fds[].
+
+       * job.h (struct child): Add job_token field to store the token for
+       this job (if any).
+
+       * job.c (reap_children): When a child is fully reaped, release the
+       token back into the pipe.
+       (free_child): If the child to be freed still has a token, put it
+       back.
+       (new_job): Initialize the job_token member.
+       (start_waiting_job): For local jobs, if we're using the pipe, get
+       a token before we check the load, etc.  We do this by performing a
+       non-blocking read in a loop.  If the read fails, no token is
+       available.  Do a select on the fd to wait for a token.  We need to
+       re-enable the signal handler for SIGCHLD even if we have a
+       non-hanging waitpid() or wait3(), so that the signal will
+       interrupt the select() and we can wake up to reap children.
+       (child_handler): Re-enable the signal handler.  The count is still
+       kept although it's not needed or used unless you don't have
+       waitpid() or wait3().
+
 1999-04-03  Paul D. Smith  <psmith@gnu.org>
 
        * remake.c (f_mtime): If: a) we found a file and b) we didn't
diff --git a/NEWS b/NEWS
index 8a1e11b..897232b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,6 @@
 GNU make NEWS                                               -*-indented-text-*-
   History of user-visible changes.
-  31 Mar 1999
+  13 Apr 1999
 
 Copyright (C) 1992,93,94,95,96,97,98,1999 Free Software Foundation, Inc.
 See the end for copying conditions.
@@ -23,6 +23,15 @@ Version 3.78
 
 * Make allows CRLF sequences as well as traditional LF, for
   compatibility with makefiles created on other operating systems.
+
+* A "job server" feature, proposed by Howard Chu <hyc@highlandsun.com>.
+
+  On systems that support POSIX pipe(2) semantics, GNU make can now pass
+  -jN options to submakes rather than forcing them all to use -j1.  The
+  top make and all its sub-make processes use a pipe to communicate with
+  each other to ensure that no more than N jobs are started across all
+  makes.  To get the old behavior of -j back, you can configure make
+  with the --disable-job-server option.
 \f
 Version 3.77
 
index d575b92..ebbef45 100644 (file)
@@ -10,6 +10,9 @@
 /* Define this if the SCCS `get' command understands the `-G<file>' option.  */
 #undef SCCS_GET_MINUS_G
 
+/* Define this to enable job server support in GNU make.  */
+#undef MAKE_JOBSERVER
+
 /* Define to be the nanoseconds member of struct stat's st_mtim,
    if it exists.  */
 #undef ST_MTIM_NSEC
 /* Define to `unsigned long' or `unsigned long long'
    if <inttypes.h> doesn't define.  */
 #undef uintmax_t
+
+/* These are for AC_FUNC_SELECT */
+
+/* Define if the system doesn't provide fd_set.  */
+#undef fd_set
+
+/* Define the type of the first arg to select().  */
+#undef fd_set_size_t
+
+/* Define this if select() args need to be cast away from fd_set (HP-UX).  */
+#undef SELECT_FD_SET_CAST
index ff51c4c..01e4f6b 100644 (file)
@@ -208,3 +208,84 @@ AC_DEFUN(jm_AC_TYPE_UINTMAX_T,
     fi
   fi
 ])
+
+
+dnl ---------------------------------------------------------------------------
+dnl From Steve Robbins <steve@nyongwa.montreal.qc.ca>
+
+dnl From a proposed change made on the autoconf list on 2 Feb 1999
+dnl http://sourceware.cygnus.com/ml/autoconf/1999-02/msg00001.html
+
+AC_DEFUN(AC_FUNC_SELECT,
+[AC_CHECK_FUNCS(select)
+if test "$ac_cv_func_select" = yes; then
+  AC_CHECK_HEADERS(unistd.h sys/types.h sys/time.h sys/select.h sys/socket.h)
+  AC_MSG_CHECKING([argument types of select()])
+  AC_CACHE_VAL(ac_cv_type_fd_set_size_t,dnl
+    [AC_CACHE_VAL(ac_cv_type_fd_set,dnl
+      [for ac_cv_type_fd_set in 'fd_set' 'int'; do
+        for ac_cv_type_fd_set_size_t in 'int' 'size_t' 'unsigned long' 'unsigned'; do
+          for ac_type_timeval in 'struct timeval' 'const struct timeval'; do
+            AC_TRY_COMPILE(dnl
+[#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif],
+[extern select ($ac_cv_type_fd_set_size_t,
+ $ac_cv_type_fd_set *,  $ac_cv_type_fd_set *, $ac_cv_type_fd_set *,
+ $ac_type_timeval *);],
+[ac_found=yes ; break 3],ac_found=no)
+          done
+        done
+      done
+    ])dnl AC_CACHE_VAL
+  ])dnl AC_CACHE_VAL
+  if test "$ac_found" = no; then
+    AC_MSG_ERROR([can't determine argument types])
+  fi
+
+  AC_MSG_RESULT([select($ac_cv_type_fd_set_size_t,$ac_cv_type_fd_set *,...)])
+  AC_DEFINE_UNQUOTED(fd_set_size_t, $ac_cv_type_fd_set_size_t)
+  ac_cast=
+  if test "$ac_cv_type_fd_set" != fd_set; then
+    # Arguments 2-4 are not fd_set.  Some weirdo systems use fd_set type for
+    # FD_SET macros, but insist that you cast the argument to select.  I don't
+    # understand why that might be, but it means we cannot define fd_set.
+    AC_EGREP_CPP(dnl
+changequote(<<,>>)dnl
+<<(^|[^a-zA-Z_0-9])fd_set[^a-zA-Z_0-9]>>dnl
+changequote([,]),dnl
+[#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif],dnl
+    # We found fd_set type in a header, need special cast
+    ac_cast="($ac_cv_type_fd_set *)",dnl
+    # No fd_set type; it is safe to define it
+    AC_DEFINE_UNQUOTED(fd_set,$ac_cv_type_fd_set))
+  fi
+  AC_DEFINE_UNQUOTED(SELECT_FD_SET_CAST,$ac_cast)
+fi
+])
index 523876e..db92dea 100644 (file)
 /* Define this if the SCCS `get' command understands the `-G<file>' option.  */
 /* #undef SCCS_GET_MINUS_G */
 
+/* Define this to enable job server support in GNU make.  */
+/* #undef MAKE_JOBSERVER */
+
+/* Define to be the nanoseconds member of struct stat's st_mtim,
+   if it exists.  */
+/* #undef ST_MTIM_NSEC */
+
 /* Define this if the C library defines the variable `sys_siglist'.  */
 /* #undef HAVE_SYS_SIGLIST */
 
index e44af44..dca4bda 100644 (file)
 /* Define this if the SCCS `get' command understands the `-G<file>' option.  */
 /* #undef SCCS_GET_MINUS_G */
 
+/* Define this to enable job server support in GNU make.  */
+/* #undef MAKE_JOBSERVER */
+
+/* Define to be the nanoseconds member of struct stat's st_mtim,
+   if it exists.  */
+/* #undef ST_MTIM_NSEC */
+
 /* Define this if the C library defines the variable `sys_siglist'.  */
 /* #undefine HAVE_SYS_SIGLIST */
 
index 79bb6cd..402db81 100644 (file)
 /* Define this if the SCCS `get' command understands the `-G<file>' option.  */
 /* #undef SCCS_GET_MINUS_G */
 
+/* Define this to enable job server support in GNU make.  */
+/* #undef MAKE_JOBSERVER */
+
+/* Define to be the nanoseconds member of struct stat's st_mtim,
+   if it exists.  */
+/* #undef ST_MTIM_NSEC */
+
 /* Define this if the C library defines the variable `sys_siglist'.  */
 /* #undef HAVE_SYS_SIGLIST */
 
index 852b6f8..7eaed6e 100644 (file)
@@ -1,4 +1,3 @@
-/* Generated automatically from configure.in by autoheader.  DO NOT EDIT!  */\r
 \r
 /* Many things are defined already by a system header.  */\r
 #include <sys/config.h>\r
@@ -9,8 +8,20 @@
 /* Version of this package (needed by automake) */\r
 #define VERSION "%VERSION%"\r
 \r
+#if __DJGPP__ > 2 || __DJGPP_MINOR__ > 1\r
+\r
+/* Define if `sys_siglist' is declared by <signal.h>.  */\r
+# define SYS_SIGLIST_DECLARED 1\r
+\r
+/* Define this if the C library defines the variable `_sys_siglist'.  */\r
+# define HAVE_SYS_SIGLIST 1\r
+\r
+#else\r
+\r
 /* Define NSIG.  */\r
-#define NSIG SIGMAX\r
+# define NSIG SIGMAX\r
+\r
+#endif\r
 \r
 /* Define if you have sigsetmask.  */\r
 #define HAVE_SIGSETMASK 1\r
index 4e46187..4bcf56f 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.90)
+AM_INIT_AUTOMAKE(make, 3.77.xx)
 AM_CONFIG_HEADER(config.h)
 
 dnl Regular configure stuff
@@ -57,7 +57,7 @@ AC_CHECK_LIB(posix4, clock_gettime)
 
 AC_CHECK_FUNCS(memmove strdup psignal mktemp pstat_getdynamic \
               clock_gettime dup2 getcwd sigsetmask getgroups setlinebuf \
-              seteuid setegid setreuid setregid strerror strsignal)
+              seteuid setegid setreuid setregid strerror strsignal pipe)
 AC_CHECK_SYMBOL(sys_siglist)
 AC_FUNC_ALLOCA
 AC_FUNC_VFORK
@@ -66,6 +66,7 @@ AC_FUNC_STRCOLL
 AC_FUNC_CLOSEDIR_VOID
 AC_FUNC_SETVBUF_REVERSED
 AC_FUNC_GETLOADAVG
+AC_FUNC_SELECT
 AC_CHECK_LIB(kstat, kstat_open)
 
 # Check out the wait reality.
@@ -124,6 +125,18 @@ AC_ARG_WITH(customs,
      ;;
 esac])
 
+dnl See if we can handle the job server feature, and if the user wants it.
+
+AC_ARG_ENABLE(job-server,
+  [  --disable-job-server    Disallow recursive make communication during -jN],
+  [make_cv_job_server="$enableval"],
+  [make_cv_job_server="yes"])
+
+case "$ac_cv_func_pipe $make_cv_job_server" in
+  "yes yes") AC_DEFINE(MAKE_JOBSERVER) ;;
+esac
+
+
 AC_CACHE_CHECK(for location of SCCS get command, make_cv_path_sccs_get, [
 if test -f /usr/sccs/get; then
   make_cv_path_sccs_get=/usr/sccs/get
diff --git a/job.c b/job.c
index 19f386b..8a998ea 100644 (file)
--- a/job.c
+++ b/job.c
@@ -1,5 +1,5 @@
 /* Job execution and handling for GNU Make.
-Copyright (C) 1988,89,90,91,92,93,94,95,96,97 Free Software Foundation, Inc.
+Copyright (C) 1988,89,90,91,92,93,94,95,96,97,99 Free Software Foundation, Inc.
 This file is part of GNU Make.
 
 GNU Make is free software; you can redistribute it and/or modify
@@ -257,10 +257,10 @@ vmsWaitForChildren(int *status)
 #endif
 
 
-/* If we can't use waitpid() or wait3(), then we use a signal handler
-   to track the number of SIGCHLD's we got.  This is less robust.  */
-
-#ifndef WAIT_NOHANG
+/* Count the number of dead children we have.  If we can use wait3() or
+   waitpid() then we'll never use this count: it's completely unnecessary.
+   But we need the handler installed to interrupt the select() call for
+   the jobs pipe, so we might as well keep it.  */
 
 static unsigned int dead_children = 0;
 
@@ -273,10 +273,9 @@ child_handler (sig)
   ++dead_children;
 
   if (debug_flag)
-    printf ("Got a SIGCHLD; %d unreaped children.\n", dead_children);
+    printf ("Got a SIGCHLD; %u unreaped children.\n", dead_children);
 }
 
-#endif /* WAIT_NOHANG */
 
 extern int shell_function_pid, shell_function_completed;
 
@@ -292,11 +291,15 @@ reap_children (block, err)
 {
   WAIT_T status;
 #ifdef WAIT_NOHANG
-  int dead_children = 1;  /* Initially, assume we have some.  */
+  /* Initially, assume we have some.  */
+  int reap_more = 1;
+# define REAP_MORE reap_more
+#else
+# define REAP_MORE dead_children
 #endif
 
   while ((children != 0 || shell_function_pid != 0) &&
-        (block || dead_children))
+        (block || REAP_MORE))
     {
       int remote = 0;
       register int pid;
@@ -312,8 +315,12 @@ reap_children (block, err)
          error (NILF, "*** Waiting for unfinished jobs....");
        }
 
-#ifndef WAIT_NOHANG
-      /* We have one less dead child to reap.
+      /* We have one less dead child to reap.  As noted in
+        child_handler() above, this count is completely unimportant for
+        all modern, POSIX-y systems that support wait3() or waitpid().
+        The rest of this comment below applies only to early, broken
+        pre-POSIX systems.  We keep the count only because... it's there...
+
         The test and decrement are not atomic; if it is compiled into:
                register = dead_children - 1;
                dead_children = register;
@@ -327,7 +334,6 @@ reap_children (block, err)
 
       if (dead_children > 0)
        --dead_children;
-#endif
 
       any_remote = 0;
       any_local = shell_function_pid != 0;
@@ -405,7 +411,7 @@ reap_children (block, err)
            {
              /* No local children are dead.  */
 #ifdef WAIT_NOHANG
-              dead_children = 0;
+              reap_more = 0;
 #endif
              if (block && any_remote)
                {
@@ -612,6 +618,22 @@ reap_children (block, err)
             live and call reap_children again.  */
          block_sigs ();
 
+#ifdef MAKE_JOBSERVER
+         /* If this job has a token out, return it.  */
+          if (c->job_token)
+           {
+             assert(job_slots_used > 0);
+             write (job_fds[1], &c->job_token, 1);
+             if (debug_flag)
+               printf ("Released token `%c' for child 0x%08lx.\n",
+                       c->job_token, (unsigned long int) c);
+             c->job_token = 0;
+           }
+#endif
+         /* There is now another slot open.  */
+         if (job_slots_used > 0)
+           --job_slots_used;
+
          /* Remove the child from the chain and free it.  */
          if (lastc == 0)
            children = c->next;
@@ -620,10 +642,6 @@ reap_children (block, err)
          if (! handling_fatal_signal) /* Don't bother if about to die.  */
            free_child (c);
 
-         /* There is now another slot open.  */
-         if (job_slots_used > 0)
-           --job_slots_used;
-
          unblock_sigs ();
 
          /* If the job failed, and the -k flag was not given, die,
@@ -662,6 +680,19 @@ free_child (child)
       free ((char *) child->environment);
     }
 
+#ifdef MAKE_JOBSERVER
+  /* If this child has a token it hasn't relinquished, give it up now.
+     This can happen if the job completes immediately, mainly because
+     all the command lines evaluated to empty strings.  */
+  if (child->job_token)
+    {
+      write (job_fds[1], &child->job_token, 1);
+      if (debug_flag)
+       printf ("Freed token `%c' for child 0x%08lx.\n",
+               child->job_token, (unsigned long int) child);
+    }
+#endif
+
   free ((char *) child);
 }
 \f
@@ -1083,28 +1114,77 @@ static int
 start_waiting_job (c)
      struct child *c;
 {
+  struct file *f = c->file;
+
   /* If we can start a job remotely, we always want to, and don't care about
      the local load average.  We record that the job should be started
      remotely in C->remote for start_job_command to test.  */
 
   c->remote = start_remote_job_p (1);
 
-  /* If this job is to be started locally, and we are already running
-     some jobs, make this one wait if the load average is too high.  */
-  if (!c->remote && job_slots_used > 0 && load_too_high ())
+  /* If not, start it locally.  */
+  if (!c->remote)
     {
-      /* Put this child on the chain of children waiting
-        for the load average to go down.  */
-      set_command_state (c->file, cs_running);
-      c->next = waiting_jobs;
-      waiting_jobs = c;
-      return 0;
+#ifdef MAKE_JOBSERVER
+      /* If this is not a recurse command and we are controlling
+        multiple jobs, obtain a token before starting child. */
+      if (job_fds[0] >= 0 && !f->cmds->any_recurse)
+       {
+         fd_set rfds;
+
+         FD_ZERO(&rfds);
+         FD_SET(job_fds[0], &rfds);
+
+         /* Read a token.  We set the non-blocking bit on this earlier,
+            so if there's no token to be read we'll fall through to the
+            select.  The select block until (a) there's data to read,
+            in which case we come back around and try to grab the token
+            before someone else does, or (b) a signal, such as SIGCHLD,
+            is caught (because we installed a handler for it).  If the
+            latter, call reap_children() to try to free up some slots.  */
+
+         while (read (job_fds[0], &c->job_token, 1) < 1)
+           {
+             int r = select (job_fds[0]+1, SELECT_FD_SET_CAST &rfds,
+                              NULL, NULL, NULL);
+
+             if (r < 0)
+               {
+#ifdef EINTR
+                 if (errno != EINTR)
+                   /* We should definitely handle this more gracefully!
+                      What kinds of things can happen here?  ^C closes the
+                      pipe?  Something else closes it?  */
+                   pfatal_with_name ("read jobs pipe");
+#endif
+                 /* We were interrupted; handle any dead children.  */
+                 reap_children (1, 0);
+               }
+           }
+
+         assert(c->job_token != 0);
+         if (debug_flag)
+           printf ("Obtained token `%c' for child 0x%08lx.\n",
+                   c->job_token, (unsigned long int) c);
+       }
+#endif
+      /* If we are running at least one job already and the load average
+        is too high, make this one wait.  */
+      if (job_slots_used > 0 && load_too_high ())
+       {
+         /* Put this child on the chain of children waiting
+            for the load average to go down.  */
+         set_command_state (f, cs_running);
+         c->next = waiting_jobs;
+         waiting_jobs = c;
+         return 0;
+       }
     }
 
   /* Start the first command; reap_children will run later command lines.  */
   start_job_command (c);
 
-  switch (c->file->command_state)
+  switch (f->command_state)
     {
     case cs_running:
       c->next = children;
@@ -1120,16 +1200,16 @@ start_waiting_job (c)
 
     case cs_not_started:
       /* All the command lines turned out to be empty.  */
-      c->file->update_status = 0;
+      f->update_status = 0;
       /* FALLTHROUGH */
 
     case cs_finished:
-      notice_finished_file (c->file);
+      notice_finished_file (f);
       free_child (c);
       break;
 
     default:
-      assert (c->file->command_state == cs_finished);
+      assert (f->command_state == cs_finished);
       break;
     }
 
@@ -1157,8 +1237,9 @@ new_job (file)
   /* Chop the commands up into lines if they aren't already.  */
   chop_commands (cmds);
 
+  /* Wait for a job slot to be freed up.  If we allow an infinite number
+     don't bother; also job_slots will == 0 if we're using the job pipe.  */
   if (job_slots != 0)
-    /* Wait for a job slot to be freed up.  */
     while (job_slots_used == job_slots)
       reap_children (1, 0);
 
@@ -1273,6 +1354,7 @@ new_job (file)
   c->command_ptr = 0;
   c->environment = 0;
   c->sh_batch_file = NULL;
+  c->job_token = 0;
 
   /* Fetch the first command line to be run.  */
   if (job_next_command (c))
diff --git a/job.h b/job.h
index 4f2a3ae..4c5e7e5 100644 (file)
--- a/job.h
+++ b/job.h
@@ -1,5 +1,5 @@
 /* Definitions for managing subprocesses in GNU Make.
-Copyright (C) 1992, 1993, 1996 Free Software Foundation, Inc.
+Copyright (C) 1992, 1993, 1996, 1999 Free Software Foundation, Inc.
 This file is part of GNU Make.
 
 GNU Make is free software; you can redistribute it and/or modify
@@ -39,13 +39,14 @@ struct child
     int efn;                   /* Completion event flag number */
     int cstatus;               /* Completion status */
 #endif
+    char *sh_batch_file;        /* Script file for shell commands */
     unsigned int remote:1;     /* Nonzero if executing remotely.  */
 
     unsigned int noerror:1;    /* Nonzero if commands contained a `-'.  */
 
     unsigned int good_stdin:1; /* Nonzero if this child has a good stdin.  */
     unsigned int deleted:1;    /* Nonzero if targets have been deleted.  */
-    char *sh_batch_file;        /* Script file for shell commands */
+    char job_token;            /* The token read from the job pipe.  */
   };
 
 extern struct child *children;
diff --git a/main.c b/main.c
index 773c999..9850fbc 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1,5 +1,5 @@
 /* Argument parsing and main program of GNU Make.
-Copyright (C) 1988,89,90,91,94,95,96,97,98 Free Software Foundation, Inc.
+Copyright (C) 1988,89,90,91,94,95,96,97,98,99 Free Software Foundation, Inc.
 This file is part of GNU Make.
 
 GNU Make is free software; you can redistribute it and/or modify
@@ -34,6 +34,9 @@ MA 02111-1307, USA.  */
 #include <windows.h>
 #include "pathstuff.h"
 #endif
+#if defined(MAKE_JOBSERVER) && defined(HAVE_FCNTL_H)
+# include <fcntl.h>
+#endif
 
 #ifdef _AMIGA
 int __stack = 20000; /* Make sure we have 20K of stack space */
@@ -83,6 +86,7 @@ struct command_switch
        flag,                   /* Turn int flag on.  */
        flag_off,               /* Turn int flag off.  */
        string,                 /* One string per switch.  */
+       int_string,             /* One string.  */
        positive_int,           /* A positive integer.  */
        floating,               /* A floating-point number (double).  */
        ignore                  /* Ignored.  */
@@ -190,9 +194,16 @@ static struct stringlist *makefiles = 0;
 unsigned int job_slots = 1;
 unsigned int default_job_slots = 1;
 
-/* Value of job_slots that means no limit.  */
+static char *job_slots_str = "1";
 
+#ifndef MAKE_JOBSERVER
+/* Value of job_slots that means no limit.  */
 static unsigned int inf_jobs = 0;
+#endif
+
+/* File descriptors for the jobs pipe.  */
+
+int job_fds[2] = { -1, -1 };
 
 /* Maximum load average at which multiple jobs will be run.
    Negative values mean unlimited, while zero means limit to
@@ -264,8 +275,13 @@ static const struct command_switch switches[] =
     { 'I', string, (char *) &include_directories, 1, 1, 0, 0, 0,
        "include-dir", "DIRECTORY",
        "Search DIRECTORY for included makefiles" },
-    { 'j', positive_int, (char *) &job_slots, 1, 1, 0,
+    { 'j',
+#ifndef MAKE_JOBSERVER
+        positive_int, (char *) &job_slots, 1, 1, 0,
        (char *) &inf_jobs, (char *) &default_job_slots,
+#else
+        int_string, (char *)&job_slots_str, 1, 1, 0, "0", "1",
+#endif
        "jobs", "N",
        "Allow N jobs at once; infinite jobs with no arg" },
     { 'k', flag, (char *) &keep_going_flag, 1, 1, 0,
@@ -1144,13 +1160,19 @@ int main (int argc, char ** argv)
          }
     }
 
-#ifndef HAVE_WAIT_NOHANG
+#if defined(MAKE_JOBSERVER) || !defined(HAVE_WAIT_NOHANG)
+  /* If we don't have a hanging wait we have to fall back to old, broken
+     functionality here and rely on the signal handler and counting
+     children.
+
+     Also, if we're using the jobs pipe we need a signal handler so that
+     SIGCHLD is not ignored; we need it to interrupt select(2) in
+     job.c:start_waiting_job() if we're waiting on the pipe for a token.  */
   {
     extern RETSIGTYPE child_handler PARAMS ((int sig));
 
     /* Set up to handle children dying.  This must be done before
-       reading in the makefiles so that `shell' function calls will work.
-       Note we only do this if we have to.   */
+       reading in the makefiles so that `shell' function calls will work.  */
 # if defined SIGCHLD
     (void) signal (SIGCHLD, child_handler);
 # endif
@@ -1236,6 +1258,59 @@ int main (int argc, char ** argv)
   decode_env_switches ("MFLAGS", 6);
 #endif
 
+#ifdef MAKE_JOBSERVER
+  /* If extended jobs are available then the -j option can have one of 4
+     formats: (1) not specified: default is "1"; (2) specified with no
+     value: default is "0" (infinite); (3) specified with a single
+     value: this means the user wants N job slots; or (4) specified with
+     2 values separated by commas.  The latter means we're a submake and
+     the two values are the read and write FDs, respectively, for the
+     pipe.  Note this last form is undocumented for the user!
+   */
+  sscanf(job_slots_str, "%d", &job_slots);
+  {
+    char *cp = index(job_slots_str, ',');
+
+    if (cp && sscanf(cp+1, "%d", &job_fds[1]) == 1)
+      {
+       job_fds[0] = job_slots;
+       job_slots = 0;
+      }
+  }
+
+  /* In case #3 above, set up the pipe and set up the submake options
+     properly.  */
+
+  if (job_slots > 1)
+    {
+      char buf[(sizeof("1024")*2)+1];
+      char c = '0';
+
+      if (pipe(job_fds) < 0)
+       pfatal_with_name("creating jobs pipe");
+
+      /* Set the read FD to nonblocking; we'll use select() to wait
+        for it in job.c.  */
+      fcntl (job_fds[0], F_SETFL, O_NONBLOCK);
+
+      for (; job_slots; --job_slots)
+       {
+         write(job_fds[1], &c, 1);
+         if (c == '9')
+           c = 'a';
+         else if (c == 'z')
+           c = 'A';
+         else if (c == 'Z')
+           c = '0'; /* Start over again!!  */
+         else
+           ++c;
+       }
+
+      sprintf(buf, "%d,%d", job_fds[0], job_fds[1]);
+      job_slots_str = xstrdup(buf);
+    }
+#endif
+
   /* Set up MAKEFLAGS and MFLAGS again, so they will be right.  */
 
   define_makeflags (1, 0);
@@ -1650,6 +1725,7 @@ init_switches ()
          long_options[i].has_arg = no_argument;
          break;
 
+       case int_string:
        case string:
        case positive_int:
        case floating:
@@ -1742,6 +1818,103 @@ handle_non_switch_argument (arg, env)
     }
 }
 
+/* Print a nice usage method.  */
+
+static void
+print_usage (bad)
+     int bad;
+{
+  register const struct command_switch *cs;
+  FILE *usageto;
+
+  if (print_version_flag)
+    print_version ();
+
+  usageto = bad ? stderr : stdout;
+
+  fprintf (usageto, "Usage: %s [options] [target] ...\n", program);
+
+  fputs ("Options:\n", usageto);
+  for (cs = switches; cs->c != '\0'; ++cs)
+    {
+      char buf[1024], shortarg[50], longarg[50], *p;
+
+      if (cs->description[0] == '-')
+       continue;
+
+      switch (long_options[cs - switches].has_arg)
+       {
+       case no_argument:
+         shortarg[0] = longarg[0] = '\0';
+         break;
+       case required_argument:
+         sprintf (longarg, "=%s", cs->argdesc);
+         sprintf (shortarg, " %s", cs->argdesc);
+         break;
+       case optional_argument:
+         sprintf (longarg, "[=%s]", cs->argdesc);
+         sprintf (shortarg, " [%s]", cs->argdesc);
+         break;
+       }
+
+      p = buf;
+
+      if (isalnum (cs->c))
+       {
+         sprintf (buf, "  -%c%s", cs->c, shortarg);
+         p += strlen (p);
+       }
+      if (cs->long_name != 0)
+       {
+         unsigned int i;
+         sprintf (p, "%s--%s%s",
+                  !isalnum (cs->c) ? "  " : ", ",
+                  cs->long_name, longarg);
+         p += strlen (p);
+         for (i = 0; i < (sizeof (long_option_aliases) /
+                          sizeof (long_option_aliases[0]));
+              ++i)
+           if (long_option_aliases[i].val == cs->c)
+             {
+               sprintf (p, ", --%s%s",
+                        long_option_aliases[i].name, longarg);
+               p += strlen (p);
+             }
+       }
+      {
+       const struct command_switch *ncs = cs;
+       while ((++ncs)->c != '\0')
+         if (ncs->description[0] == '-' &&
+             ncs->description[1] == cs->c)
+           {
+             /* This is another switch that does the same
+                one as the one we are processing.  We want
+                to list them all together on one line.  */
+             sprintf (p, ", -%c%s", ncs->c, shortarg);
+             p += strlen (p);
+             if (ncs->long_name != 0)
+               {
+                 sprintf (p, ", --%s%s", ncs->long_name, longarg);
+                 p += strlen (p);
+               }
+           }
+      }
+
+      if (p - buf > DESCRIPTION_COLUMN - 2)
+       /* The list of option names is too long to fit on the same
+          line with the description, leaving at least two spaces.
+          Print it on its own line instead.  */
+       {
+         fprintf (usageto, "%s\n", buf);
+         buf[0] = '\0';
+       }
+
+      fprintf (usageto, "%*s%s.\n",
+              - DESCRIPTION_COLUMN,
+              buf, cs->description);
+    }
+}
+
 /* Decode switches from ARGC and ARGV.
    They came from the environment if ENV is nonzero.  */
 
@@ -1806,6 +1979,20 @@ decode_switches (argc, argv, env)
                    *(int *) cs->value_ptr = cs->type == flag;
                  break;
 
+               case int_string:
+                 if (optarg == 0 && argc > optind
+                     && isdigit (argv[optind][0]))
+                   optarg = argv[optind++];
+
+                 if (!doit)
+                   break;
+
+                 if (optarg == 0)
+                   optarg = cs->noarg_value;
+
+                 *(char **)cs->value_ptr = optarg;
+                 break;
+
                case string:
                  if (!doit)
                    break;
@@ -1891,96 +2078,7 @@ positive integral argument",
 
   if (!env && (bad || print_usage_flag))
     {
-      /* Print a nice usage message.  */
-      FILE *usageto;
-
-      if (print_version_flag)
-       print_version ();
-
-      usageto = bad ? stderr : stdout;
-
-      fprintf (usageto, "Usage: %s [options] [target] ...\n", program);
-
-      fputs ("Options:\n", usageto);
-      for (cs = switches; cs->c != '\0'; ++cs)
-       {
-         char buf[1024], shortarg[50], longarg[50], *p;
-
-         if (cs->description[0] == '-')
-           continue;
-
-         switch (long_options[cs - switches].has_arg)
-           {
-           case no_argument:
-             shortarg[0] = longarg[0] = '\0';
-             break;
-           case required_argument:
-             sprintf (longarg, "=%s", cs->argdesc);
-             sprintf (shortarg, " %s", cs->argdesc);
-             break;
-           case optional_argument:
-             sprintf (longarg, "[=%s]", cs->argdesc);
-             sprintf (shortarg, " [%s]", cs->argdesc);
-             break;
-           }
-
-         p = buf;
-
-         if (isalnum (cs->c))
-           {
-             sprintf (buf, "  -%c%s", cs->c, shortarg);
-             p += strlen (p);
-           }
-         if (cs->long_name != 0)
-           {
-             unsigned int i;
-             sprintf (p, "%s--%s%s",
-                      !isalnum (cs->c) ? "  " : ", ",
-                      cs->long_name, longarg);
-             p += strlen (p);
-             for (i = 0; i < (sizeof (long_option_aliases) /
-                              sizeof (long_option_aliases[0]));
-                  ++i)
-               if (long_option_aliases[i].val == cs->c)
-                 {
-                   sprintf (p, ", --%s%s",
-                            long_option_aliases[i].name, longarg);
-                   p += strlen (p);
-                 }
-           }
-         {
-           const struct command_switch *ncs = cs;
-           while ((++ncs)->c != '\0')
-             if (ncs->description[0] == '-' &&
-                 ncs->description[1] == cs->c)
-               {
-                 /* This is another switch that does the same
-                    one as the one we are processing.  We want
-                    to list them all together on one line.  */
-                 sprintf (p, ", -%c%s", ncs->c, shortarg);
-                 p += strlen (p);
-                 if (ncs->long_name != 0)
-                   {
-                     sprintf (p, ", --%s%s", ncs->long_name, longarg);
-                     p += strlen (p);
-                   }
-               }
-         }
-
-         if (p - buf > DESCRIPTION_COLUMN - 2)
-           /* The list of option names is too long to fit on the same
-              line with the description, leaving at least two spaces.
-              Print it on its own line instead.  */
-           {
-             fprintf (usageto, "%s\n", buf);
-             buf[0] = '\0';
-           }
-
-         fprintf (usageto, "%*s%s.\n",
-                  - DESCRIPTION_COLUMN,
-                  buf, cs->description);
-       }
-
+      print_usage(bad);
       die (bad ? 2 : 0);
     }
 }
@@ -2193,6 +2291,22 @@ define_makeflags (all, makefile)
          break;
 #endif
 
+       case int_string:
+         if (all)
+           {
+             char *vp = *(char **)cs->value_ptr;
+
+             if (cs->default_value != 0
+                 && streq(vp, cs->default_value))
+               break;
+             if (cs->noarg_value != 0
+                 && streq(vp, cs->noarg_value))
+               ADD_FLAG("", 0); /* Optional value omitted; see below.  */
+             else
+               ADD_FLAG(vp, strlen(vp));
+           }
+         break;
+
        case string:
          if (all)
            {
diff --git a/make.h b/make.h
index 76a747b..9123992 100644 (file)
--- a/make.h
+++ b/make.h
@@ -1,5 +1,5 @@
 /* Miscellaneous global declarations and portability cruft for GNU Make.
-Copyright (C) 1988,89,90,91,92,93,94,95,96,97 Free Software Foundation, Inc.
+Copyright (C) 1988,89,90,91,92,93,94,95,96,97,99 Free Software Foundation, Inc.
 This file is part of GNU Make.
 
 GNU Make is free software; you can redistribute it and/or modify
@@ -458,6 +458,7 @@ extern int clock_skew_detected;
 extern int batch_mode_shell;
 
 extern unsigned int job_slots;
+extern int job_fds[2];
 #ifndef NO_FLOAT
 extern double max_load_average;
 #else
index 6d9be0a..e6b6165 100644 (file)
@@ -10,8 +10,8 @@
 @set RCSID $Id$
 @set EDITION 0.53
 @set VERSION 3.78
-@set UPDATED 22 March 1999
-@set UPDATE-MONTH March 1999
+@set UPDATED 14 April 1999
+@set UPDATE-MONTH April 1999
 @comment The ISBN number might need to change on next publication.
 @set ISBN 1-882114-80-9 @c CHANGE THIS BEFORE PRINTING AGAIN! --psmith 16jul98
 
 @c Combine the program and concept indices:
 @syncodeindex pg cp
 
-@ifinfo
-@dircategory The GNU make utility
+@dircategory GNU Packages
 @direntry
-     * Make: (make.info).               The GNU make utility.
+* Make: (make).            Remake files automatically.
 @end direntry
 
+@ifinfo
 This file documents the GNU Make utility, which determines
 automatically which pieces of a large program need to be recompiled,
 and issues the commands to recompile them.
@@ -3032,8 +3032,8 @@ there is no limit on the number of job slots.  The default number of job
 slots is one, which means serial execution (one thing at a time).
 
 One unpleasant consequence of running several commands simultaneously is
-that output from all of the commands comes when the commands send it, so
-messages from different commands may be interspersed.
+that output generated by the commands appears whenever each command
+sends it, so messages from different commands may be interspersed.
 
 Another problem is that two processes cannot both take input from the
 same device; so to make sure that only one command tries to take input
@@ -3057,6 +3057,10 @@ standard input at all if you are using the parallel execution feature; but
 if you are not using this feature, then standard input works normally in
 all commands.
 
+Finally, handling recursive @code{make} invocations raises issues.  For
+more information on this, see
+@ref{Options/Recursion, ,Communicating Options to a Sub-@code{make}}.
+
 If a command fails (is killed by a signal or exits with a nonzero
 status), and errors are not ignored for that command
 (@pxref{Errors, ,Errors in Commands}),
@@ -3546,13 +3550,22 @@ into @code{MAKEFLAGS}; these options are not passed down.@refill
 @cindex recursion, and @code{-j}
 @cindex job slots, and recursion
 The @samp{-j} option is a special case (@pxref{Parallel, ,Parallel Execution}).
-If you set it to some numeric value, @samp{-j 1} is always put into
-@code{MAKEFLAGS} instead of the value you specified.  This is because if
-the @w{@samp{-j}} option were passed down to sub-@code{make}s, you would
-get many more jobs running in parallel than you asked for.  If you give
-@samp{-j} with no numeric argument, meaning to run as many jobs as
-possible in parallel, this is passed down, since multiple infinities are
-no more than one.@refill
+If you set it to some numeric value @samp{N} and your operating system
+supports it (most any UNIX system will; others typically won't), the
+parent @code{make} and all the sub-@code{make}s will communicate to
+ensure that there are only @samp{N} jobs running at the same time
+between them all.  Note that any job that is marked recursive
+(@pxref{Instead of Execution, ,Instead of Executing the Commands})
+doesn't count against the total jobs (otherwise we could get @samp{N}
+sub-@code{make}s running and have no slots left over for any real work!)
+
+If your operating system doesn't support the above communication, then
+@samp{-j 1} is always put into @code{MAKEFLAGS} instead of the value you
+specified.  This is because if the @w{@samp{-j}} option were passed down
+to sub-@code{make}s, you would get many more jobs running in parallel
+than you asked for.  If you give @samp{-j} with no numeric argument,
+meaning to run as many jobs as possible in parallel, this is passed
+down, since multiple infinities are no more than one.@refill
 
 If you do not want to pass the other flags down, you must change the
 value of @code{MAKEFLAGS}, like this: