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