1 /* Dependency generator for Makefile fragments.
2    Copyright (C) 2000-2021 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 #include "internal.h"
27 
28 /* Not set up to just include std::vector et al, here's a simple
29    implementation.  */
30 
31 /* Keep this structure local to this file, so clients don't find it
32    easy to start making assumptions.  */
33 class mkdeps
34 {
35 public:
36   /* T has trivial cctor & dtor.  */
37   template <typename T>
38   class vec
39   {
40   private:
41     T *ary;
42     unsigned num;
43     unsigned alloc;
44 
45   public:
vec()46     vec ()
47       : ary (NULL), num (0), alloc (0)
48       {}
~vec()49     ~vec ()
50       {
51 	XDELETEVEC (ary);
52       }
53 
54   public:
size()55     unsigned size () const
56     {
57       return num;
58     }
59     const T &operator[] (unsigned ix) const
60     {
61       return ary[ix];
62     }
63     T &operator[] (unsigned ix)
64     {
65       return ary[ix];
66     }
push(const T & elt)67     void push (const T &elt)
68     {
69       if (num == alloc)
70 	{
71 	  alloc = alloc ? alloc * 2 : 16;
72 	  ary = XRESIZEVEC (T, ary, alloc);
73 	}
74       ary[num++] = elt;
75     }
76   };
77   struct velt
78   {
79     const char *str;
80     size_t len;
81   };
82 
mkdeps()83   mkdeps ()
84     : module_name (NULL), cmi_name (NULL), is_header_unit (false), quote_lwm (0)
85   {
86   }
~mkdeps()87   ~mkdeps ()
88   {
89     unsigned int i;
90 
91     for (i = targets.size (); i--;)
92       free (const_cast <char *> (targets[i]));
93     for (i = deps.size (); i--;)
94       free (const_cast <char *> (deps[i]));
95     for (i = vpath.size (); i--;)
96       XDELETEVEC (vpath[i].str);
97     for (i = modules.size (); i--;)
98       XDELETEVEC (modules[i]);
99     XDELETEVEC (module_name);
100     free (const_cast <char *> (cmi_name));
101   }
102 
103 public:
104   vec<const char *> targets;
105   vec<const char *> deps;
106   vec<velt> vpath;
107   vec<const char *> modules;
108 
109 public:
110   const char *module_name;
111   const char *cmi_name;
112   bool is_header_unit;
113   unsigned short quote_lwm;
114 };
115 
116 /* Apply Make quoting to STR, TRAIL.  Note that it's not possible to
117    quote all such characters - e.g. \n, %, *, ?, [, \ (in some
118    contexts), and ~ are not properly handled.  It isn't possible to
119    get this right in any current version of Make.  (??? Still true?
120    Old comment referred to 3.76.1.)  */
121 
122 static const char *
123 munge (const char *str, const char *trail = nullptr)
124 {
125   static unsigned alloc;
126   static char *buf;
127   unsigned dst = 0;
128 
129   for (; str; str = trail, trail = nullptr)
130     {
131       unsigned slashes = 0;
132       char c;
133       for (const char *probe = str; (c = *probe++);)
134 	{
135 	  if (alloc < dst + 4 + slashes)
136 	    {
137 	      alloc = alloc * 2 + 32;
138 	      buf = XRESIZEVEC (char, buf, alloc);
139 	    }
140 
141 	  switch (c)
142 	    {
143 	    case '\\':
144 	      slashes++;
145 	      break;
146 
147 	    case '$':
148 	      buf[dst++] = '$';
149 	      goto def;
150 
151 	    case ' ':
152 	    case '\t':
153 	      /* GNU make uses a weird quoting scheme for white space.
154 		 A space or tab preceded by 2N+1 backslashes
155 		 represents N backslashes followed by space; a space
156 		 or tab preceded by 2N backslashes represents N
157 		 backslashes at the end of a file name; and
158 		 backslashes in other contexts should not be
159 		 doubled.  */
160 	      while (slashes--)
161 		buf[dst++] = '\\';
162 	      /* FALLTHROUGH  */
163 
164 	    case '#':
165 	      buf[dst++] = '\\';
166 	      /* FALLTHROUGH  */
167 
168 	    default:
169 	    def:
170 	      slashes = 0;
171 	      break;
172 	    }
173 
174 	  buf[dst++] = c;
175 	}
176     }
177 
178   buf[dst] = 0;
179   return buf;
180 }
181 
182 /* If T begins with any of the partial pathnames listed in d->vpathv,
183    then advance T to point beyond that pathname.  */
184 static const char *
apply_vpath(class mkdeps * d,const char * t)185 apply_vpath (class mkdeps *d, const char *t)
186 {
187   if (unsigned len = d->vpath.size ())
188     for (unsigned i = len; i--;)
189       {
190 	if (!filename_ncmp (d->vpath[i].str, t, d->vpath[i].len))
191 	  {
192 	    const char *p = t + d->vpath[i].len;
193 	    if (!IS_DIR_SEPARATOR (*p))
194 	      goto not_this_one;
195 
196 	    /* Do not simplify $(vpath)/../whatever.  ??? Might not
197 	       be necessary. */
198 	    if (p[1] == '.' && p[2] == '.' && IS_DIR_SEPARATOR (p[3]))
199 	      goto not_this_one;
200 
201 	    /* found a match */
202 	    t = t + d->vpath[i].len + 1;
203 	    break;
204 	  }
205       not_this_one:;
206       }
207 
208   /* Remove leading ./ in any case.  */
209   while (t[0] == '.' && IS_DIR_SEPARATOR (t[1]))
210     {
211       t += 2;
212       /* If we removed a leading ./, then also remove any /s after the
213 	 first.  */
214       while (IS_DIR_SEPARATOR (t[0]))
215 	++t;
216     }
217 
218   return t;
219 }
220 
221 /* Public routines.  */
222 
223 class mkdeps *
deps_init(void)224 deps_init (void)
225 {
226   return new mkdeps ();
227 }
228 
229 void
deps_free(class mkdeps * d)230 deps_free (class mkdeps *d)
231 {
232   delete d;
233 }
234 
235 /* Adds a target T.  We make a copy, so it need not be a permanent
236    string.  QUOTE is true if the string should be quoted.  */
237 void
deps_add_target(class mkdeps * d,const char * t,int quote)238 deps_add_target (class mkdeps *d, const char *t, int quote)
239 {
240   t = xstrdup (apply_vpath (d, t));
241 
242   if (!quote)
243     {
244       /* Sometimes unquoted items are added after quoted ones.
245 	 Swap out the lowest quoted.  */
246       if (d->quote_lwm != d->targets.size ())
247 	{
248 	  const char *lowest = d->targets[d->quote_lwm];
249 	  d->targets[d->quote_lwm] = t;
250 	  t = lowest;
251 	}
252       d->quote_lwm++;
253     }
254 
255   d->targets.push (t);
256 }
257 
258 /* Sets the default target if none has been given already.  An empty
259    string as the default target in interpreted as stdin.  The string
260    is quoted for MAKE.  */
261 void
deps_add_default_target(class mkdeps * d,const char * tgt)262 deps_add_default_target (class mkdeps *d, const char *tgt)
263 {
264   /* Only if we have no targets.  */
265   if (d->targets.size ())
266     return;
267 
268   if (tgt[0] == '\0')
269     d->targets.push (xstrdup ("-"));
270   else
271     {
272 #ifndef TARGET_OBJECT_SUFFIX
273 # define TARGET_OBJECT_SUFFIX ".o"
274 #endif
275       const char *start = lbasename (tgt);
276       char *o = (char *) alloca (strlen (start)
277                                  + strlen (TARGET_OBJECT_SUFFIX) + 1);
278       char *suffix;
279 
280       strcpy (o, start);
281 
282       suffix = strrchr (o, '.');
283       if (!suffix)
284         suffix = o + strlen (o);
285       strcpy (suffix, TARGET_OBJECT_SUFFIX);
286 
287       deps_add_target (d, o, 1);
288     }
289 }
290 
291 void
deps_add_dep(class mkdeps * d,const char * t)292 deps_add_dep (class mkdeps *d, const char *t)
293 {
294   gcc_assert (*t);
295 
296   t = apply_vpath (d, t);
297 
298   d->deps.push (xstrdup (t));
299 }
300 
301 void
deps_add_vpath(class mkdeps * d,const char * vpath)302 deps_add_vpath (class mkdeps *d, const char *vpath)
303 {
304   const char *elem, *p;
305 
306   for (elem = vpath; *elem; elem = p)
307     {
308       for (p = elem; *p && *p != ':'; p++)
309 	continue;
310       mkdeps::velt elt;
311       elt.len = p - elem;
312       char *str = XNEWVEC (char, elt.len + 1);
313       elt.str = str;
314       memcpy (str, elem, elt.len);
315       str[elt.len] = '\0';
316       if (*p == ':')
317 	p++;
318 
319       d->vpath.push (elt);
320     }
321 }
322 
323 /* Add a new module target (there can only be one).  M is the module
324    name.   */
325 
326 void
deps_add_module_target(struct mkdeps * d,const char * m,const char * cmi,bool is_header_unit)327 deps_add_module_target (struct mkdeps *d, const char *m,
328 			const char *cmi, bool is_header_unit)
329 {
330   gcc_assert (!d->module_name);
331 
332   d->module_name = xstrdup (m);
333   d->is_header_unit = is_header_unit;
334   d->cmi_name = xstrdup (cmi);
335 }
336 
337 /* Add a new module dependency.  M is the module name.  */
338 
339 void
deps_add_module_dep(struct mkdeps * d,const char * m)340 deps_add_module_dep (struct mkdeps *d, const char *m)
341 {
342   d->modules.push (xstrdup (m));
343 }
344 
345 /* Write NAME, with a leading space to FP, a Makefile.  Advance COL as
346    appropriate, wrap at COLMAX, returning new column number.  Iff
347    QUOTE apply quoting.  Append TRAIL.  */
348 
349 static unsigned
350 make_write_name (const char *name, FILE *fp, unsigned col, unsigned colmax,
351 		 bool quote = true, const char *trail = NULL)
352 {
353   if (quote)
354     name = munge (name, trail);
355   unsigned size = strlen (name);
356 
357   if (col)
358     {
359       if (colmax && col + size> colmax)
360 	{
361 	  fputs (" \\\n", fp);
362 	  col = 0;
363 	}
364       col++;
365       fputs (" ", fp);
366     }
367 
368   col += size;
369   fputs (name, fp);
370 
371   return col;
372 }
373 
374 /* Write all the names in VEC via make_write_name.  */
375 
376 static unsigned
377 make_write_vec (const mkdeps::vec<const char *> &vec, FILE *fp,
378 		unsigned col, unsigned colmax, unsigned quote_lwm = 0,
379 		const char *trail = NULL)
380 {
381   for (unsigned ix = 0; ix != vec.size (); ix++)
382     col = make_write_name (vec[ix], fp, col, colmax, ix >= quote_lwm, trail);
383   return col;
384 }
385 
386 /* Write the dependencies to a Makefile.  If PHONY is true, add
387    .PHONY targets for all the dependencies too.  */
388 
389 static void
make_write(const cpp_reader * pfile,FILE * fp,unsigned int colmax)390 make_write (const cpp_reader *pfile, FILE *fp, unsigned int colmax)
391 {
392   const mkdeps *d = pfile->deps;
393 
394   unsigned column = 0;
395   if (colmax && colmax < 34)
396     colmax = 34;
397 
398   if (d->deps.size ())
399     {
400       column = make_write_vec (d->targets, fp, 0, colmax, d->quote_lwm);
401       if (CPP_OPTION (pfile, deps.modules) && d->cmi_name)
402 	column = make_write_name (d->cmi_name, fp, column, colmax);
403       fputs (":", fp);
404       column++;
405       make_write_vec (d->deps, fp, column, colmax);
406       fputs ("\n", fp);
407       if (CPP_OPTION (pfile, deps.phony_targets))
408 	for (unsigned i = 1; i < d->deps.size (); i++)
409 	  fprintf (fp, "%s:\n", munge (d->deps[i]));
410     }
411 
412   if (!CPP_OPTION (pfile, deps.modules))
413     return;
414 
415   if (d->modules.size ())
416     {
417       column = make_write_vec (d->targets, fp, 0, colmax, d->quote_lwm);
418       if (d->cmi_name)
419 	column = make_write_name (d->cmi_name, fp, column, colmax);
420       fputs (":", fp);
421       column++;
422       column = make_write_vec (d->modules, fp, column, colmax, 0, ".c++m");
423       fputs ("\n", fp);
424     }
425 
426   if (d->module_name)
427     {
428       if (d->cmi_name)
429 	{
430 	  /* module-name : cmi-name */
431 	  column = make_write_name (d->module_name, fp, 0, colmax,
432 				    true, ".c++m");
433 	  fputs (":", fp);
434 	  column++;
435 	  column = make_write_name (d->cmi_name, fp, column, colmax);
436 	  fputs ("\n", fp);
437 
438 	  column = fprintf (fp, ".PHONY:");
439 	  column = make_write_name (d->module_name, fp, column, colmax,
440 				    true, ".c++m");
441 	  fputs ("\n", fp);
442 	}
443 
444       if (d->cmi_name && !d->is_header_unit)
445 	{
446 	  /* An order-only dependency.
447 	      cmi-name :| first-target
448 	     We can probably drop this this in favour of Make-4.3's grouped
449 	      targets '&:'  */
450 	  column = make_write_name (d->cmi_name, fp, 0, colmax);
451 	  fputs (":|", fp);
452 	  column++;
453 	  column = make_write_name (d->targets[0], fp, column, colmax);
454 	  fputs ("\n", fp);
455 	}
456     }
457 
458   if (d->modules.size ())
459     {
460       column = fprintf (fp, "CXX_IMPORTS +=");
461       make_write_vec (d->modules, fp, column, colmax, 0, ".c++m");
462       fputs ("\n", fp);
463     }
464 }
465 
466 /* Write out dependencies according to the selected format (which is
467    only Make at the moment).  */
468 /* Really we should be opening fp here.  */
469 
470 void
deps_write(const cpp_reader * pfile,FILE * fp,unsigned int colmax)471 deps_write (const cpp_reader *pfile, FILE *fp, unsigned int colmax)
472 {
473   make_write (pfile, fp, colmax);
474 }
475 
476 /* Write out a deps buffer to a file, in a form that can be read back
477    with deps_restore.  Returns nonzero on error, in which case the
478    error number will be in errno.  */
479 
480 int
deps_save(class mkdeps * deps,FILE * f)481 deps_save (class mkdeps *deps, FILE *f)
482 {
483   unsigned int i;
484   size_t size;
485 
486   /* The cppreader structure contains makefile dependences.  Write out this
487      structure.  */
488 
489   /* The number of dependences.  */
490   size = deps->deps.size ();
491   if (fwrite (&size, sizeof (size), 1, f) != 1)
492     return -1;
493 
494   /* The length of each dependence followed by the string.  */
495   for (i = 0; i < deps->deps.size (); i++)
496     {
497       size = strlen (deps->deps[i]);
498       if (fwrite (&size, sizeof (size), 1, f) != 1)
499 	return -1;
500       if (fwrite (deps->deps[i], size, 1, f) != 1)
501 	return -1;
502     }
503 
504   return 0;
505 }
506 
507 /* Read back dependency information written with deps_save into
508    the deps sizefer.  The third argument may be NULL, in which case
509    the dependency information is just skipped, or it may be a filename,
510    in which case that filename is skipped.  */
511 
512 int
deps_restore(class mkdeps * deps,FILE * fd,const char * self)513 deps_restore (class mkdeps *deps, FILE *fd, const char *self)
514 {
515   size_t size;
516   char *buf = NULL;
517   size_t buf_size = 0;
518 
519   /* Number of dependences.  */
520   if (fread (&size, sizeof (size), 1, fd) != 1)
521     return -1;
522 
523   /* The length of each dependence string, followed by the string.  */
524   for (unsigned i = size; i--;)
525     {
526       /* Read in # bytes in string.  */
527       if (fread (&size, sizeof (size), 1, fd) != 1)
528 	return -1;
529 
530       if (size >= buf_size)
531 	{
532 	  buf_size = size + 512;
533 	  buf = XRESIZEVEC (char, buf, buf_size);
534 	}
535       if (fread (buf, 1, size, fd) != size)
536 	{
537 	  XDELETEVEC (buf);
538 	  return -1;
539 	}
540       buf[size] = 0;
541 
542       /* Generate makefile dependencies from .pch if -nopch-deps.  */
543       if (self != NULL && filename_cmp (buf, self) != 0)
544         deps_add_dep (deps, buf);
545     }
546 
547   XDELETEVEC (buf);
548   return 0;
549 }
550