summaryrefslogtreecommitdiff
path: root/job.c
diff options
context:
space:
mode:
authorPaul Smith <psmith@gnu.org>2013-09-12 04:07:52 -0400
committerPaul Smith <psmith@gnu.org>2013-09-12 04:07:52 -0400
commitdeff9dacc97cc20015d3018992f2c77cb7fab102 (patch)
tree3da393310f9936a22aa211e6870a23e98b6fdebe /job.c
parent40a49f244da5b417af8bede84ac221cee2318d88 (diff)
downloadgunmake-deff9dacc97cc20015d3018992f2c77cb7fab102.tar.gz
Enhance the output sync mode.
Create a new file, output.c, and collect functions that generate output there. We introduce a new global context specifying where output should go (to stdout or to a sync file), and the lowest level output generator chooses where to write output based on that context. This allows us to set the context globally, and all operations that write output (including functions like $(info ...) etc.) will use it. Removed the "--trace=dir" capability. It was too confusing. If you have directory tracking enabled then output sync will print the enter/leave message for each synchronized block. If you don't want that, disable directory tracking.
Diffstat (limited to 'job.c')
-rw-r--r--job.c387
1 files changed, 65 insertions, 322 deletions
diff --git a/job.c b/job.c
index 16c164e..28c8e37 100644
--- a/job.c
+++ b/job.c
@@ -242,20 +242,8 @@ unsigned long job_counter = 0;
/* Number of jobserver tokens this instance is currently using. */
unsigned int jobserver_tokens = 0;
-
-#ifdef OUTPUT_SYNC
-
-/* Semaphore for use in -j mode with output_sync. */
-static sync_handle_t sync_handle = -1;
-
-/* Is make's stdout going to the same place as stderr? */
-static int combined_output = 0;
-
-#define STREAM_OK(_s) ((fcntl (fileno (_s), F_GETFD) != -1) || (errno != EBADF))
-
-#define FD_NOT_EMPTY(_f) ((_f) >= 0 && lseek ((_f), 0, SEEK_END) > 0)
-#endif /* OUTPUT_SYNC */
+
#ifdef WINDOWS32
/*
* The macro which references this function is defined in makeint.h.
@@ -472,50 +460,12 @@ is_bourne_compatible_shell (const char *path)
}
-/* Write a message in the child's context. Write it to the child's output
- sync file if present, otherwise to the terminal. */
-
-static void
-child_out (const struct child *child, const char *msg, int out)
-{
- int fd = out ? child->outfd : child->errfd;
-
- if (!msg || msg[0] == '\0')
- return;
-
- if (fd >= 0)
- {
- int len = strlen (msg);
- int b;
-
- lseek (fd, 0, SEEK_END);
- while (1)
- {
- EINTRLOOP (b, write (fd, msg, len));
- if (b == len)
- break;
- if (b <= 0)
- return;
- len -= b;
- msg += b;
- }
- EINTRLOOP (b, write (fd, "\n", 1));
- }
- else
- {
- FILE *f = out ? stdout : stderr;
- fputs (msg, f);
- putc ('\n', f);
- fflush (f);
- }
-}
-
/* Write an error message describing the exit status given in
EXIT_CODE, EXIT_SIG, and COREDUMP, for the target TARGET_NAME.
Append "(ignored)" if IGNORED is nonzero. */
static void
-child_error (const struct child *child,
+child_error (struct child *child,
int exit_code, int exit_sig, int coredump, int ignored)
{
const char *pre = "*** ";
@@ -523,9 +473,7 @@ child_error (const struct child *child,
const char *dump = "";
const struct file *f = child->file;
const gmk_floc *flocp = &f->cmds->fileinfo;
- const char *msg;
const char *nm;
- unsigned int l;
if (ignored && silent_flag)
return;
@@ -548,31 +496,29 @@ child_error (const struct child *child,
nm = a;
}
- msg = message_s (strlen (nm) + strlen (f->name), 0,
- _("%s: recipe for target '%s' failed"), nm, f->name);
- child_out (child, msg, 1);
+ OUTPUT_SET (&child->output);
- l = strlen (pre) + strlen (f->name) + strlen (post);
+ message (0, _("%s: recipe for target '%s' failed"), nm, f->name);
#ifdef VMS
if ((exit_code & 1) != 0)
- return;
+ {
+ OUTPUT_UNSET ();
+ return;
+ }
- msg = error_s (l + INTEGER_LENGTH, NILF,
- _("%s[%s] Error 0x%x%s"), pre, f->name, exit_code, post);
+ error (NILF, _("%s[%s] Error 0x%x%s"), pre, f->name, exit_code, post);
#else
if (exit_sig == 0)
- msg = error_s (l + INTEGER_LENGTH, NILF,
- _("%s[%s] Error %d%s"), pre, f->name, exit_code, post);
+ error (NILF, _("%s[%s] Error %d%s"), pre, f->name, exit_code, post);
else
{
const char *s = strsignal (exit_sig);
- msg = error_s (l + strlen (s) + strlen (dump), NILF,
- _("%s[%s] %s%s%s"), pre, f->name, s, dump, post);
+ error (NILF, _("%s[%s] %s%s%s"), pre, f->name, s, dump, post);
}
#endif /* VMS */
- child_out (child, msg, 0);
+ OUTPUT_UNSET ();
}
@@ -609,206 +555,6 @@ child_handler (int sig UNUSED)
*/
}
-#ifdef OUTPUT_SYNC
-
-/* Set up the sync handle and configure combined_output.
- Disables output_sync on error. */
-static void
-sync_init ()
-{
-#ifdef WINDOWS32
- if ((!STREAM_OK (stdout) && !STREAM_OK (stderr))
- || (sync_handle = create_mutex ()) == -1)
- {
- perror_with_name ("output-sync suppressed: ", "stderr");
- output_sync = 0;
- }
- else
- {
- combined_output = same_stream (stdout, stderr);
- prepare_mutex_handle_string (sync_handle);
- }
-
-#else
- if (STREAM_OK (stdout))
- {
- struct stat stbuf_o, stbuf_e;
-
- sync_handle = fileno (stdout);
- combined_output =
- fstat (fileno (stdout), &stbuf_o) == 0 &&
- fstat (fileno (stderr), &stbuf_e) == 0 &&
- stbuf_o.st_dev == stbuf_e.st_dev &&
- stbuf_o.st_ino == stbuf_e.st_ino;
- }
- else if (STREAM_OK (stderr))
- sync_handle = fileno (stderr);
- else
- {
- perror_with_name ("output-sync suppressed: ", "stderr");
- output_sync = 0;
- }
-#endif
-}
-
-/* Adds file descriptors to the child structure to support output_sync; one
- for stdout and one for stderr as long as they are open. If stdout and
- stderr share a device they can share a temp file too.
- Will reset output_sync on error. */
-static void
-assign_child_tempfiles (struct child *c)
-{
- /* If we don't have a temp file, get one. */
- if (c->outfd < 0 && c->errfd < 0)
- {
- if (STREAM_OK (stdout))
- {
- c->outfd = open_tmpfd ();
- if (c->outfd < 0)
- goto error;
- CLOSE_ON_EXEC (c->outfd);
- }
-
- if (STREAM_OK (stderr))
- {
- if (c->outfd >= 0 && combined_output)
- c->errfd = c->outfd;
- else
- {
- c->errfd = open_tmpfd ();
- if (c->errfd < 0)
- goto error;
- CLOSE_ON_EXEC (c->errfd);
- }
- }
- }
-
- return;
-
- error:
- if (c->outfd >= 0)
- {
- close (c->outfd);
- c->outfd = -1;
- }
- output_sync = 0;
-}
-
-/* Support routine for sync_output() */
-static void
-pump_from_tmp (int from, FILE *to)
-{
- static char buffer[8192];
-
-#ifdef WINDOWS32
- int prev_mode;
-
- /* "from" is opened by open_tmpfd, which does it in binary mode, so
- we need the mode of "to" to match that. */
- prev_mode = _setmode (fileno (to), _O_BINARY);
-#endif
-
- if (lseek (from, 0, SEEK_SET) == -1)
- perror ("lseek()");
-
- while (1)
- {
- int len;
- EINTRLOOP (len, read (from, buffer, sizeof (buffer)));
- if (len < 0)
- perror ("read()");
- if (len <= 0)
- break;
- if (fwrite (buffer, len, 1, to) < 1)
- perror ("fwrite()");
- }
-
-#ifdef WINDOWS32
- /* Switch "to" back to its original mode, so that log messages by
- Make have the same EOL format as without --output-sync. */
- _setmode (fileno (to), prev_mode);
-#endif
-}
-
-/* Support routine for sync_output() */
-static void *
-acquire_semaphore (void)
-{
- static struct flock fl;
-
- fl.l_type = F_WRLCK;
- fl.l_whence = SEEK_SET;
- fl.l_start = 0;
- fl.l_len = 1;
- if (fcntl (sync_handle, F_SETLKW, &fl) != -1)
- return &fl;
- perror ("fcntl()");
- return NULL;
-}
-
-/* Support routine for sync_output() */
-static void
-release_semaphore (void *sem)
-{
- struct flock *flp = (struct flock *)sem;
- flp->l_type = F_UNLCK;
- if (fcntl (sync_handle, F_SETLKW, flp) == -1)
- perror ("fcntl()");
-}
-
-/* Synchronize the output of jobs in -j mode to keep the results of
- each job together. This is done by holding the results in temp files,
- one for stdout and potentially another for stderr, and only releasing
- them to "real" stdout/stderr when a semaphore can be obtained. */
-
-static void
-sync_output (struct child *c)
-{
- int outfd_not_empty = FD_NOT_EMPTY (c->outfd);
- int errfd_not_empty = FD_NOT_EMPTY (c->errfd);
-
- if (outfd_not_empty || errfd_not_empty)
- {
- /* Try to acquire the semaphore. If it fails, dump the output
- unsynchronized; still better than silently discarding it. */
- void *sem = acquire_semaphore ();
-
- /* We've entered the "critical section" during which a lock is held. We
- want to keep it as short as possible. */
-
- /* Log the working directory. Force it if we're doing dir tracing. */
- log_working_directory (1, (trace_flag & TRACE_DIRECTORY));
-
- if (outfd_not_empty)
- pump_from_tmp (c->outfd, stdout);
- if (errfd_not_empty && c->errfd != c->outfd)
- pump_from_tmp (c->errfd, stderr);
-
- /* If we're doing dir tracing, force the leave message. */
- if (trace_flag & TRACE_DIRECTORY)
- log_working_directory (0, 1);
-
- /* Exit the critical section. */
- if (sem)
- release_semaphore (sem);
-
- /* Truncate and reset the output, in case we use it again. */
- if (c->outfd >= 0)
- {
- int e;
- lseek (c->outfd, 0, SEEK_SET);
- EINTRLOOP (e, ftruncate (c->outfd, 0));
- }
- if (c->errfd >= 0 && c->errfd != c->outfd)
- {
- int e;
- lseek (c->errfd, 0, SEEK_SET);
- EINTRLOOP (e, ftruncate (c->errfd, 0));
- }
- }
-}
-#endif /* OUTPUT_SYNC */
-
extern int shell_function_pid, shell_function_completed;
/* Reap all dead children, storing the returned status and the new command
@@ -1152,7 +898,7 @@ reap_children (int block, int err)
/* If we're sync'ing per line, write the previous line's
output before starting the next one. */
if (output_sync == OUTPUT_SYNC_LINE)
- sync_output (c);
+ output_dump (&c->output);
#endif
/* Check again whether to start remotely.
Whether or not we want to changes over time.
@@ -1186,7 +932,7 @@ reap_children (int block, int err)
#ifdef OUTPUT_SYNC
/* Synchronize any remaining parallel output. */
- sync_output (c);
+ output_dump (&c->output);
#endif /* OUTPUT_SYNC */
/* At this point c->file->update_status is success or failed. But
@@ -1243,10 +989,7 @@ reap_children (int block, int err)
static void
free_child (struct child *child)
{
- if (child->outfd >= 0)
- close (child->outfd);
- if (child->errfd >= 0 && child->errfd != child->outfd)
- close (child->errfd);
+ output_close (&child->output);
if (!jobserver_tokens)
fatal (NILF, "INTERNAL: Freeing child %p (%s) but no tokens left!\n",
@@ -1388,8 +1131,6 @@ start_job_command (struct child *child)
#if !defined(_AMIGA) && !defined(WINDOWS32)
static int bad_stdin = -1;
#endif
- int print_cmd;
- int sync_cmd;
int flags;
char *p;
#ifdef VMS
@@ -1513,43 +1254,30 @@ start_job_command (struct child *child)
child->file->update_status = us_success;
notice_finished_file (child->file);
}
+
+ OUTPUT_UNSET();
return;
}
- print_cmd = (just_print_flag || (trace_flag & TRACE_RULE)
- || (!(flags & COMMANDS_SILENT) && !silent_flag));
-
-#ifdef OUTPUT_SYNC
- if (output_sync && sync_handle == -1)
- sync_init ();
-#endif
-
/* Are we going to synchronize this command's output? Do so if either we're
- in SYNC_MAKE mode or this command is not recursive. We'll also check
+ in SYNC_RECURSE mode or this command is not recursive. We'll also check
output_sync separately below in case it changes due to error. */
- sync_cmd = output_sync && (output_sync == OUTPUT_SYNC_RECURSE
- || !(flags & COMMANDS_RECURSE));
+ child->output.syncout = output_sync && (output_sync == OUTPUT_SYNC_RECURSE
+ || !(flags & COMMANDS_RECURSE));
+
+ OUTPUT_SET (&child->output);
#ifdef OUTPUT_SYNC
- if (sync_cmd)
- {
- /* If syncing, make sure we have temp files.
- Write the command to the temp file so it's output in order. */
- assign_child_tempfiles (child);
- if (print_cmd)
- child_out (child, p, 1);
- }
- else
+ if (! child->output.syncout)
/* We don't want to sync this command: to avoid misordered
output ensure any already-synced content is written. */
- sync_output (child);
+ output_dump (&child->output);
#endif /* OUTPUT_SYNC */
- /* If we're not syncing, print out the command. If silent, we call
- 'message' with null so it can log the working directory before the
- command's own error messages appear. */
- if (! sync_cmd)
- message (0, print_cmd ? "%s" : NULL, p);
+ /* Print the command if appropriate. */
+ if (just_print_flag || trace_flag
+ || (!(flags & COMMANDS_SILENT) && !silent_flag))
+ message (0, "%s", p);
/* Tell update_goal_chain that a command has been started on behalf of
this target. It is important that this happens here and not in
@@ -1598,6 +1326,9 @@ start_job_command (struct child *child)
goto next_command;
}
+ /* We're sure we're going to invoke a command: set up the output. */
+ output_start ();
+
/* Flush the output streams so they won't have things written twice. */
fflush (stdout);
@@ -1754,15 +1485,17 @@ start_job_command (struct child *child)
#ifdef OUTPUT_SYNC
/* Divert child output if output_sync in use. Don't capture
recursive make output unless we are synchronizing "make" mode. */
- if (sync_cmd)
+ if (child->output.syncout)
{
int outfd = fileno (stdout);
int errfd = fileno (stderr);
- if ((child->outfd >= 0 && (close (outfd) == -1
- || dup2 (child->outfd, outfd) == -1))
- || (child->errfd >= 0 && (close (errfd) == -1
- || dup2 (child->errfd, errfd) == -1)))
+ if ((child->output.out >= 0
+ && (close (outfd) == -1
+ || dup2 (child->output.out, outfd) == -1))
+ || (child->output.err >= 0
+ && (close (errfd) == -1
+ || dup2 (child->output.err, errfd) == -1)))
perror_with_name ("output-sync: ", "dup2()");
}
#endif /* OUTPUT_SYNC */
@@ -1867,9 +1600,9 @@ start_job_command (struct child *child)
#ifdef OUTPUT_SYNC
/* Divert child output if output_sync in use. Don't capture
recursive make output unless we are synchronizing "make" mode. */
- if (sync_cmd)
+ if (child->output.syncout)
hPID = process_easy (argv, child->environment,
- child->outfd, child->errfd);
+ child->output.out, child->output.err);
else
#endif
hPID = process_easy (argv, child->environment, -1, -1);
@@ -1906,12 +1639,13 @@ start_job_command (struct child *child)
free (argv);
#endif
+ OUTPUT_UNSET();
return;
error:
child->file->update_status = us_failed;
notice_finished_file (child->file);
- return;
+ OUTPUT_UNSET();
}
/* Try to start a child running.
@@ -2000,6 +1734,22 @@ new_job (struct file *file)
/* Chop the commands up into lines if they aren't already. */
chop_commands (cmds);
+ /* Start the command sequence, record it in a new
+ 'struct child', and add that to the chain. */
+
+ c = xcalloc (sizeof (struct child));
+ output_init (&c->output, output_sync);
+
+ c->file = file;
+ c->sh_batch_file = NULL;
+
+ /* Cache dontcare flag because file->dontcare can be changed once we
+ return. Check dontcare inheritance mechanism for details. */
+ c->dontcare = file->dontcare;
+
+ /* Start saving output in case the expansion uses $(info ...) etc. */
+ OUTPUT_SET (&c->output);
+
/* Expand the command lines and store the results in LINES. */
lines = xmalloc (cmds->ncommand_lines * sizeof (char *));
for (i = 0; i < cmds->ncommand_lines; ++i)
@@ -2104,18 +1854,7 @@ new_job (struct file *file)
file);
}
- /* Start the command sequence, record it in a new
- 'struct child', and add that to the chain. */
-
- c = xcalloc (sizeof (struct child));
- c->file = file;
c->command_lines = lines;
- c->sh_batch_file = NULL;
- c->outfd = c->errfd = -1;
-
- /* Cache dontcare flag because file->dontcare can be changed once we
- return. Check dontcare inheritance mechanism for details. */
- c->dontcare = file->dontcare;
/* Fetch the first command line to be run. */
job_next_command (c);
@@ -2251,7 +1990,7 @@ new_job (struct file *file)
/* Trace the build.
Use message here so that changes to working directories are logged. */
- if (trace_flag & TRACE_RULE)
+ if (trace_flag)
{
char *newer = allocated_variable_expand_for_file ("$?", c->file);
const char *nm;
@@ -2274,7 +2013,6 @@ new_job (struct file *file)
free (newer);
}
-
/* The job is now primed. Start it running.
(This will notice if there is in fact no recipe.) */
start_waiting_job (c);
@@ -2285,6 +2023,7 @@ new_job (struct file *file)
while (file->command_state == cs_running)
reap_children (1, 0);
+ OUTPUT_UNSET ();
return;
}
@@ -3284,11 +3023,13 @@ construct_command_argv_internal (char *line, char **restp, char *shell,
Then recurse, expanding this command line to get the final
argument list. */
+ char *new_line;
unsigned int shell_len = strlen (shell);
unsigned int line_len = strlen (line);
unsigned int sflags_len = shellflags ? strlen (shellflags) : 0;
+#ifdef WINDOWS32
char *command_ptr = NULL; /* used for batch_mode_shell mode */
- char *new_line;
+#endif
# ifdef __EMX__ /* is this necessary? */
if (!unixy_shell && shellflags)
@@ -3472,7 +3213,9 @@ construct_command_argv_internal (char *line, char **restp, char *shell,
memcpy (ap, shellflags, sflags_len);
ap += sflags_len;
*(ap++) = ' ';
+#ifdef WINDOWS32
command_ptr = ap;
+#endif
for (p = line; *p != '\0'; ++p)
{
if (restp != NULL && *p == '\n')