[Ada] Consistently use explicit Entity_Id type instead of alias
[gcc.git] / libcpp / mkdeps.c
index 5ab881380016af321dcf4368cd64c030a874f7b6..4a8e101b912f3f18079b9859f94b4bed5df6e341 100644 (file)
@@ -1,10 +1,10 @@
 /* Dependency generator for Makefile fragments.
-   Copyright (C) 2000, 2001, 2003 Free Software Foundation, Inc.
+   Copyright (C) 2000-2020 Free Software Foundation, Inc.
    Contributed by Zack Weinberg, Mar 2000
 
 This program is free software; you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
-Free Software Foundation; either version 2, or (at your option) any
+Free Software Foundation; either version 3, or (at your option) any
 later version.
 
 This program is distributed in the hope that it will be useful,
@@ -13,8 +13,8 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+along with this program; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.
 
  In other words, you are welcome to use, share and improve this program.
  You are forbidden to forbid anyone else to use, share and improve
@@ -23,207 +23,259 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 #include "config.h"
 #include "system.h"
 #include "mkdeps.h"
+#include "internal.h"
+
+/* Not set up to just include std::vector et al, here's a simple
+   implementation.  */
 
 /* Keep this structure local to this file, so clients don't find it
    easy to start making assumptions.  */
-struct deps
-{
-  const char **targetv;
-  unsigned int ntargets;       /* number of slots actually occupied */
-  unsigned int targets_size;   /* amt of allocated space - in words */
-
-  const char **depv;
-  unsigned int ndeps;
-  unsigned int deps_size;
-
-  const char **vpathv;
-  size_t *vpathlv;
-  unsigned int nvpaths;
-  unsigned int vpaths_size;
-};
-
-static const char *munge (const char *);
-
-/* Given a filename, quote characters in that filename which are
-   significant to Make.  Note that it's not possible to quote all such
-   characters - e.g. \n, %, *, ?, [, \ (in some contexts), and ~ are
-   not properly handled.  It isn't possible to get this right in any
-   current version of Make.  (??? Still true?  Old comment referred to
-   3.76.1.)  */
-
-static const char *
-munge (const char *filename)
+class mkdeps
 {
-  int len;
-  const char *p, *q;
-  char *dst, *buffer;
-
-  for (p = filename, len = 0; *p; p++, len++)
+public:
+  /* T has trivial cctor & dtor.  */
+  template <typename T>
+  class vec
+  {
+  private:
+    T *ary;
+    unsigned num;
+    unsigned alloc;
+
+  public:
+    vec ()
+      : ary (NULL), num (0), alloc (0)
+      {}
+    ~vec ()
+      {
+       XDELETEVEC (ary);
+      }
+
+  public:
+    unsigned size () const
     {
-      switch (*p)
-       {
-       case ' ':
-       case '\t':
-         /* GNU make uses a weird quoting scheme for white space.
-            A space or tab preceded by 2N+1 backslashes represents
-            N backslashes followed by space; a space or tab
-            preceded by 2N backslashes represents N backslashes at
-            the end of a file name; and backslashes in other
-            contexts should not be doubled.  */
-         for (q = p - 1; filename <= q && *q == '\\';  q--)
-           len++;
-         len++;
-         break;
-
-       case '$':
-         /* '$' is quoted by doubling it.  */
-         len++;
-         break;
-       }
+      return num;
     }
-
-  /* Now we know how big to make the buffer.  */
-  buffer = xmalloc (len + 1);
-
-  for (p = filename, dst = buffer; *p; p++, dst++)
+    const T &operator[] (unsigned ix) const
+    {
+      return ary[ix];
+    }
+    T &operator[] (unsigned ix)
     {
-      switch (*p)
+      return ary[ix];
+    }
+    void push (const T &elt)
+    {
+      if (num == alloc)
        {
-       case ' ':
-       case '\t':
-         for (q = p - 1; filename <= q && *q == '\\';  q--)
-           *dst++ = '\\';
-         *dst++ = '\\';
-         break;
-
-       case '$':
-         *dst++ = '$';
-         break;
-
-       default:
-         /* nothing */;
+         alloc = alloc ? alloc * 2 : 16;
+         ary = XRESIZEVEC (T, ary, alloc);
        }
-      *dst = *p;
+      ary[num++] = elt;
     }
+  };
+  struct velt
+  {
+    const char *str;
+    size_t len;
+  };
+
+  mkdeps ()
+    : module_name (NULL), cmi_name (NULL), is_header_unit (false), quote_lwm (0)
+  {
+  }
+  ~mkdeps ()
+  {
+    unsigned int i;
+
+    for (i = targets.size (); i--;)
+      free (const_cast <char *> (targets[i]));
+    for (i = deps.size (); i--;)
+      free (const_cast <char *> (deps[i]));
+    for (i = vpath.size (); i--;)
+      XDELETEVEC (vpath[i].str);
+    for (i = modules.size (); i--;)
+      XDELETEVEC (modules[i]);
+    XDELETEVEC (module_name);
+    free (const_cast <char *> (cmi_name));
+  }
+
+public:
+  vec<const char *> targets;
+  vec<const char *> deps;
+  vec<velt> vpath;
+  vec<const char *> modules;
+
+public:
+  const char *module_name;
+  const char *cmi_name;
+  bool is_header_unit;
+  unsigned short quote_lwm;
+};
 
-  *dst = '\0';
-  return buffer;
-}
+/* Apply Make quoting to STR, TRAIL.  Note that it's not possible to
+   quote all such characters - e.g. \n, %, *, ?, [, \ (in some
+   contexts), and ~ are not properly handled.  It isn't possible to
+   get this right in any current version of Make.  (??? Still true?
+   Old comment referred to 3.76.1.)  */
 
-/* If T begins with any of the partial pathnames listed in d->vpathv,
-   then advance T to point beyond that pathname.  */
 static const char *
-apply_vpath (struct deps *d, const char *t)
+munge (const char *str, const char *trail = nullptr)
 {
-  if (d->vpathv)
+  static unsigned alloc;
+  static char *buf;
+  unsigned dst = 0;
+
+  for (; str; str = trail, trail = nullptr)
     {
-      unsigned int i;
-      for (i = 0; i < d->nvpaths; i++)
+      unsigned slashes = 0;
+      char c;
+      for (const char *probe = str; (c = *probe++);)
        {
-         if (!strncmp (d->vpathv[i], t, d->vpathlv[i]))
+         if (alloc < dst + 4 + slashes)
            {
-             const char *p = t + d->vpathlv[i];
-             if (!IS_DIR_SEPARATOR (*p))
-               goto not_this_one;
+             alloc = alloc * 2 + 32;
+             buf = XRESIZEVEC (char, buf, alloc);
+           }
 
-             /* Do not simplify $(vpath)/../whatever.  ??? Might not
-                be necessary. */
-             if (p[1] == '.' && p[2] == '.' && IS_DIR_SEPARATOR (p[3]))
-               goto not_this_one;
+         switch (c)
+           {
+           case '\\':
+             slashes++;
+             break;
 
-             /* found a match */
-             t = t + d->vpathlv[i] + 1;
+           case '$':
+             buf[dst++] = '$';
+             goto def;
+
+           case ' ':
+           case '\t':
+             /* GNU make uses a weird quoting scheme for white space.
+                A space or tab preceded by 2N+1 backslashes
+                represents N backslashes followed by space; a space
+                or tab preceded by 2N backslashes represents N
+                backslashes at the end of a file name; and
+                backslashes in other contexts should not be
+                doubled.  */
+             while (slashes--)
+               buf[dst++] = '\\';
+             /* FALLTHROUGH  */
+
+           case '#':
+           case ':':
+             buf[dst++] = '\\';
+             /* FALLTHROUGH  */
+
+           default:
+           def:
+             slashes = 0;
              break;
            }
-       not_this_one:;
+
+         buf[dst++] = c;
        }
     }
 
+  buf[dst] = 0;
+  return buf;
+}
+
+/* If T begins with any of the partial pathnames listed in d->vpathv,
+   then advance T to point beyond that pathname.  */
+static const char *
+apply_vpath (class mkdeps *d, const char *t)
+{
+  if (unsigned len = d->vpath.size ())
+    for (unsigned i = len; i--;)
+      {
+       if (!filename_ncmp (d->vpath[i].str, t, d->vpath[i].len))
+         {
+           const char *p = t + d->vpath[i].len;
+           if (!IS_DIR_SEPARATOR (*p))
+             goto not_this_one;
+
+           /* Do not simplify $(vpath)/../whatever.  ??? Might not
+              be necessary. */
+           if (p[1] == '.' && p[2] == '.' && IS_DIR_SEPARATOR (p[3]))
+             goto not_this_one;
+
+           /* found a match */
+           t = t + d->vpath[i].len + 1;
+           break;
+         }
+      not_this_one:;
+      }
+
   /* Remove leading ./ in any case.  */
   while (t[0] == '.' && IS_DIR_SEPARATOR (t[1]))
-    t += 2;
+    {
+      t += 2;
+      /* If we removed a leading ./, then also remove any /s after the
+        first.  */
+      while (IS_DIR_SEPARATOR (t[0]))
+       ++t;
+    }
 
   return t;
 }
 
 /* Public routines.  */
 
-struct deps *
+class mkdeps *
 deps_init (void)
 {
-  return xcalloc (sizeof (struct deps), 1);
+  return new mkdeps ();
 }
 
 void
-deps_free (struct deps *d)
+deps_free (class mkdeps *d)
 {
-  unsigned int i;
-
-  if (d->targetv)
-    {
-      for (i = 0; i < d->ntargets; i++)
-       free ((void *) d->targetv[i]);
-      free (d->targetv);
-    }
-
-  if (d->depv)
-    {
-      for (i = 0; i < d->ndeps; i++)
-       free ((void *) d->depv[i]);
-      free (d->depv);
-    }
-
-  if (d->vpathv)
-    {
-      for (i = 0; i < d->nvpaths; i++)
-       free ((void *) d->vpathv[i]);
-      free (d->vpathv);
-      free (d->vpathlv);
-    }
-
-  free (d);
+  delete d;
 }
 
 /* Adds a target T.  We make a copy, so it need not be a permanent
    string.  QUOTE is true if the string should be quoted.  */
 void
-deps_add_target (struct deps *d, const char *t, int quote)
+deps_add_target (class mkdeps *d, const char *t, int quote)
 {
-  if (d->ntargets == d->targets_size)
+  t = xstrdup (apply_vpath (d, t));
+
+  if (!quote)
     {
-      d->targets_size = d->targets_size * 2 + 4;
-      d->targetv = xrealloc (d->targetv,
-                            d->targets_size * sizeof (const char *));
+      /* Sometimes unquoted items are added after quoted ones.
+        Swap out the lowest quoted.  */
+      if (d->quote_lwm != d->targets.size ())
+       {
+         const char *lowest = d->targets[d->quote_lwm];
+         d->targets[d->quote_lwm] = t;
+         t = lowest;
+       }
+      d->quote_lwm++;
     }
 
-  t = apply_vpath (d, t);
-  if (quote)
-    t = munge (t);  /* Also makes permanent copy.  */
-  else
-    t = xstrdup (t);
-
-  d->targetv[d->ntargets++] = t;
+  d->targets.push (t);
 }
 
 /* Sets the default target if none has been given already.  An empty
    string as the default target in interpreted as stdin.  The string
    is quoted for MAKE.  */
 void
