(root)/
make-4.4/
src/
output.c
       1  /* Output to stdout / stderr for GNU make
       2  Copyright (C) 2013-2022 Free Software Foundation, Inc.
       3  This file is part of GNU Make.
       4  
       5  GNU Make is free software; you can redistribute it and/or modify it under the
       6  terms of the GNU General Public License as published by the Free Software
       7  Foundation; either version 3 of the License, or (at your option) any later
       8  version.
       9  
      10  GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
      11  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      12  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      13  
      14  You should have received a copy of the GNU General Public License along with
      15  this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  #include "makeint.h"
      18  #include "os.h"
      19  #include "output.h"
      20  
      21  /* GNU make no longer supports pre-ANSI89 environments.  */
      22  
      23  #include <assert.h>
      24  #include <stdio.h>
      25  #include <stdarg.h>
      26  
      27  #ifdef HAVE_UNISTD_H
      28  # include <unistd.h>
      29  #endif
      30  
      31  #ifdef HAVE_FCNTL_H
      32  # include <fcntl.h>
      33  #else
      34  # include <sys/file.h>
      35  #endif
      36  
      37  #ifdef WINDOWS32
      38  # include <windows.h>
      39  # include <io.h>
      40  # include "sub_proc.h"
      41  #endif /* WINDOWS32 */
      42  
      43  struct output *output_context = NULL;
      44  unsigned int stdio_traced = 0;
      45  
      46  #define OUTPUT_NONE (-1)
      47  
      48  #define OUTPUT_ISSET(_out) ((_out)->out >= 0 || (_out)->err >= 0)
      49  
      50  /* Write a string to the current STDOUT or STDERR.  */
      51  static void
      52  _outputs (struct output *out, int is_err, const char *msg)
      53  {
      54    FILE *f;
      55  
      56    if (out && out->syncout)
      57      {
      58        int fd = is_err ? out->err : out->out;
      59        if (fd != OUTPUT_NONE)
      60          {
      61            size_t len = strlen (msg);
      62            int r;
      63            EINTRLOOP (r, lseek (fd, 0, SEEK_END));
      64            writebuf (fd, msg, len);
      65            return;
      66          }
      67      }
      68  
      69    f = is_err ? stderr : stdout;
      70    fputs (msg, f);
      71    fflush (f);
      72  }
      73  
      74  /* Write a message indicating that we've just entered or
      75     left (according to ENTERING) the current directory.  */
      76  
      77  static int
      78  log_working_directory (int entering)
      79  {
      80    static char *buf = NULL;
      81    static size_t len = 0;
      82    size_t need;
      83    const char *fmt;
      84    char *p;
      85  
      86    /* Get enough space for the longest possible output.  */
      87    need = strlen (program) + INTSTR_LENGTH + 2 + 1;
      88    if (starting_directory)
      89      need += strlen (starting_directory);
      90  
      91    /* Use entire sentences to give the translators a fighting chance.  */
      92    if (makelevel == 0)
      93      if (starting_directory == 0)
      94        if (entering)
      95          fmt = _("%s: Entering an unknown directory\n");
      96        else
      97          fmt = _("%s: Leaving an unknown directory\n");
      98      else
      99        if (entering)
     100          fmt = _("%s: Entering directory '%s'\n");
     101        else
     102          fmt = _("%s: Leaving directory '%s'\n");
     103    else
     104      if (starting_directory == 0)
     105        if (entering)
     106          fmt = _("%s[%u]: Entering an unknown directory\n");
     107        else
     108          fmt = _("%s[%u]: Leaving an unknown directory\n");
     109      else
     110        if (entering)
     111          fmt = _("%s[%u]: Entering directory '%s'\n");
     112        else
     113          fmt = _("%s[%u]: Leaving directory '%s'\n");
     114  
     115    need += strlen (fmt);
     116  
     117    if (need > len)
     118      {
     119        buf = xrealloc (buf, need);
     120        len = need;
     121      }
     122  
     123    p = buf;
     124    if (print_data_base_flag)
     125      {
     126        *(p++) = '#';
     127        *(p++) = ' ';
     128      }
     129  
     130    if (makelevel == 0)
     131      if (starting_directory == 0)
     132        sprintf (p, fmt , program);
     133      else
     134        sprintf (p, fmt, program, starting_directory);
     135    else if (starting_directory == 0)
     136      sprintf (p, fmt, program, makelevel);
     137    else
     138      sprintf (p, fmt, program, makelevel, starting_directory);
     139  
     140    _outputs (NULL, 0, buf);
     141  
     142    return 1;
     143  }
     144  
     145  
     146  #ifndef NO_OUTPUT_SYNC
     147  
     148  /* Support routine for output_sync() */
     149  static void
     150  pump_from_tmp (int from, FILE *to)
     151  {
     152    static char buffer[8192];
     153  
     154  #ifdef WINDOWS32
     155    int prev_mode;
     156  
     157    /* "from" is opened by open_tmpfd, which does it in binary mode, so
     158       we need the mode of "to" to match that.  */
     159    prev_mode = _setmode (fileno (to), _O_BINARY);
     160  #endif
     161  
     162    if (lseek (from, 0, SEEK_SET) == -1)
     163      perror ("lseek()");
     164  
     165    while (1)
     166      {
     167        int len;
     168        EINTRLOOP (len, read (from, buffer, sizeof (buffer)));
     169        if (len < 0)
     170          perror ("read()");
     171        if (len <= 0)
     172          break;
     173        if (fwrite (buffer, len, 1, to) < 1)
     174          {
     175            perror ("fwrite()");
     176            break;
     177          }
     178        fflush (to);
     179      }
     180  
     181  #ifdef WINDOWS32
     182    /* Switch "to" back to its original mode, so that log messages by
     183       Make have the same EOL format as without --output-sync.  */
     184    _setmode (fileno (to), prev_mode);
     185  #endif
     186  }
     187  
     188  /* Returns a file descriptor to a temporary file, that will be automatically
     189     deleted on exit.  */
     190  int
     191  output_tmpfd (void)
     192  {
     193    int fd = get_tmpfd (NULL);
     194    fd_set_append (fd);
     195    return fd;
     196  }
     197  
     198  /* Adds file descriptors to the child structure to support output_sync; one
     199     for stdout and one for stderr as long as they are open.  If stdout and
     200     stderr share a device they can share a temp file too.
     201     Will reset output_sync on error.  */
     202  static void
     203  setup_tmpfile (struct output *out)
     204  {
     205    static unsigned int in_setup = 0;
     206    unsigned int io_state;
     207  
     208    /* If something fails during setup we might recurse back into this function
     209       while writing errors.  Make sure we don't do so infinitely.  */
     210    if (in_setup)
     211      return;
     212    in_setup = 1;
     213  
     214    io_state = check_io_state ();
     215  
     216    if (NONE_SET (io_state, IO_STDOUT_OK|IO_STDERR_OK))
     217      {
     218        /* This is probably useless since stdout/stderr aren't working. */
     219        perror_with_name ("output-sync suppressed: ", "stderr");
     220        goto error;
     221      }
     222  
     223    if (ANY_SET (io_state, IO_STDOUT_OK))
     224      {
     225        int fd = output_tmpfd ();
     226        if (fd < 0)
     227          goto error;
     228        fd_noinherit (fd);
     229        out->out = fd;
     230      }
     231  
     232    if (ANY_SET (io_state, IO_STDERR_OK))
     233      {
     234        if (out->out != OUTPUT_NONE && ANY_SET (io_state, IO_COMBINED_OUTERR))
     235          out->err = out->out;
     236        else
     237          {
     238            int fd = output_tmpfd ();
     239            if (fd < 0)
     240              goto error;
     241            fd_noinherit (fd);
     242            out->err = fd;
     243          }
     244      }
     245  
     246    in_setup = 0;
     247    return;
     248  
     249    /* If we failed to create a temp file, disable output sync going forward.  */
     250   error:
     251    output_close (out);
     252    output_sync = OUTPUT_SYNC_NONE;
     253    osync_clear ();
     254    in_setup = 0;
     255  }
     256  
     257  /* Synchronize the output of jobs in -j mode to keep the results of
     258     each job together. This is done by holding the results in temp files,
     259     one for stdout and potentially another for stderr, and only releasing
     260     them to "real" stdout/stderr when a semaphore can be obtained. */
     261  
     262  void
     263  output_dump (struct output *out)
     264  {
     265  #define FD_NOT_EMPTY(_f) ((_f) != OUTPUT_NONE && lseek ((_f), 0, SEEK_END) > 0)
     266  
     267    int outfd_not_empty = FD_NOT_EMPTY (out->out);
     268    int errfd_not_empty = FD_NOT_EMPTY (out->err);
     269  
     270    if (outfd_not_empty || errfd_not_empty)
     271      {
     272        int traced = 0;
     273  
     274        /* Try to acquire the semaphore.  If it fails, dump the output
     275           unsynchronized; still better than silently discarding it.
     276           We want to keep this lock for as little time as possible.  */
     277        if (!osync_acquire ())
     278          {
     279            O (error, NILF,
     280               _("warning: Cannot acquire output lock, disabling output sync."));
     281            osync_clear ();
     282          }
     283  
     284        /* Log the working directory for this dump.  */
     285        if (print_directory && output_sync != OUTPUT_SYNC_RECURSE)
     286          traced = log_working_directory (1);
     287  
     288        if (outfd_not_empty)
     289          pump_from_tmp (out->out, stdout);
     290        if (errfd_not_empty && out->err != out->out)
     291          pump_from_tmp (out->err, stderr);
     292  
     293        if (traced)
     294          log_working_directory (0);
     295  
     296        /* Exit the critical section.  */
     297        osync_release ();
     298  
     299        /* Truncate and reset the output, in case we use it again.  */
     300        if (out->out != OUTPUT_NONE)
     301          {
     302            int e;
     303            lseek (out->out, 0, SEEK_SET);
     304            EINTRLOOP (e, ftruncate (out->out, 0));
     305          }
     306        if (out->err != OUTPUT_NONE && out->err != out->out)
     307          {
     308            int e;
     309            lseek (out->err, 0, SEEK_SET);
     310            EINTRLOOP (e, ftruncate (out->err, 0));
     311          }
     312      }
     313  }
     314  #endif /* NO_OUTPUT_SYNC */
     315  
     316  
     317  void
     318  output_init (struct output *out)
     319  {
     320    if (out)
     321      {
     322        out->out = out->err = OUTPUT_NONE;
     323        out->syncout = !!output_sync;
     324        return;
     325      }
     326  
     327    /* Force stdout/stderr into append mode (if they are files) to ensure
     328       parallel jobs won't lose output due to overlapping writes.  */
     329    fd_set_append (fileno (stdout));
     330    fd_set_append (fileno (stderr));
     331  }
     332  
     333  void
     334  output_close (struct output *out)
     335  {
     336    if (! out)
     337      {
     338        if (stdio_traced)
     339          log_working_directory (0);
     340        return;
     341      }
     342  
     343  #ifndef NO_OUTPUT_SYNC
     344    output_dump (out);
     345  #endif
     346  
     347    if (out->out >= 0)
     348      close (out->out);
     349    if (out->err >= 0 && out->err != out->out)
     350      close (out->err);
     351  
     352    output_init (out);
     353  }
     354  
     355  /* We're about to generate output: be sure it's set up.  */
     356  void
     357  output_start (void)
     358  {
     359  #ifndef NO_OUTPUT_SYNC
     360    /* If we're syncing output make sure the temporary file is set up.  */
     361    if (output_context && output_context->syncout)
     362      if (! OUTPUT_ISSET(output_context))
     363        setup_tmpfile (output_context);
     364  #endif
     365  
     366    /* If we're not syncing this output per-line or per-target, make sure we emit
     367       the "Entering..." message where appropriate.  */
     368    if (output_sync == OUTPUT_SYNC_NONE || output_sync == OUTPUT_SYNC_RECURSE)
     369      if (! stdio_traced && print_directory)
     370        stdio_traced = log_working_directory (1);
     371  }
     372  
     373  void
     374  outputs (int is_err, const char *msg)
     375  {
     376    if (! msg || *msg == '\0')
     377      return;
     378  
     379    output_start ();
     380  
     381    _outputs (output_context, is_err, msg);
     382  }
     383  
     384  
     385  static struct fmtstring
     386    {
     387      char *buffer;
     388      size_t size;
     389    } fmtbuf = { NULL, 0 };
     390  
     391  static char *
     392  get_buffer (size_t need)
     393  {
     394    /* Make sure we have room.  NEED includes space for \0.  */
     395    if (need > fmtbuf.size)
     396      {
     397        fmtbuf.size += need * 2;
     398        fmtbuf.buffer = xrealloc (fmtbuf.buffer, fmtbuf.size);
     399      }
     400  
     401    fmtbuf.buffer[need-1] = '\0';
     402  
     403    return fmtbuf.buffer;
     404  }
     405  
     406  /* Print a message on stdout.  */
     407  
     408  void
     409  message (int prefix, size_t len, const char *fmt, ...)
     410  {
     411    va_list args;
     412    char *start;
     413    char *p;
     414  
     415    len += strlen (fmt) + strlen (program) + INTSTR_LENGTH + 4 + 1 + 1;
     416    start = p = get_buffer (len);
     417  
     418    if (prefix)
     419      {
     420        if (makelevel == 0)
     421          sprintf (p, "%s: ", program);
     422        else
     423          sprintf (p, "%s[%u]: ", program, makelevel);
     424        p += strlen (p);
     425      }
     426  
     427    va_start (args, fmt);
     428    vsprintf (p, fmt, args);
     429    va_end (args);
     430  
     431    strcat (p, "\n");
     432  
     433    assert (start[len-1] == '\0');
     434    outputs (0, start);
     435  }
     436  
     437  /* Print an error message.  */
     438  
     439  void
     440  error (const floc *flocp, size_t len, const char *fmt, ...)
     441  {
     442    va_list args;
     443    char *start;
     444    char *p;
     445  
     446    len += (strlen (fmt) + strlen (program)
     447            + (flocp && flocp->filenm ? strlen (flocp->filenm) : 0)
     448            + INTSTR_LENGTH + 4 + 1 + 1);
     449    start = p = get_buffer (len);
     450  
     451    if (flocp && flocp->filenm)
     452      sprintf (p, "%s:%lu: ", flocp->filenm, flocp->lineno + flocp->offset);
     453    else if (makelevel == 0)
     454      sprintf (p, "%s: ", program);
     455    else
     456      sprintf (p, "%s[%u]: ", program, makelevel);
     457    p += strlen (p);
     458  
     459    va_start (args, fmt);
     460    vsprintf (p, fmt, args);
     461    va_end (args);
     462  
     463    strcat (p, "\n");
     464  
     465    assert (start[len-1] == '\0');
     466    outputs (1, start);
     467  }
     468  
     469  /* Print an error message and exit.  */
     470  
     471  void
     472  fatal (const floc *flocp, size_t len, const char *fmt, ...)
     473  {
     474    va_list args;
     475    const char *stop = _(".  Stop.\n");
     476    char *start;
     477    char *p;
     478  
     479    len += (strlen (fmt) + strlen (program)
     480            + (flocp && flocp->filenm ? strlen (flocp->filenm) : 0)
     481            + INTSTR_LENGTH + 8 + strlen (stop) + 1);
     482    start = p = get_buffer (len);
     483  
     484    if (flocp && flocp->filenm)
     485      sprintf (p, "%s:%lu: *** ", flocp->filenm, flocp->lineno + flocp->offset);
     486    else if (makelevel == 0)
     487      sprintf (p, "%s: *** ", program);
     488    else
     489      sprintf (p, "%s[%u]: *** ", program, makelevel);
     490    p += strlen (p);
     491  
     492    va_start (args, fmt);
     493    vsprintf (p, fmt, args);
     494    va_end (args);
     495  
     496    strcat (p, stop);
     497  
     498    assert (start[len-1] == '\0');
     499    outputs (1, start);
     500  
     501    die (MAKE_FAILURE);
     502  }
     503  
     504  /* Print an error message from errno.  */
     505  
     506  void
     507  perror_with_name (const char *str, const char *name)
     508  {
     509    const char *err = strerror (errno);
     510    OSSS (error, NILF, _("%s%s: %s"), str, name, err);
     511  }
     512  
     513  /* Print an error message from errno and exit.  */
     514  
     515  void
     516  pfatal_with_name (const char *name)
     517  {
     518    const char *err = strerror (errno);
     519    OSS (fatal, NILF, _("%s: %s"), name, err);
     520  
     521    /* NOTREACHED */
     522  }
     523  
     524  /* Print a message about out of memory (not using more heap) and exit.
     525     Our goal here is to be sure we don't try to allocate more memory, which
     526     means we don't want to use string translations or normal cleanup.  */
     527  
     528  void
     529  out_of_memory ()
     530  {
     531    writebuf (FD_STDOUT, program, strlen (program));
     532    writebuf (FD_STDOUT, STRING_SIZE_TUPLE (": *** virtual memory exhausted\n"));
     533    exit (MAKE_FAILURE);
     534  }