preprocessor: Fix profiled bootstrap warning [pr97858]
[gcc.git] / libcpp / mkdeps.c
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 #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:
46 vec ()
47 : ary (NULL), num (0), alloc (0)
48 {}
49 ~vec ()
50 {
51 XDELETEVEC (ary);
52 }
53
54 public:
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 }
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
83 mkdeps ()
84 : quote_lwm (0)
85 {
86 }
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 }
98
99 public:
100 vec<const char *> targets;
101 vec<const char *> deps;
102 vec<velt> vpath;
103
104 public:
105 unsigned short quote_lwm;
106 };
107
108 /* Apply Make quoting to STR, TRAIL. Note that it's not possible to
109 quote all such characters - e.g. \n, %, *, ?, [, \ (in some
110 contexts), and ~ are not properly handled. It isn't possible to
111 get this right in any current version of Make. (??? Still true?
112 Old comment referred to 3.76.1.) */
113
114 static const char *
115 munge (const char *str, const char *trail = nullptr)
116 {
117 static unsigned alloc;
118 static char *buf;
119 unsigned dst = 0;
120
121 for (; str; str = trail, trail = nullptr)
122 {
123 unsigned slashes = 0;
124 char c;
125 for (const char *probe = str; (c = *probe++);)
126 {
127 if (alloc < dst + 4 + slashes)
128 {
129 alloc = alloc * 2 + 32;
130 buf = XRESIZEVEC (char, buf, alloc);
131 }
132
133 switch (c)
134 {
135 case '\\':
136 slashes++;
137 break;
138
139 case '$':
140 buf[dst++] = '$';
141 goto def;
142
143 case ' ':
144 case '\t':
145 /* GNU make uses a weird quoting scheme for white space.
146 A space or tab preceded by 2N+1 backslashes
147 represents N backslashes followed by space; a space
148 or tab preceded by 2N backslashes represents N
149 backslashes at the end of a file name; and
150 backslashes in other contexts should not be
151 doubled. */
152 while (slashes--)
153 buf[dst++] = '\\';
154 /* FALLTHROUGH */
155
156 case '#':
157 case ':':
158 buf[dst++] = '\\';
159 /* FALLTHROUGH */
160
161 default:
162 def:
163 slashes = 0;
164 break;
165 }
166
167 buf[dst++] = c;
168 }
169 }
170
171 buf[dst] = 0;
172 return buf;
173 }
174
175 /* If T begins with any of the partial pathnames listed in d->vpathv,
176 then advance T to point beyond that pathname. */
177 static const char *
178 apply_vpath (class mkdeps *d, const char *t)
179 {
180 if (unsigned len = d->vpath.size ())
181 for (unsigned i = len; i--;)
182 {
183 if (!filename_ncmp (d->vpath[i].str, t, d->vpath[i].len))
184 {
185 const char *p = t + d->vpath[i].len;
186 if (!IS_DIR_SEPARATOR (*p))
187 goto not_this_one;
188
189 /* Do not simplify $(vpath)/../whatever. ??? Might not
190 be necessary. */
191 if (p[1] == '.' && p[2] == '.' && IS_DIR_SEPARATOR (p[3]))
192 goto not_this_one;
193
194 /* found a match */
195 t = t + d->vpath[i].len + 1;
196 break;
197 }
198 not_this_one:;
199 }
200
201 /* Remove leading ./ in any case. */
202 while (t[0] == '.' && IS_DIR_SEPARATOR (t[1]))
203 {
204 t += 2;
205 /* If we removed a leading ./, then also remove any /s after the
206 first. */
207 while (IS_DIR_SEPARATOR (t[0]))
208 ++t;
209 }
210
211 return t;
212 }
213
214 /* Public routines. */
215
216 class mkdeps *
217 deps_init (void)
218 {
219 return new mkdeps ();
220 }
221
222 void
223 deps_free (class mkdeps *d)
224 {
225 delete d;
226 }
227
228 /* Adds a target T. We make a copy, so it need not be a permanent
229 string. QUOTE is true if the string should be quoted. */
230 void
231 deps_add_target (class mkdeps *d, const char *t, int quote)
232 {
233 t = xstrdup (apply_vpath (d, t));
234
235 if (!quote)
236 {
237 /* Sometimes unquoted items are added after quoted ones.
238 Swap out the lowest quoted. */
239 if (d->quote_lwm != d->targets.size ())
240 {
241 const char *lowest = d->targets[d->quote_lwm];
242 d->targets[d->quote_lwm] = t;
243 t = lowest;
244 }
245 d->quote_lwm++;
246 }
247
248 d->targets.push (t);
249 }
250
251 /* Sets the default target if none has been given already. An empty
252 string as the default target in interpreted as stdin. The string
253 is quoted for MAKE. */
254 void
255 deps_add_default_target (class mkdeps *d, const char *tgt)
256 {
257 /* Only if we have no targets. */
258 if (d->targets.size ())
259 return;
260
261 if (tgt[0] == '\0')
262 d->targets.push (xstrdup ("-"));
263 else
264 {
265 #ifndef TARGET_OBJECT_SUFFIX
266 # define TARGET_OBJECT_SUFFIX ".o"
267 #endif
268 const char *start = lbasename (tgt);
269 char *o = (char *) alloca (strlen (start)
270 + strlen (TARGET_OBJECT_SUFFIX) + 1);
271 char *suffix;
272
273 strcpy (o, start);
274
275 suffix = strrchr (o, '.');
276 if (!suffix)
277 suffix = o + strlen (o);
278 strcpy (suffix, TARGET_OBJECT_SUFFIX);
279
280 deps_add_target (d, o, 1);
281 }
282 }
283
284 void
285 deps_add_dep (class mkdeps *d, const char *t)
286 {
287 gcc_assert (*t);
288
289 t = apply_vpath (d, t);
290
291 d->deps.push (xstrdup (t));
292 }
293
294 void
295 deps_add_vpath (class mkdeps *d, const char *vpath)
296 {
297 const char *elem, *p;
298
299 for (elem = vpath; *elem; elem = p)
300 {
301 for (p = elem; *p && *p != ':'; p++)
302 continue;
303 mkdeps::velt elt;
304 elt.len = p - elem;
305 char *str = XNEWVEC (char, elt.len + 1);
306 elt.str = str;
307 memcpy (str, elem, elt.len);
308 str[elt.len] = '\0';
309 if (*p == ':')
310 p++;
311
312 d->vpath.push (elt);
313 }
314 }
315
316 /* Write NAME, with a leading space to FP, a Makefile. Advance COL as
317 appropriate, wrap at COLMAX, returning new column number. Iff
318 QUOTE apply quoting. Append TRAIL. */
319
320 static unsigned
321 make_write_name (const char *name, FILE *fp, unsigned col, unsigned colmax,
322 bool quote = true, const char *trail = NULL)
323 {
324 if (quote)
325 name = munge (name, trail);
326 unsigned size = strlen (name);
327
328 if (col)
329 {
330 if (colmax && col + size> colmax)
331 {
332 fputs (" \\\n", fp);
333 col = 0;
334 }
335 col++;
336 fputs (" ", fp);
337 }
338
339 col += size;
340 fputs (name, fp);
341
342 return col;
343 }
344
345 /* Write all the names in VEC via make_write_name. */
346
347 static unsigned
348 make_write_vec (const mkdeps::vec<const char *> &vec, FILE *fp,
349 unsigned col, unsigned colmax, unsigned quote_lwm = 0,
350 const char *trail = NULL)
351 {
352 for (unsigned ix = 0; ix != vec.size (); ix++)
353 col = make_write_name (vec[ix], fp, col, colmax, ix >= quote_lwm, trail);
354 return col;
355 }
356
357 /* Write the dependencies to a Makefile. If PHONY is true, add
358 .PHONY targets for all the dependencies too. */
359
360 static void
361 make_write (const cpp_reader *pfile, FILE *fp, unsigned int colmax)
362 {
363 const mkdeps *d = pfile->deps;
364
365 unsigned column = 0;
366 if (colmax && colmax < 34)
367 colmax = 34;
368
369 if (d->deps.size ())
370 {
371 column = make_write_vec (d->targets, fp, 0, colmax, d->quote_lwm);
372 fputs (":", fp);
373 column++;
374 make_write_vec (d->deps, fp, column, colmax);
375 fputs ("\n", fp);
376 if (CPP_OPTION (pfile, deps.phony_targets))
377 for (unsigned i = 1; i < d->deps.size (); i++)
378 fprintf (fp, "%s:\n", munge (d->deps[i]));
379 }
380 }
381
382 /* Write out dependencies according to the selected format (which is
383 only Make at the moment). */
384 /* Really we should be opening fp here. */
385
386 void
387 deps_write (const cpp_reader *pfile, FILE *fp, unsigned int colmax)
388 {
389 make_write (pfile, fp, colmax);
390 }
391
392 /* Write out a deps buffer to a file, in a form that can be read back
393 with deps_restore. Returns nonzero on error, in which case the
394 error number will be in errno. */
395
396 int
397 deps_save (class mkdeps *deps, FILE *f)
398 {
399 unsigned int i;
400 size_t size;
401
402 /* The cppreader structure contains makefile dependences. Write out this
403 structure. */
404
405 /* The number of dependences. */
406 size = deps->deps.size ();
407 if (fwrite (&size, sizeof (size), 1, f) != 1)
408 return -1;
409
410 /* The length of each dependence followed by the string. */
411 for (i = 0; i < deps->deps.size (); i++)
412 {
413 size = strlen (deps->deps[i]);
414 if (fwrite (&size, sizeof (size), 1, f) != 1)
415 return -1;
416 if (fwrite (deps->deps[i], size, 1, f) != 1)
417 return -1;
418 }
419
420 return 0;
421 }
422
423 /* Read back dependency information written with deps_save into
424 the deps sizefer. The third argument may be NULL, in which case
425 the dependency information is just skipped, or it may be a filename,
426 in which case that filename is skipped. */
427
428 int
429 deps_restore (class mkdeps *deps, FILE *fd, const char *self)
430 {
431 size_t size;
432 char *buf = NULL;
433 size_t buf_size = 0;
434
435 /* Number of dependences. */
436 if (fread (&size, sizeof (size), 1, fd) != 1)
437 return -1;
438
439 /* The length of each dependence string, followed by the string. */
440 for (unsigned i = size; i--;)
441 {
442 /* Read in # bytes in string. */
443 if (fread (&size, sizeof (size), 1, fd) != 1)
444 return -1;
445
446 if (size >= buf_size)
447 {
448 buf_size = size + 512;
449 buf = XRESIZEVEC (char, buf, buf_size);
450 }
451 if (fread (buf, 1, size, fd) != size)
452 {
453 XDELETEVEC (buf);
454 return -1;
455 }
456 buf[size] = 0;
457
458 /* Generate makefile dependencies from .pch if -nopch-deps. */
459 if (self != NULL && filename_cmp (buf, self) != 0)
460 deps_add_dep (deps, buf);
461 }
462
463 XDELETEVEC (buf);
464 return 0;
465 }