struct / union in initializer, RFE #901.
[sdcc.git] / sdcc / support / cpp / libcpp / mkdeps.cc
blob57efbd76814724ea19ea5599f7ea75c7af5eb5e8
1 /* Dependency generator for Makefile fragments.
2 Copyright (C) 2000-2022 Free Software Foundation, Inc.
3 Contributed by Zack Weinberg, Mar 2000
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.
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.
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/>.
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! */
24 #include "config.h"
25 #include "system.h"
26 #include "mkdeps.h"
27 #include "internal.h"
29 #define untested() { fprintf (stderr, "@@#\n@@@:%s:%d:%s\n", __FILE__, __LINE__, __func__); }
31 /* Not set up to just include std::vector et al, here's a simple
32 implementation. */
34 /* Keep this structure local to this file, so clients don't find it
35 easy to start making assumptions. */
36 class mkdeps
38 public:
39 /* T has trivial cctor & dtor. */
40 template <typename T>
41 class vec
43 private:
44 T *ary;
45 unsigned num;
46 unsigned alloc;
48 public:
49 vec ()
50 : ary (NULL), num (0), alloc (0)
52 ~vec ()
54 XDELETEVEC (ary);
57 public:
58 unsigned size () const
60 return num;
62 const T &operator[] (unsigned ix) const
64 return ary[ix];
66 T &operator[] (unsigned ix)
68 return ary[ix];
70 void push (const T &elt)
72 if (num == alloc)
74 alloc = alloc ? alloc * 2 : 16;
75 ary = XRESIZEVEC (T, ary, alloc);
77 ary[num++] = elt;
80 struct velt
82 const char *str;
83 size_t len;
86 mkdeps ()
87 : module_name (NULL), cmi_name (NULL), is_header_unit (false), quote_lwm (0)
90 ~mkdeps ()
92 unsigned int i;
94 for (i = targets.size (); i--;)
95 free (const_cast <char *> (targets[i]));
96 for (i = deps.size (); i--;)
97 free (const_cast <char *> (deps[i]));
98 for (i = vpath.size (); i--;)
99 XDELETEVEC (vpath[i].str);
100 for (i = modules.size (); i--;)
101 XDELETEVEC (modules[i]);
102 XDELETEVEC (module_name);
103 free (const_cast <char *> (cmi_name));
106 public:
107 vec<const char *> targets;
108 vec<const char *> deps;
109 vec<velt> vpath;
110 vec<const char *> modules;
112 public:
113 const char *module_name;
114 const char *cmi_name;
115 bool is_header_unit;
116 unsigned short quote_lwm;
119 /* Apply Make quoting to STR, TRAIL. Note that it's not possible to
120 quote all such characters - e.g. \n, %, *, ?, [, \ (in some
121 contexts), and ~ are not properly handled. It isn't possible to
122 get this right in any current version of Make. (??? Still true?
123 Old comment referred to 3.76.1.) */
125 static const char *
126 munge (const char *str, const char *trail = nullptr)
128 static unsigned alloc;
129 static char *buf;
130 unsigned dst = 0;
132 for (; str; str = trail, trail = nullptr)
134 unsigned slashes = 0;
135 char c;
136 for (const char *probe = str; (c = *probe++);)
138 if (alloc < dst + 4 + slashes)
140 alloc = alloc * 2 + 32;
141 buf = XRESIZEVEC (char, buf, alloc);
144 switch (c)
146 case '\\':
147 slashes++;
148 break;
150 case '$':
151 buf[dst++] = '$';
152 goto def;
154 case ' ':
155 case '\t':
156 /* GNU make uses a weird quoting scheme for white space.
157 A space or tab preceded by 2N+1 backslashes
158 represents N backslashes followed by space; a space
159 or tab preceded by 2N backslashes represents N
160 backslashes at the end of a file name; and
161 backslashes in other contexts should not be
162 doubled. */
163 while (slashes--)
164 buf[dst++] = '\\';
165 /* FALLTHROUGH */
167 case '#':
168 buf[dst++] = '\\';
169 /* FALLTHROUGH */
171 default:
172 def:
173 slashes = 0;
174 break;
177 buf[dst++] = c;
181 buf[dst] = 0;
182 return buf;
185 /* If T begins with any of the partial pathnames listed in d->vpathv,
186 then advance T to point beyond that pathname. */
187 static const char *
188 apply_vpath (class mkdeps *d, const char *t)
190 if (unsigned len = d->vpath.size ())
191 for (unsigned i = len; i--;)
193 if (!filename_ncmp (d->vpath[i].str, t, d->vpath[i].len))
195 const char *p = t + d->vpath[i].len;
196 if (!IS_DIR_SEPARATOR (*p))
197 goto not_this_one;
199 /* Do not simplify $(vpath)/../whatever. ??? Might not
200 be necessary. */
201 if (p[1] == '.' && p[2] == '.' && IS_DIR_SEPARATOR (p[3]))
202 goto not_this_one;
204 /* found a match */
205 t = t + d->vpath[i].len + 1;
206 break;
208 not_this_one:;
211 /* Remove leading ./ in any case. */
212 while (t[0] == '.' && IS_DIR_SEPARATOR (t[1]))
214 t += 2;
215 /* If we removed a leading ./, then also remove any /s after the
216 first. */
217 while (IS_DIR_SEPARATOR (t[0]))
218 ++t;
221 return t;
224 /* Public routines. */
226 class mkdeps *
227 deps_init (void)
229 return new mkdeps ();
232 void
233 deps_free (class mkdeps *d)
235 delete d;
238 /* Adds a target T. We make a copy, so it need not be a permanent
239 string. QUOTE is true if the string should be quoted. */
240 void
241 deps_add_target (class mkdeps *d, const char *t, int quote)
243 t = xstrdup (apply_vpath (d, t));
245 if (!quote)
247 /* Sometimes unquoted items are added after quoted ones.
248 Swap out the lowest quoted. */
249 if (d->quote_lwm != d->targets.size ())
251 const char *lowest = d->targets[d->quote_lwm];
252 d->targets[d->quote_lwm] = t;
253 t = lowest;
255 d->quote_lwm++;
258 d->targets.push (t);
261 /* Sets the default target if none has been given already. An empty
262 string as the default target in interpreted as stdin. The string
263 is quoted for MAKE. */
264 void
265 deps_add_default_target (const class cpp_reader *pfile, class mkdeps *d, const char *tgt)
267 /* Only if we have no targets. */
268 if (d->targets.size ())
269 return;
271 if (tgt[0] == '\0')
272 { untested();
273 d->targets.push (xstrdup ("-"));
275 else
277 #ifndef TARGET_OBJECT_SUFFIX
278 # define TARGET_OBJECT_SUFFIX ".o"
279 #endif
280 const char *start = lbasename (tgt);
282 char *suffix;
283 const char *obj_ext;
285 if (NULL == CPP_OPTION (pfile, obj_ext))
287 obj_ext = TARGET_OBJECT_SUFFIX;
289 else if (CPP_OPTION (pfile, obj_ext)[0] != '.')
290 { untested();
291 char *t = (char*) alloca (strlen (CPP_OPTION (pfile, obj_ext)) + 2);
292 t[0] = '.';
293 strcpy (&t[1], CPP_OPTION (pfile, obj_ext));
294 obj_ext = t;
296 else
298 obj_ext = CPP_OPTION (pfile, obj_ext);
301 // if obj_ext != ".o":
302 // fprintf(stderr, "custom objext %s, deprecate this?\n", obj_ext);
304 char *o = (char *) alloca (strlen (start) + strlen (obj_ext) + 1);
306 strcpy (o, start);
308 suffix = strrchr (o, '.');
309 if (!suffix){ untested();
310 suffix = o + strlen (o);
311 }else{
313 strcpy (suffix, obj_ext);
315 deps_add_target (d, o, 1);
319 void
320 deps_add_dep (class mkdeps *d, const char *t)
322 gcc_assert (*t);
324 t = apply_vpath (d, t);
326 d->deps.push (xstrdup (t));
329 void
330 deps_add_vpath (class mkdeps *d, const char *vpath)
332 const char *elem, *p;
334 for (elem = vpath; *elem; elem = p)
336 for (p = elem; *p && *p != ':'; p++)
337 continue;
338 mkdeps::velt elt;
339 elt.len = p - elem;
340 char *str = XNEWVEC (char, elt.len + 1);
341 elt.str = str;
342 memcpy (str, elem, elt.len);
343 str[elt.len] = '\0';
344 if (*p == ':')
345 p++;
347 d->vpath.push (elt);
351 /* Add a new module target (there can only be one). M is the module
352 name. */
354 void
355 deps_add_module_target (struct mkdeps *d, const char *m,
356 const char *cmi, bool is_header_unit)
358 gcc_assert (!d->module_name);
360 d->module_name = xstrdup (m);
361 d->is_header_unit = is_header_unit;
362 d->cmi_name = xstrdup (cmi);
365 /* Add a new module dependency. M is the module name. */
367 void
368 deps_add_module_dep (struct mkdeps *d, const char *m)
370 d->modules.push (xstrdup (m));
373 /* Write NAME, with a leading space to FP, a Makefile. Advance COL as
374 appropriate, wrap at COLMAX, returning new column number. Iff
375 QUOTE apply quoting. Append TRAIL. */
377 static unsigned
378 make_write_name (const char *name, FILE *fp, unsigned col, unsigned colmax,
379 bool quote = true, const char *trail = NULL)
381 if (quote)
382 name = munge (name, trail);
383 unsigned size = strlen (name);
385 if (col)
387 if (colmax && col + size> colmax)
389 fputs (" \\\n", fp);
390 col = 0;
392 col++;
393 fputs (" ", fp);
396 col += size;
397 fputs (name, fp);
399 return col;
402 /* Write all the names in VEC via make_write_name. */
404 static unsigned
405 make_write_vec (const mkdeps::vec<const char *> &vec, FILE *fp,
406 unsigned col, unsigned colmax, unsigned quote_lwm = 0,
407 const char *trail = NULL)
409 for (unsigned ix = 0; ix != vec.size (); ix++)
410 col = make_write_name (vec[ix], fp, col, colmax, ix >= quote_lwm, trail);
411 return col;
414 /* Write the dependencies to a Makefile. If PHONY is true, add
415 .PHONY targets for all the dependencies too. */
417 static void
418 make_write (const cpp_reader *pfile, FILE *fp, unsigned int colmax)
420 const mkdeps *d = pfile->deps;
422 unsigned column = 0;
423 if (colmax && colmax < 34)
424 colmax = 34;
426 if (d->deps.size ())
428 column = make_write_vec (d->targets, fp, 0, colmax, d->quote_lwm);
429 if (CPP_OPTION (pfile, deps.modules) && d->cmi_name)
430 column = make_write_name (d->cmi_name, fp, column, colmax);
431 fputs (":", fp);
432 column++;
433 make_write_vec (d->deps, fp, column, colmax);
434 fputs ("\n", fp);
435 if (CPP_OPTION (pfile, deps.phony_targets))
436 for (unsigned i = 1; i < d->deps.size (); i++)
437 fprintf (fp, "%s:\n", munge (d->deps[i]));
440 if (!CPP_OPTION (pfile, deps.modules))
441 return;
443 if (d->modules.size ())
445 column = make_write_vec (d->targets, fp, 0, colmax, d->quote_lwm);
446 if (d->cmi_name)
447 column = make_write_name (d->cmi_name, fp, column, colmax);
448 fputs (":", fp);
449 column++;
450 column = make_write_vec (d->modules, fp, column, colmax, 0, ".c++m");
451 fputs ("\n", fp);
454 if (d->module_name)
456 if (d->cmi_name)
458 /* module-name : cmi-name */
459 column = make_write_name (d->module_name, fp, 0, colmax,
460 true, ".c++m");
461 fputs (":", fp);
462 column++;
463 column = make_write_name (d->cmi_name, fp, column, colmax);
464 fputs ("\n", fp);
466 column = fprintf (fp, ".PHONY:");
467 column = make_write_name (d->module_name, fp, column, colmax,
468 true, ".c++m");
469 fputs ("\n", fp);
472 if (d->cmi_name && !d->is_header_unit)
474 /* An order-only dependency.
475 cmi-name :| first-target
476 We can probably drop this this in favour of Make-4.3's grouped
477 targets '&:' */
478 column = make_write_name (d->cmi_name, fp, 0, colmax);
479 fputs (":|", fp);
480 column++;
481 column = make_write_name (d->targets[0], fp, column, colmax);
482 fputs ("\n", fp);
486 if (d->modules.size ())
488 column = fprintf (fp, "CXX_IMPORTS +=");
489 make_write_vec (d->modules, fp, column, colmax, 0, ".c++m");
490 fputs ("\n", fp);
494 /* Write out dependencies according to the selected format (which is
495 only Make at the moment). */
496 /* Really we should be opening fp here. */
498 void
499 deps_write (const cpp_reader *pfile, FILE *fp, unsigned int colmax)
501 make_write (pfile, fp, colmax);
504 /* Write out a deps buffer to a file, in a form that can be read back
505 with deps_restore. Returns nonzero on error, in which case the
506 error number will be in errno. */
509 deps_save (class mkdeps *deps, FILE *f)
511 unsigned int i;
512 size_t size;
514 /* The cppreader structure contains makefile dependences. Write out this
515 structure. */
517 /* The number of dependences. */
518 size = deps->deps.size ();
519 if (fwrite (&size, sizeof (size), 1, f) != 1)
520 return -1;
522 /* The length of each dependence followed by the string. */
523 for (i = 0; i < deps->deps.size (); i++)
525 size = strlen (deps->deps[i]);
526 if (fwrite (&size, sizeof (size), 1, f) != 1)
527 return -1;
528 if (fwrite (deps->deps[i], size, 1, f) != 1)
529 return -1;
532 return 0;
535 /* Read back dependency information written with deps_save into
536 the deps sizefer. The third argument may be NULL, in which case
537 the dependency information is just skipped, or it may be a filename,
538 in which case that filename is skipped. */
541 deps_restore (class mkdeps *deps, FILE *fd, const char *self)
543 size_t size;
544 char *buf = NULL;
545 size_t buf_size = 0;
547 /* Number of dependences. */
548 if (fread (&size, sizeof (size), 1, fd) != 1)
549 return -1;
551 /* The length of each dependence string, followed by the string. */
552 for (unsigned i = size; i--;)
554 /* Read in # bytes in string. */
555 if (fread (&size, sizeof (size), 1, fd) != 1)
556 return -1;
558 if (size >= buf_size)
560 buf_size = size + 512;
561 buf = XRESIZEVEC (char, buf, buf_size);
563 if (fread (buf, 1, size, fd) != size)
565 XDELETEVEC (buf);
566 return -1;
568 buf[size] = 0;
570 /* Generate makefile dependencies from .pch if -nopch-deps. */
571 if (self != NULL && filename_cmp (buf, self) != 0)
572 deps_add_dep (deps, buf);
575 XDELETEVEC (buf);
576 return 0;