summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Smith <psmith@gnu.org>1999-04-25 04:30:55 +0000
committerPaul Smith <psmith@gnu.org>1999-04-25 04:30:55 +0000
commitfc0fe4103ac983d88b83dad0daf97664ffa8e04b (patch)
tree7f2a029706a5547af16132ae451a673936b353bf
parent7f3ffd401903e04637c0e822e152e8dc632264e5 (diff)
downloadgunmake-fc0fe4103ac983d88b83dad0daf97664ffa8e04b.tar.gz
* Add new jobserver feature.
* Small updates to the manual * E.Zaretskii fix for new DJGPP version.
-rw-r--r--ChangeLog65
-rw-r--r--NEWS11
-rw-r--r--acconfig.h14
-rw-r--r--acinclude.m481
-rw-r--r--config.ami.template7
-rw-r--r--config.h-vms.template7
-rw-r--r--config.h.W32.template7
-rw-r--r--configh.dos.template15
-rw-r--r--configure.in17
-rw-r--r--job.c144
-rw-r--r--job.h5
-rw-r--r--main.c306
-rw-r--r--make.h3
-rw-r--r--make.texinfo41
14 files changed, 574 insertions, 149 deletions
diff --git a/ChangeLog b/ChangeLog
index e951eaf..cb8832a 100644
--- 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
--- 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.
Version 3.77
diff --git a/acconfig.h b/acconfig.h
index d575b92..ebbef45 100644
--- a/acconfig.h
+++ b/acconfig.h
@@ -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
@@ -26,3 +29,14 @@
/* 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
diff --git a/acinclude.m4 b/acinclude.m4
index ff51c4c..01e4f6b 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -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
+])
diff --git a/config.ami.template b/config.ami.template
index 523876e..db92dea 100644
--- a/config.ami.template
+++ b/config.ami.template
@@ -176,6 +176,13 @@
/* 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 */
diff --git a/config.h-vms.template b/config.h-vms.template
index e44af44..dca4bda 100644
--- a/config.h-vms.template
+++ b/config.h-vms.template
@@ -180,6 +180,13 @@
/* 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 */
diff --git a/config.h.W32.template b/config.h.W32.template
index 79bb6cd..402db81 100644
--- a/config.h.W32.template
+++ b/config.h.W32.template
@@ -191,6 +191,13 @@
/* 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 */
diff --git a/configh.dos.template b/configh.dos.template
index 852b6f8..7eaed6e 100644
--- a/configh.dos.template
+++ b/configh.dos.template
@@ -1,4 +1,3 @@
-/* Generated automatically from configure.in by autoheader. DO NOT EDIT! */
/* Many things are defined already by a system header. */
#include <sys/config.h>
@@ -9,8 +8,20 @@
/* Version of this package (needed by automake) */
#define VERSION "%VERSION%"
+#if __DJGPP__ > 2 || __DJGPP_MINOR__ > 1
+
+/* Define if `sys_siglist' is declared by <signal.h>. */
+# define SYS_SIGLIST_DECLARED 1
+
+/* Define this if the C library defines the variable `_sys_siglist'. */
+# define HAVE_SYS_SIGLIST 1
+
+#else
+
/* Define NSIG. */
-#define NSIG SIGMAX
+# define NSIG SIGMAX
+
+#endif
/* Define if you have sigsetmask. */
#define HAVE_SIGSETMASK 1
diff --git a/configure.in b/configure.in
index 4e46187..4bcf56f 100644
--- a/configure.in
+++ b/configure.in
@@ -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
--- 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);
}
@@ -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
--- 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
--- 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
--- 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
diff --git a/make.texinfo b/make.texinfo
index 6d9be0a..e6b6165 100644
--- a/make.texinfo
+++ b/make.texinfo
@@ -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
@@ -24,12 +24,12 @@
@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: