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