diff options
author | Paul Smith <psmith@gnu.org> | 1999-04-25 04:30:55 +0000 |
---|---|---|
committer | Paul Smith <psmith@gnu.org> | 1999-04-25 04:30:55 +0000 |
commit | fc0fe4103ac983d88b83dad0daf97664ffa8e04b (patch) | |
tree | 7f2a029706a5547af16132ae451a673936b353bf | |
parent | 7f3ffd401903e04637c0e822e152e8dc632264e5 (diff) | |
download | gunmake-fc0fe4103ac983d88b83dad0daf97664ffa8e04b.tar.gz |
* Add new jobserver feature.
* Small updates to the manual
* E.Zaretskii fix for new DJGPP version.
-rw-r--r-- | ChangeLog | 65 | ||||
-rw-r--r-- | NEWS | 11 | ||||
-rw-r--r-- | acconfig.h | 14 | ||||
-rw-r--r-- | acinclude.m4 | 81 | ||||
-rw-r--r-- | config.ami.template | 7 | ||||
-rw-r--r-- | config.h-vms.template | 7 | ||||
-rw-r--r-- | config.h.W32.template | 7 | ||||
-rw-r--r-- | configh.dos.template | 15 | ||||
-rw-r--r-- | configure.in | 17 | ||||
-rw-r--r-- | job.c | 144 | ||||
-rw-r--r-- | job.h | 5 | ||||
-rw-r--r-- | main.c | 306 | ||||
-rw-r--r-- | make.h | 3 | ||||
-rw-r--r-- | make.texinfo | 41 |
14 files changed, 574 insertions, 149 deletions
@@ -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 @@ -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 @@ -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 @@ -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)) @@ -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; @@ -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) { @@ -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: |