]>
Commit | Line | Data |
---|---|---|
49e6c08e | 1 | /* Dependency generator for Makefile fragments. |
8d9254fc | 2 | Copyright (C) 2000-2020 Free Software Foundation, Inc. |
49e6c08e ZW |
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 | |
748086b7 | 7 | Free Software Foundation; either version 3, or (at your option) any |
49e6c08e ZW |
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 | |
748086b7 JJ |
16 | along with this program; see the file COPYING3. If not see |
17 | <http://www.gnu.org/licenses/>. | |
49e6c08e ZW |
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 | ||
d7b6aee8 NS |
27 | /* Not set up to just include std::vector et al, here's a simple |
28 | implementation. */ | |
29 | ||
03b9ab42 NB |
30 | /* Keep this structure local to this file, so clients don't find it |
31 | easy to start making assumptions. */ | |
6c1dae73 | 32 | class mkdeps |
03b9ab42 | 33 | { |
d7b6aee8 NS |
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 | |
49e6c08e | 55 | { |
d7b6aee8 | 56 | return num; |
49e6c08e | 57 | } |
d7b6aee8 | 58 | const T &operator[] (unsigned ix) const |
49e6c08e | 59 | { |
d7b6aee8 NS |
60 | return ary[ix]; |
61 | } | |
66d7749b NS |
62 | T &operator[] (unsigned ix) |
63 | { | |
64 | return ary[ix]; | |
65 | } | |
d7b6aee8 NS |
66 | void push (const T &elt) |
67 | { | |
68 | if (num == alloc) | |
49e6c08e | 69 | { |
d7b6aee8 NS |
70 | alloc = alloc ? alloc * 2 : 16; |
71 | ary = XRESIZEVEC (T, ary, alloc); | |
49e6c08e | 72 | } |
d7b6aee8 | 73 | ary[num++] = elt; |
49e6c08e | 74 | } |
d7b6aee8 NS |
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 | }; | |
49e6c08e | 106 | |
d7b6aee8 NS |
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.) */ | |
49e6c08e | 112 | |
c6e83800 | 113 | static const char * |
d7b6aee8 | 114 | munge (const char *str, const char *trail = NULL, ...) |
49e6c08e | 115 | { |
d7b6aee8 NS |
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) | |
c6e83800 | 124 | { |
d7b6aee8 NS |
125 | unsigned slashes = 0; |
126 | char c; | |
127 | for (const char *probe = str; (c = *probe++);) | |
c6e83800 | 128 | { |
d7b6aee8 | 129 | if (alloc < dst + 4 + slashes) |
c6e83800 | 130 | { |
d7b6aee8 NS |
131 | alloc = alloc * 2 + 32; |
132 | buf = XRESIZEVEC (char, buf, alloc); | |
133 | } | |
c6e83800 | 134 | |
d7b6aee8 NS |
135 | switch (c) |
136 | { | |
137 | case '\\': | |
138 | slashes++; | |
139 | break; | |
c6e83800 | 140 | |
d7b6aee8 NS |
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; | |
c6e83800 ZW |
166 | break; |
167 | } | |
d7b6aee8 NS |
168 | |
169 | buf[dst++] = c; | |
c6e83800 | 170 | } |
d7b6aee8 NS |
171 | |
172 | if (first) | |
173 | str = trail; | |
174 | else | |
175 | str = va_arg (args, const char *); | |
c6e83800 | 176 | } |
d7b6aee8 NS |
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 * | |
99b1c316 | 187 | apply_vpath (class mkdeps *d, const char *t) |
d7b6aee8 NS |
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 | } | |
49e6c08e | 209 | |
c6e83800 ZW |
210 | /* Remove leading ./ in any case. */ |
211 | while (t[0] == '.' && IS_DIR_SEPARATOR (t[1])) | |
67e64439 TT |
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 | } | |
49e6c08e | 219 | |
c6e83800 ZW |
220 | return t; |
221 | } | |
49e6c08e | 222 | |
c6e83800 | 223 | /* Public routines. */ |
49e6c08e | 224 | |
99b1c316 | 225 | class mkdeps * |
c6e83800 ZW |
226 | deps_init (void) |
227 | { | |
d7b6aee8 | 228 | return new mkdeps (); |
49e6c08e ZW |
229 | } |
230 | ||
231 | void | |
99b1c316 | 232 | deps_free (class mkdeps *d) |
49e6c08e | 233 | { |
d7b6aee8 | 234 | delete d; |
49e6c08e ZW |
235 | } |
236 | ||
a5a4ce3c NB |
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. */ | |
49e6c08e | 239 | void |
99b1c316 | 240 | deps_add_target (class mkdeps *d, const char *t, int quote) |
49e6c08e | 241 | { |
66d7749b NS |
242 | t = xstrdup (apply_vpath (d, t)); |
243 | ||
d7b6aee8 | 244 | if (!quote) |
49e6c08e | 245 | { |
66d7749b NS |
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 | } | |
d7b6aee8 | 254 | d->quote_lwm++; |
49e6c08e | 255 | } |
05bccae2 | 256 | |
66d7749b | 257 | d->targets.push (t); |
49e6c08e ZW |
258 | } |
259 | ||
03b9ab42 | 260 | /* Sets the default target if none has been given already. An empty |
a5a4ce3c NB |
261 | string as the default target in interpreted as stdin. The string |
262 | is quoted for MAKE. */ | |
49e6c08e | 263 | void |
99b1c316 | 264 | deps_add_default_target (class mkdeps *d, const char *tgt) |
49e6c08e | 265 | { |
03b9ab42 | 266 | /* Only if we have no targets. */ |
d7b6aee8 | 267 | if (d->targets.size ()) |
03b9ab42 | 268 | return; |
49e6c08e | 269 | |
03b9ab42 | 270 | if (tgt[0] == '\0') |
a5a4ce3c | 271 | deps_add_target (d, "-", 1); |
49e6c08e | 272 | else |
03b9ab42 | 273 | { |
45936a85 DD |
274 | #ifndef TARGET_OBJECT_SUFFIX |
275 | # define TARGET_OBJECT_SUFFIX ".o" | |
03b9ab42 | 276 | #endif |
0821bff7 | 277 | const char *start = lbasename (tgt); |
c3f829c1 GDR |
278 | char *o = (char *) alloca (strlen (start) |
279 | + strlen (TARGET_OBJECT_SUFFIX) + 1); | |
48ce6bbb | 280 | char *suffix; |
03b9ab42 | 281 | |
48ce6bbb | 282 | strcpy (o, start); |
0c20a65f | 283 | |
48ce6bbb NS |
284 | suffix = strrchr (o, '.'); |
285 | if (!suffix) | |
286 | suffix = o + strlen (o); | |
45936a85 | 287 | strcpy (suffix, TARGET_OBJECT_SUFFIX); |
0c20a65f | 288 | |
a5a4ce3c | 289 | deps_add_target (d, o, 1); |
03b9ab42 | 290 | } |
49e6c08e ZW |
291 | } |
292 | ||
293 | void | |
99b1c316 | 294 | deps_add_dep (class mkdeps *d, const char *t) |
49e6c08e | 295 | { |
61145d93 NS |
296 | gcc_assert (*t); |
297 | ||
d7b6aee8 | 298 | t = apply_vpath (d, t); |
49e6c08e | 299 | |
d7b6aee8 | 300 | d->deps.push (xstrdup (t)); |
49e6c08e ZW |
301 | } |
302 | ||
c6e83800 | 303 | void |
99b1c316 | 304 | deps_add_vpath (class mkdeps *d, const char *vpath) |
c6e83800 ZW |
305 | { |
306 | const char *elem, *p; | |
c6e83800 ZW |
307 | |
308 | for (elem = vpath; *elem; elem = p) | |
309 | { | |
d7b6aee8 NS |
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'; | |
c6e83800 ZW |
318 | if (*p == ':') |
319 | p++; | |
320 | ||
d7b6aee8 | 321 | d->vpath.push (elt); |
c6e83800 ZW |
322 | } |
323 | } | |
324 | ||
d7b6aee8 NS |
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. */ | |
49e6c08e | 328 | |
d7b6aee8 NS |
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); | |
49e6c08e | 336 | |
d7b6aee8 | 337 | if (col) |
49e6c08e | 338 | { |
d7b6aee8 | 339 | if (colmax && col + size> colmax) |
49e6c08e | 340 | { |
d7b6aee8 NS |
341 | fputs (" \\\n", fp); |
342 | col = 0; | |
49e6c08e | 343 | } |
d7b6aee8 NS |
344 | col++; |
345 | fputs (" ", fp); | |
49e6c08e ZW |
346 | } |
347 | ||
d7b6aee8 NS |
348 | col += size; |
349 | fputs (name, fp); | |
49e6c08e | 350 | |
d7b6aee8 | 351 | return col; |
49e6c08e | 352 | } |
0c20a65f | 353 | |
d7b6aee8 NS |
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) | |
49e6c08e | 360 | { |
d7b6aee8 NS |
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 | } | |
49e6c08e | 365 | |
d7b6aee8 NS |
366 | /* Write the dependencies to a Makefile. If PHONY is true, add |
367 | .PHONY targets for all the dependencies too. */ | |
368 | ||
369 | static void | |
99b1c316 | 370 | make_write (const class mkdeps *d, FILE *fp, bool phony, unsigned int colmax) |
d7b6aee8 NS |
371 | { |
372 | unsigned column = 0; | |
373 | if (colmax && colmax < 34) | |
374 | colmax = 34; | |
375 | ||
376 | if (d->deps.size ()) | |
49e6c08e | 377 | { |
d7b6aee8 NS |
378 | column = make_write_vec (d->targets, fp, 0, colmax, d->quote_lwm); |
379 | fputs (":", fp); | |
380 | column++; | |
8ba6ea87 | 381 | make_write_vec (d->deps, fp, column, colmax); |
d7b6aee8 NS |
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])); | |
49e6c08e ZW |
386 | } |
387 | } | |
17211ab5 | 388 | |
d7b6aee8 NS |
389 | /* Write out dependencies according to the selected format (which is |
390 | only Make at the moment). */ | |
391 | ||
392 | void | |
99b1c316 | 393 | deps_write (const class mkdeps *d, FILE *fp, bool phony, unsigned int colmax) |
d7b6aee8 NS |
394 | { |
395 | make_write (d, fp, phony, colmax); | |
396 | } | |
397 | ||
17211ab5 GK |
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 | |
99b1c316 | 403 | deps_save (class mkdeps *deps, FILE *f) |
17211ab5 GK |
404 | { |
405 | unsigned int i; | |
d7b6aee8 | 406 | size_t size; |
17211ab5 GK |
407 | |
408 | /* The cppreader structure contains makefile dependences. Write out this | |
409 | structure. */ | |
410 | ||
411 | /* The number of dependences. */ | |
d7b6aee8 NS |
412 | size = deps->deps.size (); |
413 | if (fwrite (&size, sizeof (size), 1, f) != 1) | |
414 | return -1; | |
415 | ||
17211ab5 | 416 | /* The length of each dependence followed by the string. */ |
d7b6aee8 | 417 | for (i = 0; i < deps->deps.size (); i++) |
17211ab5 | 418 | { |
d7b6aee8 NS |
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; | |
17211ab5 GK |
424 | } |
425 | ||
426 | return 0; | |
427 | } | |
428 | ||
429 | /* Read back dependency information written with deps_save into | |
d7b6aee8 | 430 | the deps sizefer. The third argument may be NULL, in which case |
17211ab5 GK |
431 | the dependency information is just skipped, or it may be a filename, |
432 | in which case that filename is skipped. */ | |
433 | ||
434 | int | |
99b1c316 | 435 | deps_restore (class mkdeps *deps, FILE *fd, const char *self) |
17211ab5 | 436 | { |
d7b6aee8 NS |
437 | size_t size; |
438 | char *buf = NULL; | |
439 | size_t buf_size = 0; | |
17211ab5 GK |
440 | |
441 | /* Number of dependences. */ | |
d7b6aee8 | 442 | if (fread (&size, sizeof (size), 1, fd) != 1) |
17211ab5 GK |
443 | return -1; |
444 | ||
445 | /* The length of each dependence string, followed by the string. */ | |
d7b6aee8 | 446 | for (unsigned i = size; i--;) |
17211ab5 GK |
447 | { |
448 | /* Read in # bytes in string. */ | |
d7b6aee8 NS |
449 | if (fread (&size, sizeof (size), 1, fd) != 1) |
450 | return -1; | |
451 | ||
452 | if (size >= buf_size) | |
17211ab5 | 453 | { |
d7b6aee8 | 454 | buf_size = size + 512; |
c3f829c1 | 455 | buf = XRESIZEVEC (char, buf, buf_size); |
17211ab5 | 456 | } |
d7b6aee8 | 457 | if (fread (buf, 1, size, fd) != size) |
55e7f907 | 458 | { |
d7b6aee8 | 459 | XDELETEVEC (buf); |
55e7f907 TB |
460 | return -1; |
461 | } | |
d7b6aee8 | 462 | buf[size] = 0; |
17211ab5 | 463 | |
0c20a65f | 464 | /* Generate makefile dependencies from .pch if -nopch-deps. */ |
4489800d | 465 | if (self != NULL && filename_cmp (buf, self) != 0) |
17211ab5 GK |
466 | deps_add_dep (deps, buf); |
467 | } | |
468 | ||
d7b6aee8 | 469 | XDELETEVEC (buf); |
17211ab5 GK |
470 | return 0; |
471 | } |