]>
Commit | Line | Data |
---|---|---|
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: | |
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 | T &operator[] (unsigned ix) | |
63 | { | |
64 | return ary[ix]; | |
65 | } | |
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 | ||
82 | mkdeps () | |
83 | : quote_lwm (0) | |
84 | { | |
85 | } | |
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 | case ':': | |
160 | buf[dst++] = '\\'; | |
161 | /* FALLTHROUGH */ | |
162 | ||
163 | default: | |
164 | def: | |
165 | slashes = 0; | |
166 | break; | |
167 | } | |
168 | ||
169 | buf[dst++] = c; | |
170 | } | |
171 | ||
172 | if (first) | |
173 | str = trail; | |
174 | else | |
175 | str = va_arg (args, const char *); | |
176 | } | |
177 | if (trail) | |
178 | va_end (args); | |
179 | ||
180 | buf[dst] = 0; | |
181 | return buf; | |
182 | } | |
183 | ||
184 | /* If T begins with any of the partial pathnames listed in d->vpathv, | |
185 | then advance T to point beyond that pathname. */ | |
186 | static const char * | |
187 | apply_vpath (class mkdeps *d, const char *t) | |
188 | { | |
189 | if (unsigned len = d->vpath.size ()) | |
190 | for (unsigned i = len; i--;) | |
191 | { | |
192 | if (!filename_ncmp (d->vpath[i].str, t, d->vpath[i].len)) | |
193 | { | |
194 | const char *p = t + d->vpath[i].len; | |
195 | if (!IS_DIR_SEPARATOR (*p)) | |
196 | goto not_this_one; | |
197 | ||
198 | /* Do not simplify $(vpath)/../whatever. ??? Might not | |
199 | be necessary. */ | |
200 | if (p[1] == '.' && p[2] == '.' && IS_DIR_SEPARATOR (p[3])) | |
201 | goto not_this_one; | |
202 | ||
203 | /* found a match */ | |
204 | t = t + d->vpath[i].len + 1; | |
205 | break; | |
206 | } | |
207 | not_this_one:; | |
208 | } | |
209 | ||
210 | /* Remove leading ./ in any case. */ | |
211 | while (t[0] == '.' && IS_DIR_SEPARATOR (t[1])) | |
212 | { | |
213 | t += 2; | |
214 | /* If we removed a leading ./, then also remove any /s after the | |
215 | first. */ | |
216 | while (IS_DIR_SEPARATOR (t[0])) | |
217 | ++t; | |
218 | } | |
219 | ||
220 | return t; | |
221 | } | |
222 | ||
223 | /* Public routines. */ | |
224 | ||
225 | class mkdeps * | |
226 | deps_init (void) | |
227 | { | |
228 | return new mkdeps (); | |
229 | } | |
230 | ||
231 | void | |
232 | deps_free (class mkdeps *d) | |
233 | { | |
234 | delete d; | |
235 | } | |
236 | ||
237 | /* Adds a target T. We make a copy, so it need not be a permanent | |
238 | string. QUOTE is true if the string should be quoted. */ | |
239 | void | |
240 | deps_add_target (class mkdeps *d, const char *t, int quote) | |
241 | { | |
242 | t = xstrdup (apply_vpath (d, t)); | |
243 | ||
244 | if (!quote) | |
245 | { | |
246 | /* Sometimes unquoted items are added after quoted ones. | |
247 | Swap out the lowest quoted. */ | |
248 | if (d->quote_lwm != d->targets.size ()) | |
249 | { | |
250 | const char *lowest = d->targets[d->quote_lwm]; | |
251 | d->targets[d->quote_lwm] = t; | |
252 | t = lowest; | |
253 | } | |
254 | d->quote_lwm++; | |
255 | } | |
256 | ||
257 | d->targets.push (t); | |
258 | } | |
259 | ||
260 | /* Sets the default target if none has been given already. An empty | |
261 | string as the default target in interpreted as stdin. The string | |
262 | is quoted for MAKE. */ | |
263 | void | |
264 | deps_add_default_target (class mkdeps *d, const char *tgt) | |
265 | { | |
266 | /* Only if we have no targets. */ | |
267 | if (d->targets.size ()) | |
268 | return; | |
269 | ||
270 | if (tgt[0] == '\0') | |
271 | deps_add_target (d, "-", 1); | |
272 | else | |
273 | { | |
274 | #ifndef TARGET_OBJECT_SUFFIX | |
275 | # define TARGET_OBJECT_SUFFIX ".o" | |
276 | #endif | |
277 | const char *start = lbasename (tgt); | |
278 | char *o = (char *) alloca (strlen (start) | |
279 | + strlen (TARGET_OBJECT_SUFFIX) + 1); | |
280 | char *suffix; | |
281 | ||
282 | strcpy (o, start); | |
283 | ||
284 | suffix = strrchr (o, '.'); | |
285 | if (!suffix) | |
286 | suffix = o + strlen (o); | |
287 | strcpy (suffix, TARGET_OBJECT_SUFFIX); | |
288 | ||
289 | deps_add_target (d, o, 1); | |
290 | } | |
291 | } | |
292 | ||
293 | void | |
294 | deps_add_dep (class mkdeps *d, const char *t) | |
295 | { | |
296 | gcc_assert (*t); | |
297 | ||
298 | t = apply_vpath (d, t); | |
299 | ||
300 | d->deps.push (xstrdup (t)); | |
301 | } | |
302 | ||
303 | void | |
304 | deps_add_vpath (class mkdeps *d, const char *vpath) | |
305 | { | |
306 | const char *elem, *p; | |
307 | ||
308 | for (elem = vpath; *elem; elem = p) | |
309 | { | |
310 | for (p = elem; *p && *p != ':'; p++) | |
311 | continue; | |
312 | mkdeps::velt elt; | |
313 | elt.len = p - elem; | |
314 | char *str = XNEWVEC (char, elt.len + 1); | |
315 | elt.str = str; | |
316 | memcpy (str, elem, elt.len); | |
317 | str[elt.len] = '\0'; | |
318 | if (*p == ':') | |
319 | p++; | |
320 | ||
321 | d->vpath.push (elt); | |
322 | } | |
323 | } | |
324 | ||
325 | /* Write NAME, with a leading space to FP, a Makefile. Advance COL as | |
326 | appropriate, wrap at COLMAX, returning new column number. Iff | |
327 | QUOTE apply quoting. Append TRAIL. */ | |
328 | ||
329 | static unsigned | |
330 | make_write_name (const char *name, FILE *fp, unsigned col, unsigned colmax, | |
331 | bool quote = true, const char *trail = NULL) | |
332 | { | |
333 | if (quote) | |
334 | name = munge (name, trail, NULL); | |
335 | unsigned size = strlen (name); | |
336 | ||
337 | if (col) | |
338 | { | |
339 | if (colmax && col + size> colmax) | |
340 | { | |
341 | fputs (" \\\n", fp); | |
342 | col = 0; | |
343 | } | |
344 | col++; | |
345 | fputs (" ", fp); | |
346 | } | |
347 | ||
348 | col += size; | |
349 | fputs (name, fp); | |
350 | ||
351 | return col; | |
352 | } | |
353 | ||
354 | /* Write all the names in VEC via make_write_name. */ | |
355 | ||
356 | static unsigned | |
357 | make_write_vec (const mkdeps::vec<const char *> &vec, FILE *fp, | |
358 | unsigned col, unsigned colmax, unsigned quote_lwm = 0, | |
359 | const char *trail = NULL) | |
360 | { | |
361 | for (unsigned ix = 0; ix != vec.size (); ix++) | |
362 | col = make_write_name (vec[ix], fp, col, colmax, ix >= quote_lwm, trail); | |
363 | return col; | |
364 | } | |
365 | ||
366 | /* Write the dependencies to a Makefile. If PHONY is true, add | |
367 | .PHONY targets for all the dependencies too. */ | |
368 | ||
369 | static void | |
370 | make_write (const class mkdeps *d, FILE *fp, bool phony, unsigned int colmax) | |
371 | { | |
372 | unsigned column = 0; | |
373 | if (colmax && colmax < 34) | |
374 | colmax = 34; | |
375 | ||
376 | if (d->deps.size ()) | |
377 | { | |
378 | column = make_write_vec (d->targets, fp, 0, colmax, d->quote_lwm); | |
379 | fputs (":", fp); | |
380 | column++; | |
381 | make_write_vec (d->deps, fp, column, colmax); | |
382 | fputs ("\n", fp); | |
383 | if (phony) | |
384 | for (unsigned i = 1; i < d->deps.size (); i++) | |
385 | fprintf (fp, "%s:\n", munge (d->deps[i])); | |
386 | } | |
387 | } | |
388 | ||
389 | /* Write out dependencies according to the selected format (which is | |
390 | only Make at the moment). */ | |
391 | ||
392 | void | |
393 | deps_write (const class mkdeps *d, FILE *fp, bool phony, unsigned int colmax) | |
394 | { | |
395 | make_write (d, fp, phony, colmax); | |
396 | } | |
397 | ||
398 | /* Write out a deps buffer to a file, in a form that can be read back | |
399 | with deps_restore. Returns nonzero on error, in which case the | |
400 | error number will be in errno. */ | |
401 | ||
402 | int | |
403 | deps_save (class mkdeps *deps, FILE *f) | |
404 | { | |
405 | unsigned int i; | |
406 | size_t size; | |
407 | ||
408 | /* The cppreader structure contains makefile dependences. Write out this | |
409 | structure. */ | |
410 | ||
411 | /* The number of dependences. */ | |
412 | size = deps->deps.size (); | |
413 | if (fwrite (&size, sizeof (size), 1, f) != 1) | |
414 | return -1; | |
415 | ||
416 | /* The length of each dependence followed by the string. */ | |
417 | for (i = 0; i < deps->deps.size (); i++) | |
418 | { | |
419 | size = strlen (deps->deps[i]); | |
420 | if (fwrite (&size, sizeof (size), 1, f) != 1) | |
421 | return -1; | |
422 | if (fwrite (deps->deps[i], size, 1, f) != 1) | |
423 | return -1; | |
424 | } | |
425 | ||
426 | return 0; | |
427 | } | |
428 | ||
429 | /* Read back dependency information written with deps_save into | |
430 | the deps sizefer. The third argument may be NULL, in which case | |
431 | the dependency information is just skipped, or it may be a filename, | |
432 | in which case that filename is skipped. */ | |
433 | ||
434 | int | |
435 | deps_restore (class mkdeps *deps, FILE *fd, const char *self) | |
436 | { | |
437 | size_t size; | |
438 | char *buf = NULL; | |
439 | size_t buf_size = 0; | |
440 | ||
441 | /* Number of dependences. */ | |
442 | if (fread (&size, sizeof (size), 1, fd) != 1) | |
443 | return -1; | |
444 | ||
445 | /* The length of each dependence string, followed by the string. */ | |
446 | for (unsigned i = size; i--;) | |
447 | { | |
448 | /* Read in # bytes in string. */ | |
449 | if (fread (&size, sizeof (size), 1, fd) != 1) | |
450 | return -1; | |
451 | ||
452 | if (size >= buf_size) | |
453 | { | |
454 | buf_size = size + 512; | |
455 | buf = XRESIZEVEC (char, buf, buf_size); | |
456 | } | |
457 | if (fread (buf, 1, size, fd) != size) | |
458 | { | |
459 | XDELETEVEC (buf); | |
460 | return -1; | |
461 | } | |
462 | buf[size] = 0; | |
463 | ||
464 | /* Generate makefile dependencies from .pch if -nopch-deps. */ | |
465 | if (self != NULL && filename_cmp (buf, self) != 0) | |
466 | deps_add_dep (deps, buf); | |
467 | } | |
468 | ||
469 | XDELETEVEC (buf); | |
470 | return 0; | |
471 | } |