source: trunk/src/kmk/output.c@ 3188

Last change on this file since 3188 was 3188, checked in by bird, 7 years ago

kmk,lib,kWorker: Console output on windows cleanups.

  • Property svn:eol-style set to native
File size: 17.6 KB
Line 
1/* Output to stdout / stderr for GNU make
2Copyright (C) 2013-2016 Free Software Foundation, Inc.
3This file is part of GNU Make.
4
5GNU Make is free software; you can redistribute it and/or modify it under the
6terms of the GNU General Public License as published by the Free Software
7Foundation; either version 3 of the License, or (at your option) any later
8version.
9
10GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
11WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program. If not, see <http://www.gnu.org/licenses/>. */
16
17#include "makeint.h"
18#include "job.h"
19
20/* GNU make no longer supports pre-ANSI89 environments. */
21
22#include <assert.h>
23#include <stdio.h>
24#include <stdarg.h>
25
26#ifdef HAVE_UNISTD_H
27# include <unistd.h>
28#endif
29
30#ifdef HAVE_FCNTL_H
31# include <fcntl.h>
32#else
33# include <sys/file.h>
34#endif
35
36#ifdef WINDOWS32
37# include <windows.h>
38# include <io.h>
39# ifndef CONFIG_NEW_WIN_CHILDREN
40# include "sub_proc.h"
41# else
42# include "w32/winchildren.h"
43# endif
44#endif /* WINDOWS32 */
45#ifdef KBUILD_OS_WINDOWS
46# include "console.h"
47#endif
48
49struct output *output_context = NULL;
50unsigned int stdio_traced = 0;
51
52#define OUTPUT_NONE (-1)
53
54#define OUTPUT_ISSET(_out) ((_out)->out >= 0 || (_out)->err >= 0)
55
56#ifdef HAVE_FCNTL_H
57# define STREAM_OK(_s) ((fcntl (fileno (_s), F_GETFD) != -1) || (errno != EBADF))
58#else
59# define STREAM_OK(_s) 1
60#endif
61
62/* Write a string to the current STDOUT or STDERR. */
63static void
64_outputs (struct output *out, int is_err, const char *msg)
65{
66 if (! out || ! out->syncout)
67 {
68 FILE *f = is_err ? stderr : stdout;
69#ifdef KBUILD_OS_WINDOWS
70 /** @todo check if fputs is also subject to char-by-char stupidity */
71 maybe_con_fwrite(msg, strlen(msg), 1, f);
72#else
73 fputs (msg, f);
74#endif
75 fflush (f);
76 }
77 else
78 {
79 int fd = is_err ? out->err : out->out;
80 int len = strlen (msg);
81 int r;
82
83 EINTRLOOP (r, lseek (fd, 0, SEEK_END));
84 while (1)
85 {
86 EINTRLOOP (r, write (fd, msg, len));
87 if (r == len || r <= 0)
88 break;
89 len -= r;
90 msg += r;
91 }
92 }
93}
94
95
96/* Write a message indicating that we've just entered or
97 left (according to ENTERING) the current directory. */
98
99static int
100log_working_directory (int entering)
101{
102 static char *buf = NULL;
103 static unsigned int len = 0;
104 unsigned int need;
105 const char *fmt;
106 char *p;
107
108 /* Get enough space for the longest possible output. */
109 need = strlen (program) + INTSTR_LENGTH + 2 + 1;
110 if (starting_directory)
111 need += strlen (starting_directory);
112
113 /* Use entire sentences to give the translators a fighting chance. */
114 if (makelevel == 0)
115 if (starting_directory == 0)
116 if (entering)
117 fmt = _("%s: Entering an unknown directory\n");
118 else
119 fmt = _("%s: Leaving an unknown directory\n");
120 else
121 if (entering)
122 fmt = _("%s: Entering directory '%s'\n");
123 else
124 fmt = _("%s: Leaving directory '%s'\n");
125 else
126 if (starting_directory == 0)
127 if (entering)
128 fmt = _("%s[%u]: Entering an unknown directory\n");
129 else
130 fmt = _("%s[%u]: Leaving an unknown directory\n");
131 else
132 if (entering)
133 fmt = _("%s[%u]: Entering directory '%s'\n");
134 else
135 fmt = _("%s[%u]: Leaving directory '%s'\n");
136
137 need += strlen (fmt);
138
139 if (need > len)
140 {
141 buf = xrealloc (buf, need);
142 len = need;
143 }
144
145 p = buf;
146 if (print_data_base_flag)
147 {
148 *(p++) = '#';
149 *(p++) = ' ';
150 }
151
152 if (makelevel == 0)
153 if (starting_directory == 0)
154 sprintf (p, fmt , program);
155 else
156 sprintf (p, fmt, program, starting_directory);
157 else if (starting_directory == 0)
158 sprintf (p, fmt, program, makelevel);
159 else
160 sprintf (p, fmt, program, makelevel, starting_directory);
161
162 _outputs (NULL, 0, buf);
163
164 return 1;
165}
166
167/* Set a file descriptor to be in O_APPEND mode.
168 If it fails, just ignore it. */
169
170static void
171set_append_mode (int fd)
172{
173#if defined(F_GETFL) && defined(F_SETFL) && defined(O_APPEND)
174 int flags = fcntl (fd, F_GETFL, 0);
175 if (flags >= 0)
176 fcntl (fd, F_SETFL, flags | O_APPEND);
177#endif
178}
179
180
181
182#ifndef NO_OUTPUT_SYNC
183
184/* Semaphore for use in -j mode with output_sync. */
185static sync_handle_t sync_handle = -1;
186
187#define FD_NOT_EMPTY(_f) ((_f) != OUTPUT_NONE && lseek ((_f), 0, SEEK_END) > 0)
188
189/* Set up the sync handle. Disables output_sync on error. */
190static int
191sync_init (void)
192{
193 int combined_output = 0;
194
195#ifdef WINDOWS32
196 if ((!STREAM_OK (stdout) && !STREAM_OK (stderr))
197 || (sync_handle = create_mutex ()) == -1)
198 {
199 perror_with_name ("output-sync suppressed: ", "stderr");
200 output_sync = 0;
201 }
202 else
203 {
204 combined_output = same_stream (stdout, stderr);
205 prepare_mutex_handle_string (sync_handle);
206 }
207
208#else
209 if (STREAM_OK (stdout))
210 {
211 struct stat stbuf_o, stbuf_e;
212
213 sync_handle = fileno (stdout);
214 combined_output = (fstat (fileno (stdout), &stbuf_o) == 0
215 && fstat (fileno (stderr), &stbuf_e) == 0
216 && stbuf_o.st_dev == stbuf_e.st_dev
217 && stbuf_o.st_ino == stbuf_e.st_ino);
218 }
219 else if (STREAM_OK (stderr))
220 sync_handle = fileno (stderr);
221 else
222 {
223 perror_with_name ("output-sync suppressed: ", "stderr");
224 output_sync = 0;
225 }
226#endif
227
228 return combined_output;
229}
230
231/* Support routine for output_sync() */
232static void
233pump_from_tmp (int from, FILE *to)
234{
235 static char buffer[8192];
236
237#ifdef WINDOWS32
238 int prev_mode;
239
240 /* "from" is opened by open_tmpfd, which does it in binary mode, so
241 we need the mode of "to" to match that. */
242 prev_mode = _setmode (fileno (to), _O_BINARY);
243#endif
244
245 if (lseek (from, 0, SEEK_SET) == -1)
246 perror ("lseek()");
247
248 while (1)
249 {
250 int len;
251 EINTRLOOP (len, read (from, buffer, sizeof (buffer)));
252 if (len < 0)
253 perror ("read()");
254 if (len <= 0)
255 break;
256 if (fwrite (buffer, len, 1, to) < 1)
257 {
258 perror ("fwrite()");
259 break;
260 }
261 fflush (to);
262 }
263
264#ifdef WINDOWS32
265 /* Switch "to" back to its original mode, so that log messages by
266 Make have the same EOL format as without --output-sync. */
267 _setmode (fileno (to), prev_mode);
268#endif
269}
270
271/* Obtain the lock for writing output. */
272static void *
273acquire_semaphore (void)
274{
275 static struct flock fl;
276
277 fl.l_type = F_WRLCK;
278 fl.l_whence = SEEK_SET;
279 fl.l_start = 0;
280 fl.l_len = 1;
281 if (fcntl (sync_handle, F_SETLKW, &fl) != -1)
282 return &fl;
283 perror ("fcntl()");
284 return NULL;
285}
286
287/* Release the lock for writing output. */
288static void
289release_semaphore (void *sem)
290{
291 struct flock *flp = (struct flock *)sem;
292 flp->l_type = F_UNLCK;
293 if (fcntl (sync_handle, F_SETLKW, flp) == -1)
294 perror ("fcntl()");
295}
296
297/* Returns a file descriptor to a temporary file. The file is automatically
298 closed/deleted on exit. Don't use a FILE* stream. */
299int
300output_tmpfd (void)
301{
302 int fd = -1;
303 FILE *tfile = tmpfile ();
304
305 if (! tfile)
306 pfatal_with_name ("tmpfile");
307
308 /* Create a duplicate so we can close the stream. */
309 fd = dup (fileno (tfile));
310 if (fd < 0)
311 pfatal_with_name ("dup");
312
313 fclose (tfile);
314
315 set_append_mode (fd);
316
317 return fd;
318}
319
320/* Adds file descriptors to the child structure to support output_sync; one
321 for stdout and one for stderr as long as they are open. If stdout and
322 stderr share a device they can share a temp file too.
323 Will reset output_sync on error. */
324static void
325setup_tmpfile (struct output *out)
326{
327 /* Is make's stdout going to the same place as stderr? */
328 static int combined_output = -1;
329
330 if (combined_output < 0)
331 combined_output = sync_init ();
332
333 if (STREAM_OK (stdout))
334 {
335 int fd = output_tmpfd ();
336 if (fd < 0)
337 goto error;
338 CLOSE_ON_EXEC (fd);
339 out->out = fd;
340 }
341
342 if (STREAM_OK (stderr))
343 {
344 if (out->out != OUTPUT_NONE && combined_output)
345 out->err = out->out;
346 else
347 {
348 int fd = output_tmpfd ();
349 if (fd < 0)
350 goto error;
351 CLOSE_ON_EXEC (fd);
352 out->err = fd;
353 }
354 }
355
356 return;
357
358 /* If we failed to create a temp file, disable output sync going forward. */
359 error:
360 output_close (out);
361 output_sync = OUTPUT_SYNC_NONE;
362}
363
364/* Synchronize the output of jobs in -j mode to keep the results of
365 each job together. This is done by holding the results in temp files,
366 one for stdout and potentially another for stderr, and only releasing
367 them to "real" stdout/stderr when a semaphore can be obtained. */
368
369void
370output_dump (struct output *out)
371{
372 int outfd_not_empty = FD_NOT_EMPTY (out->out);
373 int errfd_not_empty = FD_NOT_EMPTY (out->err);
374
375 if (outfd_not_empty || errfd_not_empty)
376 {
377 int traced = 0;
378
379 /* Try to acquire the semaphore. If it fails, dump the output
380 unsynchronized; still better than silently discarding it.
381 We want to keep this lock for as little time as possible. */
382 void *sem = acquire_semaphore ();
383
384 /* Log the working directory for this dump. */
385 if (print_directory_flag && output_sync != OUTPUT_SYNC_RECURSE)
386 traced = log_working_directory (1);
387
388 if (outfd_not_empty)
389 pump_from_tmp (out->out, stdout);
390 if (errfd_not_empty && out->err != out->out)
391 pump_from_tmp (out->err, stderr);
392
393 if (traced)
394 log_working_directory (0);
395
396 /* Exit the critical section. */
397 if (sem)
398 release_semaphore (sem);
399
400 /* Truncate and reset the output, in case we use it again. */
401 if (out->out != OUTPUT_NONE)
402 {
403 int e;
404 lseek (out->out, 0, SEEK_SET);
405 EINTRLOOP (e, ftruncate (out->out, 0));
406 }
407 if (out->err != OUTPUT_NONE && out->err != out->out)
408 {
409 int e;
410 lseek (out->err, 0, SEEK_SET);
411 EINTRLOOP (e, ftruncate (out->err, 0));
412 }
413 }
414}
415#endif /* NO_OUTPUT_SYNC */
416
417
418
419/* Provide support for temporary files. */
420
421#ifndef HAVE_STDLIB_H
422# ifdef HAVE_MKSTEMP
423int mkstemp (char *template);
424# else
425char *mktemp (char *template);
426# endif
427#endif
428
429FILE *
430output_tmpfile (char **name, const char *template)
431{
432#ifdef HAVE_FDOPEN
433 int fd;
434#endif
435
436#if defined HAVE_MKSTEMP || defined HAVE_MKTEMP
437# define TEMPLATE_LEN strlen (template)
438#else
439# define TEMPLATE_LEN L_tmpnam
440#endif
441 *name = xmalloc (TEMPLATE_LEN + 1);
442 strcpy (*name, template);
443
444#if defined HAVE_MKSTEMP && defined HAVE_FDOPEN
445 /* It's safest to use mkstemp(), if we can. */
446 fd = mkstemp (*name);
447 if (fd == -1)
448 return 0;
449 return fdopen (fd, "w");
450#else
451# ifdef HAVE_MKTEMP
452 (void) mktemp (*name);
453# else
454 (void) tmpnam (*name);
455# endif
456
457# ifdef HAVE_FDOPEN
458 /* Can't use mkstemp(), but guard against a race condition. */
459 EINTRLOOP (fd, open (*name, O_CREAT|O_EXCL|O_WRONLY, 0600));
460 if (fd == -1)
461 return 0;
462 return fdopen (fd, "w");
463# else
464 /* Not secure, but what can we do? */
465 return fopen (*name, "w");
466# endif
467#endif
468}
469
470
471
472/* This code is stolen from gnulib.
473 If/when we abandon the requirement to work with K&R compilers, we can
474 remove this (and perhaps other parts of GNU make!) and migrate to using
475 gnulib directly.
476
477 This is called only through atexit(), which means die() has already been
478 invoked. So, call exit() here directly. Apparently that works...?
479*/
480
481/* Close standard output, exiting with status 'exit_failure' on failure.
482 If a program writes *anything* to stdout, that program should close
483 stdout and make sure that it succeeds before exiting. Otherwise,
484 suppose that you go to the extreme of checking the return status
485 of every function that does an explicit write to stdout. The last
486 printf can succeed in writing to the internal stream buffer, and yet
487 the fclose(stdout) could still fail (due e.g., to a disk full error)
488 when it tries to write out that buffered data. Thus, you would be
489 left with an incomplete output file and the offending program would
490 exit successfully. Even calling fflush is not always sufficient,
491 since some file systems (NFS and CODA) buffer written/flushed data
492 until an actual close call.
493
494 Besides, it's wasteful to check the return value from every call
495 that writes to stdout -- just let the internal stream state record
496 the failure. That's what the ferror test is checking below.
497
498 It's important to detect such failures and exit nonzero because many
499 tools (most notably 'make' and other build-management systems) depend
500 on being able to detect failure in other tools via their exit status. */
501
502static void
503close_stdout (void)
504{
505 int prev_fail = ferror (stdout);
506 int fclose_fail = fclose (stdout);
507
508 if (prev_fail || fclose_fail)
509 {
510 if (fclose_fail)
511 perror_with_name (_("write error: stdout"), "");
512 else
513 O (error, NILF, _("write error: stdout"));
514 exit (MAKE_TROUBLE);
515 }
516}
517
518
519
520void
521output_init (struct output *out)
522{
523 if (out)
524 {
525 out->out = out->err = OUTPUT_NONE;
526 out->syncout = !!output_sync;
527 return;
528 }
529
530 /* Configure this instance of make. Be sure stdout is line-buffered. */
531
532#ifdef HAVE_SETVBUF
533# ifdef SETVBUF_REVERSED
534 setvbuf (stdout, _IOLBF, xmalloc (BUFSIZ), BUFSIZ);
535# else /* setvbuf not reversed. */
536 /* Some buggy systems lose if we pass 0 instead of allocating ourselves. */
537 setvbuf (stdout, 0, _IOLBF, BUFSIZ);
538# endif /* setvbuf reversed. */
539#elif HAVE_SETLINEBUF
540 setlinebuf (stdout);
541#endif /* setlinebuf missing. */
542
543 /* Force stdout/stderr into append mode. This ensures parallel jobs won't
544 lose output due to overlapping writes. */
545 set_append_mode (fileno (stdout));
546 set_append_mode (fileno (stderr));
547
548#ifdef HAVE_ATEXIT
549 if (STREAM_OK (stdout))
550 atexit (close_stdout);
551#endif
552}
553
554void
555output_close (struct output *out)
556{
557 if (! out)
558 {
559 if (stdio_traced)
560 log_working_directory (0);
561 return;
562 }
563
564#ifndef NO_OUTPUT_SYNC
565 output_dump (out);
566#endif
567
568 if (out->out >= 0)
569 close (out->out);
570 if (out->err >= 0 && out->err != out->out)
571 close (out->err);
572
573 output_init (out);
574}
575
576/* We're about to generate output: be sure it's set up. */
577void
578output_start (void)
579{
580#ifndef NO_OUTPUT_SYNC
581 /* If we're syncing output make sure the temporary file is set up. */
582 if (output_context && output_context->syncout)
583 if (! OUTPUT_ISSET(output_context))
584 setup_tmpfile (output_context);
585#endif
586
587 /* If we're not syncing this output per-line or per-target, make sure we emit
588 the "Entering..." message where appropriate. */
589 if (output_sync == OUTPUT_SYNC_NONE || output_sync == OUTPUT_SYNC_RECURSE)
590 if (! stdio_traced && print_directory_flag)
591 stdio_traced = log_working_directory (1);
592}
593
594void
595outputs (int is_err, const char *msg)
596{
597 if (! msg || *msg == '\0')
598 return;
599
600 output_start ();
601
602 _outputs (output_context, is_err, msg);
603}
604
605
606
607static struct fmtstring
608 {
609 char *buffer;
610 size_t size;
611 } fmtbuf = { NULL, 0 };
612
613static char *
614get_buffer (size_t need)
615{
616 /* Make sure we have room. NEED includes space for \0. */
617 if (need > fmtbuf.size)
618 {
619 fmtbuf.size += need * 2;
620 fmtbuf.buffer = xrealloc (fmtbuf.buffer, fmtbuf.size);
621 }
622
623 fmtbuf.buffer[need-1] = '\0';
624
625 return fmtbuf.buffer;
626}
627
628/* Print a message on stdout. */
629
630void
631message (int prefix, size_t len, const char *fmt, ...)
632{
633 va_list args;
634 char *p;
635
636 len += strlen (fmt) + strlen (program) + INTSTR_LENGTH + 4 + 1 + 1;
637 p = get_buffer (len);
638
639 if (prefix)
640 {
641 if (makelevel == 0)
642 sprintf (p, "%s: ", program);
643 else
644 sprintf (p, "%s[%u]: ", program, makelevel);
645 p += strlen (p);
646 }
647
648 va_start (args, fmt);
649 vsprintf (p, fmt, args);
650 va_end (args);
651
652 strcat (p, "\n");
653
654 assert (fmtbuf.buffer[len-1] == '\0');
655 outputs (0, fmtbuf.buffer);
656}
657
658/* Print an error message. */
659
660void
661error (const floc *flocp, size_t len, const char *fmt, ...)
662{
663 va_list args;
664 char *p;
665
666 len += (strlen (fmt) + strlen (program)
667 + (flocp && flocp->filenm ? strlen (flocp->filenm) : 0)
668 + INTSTR_LENGTH + 4 + 1 + 1);
669 p = get_buffer (len);
670
671 if (flocp && flocp->filenm)
672 sprintf (p, "%s:%lu: ", flocp->filenm, flocp->lineno + flocp->offset);
673 else if (makelevel == 0)
674 sprintf (p, "%s: ", program);
675 else
676 sprintf (p, "%s[%u]: ", program, makelevel);
677 p += strlen (p);
678
679 va_start (args, fmt);
680 vsprintf (p, fmt, args);
681 va_end (args);
682
683 strcat (p, "\n");
684
685 assert (fmtbuf.buffer[len-1] == '\0');
686 outputs (1, fmtbuf.buffer);
687}
688
689/* Print an error message and exit. */
690
691void
692fatal (const floc *flocp, size_t len, const char *fmt, ...)
693{
694 va_list args;
695 const char *stop = _(". Stop.\n");
696 char *p;
697
698 len += (strlen (fmt) + strlen (program)
699 + (flocp && flocp->filenm ? strlen (flocp->filenm) : 0)
700 + INTSTR_LENGTH + 8 + strlen (stop) + 1);
701 p = get_buffer (len);
702
703 if (flocp && flocp->filenm)
704 sprintf (p, "%s:%lu: *** ", flocp->filenm, flocp->lineno + flocp->offset);
705 else if (makelevel == 0)
706 sprintf (p, "%s: *** ", program);
707 else
708 sprintf (p, "%s[%u]: *** ", program, makelevel);
709 p += strlen (p);
710
711 va_start (args, fmt);
712 vsprintf (p, fmt, args);
713 va_end (args);
714
715 strcat (p, stop);
716
717 assert (fmtbuf.buffer[len-1] == '\0');
718 outputs (1, fmtbuf.buffer);
719
720 die (MAKE_FAILURE);
721}
722
723/* Print an error message from errno. */
724
725void
726perror_with_name (const char *str, const char *name)
727{
728 const char *err = strerror (errno);
729 OSSS (error, NILF, _("%s%s: %s"), str, name, err);
730}
731
732/* Print an error message from errno and exit. */
733
734void
735pfatal_with_name (const char *name)
736{
737 const char *err = strerror (errno);
738 OSS (fatal, NILF, _("%s: %s"), name, err);
739
740 /* NOTREACHED */
741}
Note: See TracBrowser for help on using the repository browser.