/* Dependency generator for Makefile fragments.
- Copyright (C) 2000-2013 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
#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;
-
- case '#':
- /* '#' is quoted with a backslash. */
- len++;
- break;
- }
+ return num;
}
-
- /* Now we know how big to make the buffer. */
- buffer = XNEWVEC (char, 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;
-
- 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 (!filename_ncmp (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]))
{
/* Public routines. */
-struct deps *
+class mkdeps *
deps_init (void)
{
- return XCNEW (struct deps);
+ 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 = XRESIZEVEC (const char *, d->targetv, d->targets_size);
+ /* 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
}
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 = XRESIZEVEC (const char *, d->depv, d->deps_size);
- }
- 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 = XNEWVEC (char, 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 = XRESIZEVEC (const char *, d->vpathv, d->vpaths_size);
- d->vpathlv = XRESIZEVEC (size_t, d->vpathlv, d->vpaths_size);
+ 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 (i)
- {
- if (colmax && column > colmax)
- {
- fputs (" \\\n ", fp);
- column = 1 + size;
- }
- else
- {
- 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);
- column++;
+ if (!CPP_OPTION (pfile, deps.modules))
+ return;
- for (i = 0; i < d->ndeps; i++)
+ if (d->modules.size ())
{
- size = strlen (d->depv[i]);
- column += size;
- if (colmax && column > colmax)
+ 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);
+ }
+
+ if (d->module_name)
+ {
+ 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);
}
- else
+
+ 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
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;
+ 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;
- buf = XNEWVEC (char, buf_size);
-
/* 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))
- {
- free (buf);
- return -1;
- }
- if (buf_size < num_to_read + 1)
+ if (fread (&size, sizeof (size), 1, fd) != 1)
+ return -1;
+
+ if (size >= buf_size)
{
- buf_size = num_to_read + 1 + 127;
+ buf_size = size + 512;
buf = XRESIZEVEC (char, buf, buf_size);
}
- if (fread (buf, 1, num_to_read, fd) != num_to_read)
+ if (fread (buf, 1, size, fd) != size)
{
- free (buf);
+ XDELETEVEC (buf);
return -1;
}
- buf[num_to_read] = '\0';
+ buf[size] = 0;
/* Generate makefile dependencies from .pch if -nopch-deps. */
if (self != NULL && filename_cmp (buf, self) != 0)
deps_add_dep (deps, buf);
}
- free (buf);
+ XDELETEVEC (buf);
return 0;
}