123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549 |
- /* Dependency generator for Makefile fragments.
- Copyright (C) 2000-2022 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 3, or (at your option) any
- later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- 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; 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
- what you give them. Help stamp out software-hoarding! */
- #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. */
- class mkdeps
- {
- 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
- {
- return num;
- }
- const T &operator[] (unsigned ix) const
- {
- return ary[ix];
- }
- T &operator[] (unsigned ix)
- {
- return ary[ix];
- }
- void push (const T &elt)
- {
- if (num == alloc)
- {
- alloc = alloc ? alloc * 2 : 16;
- ary = XRESIZEVEC (T, ary, alloc);
- }
- 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;
- };
- /* 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.) */
- static const char *
- munge (const char *str, const char *trail = nullptr)
- {
- static unsigned alloc;
- static char *buf;
- unsigned dst = 0;
- for (; str; str = trail, trail = nullptr)
- {
- unsigned slashes = 0;
- char c;
- for (const char *probe = str; (c = *probe++);)
- {
- if (alloc < dst + 4 + slashes)
- {
- alloc = alloc * 2 + 32;
- buf = XRESIZEVEC (char, buf, alloc);
- }
- switch (c)
- {
- case '\\':
- slashes++;
- break;
- 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 '#':
- buf[dst++] = '\\';
- /* FALLTHROUGH */
- default:
- def:
- slashes = 0;
- break;
- }
- 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;
- /* If we removed a leading ./, then also remove any /s after the
- first. */
- while (IS_DIR_SEPARATOR (t[0]))
- ++t;
- }
- return t;
- }
- /* Public routines. */
- class mkdeps *
- deps_init (void)
- {
- return new mkdeps ();
- }
- void
- deps_free (class mkdeps *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 (class mkdeps *d, const char *t, int quote)
- {
- t = xstrdup (apply_vpath (d, t));
- if (!quote)
- {
- /* 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++;
- }
- 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 (class mkdeps *d, const char *tgt)
- {
- /* Only if we have no targets. */
- if (d->targets.size ())
- return;
- if (tgt[0] == '\0')
- d->targets.push (xstrdup ("-"));
- else
- {
- #ifndef TARGET_OBJECT_SUFFIX
- # define TARGET_OBJECT_SUFFIX ".o"
- #endif
- const char *start = lbasename (tgt);
- char *o = (char *) alloca (strlen (start)
- + strlen (TARGET_OBJECT_SUFFIX) + 1);
- char *suffix;
- strcpy (o, start);
- suffix = strrchr (o, '.');
- if (!suffix)
- suffix = o + strlen (o);
- strcpy (suffix, TARGET_OBJECT_SUFFIX);
- deps_add_target (d, o, 1);
- }
- }
- void
- deps_add_dep (class mkdeps *d, const char *t)
- {
- gcc_assert (*t);
- t = apply_vpath (d, t);
- d->deps.push (xstrdup (t));
- }
- void
- deps_add_vpath (class mkdeps *d, const char *vpath)
- {
- const char *elem, *p;
- for (elem = vpath; *elem; elem = p)
- {
- 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++;
- 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)
- {
- fputs (" \\\n", fp);
- col = 0;
- }
- col++;
- fputs (" ", fp);
- }
- col += size;
- fputs (name, fp);
- return col;
- }
- /* 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)
- {
- const mkdeps *d = pfile->deps;
- unsigned column = 0;
- if (colmax && colmax < 34)
- colmax = 34;
- if (d->deps.size ())
- {
- 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]));
- }
- 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);
- }
- if (d->module_name)
- {
- if (d->cmi_name)
- {
- /* 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 (d->cmi_name && !d->is_header_unit)
- {
- /* 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);
- }
- }
-
- 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_write (const cpp_reader *pfile, FILE *fp, unsigned int colmax)
- {
- make_write (pfile, fp, colmax);
- }
- /* Write out a deps buffer to a file, in a form that can be read back
- with deps_restore. Returns nonzero on error, in which case the
- error number will be in errno. */
- int
- 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. */
- 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->deps.size (); i++)
- {
- 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 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 (class mkdeps *deps, FILE *fd, const char *self)
- {
- size_t size;
- char *buf = NULL;
- size_t buf_size = 0;
- /* Number of dependences. */
- if (fread (&size, sizeof (size), 1, fd) != 1)
- return -1;
- /* The length of each dependence string, followed by the string. */
- for (unsigned i = size; i--;)
- {
- /* Read in # bytes in string. */
- if (fread (&size, sizeof (size), 1, fd) != 1)
- return -1;
- if (size >= buf_size)
- {
- buf_size = size + 512;
- buf = XRESIZEVEC (char, buf, buf_size);
- }
- 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 && filename_cmp (buf, self) != 0)
- deps_add_dep (deps, buf);
- }
- XDELETEVEC (buf);
- return 0;
- }
|