-deps_add_default_target (struct deps *d, const char *tgt)
+deps_add_default_target (class mkdeps *d, const char *tgt)
 {
   /* Only if we have no targets.  */
-  if (d->ntargets)
+  if (d->targets.size ())
     return;
 
   if (tgt[0] == '\0')
-    deps_add_target (d, "-", 1);
+    d->targets.push (xstrdup ("-"));
   else
     {
 #ifndef TARGET_OBJECT_SUFFIX
 # define TARGET_OBJECT_SUFFIX ".o"
 #endif
       const char *start = lbasename (tgt);
-      char *o = alloca (strlen (start) + strlen (TARGET_OBJECT_SUFFIX) + 1);
+      char *o = (char *) alloca (strlen (start)
+                                 + strlen (TARGET_OBJECT_SUFFIX) + 1);
       char *suffix;
 
       strcpy (o, start);
@@ -238,109 +290,188 @@ deps_add_default_target (struct deps *d, const char *tgt)
 }
 
 void
-deps_add_dep (struct deps *d, const char *t)
+deps_add_dep (class mkdeps *d, const char *t)
 {
-  t = munge (apply_vpath (d, t));  /* Also makes permanent copy.  */
+  gcc_assert (*t);
 
-  if (d->ndeps == d->deps_size)
-    {
-      d->deps_size = d->deps_size * 2 + 8;
-      d->depv = xrealloc (d->depv, d->deps_size * sizeof (const char *));
-    }
-  d->depv[d->ndeps++] = t;
+  t = apply_vpath (d, t);
+
+  d->deps.push (xstrdup (t));
 }
 
 void
-deps_add_vpath (struct deps *d, const char *vpath)
+deps_add_vpath (class mkdeps *d, const char *vpath)
 {
   const char *elem, *p;
-  char *copy;
-  size_t len;
 
   for (elem = vpath; *elem; elem = p)
     {
-      for (p = elem; *p && *p != ':'; p++);
-      len = p - elem;
-      copy = xmalloc (len + 1);
-      memcpy (copy, elem, len);
-      copy[len] = '\0';
+      for (p = elem; *p && *p != ':'; p++)
+       continue;
+      mkdeps::velt elt;
+      elt.len = p - elem;
+      char *str = XNEWVEC (char, elt.len + 1);
+      elt.str = str;
+      memcpy (str, elem, elt.len);
+      str[elt.len] = '\0';
       if (*p == ':')
        p++;
 
-      if (d->nvpaths == d->vpaths_size)
+      d->vpath.push (elt);
+    }
+}
+
+/* Add a new module target (there can only be one).  M is the module
+   name.   */
+
+void
+deps_add_module_target (struct mkdeps *d, const char *m,
+                       const char *cmi, bool is_header_unit)
+{
+  gcc_assert (!d->module_name);
+  
+  d->module_name = xstrdup (m);
+  d->is_header_unit = is_header_unit;
+  d->cmi_name = xstrdup (cmi);
+}
+
+/* Add a new module dependency.  M is the module name.  */
+
+void
+deps_add_module_dep (struct mkdeps *d, const char *m)
+{
+  d->modules.push (xstrdup (m));
+}
+
+/* Write NAME, with a leading space to FP, a Makefile.  Advance COL as
+   appropriate, wrap at COLMAX, returning new column number.  Iff
+   QUOTE apply quoting.  Append TRAIL.  */
+
+static unsigned
+make_write_name (const char *name, FILE *fp, unsigned col, unsigned colmax,
+                bool quote = true, const char *trail = NULL)
+{
+  if (quote)
+    name = munge (name, trail);
+  unsigned size = strlen (name);
+
+  if (col)
+    {
+      if (colmax && col + size> colmax)
        {
-         d->vpaths_size = d->vpaths_size * 2 + 8;
-         d->vpathv = xrealloc (d->vpathv,
-                               d->vpaths_size * sizeof (const char *));
-         d->vpathlv = xrealloc (d->vpathlv, d->vpaths_size * sizeof (size_t));
+         fputs (" \\\n", fp);
+         col = 0;
        }
-      d->vpathv[d->nvpaths] = copy;
-      d->vpathlv[d->nvpaths] = len;
-      d->nvpaths++;
+      col++;
+      fputs (" ", fp);
     }
+
+  col += size;
+  fputs (name, fp);
+
+  return col;
 }
 
-void
-deps_write (const struct deps *d, FILE *fp, unsigned int colmax)
+/* Write all the names in VEC via make_write_name.  */
+
+static unsigned
+make_write_vec (const mkdeps::vec<const char *> &vec, FILE *fp,
+               unsigned col, unsigned colmax, unsigned quote_lwm = 0,
+               const char *trail = NULL)
+{
+  for (unsigned ix = 0; ix != vec.size (); ix++)
+    col = make_write_name (vec[ix], fp, col, colmax, ix >= quote_lwm, trail);
+  return col;
+}
+
+/* Write the dependencies to a Makefile.  If PHONY is true, add
+   .PHONY targets for all the dependencies too.  */
+
+static void
+make_write (const cpp_reader *pfile, FILE *fp, unsigned int colmax)
 {
-  unsigned int size, i, column;
+  const mkdeps *d = pfile->deps;
 
-  column = 0;
+  unsigned column = 0;
   if (colmax && colmax < 34)
     colmax = 34;
 
-  for (i = 0; i < d->ntargets; i++)
+  if (d->deps.size ())
     {
-      size = strlen (d->targetv[i]);
-      column += size;
-      if (colmax && column > colmax)
-       {
-         fputs (" \\\n ", fp);
-         column = 1 + size;
-       }
-      if (i)
-       {
-         putc (' ', fp);
-         column++;
-       }
-      fputs (d->targetv[i], fp);
+      column = make_write_vec (d->targets, fp, 0, colmax, d->quote_lwm);
+      if (CPP_OPTION (pfile, deps.modules) && d->cmi_name)
+       column = make_write_name (d->cmi_name, fp, column, colmax);
+      fputs (":", fp);
+      column++;
+      make_write_vec (d->deps, fp, column, colmax);
+      fputs ("\n", fp);
+      if (CPP_OPTION (pfile, deps.phony_targets))
+       for (unsigned i = 1; i < d->deps.size (); i++)
+         fprintf (fp, "%s:\n", munge (d->deps[i]));
     }
 
-  putc (':', fp);
-  putc (' ', fp);
-  column += 2;
+  if (!CPP_OPTION (pfile, deps.modules))
+    return;
+
+  if (d->modules.size ())
+    {
+      column = make_write_vec (d->targets, fp, 0, colmax, d->quote_lwm);
+      if (d->cmi_name)
+       column = make_write_name (d->cmi_name, fp, column, colmax);
+      fputs (":", fp);
+      column++;
+      column = make_write_vec (d->modules, fp, column, colmax, 0, ".c++m");
+      fputs ("\n", fp);
+    }
 
-  for (i = 0; i < d->ndeps; i++)
+  if (d->module_name)
     {
-      size = strlen (d->depv[i]);
-      column += size;
-      if (colmax && column > colmax)
+      if (d->cmi_name)
        {
-         fputs (" \\\n ", fp);
-         column = 1 + size;
+         /* module-name : cmi-name */
+         column = make_write_name (d->module_name, fp, 0, colmax,
+                                   true, ".c++m");
+         fputs (":", fp);
+         column++;
+         column = make_write_name (d->cmi_name, fp, column, colmax);
+         fputs ("\n", fp);
+
+         column = fprintf (fp, ".PHONY:");
+         column = make_write_name (d->module_name, fp, column, colmax,
+                                   true, ".c++m");
+         fputs ("\n", fp);
        }
-      if (i)
+
+      if (d->cmi_name && !d->is_header_unit)
        {
-         putc (' ', fp);
+         /* An order-only dependency.
+             cmi-name :| first-target
+            We can probably drop this this in favour of Make-4.3's grouped
+             targets '&:'  */
+         column = make_write_name (d->cmi_name, fp, 0, colmax);
+         fputs (":|", fp);
          column++;
+         column = make_write_name (d->targets[0], fp, column, colmax);
+         fputs ("\n", fp);
        }
-      fputs (d->depv[i], fp);
     }
-  putc ('\n', fp);
+  
+  if (d->modules.size ())
+    {
+      column = fprintf (fp, "CXX_IMPORTS +=");
+      make_write_vec (d->modules, fp, column, colmax, 0, ".c++m");
+      fputs ("\n", fp);
+    }
 }
 
+/* Write out dependencies according to the selected format (which is
+   only Make at the moment).  */
+/* Really we should be opening fp here.  */
+
 void
-deps_phony_targets (const struct deps *d, FILE *fp)
+deps_write (const cpp_reader *pfile, FILE *fp, unsigned int colmax)
 {
-  unsigned int i;
-
-  for (i = 1; i < d->ndeps; i++)
-    {
-      putc ('\n', fp);
-      fputs (d->depv[i], fp);
-      putc (':', fp);
-      putc ('\n', fp);
-    }
+  make_write (pfile, fp, colmax);
 }
 
 /* Write out a deps buffer to a file, in a form that can be read back
@@ -348,66 +479,72 @@ deps_phony_targets (const struct deps *d, FILE *fp)
    error number will be in errno.  */
 
 int
-deps_save (struct deps *deps, FILE *f)
+deps_save (class mkdeps *deps, FILE *f)
 {
   unsigned int i;
+  size_t size;
 
   /* The cppreader structure contains makefile dependences.  Write out this
      structure.  */
 
   /* The number of dependences.  */
-  if (fwrite (&deps->ndeps, sizeof (deps->ndeps), 1, f) != 1)
-      return -1;
+  size = deps->deps.size ();
+  if (fwrite (&size, sizeof (size), 1, f) != 1)
+    return -1;
+
   /* The length of each dependence followed by the string.  */
-  for (i = 0; i < deps->ndeps; i++)
+  for (i = 0; i < deps->deps.size (); i++)
     {
-      size_t num_to_write = strlen (deps->depv[i]);
-      if (fwrite (&num_to_write, sizeof (size_t), 1, f) != 1)
-          return -1;
-      if (fwrite (deps->depv[i], num_to_write, 1, f) != 1)
-          return -1;
+      size = strlen (deps->deps[i]);
+      if (fwrite (&size, sizeof (size), 1, f) != 1)
+       return -1;
+      if (fwrite (deps->deps[i], size, 1, f) != 1)
+       return -1;
     }
 
   return 0;
 }
 
 /* Read back dependency information written with deps_save into
-   the deps buffer.  The third argument may be NULL, in which case
+   the deps sizefer.  The third argument may be NULL, in which case
    the dependency information is just skipped, or it may be a filename,
    in which case that filename is skipped.  */
 
 int
-deps_restore (struct deps *deps, FILE *fd, const char *self)
+deps_restore (class mkdeps *deps, FILE *fd, const char *self)
 {
-  unsigned int i, count;
-  size_t num_to_read;
-  size_t buf_size = 512;
-  char *buf = xmalloc (buf_size);
+  size_t size;
+  char *buf = NULL;
+  size_t buf_size = 0;
 
   /* Number of dependences.  */
-  if (fread (&count, 1, sizeof (count), fd) != sizeof (count))
+  if (fread (&size, sizeof (size), 1, fd) != 1)
     return -1;
 
   /* The length of each dependence string, followed by the string.  */
-  for (i = 0; i < count; i++)
+  for (unsigned i = size; i--;)
     {
       /* Read in # bytes in string.  */
-      if (fread (&num_to_read, 1, sizeof (size_t), fd) != sizeof (size_t))
+      if (fread (&size, sizeof (size), 1, fd) != 1)
        return -1;
-      if (buf_size < num_to_read + 1)
+
+      if (size >= buf_size)
        {
-         buf_size = num_to_read + 1 + 127;
-         buf = xrealloc (buf, buf_size);
+         buf_size = size + 512;
+         buf = XRESIZEVEC (char, buf, buf_size);
        }
-      if (fread (buf, 1, num_to_read, fd) != num_to_read)
-       return -1;
-      buf[num_to_read] = '\0';
+      if (fread (buf, 1, size, fd) != size)
+       {
+         XDELETEVEC (buf);
+         return -1;
+       }
+      buf[size] = 0;
 
       /* Generate makefile dependencies from .pch if -nopch-deps.  */
-      if (self != NULL && strcmp (buf, self) != 0)
+      if (self != NULL && filename_cmp (buf, self) != 0)
         deps_add_dep (deps, buf);
     }
 
-  free (buf);
+  XDELETEVEC (buf);
   return 0;
 }