VirtualBox

source: kBuild/trunk/src/kmk/output.c@ 3154

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

kmk: Merged in changes from GNU make 4.2.1 (2e55f5e4abdc0e38c1d64be703b446695e70b3b6 / https://git.savannah.gnu.org/git/make.git).

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette