xref: /netbsd-src/external/gpl3/gcc.old/dist/libcpp/mkdeps.c (revision 8feb0f0b7eaff0608f8350bbfa3098827b4bb91b)
1 /* Dependency generator for Makefile fragments.
2    Copyright (C) 2000-2020 Free Software Foundation, Inc.
3    Contributed by Zack Weinberg, Mar 2000
4 
5 This program is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by the
7 Free Software Foundation; either version 3, or (at your option) any
8 later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING3.  If not see
17 <http://www.gnu.org/licenses/>.
18 
19  In other words, you are welcome to use, share and improve this program.
20  You are forbidden to forbid anyone else to use, share and improve
21  what you give them.   Help stamp out software-hoarding!  */
22 
23 #include "config.h"
24 #include "system.h"
25 #include "mkdeps.h"
26 
27 /* Not set up to just include std::vector et al, here's a simple
28    implementation.  */
29 
30 /* Keep this structure local to this file, so clients don't find it
31    easy to start making assumptions.  */
32 class mkdeps
33 {
34 public:
35   /* T has trivial cctor & dtor.  */
36   template <typename T>
37   class vec
38   {
39   private:
40     T *ary;
41     unsigned num;
42     unsigned alloc;
43 
44   public:
vec()45     vec ()
46       : ary (NULL), num (0), alloc (0)
47       {}
~vec()48     ~vec ()
49       {
50 	XDELETEVEC (ary);
51       }
52 
53   public:
size()54     unsigned size () const
55     {
56       return num;
57     }
58     const T &operator[] (unsigned ix) const
59     {
60       return ary[ix];
61     }
62     T &operator[] (unsigned ix)
63     {
64       return ary[ix];
65     }
push(const T & elt)66     void push (const T &elt)
67     {
68       if (num == alloc)
69 	{
70 	  alloc = alloc ? alloc * 2 : 16;
71 	  ary = XRESIZEVEC (T, ary, alloc);
72 	}
73       ary[num++] = elt;
74     }
75   };
76   struct velt
77   {
78     const char *str;
79     size_t len;
80   };
81 
mkdeps()82   mkdeps ()
83     : quote_lwm (0)
84   {
85   }
~mkdeps()86   ~mkdeps ()
87   {
88     unsigned int i;
89 
90     for (i = targets.size (); i--;)
91       free (const_cast <char *> (targets[i]));
92     for (i = deps.size (); i--;)
93       free (const_cast <char *> (deps[i]));
94     for (i = vpath.size (); i--;)
95       XDELETEVEC (vpath[i].str);
96   }
97 
98 public:
99   vec<const char *> targets;
100   vec<const char *> deps;
101   vec<velt> vpath;
102 
103 public:
104   unsigned short quote_lwm;
105 };
106 
107 /* Apply Make quoting to STR, TRAIL etc.  Note that it's not possible
108    to quote all such characters - e.g. \n, %, *, ?, [, \ (in some
109    contexts), and ~ are not properly handled.  It isn't possible to
110    get this right in any current version of Make.  (??? Still true?
111    Old comment referred to 3.76.1.)  */
112 
113 static const char *
114 munge (const char *str, const char *trail = NULL, ...)
115 {
116   static unsigned alloc;
117   static char *buf;
118   unsigned dst = 0;
119   va_list args;
120   if (trail)
121     va_start (args, trail);
122 
123   for (bool first = true; str; first = false)
124     {
125       unsigned slashes = 0;
126       char c;
127       for (const char *probe = str; (c = *probe++);)
128 	{
129 	  if (alloc < dst + 4 + slashes)
130 	    {
131 	      alloc = alloc * 2 + 32;
132 	      buf = XRESIZEVEC (char, buf, alloc);
133 	    }
134 
135 	  switch (c)
136 	    {
137 	    case '\\':
138 	      slashes++;
139 	      break;
140 
141 	    case '$':
142 	      buf[dst++] = '$';
143 	      goto def;
144 
145 	    case ' ':
146 	    case '\t':
147 	      /* GNU make uses a weird quoting scheme for white space.
148 		 A space or tab preceded by 2N+1 backslashes
149 		 represents N backslashes followed by space; a space
150 		 or tab preceded by 2N backslashes represents N
151 		 backslashes at the end of a file name; and
152 		 backslashes in other contexts should not be
153 		 doubled.  */
154 	      while (slashes--)
155 		buf[dst++] = '\\';
156 	      /* FALLTHROUGH  */
157 
158 	    case '#':
159 	      buf[dst++] = '\\';
160 	      /* FALLTHROUGH  */
161 
162 	    default:
163 	    def:
164 	      slashes = 0;
165 	      break;
166 	    }
167 
168 	  buf[dst++] = c;
169 	}
170 
171       if (first)
172 	str = trail;
173       else
174 	str = va_arg (args, const char *);
175     }
176   if (trail)
177     va_end (args);
178 
179   buf[dst] = 0;
180   return buf;
181 }
182 
183 /* If T begins with any of the partial pathnames listed in d->vpathv,
184    then advance T to point beyond that pathname.  */
185 static const char *
apply_vpath(class mkdeps * d,const char * t)186 apply_vpath (class mkdeps *d, const char *t)
187 {
188   if (unsigned len = d->vpath.size ())
189     for (unsigned i = len; i--;)
190       {
191 	if (!filename_ncmp (d->vpath[i].str, t, d->vpath[i].len))
192 	  {
193 	    const char *p = t + d->vpath[i].len;
194 	    if (!IS_DIR_SEPARATOR (*p))
195 	      goto not_this_one;
196 
197 	    /* Do not simplify $(vpath)/../whatever.  ??? Might not
198 	       be necessary. */
199 	    if (p[1] == '.' && p[2] == '.' && IS_DIR_SEPARATOR (p[3]))
200 	      goto not_this_one;
201 
202 	    /* found a match */
203 	    t = t + d->vpath[i].len + 1;
204 	    break;
205 	  }
206       not_this_one:;
207       }
208 
209   /* Remove leading ./ in any case.  */
210   while (t[0] == '.' && IS_DIR_SEPARATOR (t[1]))
211     {
212       t += 2;
213       /* If we removed a leading ./, then also remove any /s after the
214 	 first.  */
215       while (IS_DIR_SEPARATOR (t[0]))
216 	++t;
217     }
218 
219   return t;
220 }
221 
222 /* Public routines.  */
223 
224 class mkdeps *
deps_init(void)225 deps_init (void)
226 {
227   return new mkdeps ();
228 }
229 
230 void
deps_free(class mkdeps * d)231 deps_free (class mkdeps *d)
232 {
233   delete d;
234 }
235 
236 /* Adds a target T.  We make a copy, so it need not be a permanent
237    string.  QUOTE is true if the string should be quoted.  */
238 void
deps_add_target(class mkdeps * d,const char * t,int quote)239 deps_add_target (class mkdeps *d, const char *t, int quote)
240 {
241   t = xstrdup (apply_vpath (d, t));
242 
243   if (!quote)
244     {
245       /* Sometimes unquoted items are added after quoted ones.
246 	 Swap out the lowest quoted.  */
247       if (d->quote_lwm != d->targets.size ())
248 	{
249 	  const char *lowest = d->targets[d->quote_lwm];
250 	  d->targets[d->quote_lwm] = t;
251 	  t = lowest;
252 	}
253       d->quote_lwm++;
254     }
255 
256   d->targets.push (t);
257 }
258 
259 /* Sets the default target if none has been given already.  An empty
260    string as the default target in interpreted as stdin.  The string
261    is quoted for MAKE.  */
262 void
deps_add_default_target(class mkdeps * d,const char * tgt)263 deps_add_default_target (class mkdeps *d, const char *tgt)
264 {
265   /* Only if we have no targets.  */
266   if (d->targets.size ())
267     return;
268 
269   if (tgt[0] == '\0')
270     d->targets.push (xstrdup ("-"));
271   else
272     {
273 #ifndef TARGET_OBJECT_SUFFIX
274 # define TARGET_OBJECT_SUFFIX ".o"
275 #endif
276       const char *start = lbasename (tgt);
277       char *o = (char *) alloca (strlen (start)
278                                  + strlen (TARGET_OBJECT_SUFFIX) + 1);
279       char *suffix;
280 
281       strcpy (o, start);
282 
283       suffix = strrchr (o, '.');
284       if (!suffix)
285         suffix = o + strlen (o);
286       strcpy (suffix, TARGET_OBJECT_SUFFIX);
287 
288       deps_add_target (d, o, 1);
289     }
290 }
291 
292 void
deps_add_dep(class mkdeps * d,const char * t)293 deps_add_dep (class mkdeps *d, const char *t)
294 {
295   gcc_assert (*t);
296 
297   t = apply_vpath (d, t);
298 
299   d->deps.push (xstrdup (t));
300 }
301 
302 void
deps_add_vpath(class mkdeps * d,const char * vpath)303 deps_add_vpath (class mkdeps *d, const char *vpath)
304 {
305   const char *elem, *p;
306 
307   for (elem = vpath; *elem; elem = p)
308     {
309       for (p = elem; *p && *p != ':'; p++)
310 	continue;
311       mkdeps::velt elt;
312       elt.len = p - elem;
313       char *str = XNEWVEC (char, elt.len + 1);
314       elt.str = str;
315       memcpy (str, elem, elt.len);
316       str[elt.len] = '\0';
317       if (*p == ':')
318 	p++;
319 
320       d->vpath.push (elt);
321     }
322 }
323 
324 /* Write NAME, with a leading space to FP, a Makefile.  Advance COL as
325    appropriate, wrap at COLMAX, returning new column number.  Iff
326    QUOTE apply quoting.  Append TRAIL.  */
327 
328 static unsigned
329 make_write_name (const char *name, FILE *fp, unsigned col, unsigned colmax,
330 		 bool quote = true, const char *trail = NULL)
331 {
332   if (quote)
333     name = munge (name, trail, NULL);
334   unsigned size = strlen (name);
335 
336   if (col)
337     {
338       if (colmax && col + size> colmax)
339 	{
340 	  fputs (" \\\n", fp);
341 	  col = 0;
342 	}
343       col++;
344       fputs (" ", fp);
345     }
346 
347   col += size;
348   fputs (name, fp);
349 
350   return col;
351 }
352 
353 /* Write all the names in VEC via make_write_name.  */
354 
355 static unsigned
356 make_write_vec (const mkdeps::vec<const char *> &vec, FILE *fp,
357 		unsigned col, unsigned colmax, unsigned quote_lwm = 0,
358 		const char *trail = NULL)
359 {
360   for (unsigned ix = 0; ix != vec.size (); ix++)
361     col = make_write_name (vec[ix], fp, col, colmax, ix >= quote_lwm, trail);
362   return col;
363 }
364 
365 /* Write the dependencies to a Makefile.  If PHONY is true, add
366    .PHONY targets for all the dependencies too.  */
367 
368 static void
make_write(const class mkdeps * d,FILE * fp,bool phony,unsigned int colmax)369 make_write (const class mkdeps *d, FILE *fp, bool phony, unsigned int colmax)
370 {
371   unsigned column = 0;
372   if (colmax && colmax < 34)
373     colmax = 34;
374 
375   if (d->deps.size ())
376     {
377       column = make_write_vec (d->targets, fp, 0, colmax, d->quote_lwm);
378       fputs (":", fp);
379       column++;
380       make_write_vec (d->deps, fp, column, colmax);
381       fputs ("\n", fp);
382       if (phony)
383 	for (unsigned i = 1; i < d->deps.size (); i++)
384 	  fprintf (fp, "%s:\n", munge (d->deps[i]));
385     }
386 }
387 
388 /* Write out dependencies according to the selected format (which is
389    only Make at the moment).  */
390 
391 void
deps_write(const class mkdeps * d,FILE * fp,bool phony,unsigned int colmax)392 deps_write (const class mkdeps *d, FILE *fp, bool phony, unsigned int colmax)
393 {
394   make_write (d, fp, phony, colmax);
395 }
396 
397 /* Write out a deps buffer to a file, in a form that can be read back
398    with deps_restore.  Returns nonzero on error, in which case the
399    error number will be in errno.  */
400 
401 int
deps_save(class mkdeps * deps,FILE * f)402 deps_save (class mkdeps *deps, FILE *f)
403 {
404   unsigned int i;
405   size_t size;
406 
407   /* The cppreader structure contains makefile dependences.  Write out this
408      structure.  */
409 
410   /* The number of dependences.  */
411   size = deps->deps.size ();
412   if (fwrite (&size, sizeof (size), 1, f) != 1)
413     return -1;
414 
415   /* The length of each dependence followed by the string.  */
416   for (i = 0; i < deps->deps.size (); i++)
417     {
418       size = strlen (deps->deps[i]);
419       if (fwrite (&size, sizeof (size), 1, f) != 1)
420 	return -1;
421       if (fwrite (deps->deps[i], size, 1, f) != 1)
422 	return -1;
423     }
424 
425   return 0;
426 }
427 
428 /* Read back dependency information written with deps_save into
429    the deps sizefer.  The third argument may be NULL, in which case
430    the dependency information is just skipped, or it may be a filename,
431    in which case that filename is skipped.  */
432 
433 int
deps_restore(class mkdeps * deps,FILE * fd,const char * self)434 deps_restore (class mkdeps *deps, FILE *fd, const char *self)
435 {
436   size_t size;
437   char *buf = NULL;
438   size_t buf_size = 0;
439 
440   /* Number of dependences.  */
441   if (fread (&size, sizeof (size), 1, fd) != 1)
442     return -1;
443 
444   /* The length of each dependence string, followed by the string.  */
445   for (unsigned i = size; i--;)
446     {
447       /* Read in # bytes in string.  */
448       if (fread (&size, sizeof (size), 1, fd) != 1)
449 	return -1;
450 
451       if (size >= buf_size)
452 	{
453 	  buf_size = size + 512;
454 	  buf = XRESIZEVEC (char, buf, buf_size);
455 	}
456       if (fread (buf, 1, size, fd) != size)
457 	{
458 	  XDELETEVEC (buf);
459 	  return -1;
460 	}
461       buf[size] = 0;
462 
463       /* Generate makefile dependencies from .pch if -nopch-deps.  */
464       if (self != NULL && filename_cmp (buf, self) != 0)
465         deps_add_dep (deps, buf);
466     }
467 
468   XDELETEVEC (buf);
469   return 0;
470 }
471