tree-optimization/115599 - reassoc qsort comparator issue
[official-gcc.git] / libcpp / mkdeps.cc
blob622d0dc2ef8ab06ba0ce77048c54f2ada166345b
1 /* Dependency generator for Makefile fragments.
2 Copyright (C) 2000-2024 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! */
23 #include "config.h"
24 #include "system.h"
25 #include "mkdeps.h"
26 #include "internal.h"
28 /* Not set up to just include std::vector et al, here's a simple
29 implementation. */
31 /* Keep this structure local to this file, so clients don't find it
32 easy to start making assumptions. */
33 class mkdeps
35 public:
36 /* T has trivial cctor & dtor. */
37 template <typename T>
38 class vec
40 private:
41 T *ary;
42 unsigned num;
43 unsigned alloc;
45 public:
46 vec ()
47 : ary (NULL), num (0), alloc (0)
49 ~vec ()
51 XDELETEVEC (ary);
54 public:
55 unsigned size () const
57 return num;
59 const T &operator[] (unsigned ix) const
61 return ary[ix];
63 T &operator[] (unsigned ix)
65 return ary[ix];
67 void push (const T &elt)
69 if (num == alloc)
71 alloc = alloc ? alloc * 2 : 16;
72 ary = XRESIZEVEC (T, ary, alloc);
74 ary[num++] = elt;
77 struct velt
79 const char *str;
80 size_t len;
83 mkdeps ()
84 : primary_output (NULL), module_name (NULL), cmi_name (NULL)
85 , is_header_unit (false), is_exported (false), quote_lwm (0)
88 ~mkdeps ()
90 unsigned int i;
92 for (i = targets.size (); i--;)
93 free (const_cast <char *> (targets[i]));
94 free (const_cast <char *> (primary_output));
95 for (i = fdeps_targets.size (); i--;)
96 free (const_cast <char *> (fdeps_targets[i]));
97 for (i = deps.size (); i--;)
98 free (const_cast <char *> (deps[i]));
99 for (i = vpath.size (); i--;)
100 XDELETEVEC (vpath[i].str);
101 for (i = modules.size (); i--;)
102 XDELETEVEC (modules[i]);
103 XDELETEVEC (module_name);
104 free (const_cast <char *> (cmi_name));
107 public:
108 vec<const char *> targets;
109 vec<const char *> deps;
110 const char * primary_output;
111 vec<const char *> fdeps_targets;
112 vec<velt> vpath;
113 vec<const char *> modules;
115 public:
116 const char *module_name;
117 const char *cmi_name;
118 bool is_header_unit;
119 bool is_exported;
120 unsigned short quote_lwm;
123 /* Apply Make quoting to STR, TRAIL. Note that it's not possible to
124 quote all such characters - e.g. \n, %, *, ?, [, \ (in some
125 contexts), and ~ are not properly handled. It isn't possible to
126 get this right in any current version of Make. (??? Still true?
127 Old comment referred to 3.76.1.) */
129 static const char *
130 munge (const char *str, const char *trail = nullptr)
132 static unsigned alloc;
133 static char *buf;
134 unsigned dst = 0;
136 for (; str; str = trail, trail = nullptr)
138 unsigned slashes = 0;
139 char c;
140 for (const char *probe = str; (c = *probe++);)
142 if (alloc < dst + 4 + slashes)
144 alloc = alloc * 2 + 32;
145 buf = XRESIZEVEC (char, buf, alloc);
148 switch (c)
150 case '\\':
151 slashes++;
152 break;
154 case '$':
155 buf[dst++] = '$';
156 goto def;
158 case ' ':
159 case '\t':
160 /* GNU make uses a weird quoting scheme for white space.
161 A space or tab preceded by 2N+1 backslashes
162 represents N backslashes followed by space; a space
163 or tab preceded by 2N backslashes represents N
164 backslashes at the end of a file name; and
165 backslashes in other contexts should not be
166 doubled. */
167 while (slashes--)
168 buf[dst++] = '\\';
169 /* FALLTHROUGH */
171 case '#':
172 buf[dst++] = '\\';
173 /* FALLTHROUGH */
175 default:
176 def:
177 slashes = 0;
178 break;
181 buf[dst++] = c;
185 buf[dst] = 0;
186 return buf;
189 /* If T begins with any of the partial pathnames listed in d->vpathv,
190 then advance T to point beyond that pathname. */
191 static const char *
192 apply_vpath (class mkdeps *d, const char *t)
194 if (unsigned len = d->vpath.size ())
195 for (unsigned i = len; i--;)
197 if (!filename_ncmp (d->vpath[i].str, t, d->vpath[i].len))
199 const char *p = t + d->vpath[i].len;
200 if (!IS_DIR_SEPARATOR (*p))
201 goto not_this_one;
203 /* Do not simplify $(vpath)/../whatever. ??? Might not
204 be necessary. */
205 if (p[1] == '.' && p[2] == '.' && IS_DIR_SEPARATOR (p[3]))
206 goto not_this_one;
208 /* found a match */
209 t = t + d->vpath[i].len + 1;
210 break;
212 not_this_one:;
215 /* Remove leading ./ in any case. */
216 while (t[0] == '.' && IS_DIR_SEPARATOR (t[1]))
218 t += 2;
219 /* If we removed a leading ./, then also remove any /s after the
220 first. */
221 while (IS_DIR_SEPARATOR (t[0]))
222 ++t;
225 return t;
228 /* Public routines. */
230 class mkdeps *
231 deps_init (void)
233 return new mkdeps ();
236 void
237 deps_free (class mkdeps *d)
239 delete d;
242 /* Adds a target T. We make a copy, so it need not be a permanent
243 string. QUOTE is true if the string should be quoted. */
244 void
245 deps_add_target (class mkdeps *d, const char *t, int quote)
247 t = xstrdup (apply_vpath (d, t));
249 if (!quote)
251 /* Sometimes unquoted items are added after quoted ones.
252 Swap out the lowest quoted. */
253 if (d->quote_lwm != d->targets.size ())
255 const char *lowest = d->targets[d->quote_lwm];
256 d->targets[d->quote_lwm] = t;
257 t = lowest;
259 d->quote_lwm++;
262 d->targets.push (t);
265 /* Sets the default target if none has been given already. An empty
266 string as the default target in interpreted as stdin. The string
267 is quoted for MAKE. */
268 void
269 deps_add_default_target (class mkdeps *d, const char *tgt)
271 /* Only if we have no targets. */
272 if (d->targets.size ())
273 return;
275 if (tgt[0] == '\0')
276 d->targets.push (xstrdup ("-"));
277 else
279 #ifndef TARGET_OBJECT_SUFFIX
280 # define TARGET_OBJECT_SUFFIX ".o"
281 #endif
282 const char *start = lbasename (tgt);
283 char *o = (char *) alloca (strlen (start)
284 + strlen (TARGET_OBJECT_SUFFIX) + 1);
285 char *suffix;
287 strcpy (o, start);
289 suffix = strrchr (o, '.');
290 if (!suffix)
291 suffix = o + strlen (o);
292 strcpy (suffix, TARGET_OBJECT_SUFFIX);
294 deps_add_target (d, o, 1);
298 /* Adds a target O. We make a copy, so it need not be a permanent
299 string.
301 This is the target associated with the rule that (in a C++ modules build)
302 compiles the source that is being scanned for dynamic dependencies. It is
303 used to associate the structured dependency information with that rule as
304 needed. */
305 void
306 fdeps_add_target (struct mkdeps *d, const char *o, bool is_primary)
308 o = apply_vpath (d, o);
309 if (is_primary)
311 if (d->primary_output)
312 d->fdeps_targets.push (d->primary_output);
313 d->primary_output = xstrdup (o);
314 } else
315 d->fdeps_targets.push (xstrdup (o));
318 void
319 deps_add_dep (class mkdeps *d, const char *t)
321 gcc_assert (*t);
323 t = apply_vpath (d, t);
325 d->deps.push (xstrdup (t));
328 void
329 deps_add_vpath (class mkdeps *d, const char *vpath)
331 const char *elem, *p;
333 for (elem = vpath; *elem; elem = p)
335 for (p = elem; *p && *p != ':'; p++)
336 continue;
337 mkdeps::velt elt;
338 elt.len = p - elem;
339 char *str = XNEWVEC (char, elt.len + 1);
340 elt.str = str;
341 memcpy (str, elem, elt.len);
342 str[elt.len] = '\0';
343 if (*p == ':')
344 p++;
346 d->vpath.push (elt);
350 /* Add a new module target (there can only be one). M is the module
351 name. */
353 void
354 deps_add_module_target (struct mkdeps *d, const char *m,
355 const char *cmi, bool is_header_unit, bool is_exported)
357 gcc_assert (!d->module_name);
359 d->module_name = xstrdup (m);
360 d->is_header_unit = is_header_unit;
361 d->is_exported = is_exported;
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. */
416 static void
417 make_write (const cpp_reader *pfile, FILE *fp, unsigned int colmax)
419 const mkdeps *d = pfile->deps;
421 unsigned column = 0;
422 if (colmax && colmax < 34)
423 colmax = 34;
425 /* Write out C++ modules information if no other `-fdeps-format=`
426 option is given. */
427 cpp_fdeps_format fdeps_format = CPP_OPTION (pfile, deps.fdeps_format);
428 bool write_make_modules_deps = (fdeps_format == FDEPS_FMT_NONE
429 && CPP_OPTION (pfile, deps.modules));
431 if (d->deps.size ())
433 column = make_write_vec (d->targets, fp, 0, colmax, d->quote_lwm);
434 if (write_make_modules_deps && d->cmi_name)
435 column = make_write_name (d->cmi_name, fp, column, colmax);
436 fputs (":", fp);
437 column++;
438 make_write_vec (d->deps, fp, column, colmax);
439 fputs ("\n", fp);
440 if (CPP_OPTION (pfile, deps.phony_targets))
441 for (unsigned i = 1; i < d->deps.size (); i++)
442 fprintf (fp, "%s:\n", munge (d->deps[i]));
445 if (!write_make_modules_deps)
446 return;
448 if (d->modules.size ())
450 column = make_write_vec (d->targets, fp, 0, colmax, d->quote_lwm);
451 if (d->cmi_name)
452 column = make_write_name (d->cmi_name, fp, column, colmax);
453 fputs (":", fp);
454 column++;
455 column = make_write_vec (d->modules, fp, column, colmax, 0, ".c++-module");
456 fputs ("\n", fp);
459 if (d->module_name)
461 if (d->cmi_name)
463 /* module-name : cmi-name */
464 column = make_write_name (d->module_name, fp, 0, colmax,
465 true, ".c++-module");
466 fputs (":", fp);
467 column++;
468 column = make_write_name (d->cmi_name, fp, column, colmax);
469 fputs ("\n", fp);
471 column = fprintf (fp, ".PHONY:");
472 column = make_write_name (d->module_name, fp, column, colmax,
473 true, ".c++-module");
474 fputs ("\n", fp);
477 if (d->cmi_name && !d->is_header_unit)
479 /* An order-only dependency.
480 cmi-name :| first-target
481 We can probably drop this this in favour of Make-4.3's grouped
482 targets '&:' */
483 column = make_write_name (d->cmi_name, fp, 0, colmax);
484 fputs (":|", fp);
485 column++;
486 column = make_write_name (d->targets[0], fp, column, colmax);
487 fputs ("\n", fp);
491 if (d->modules.size ())
493 column = fprintf (fp, "CXX_IMPORTS +=");
494 make_write_vec (d->modules, fp, column, colmax, 0, ".c++-module");
495 fputs ("\n", fp);
499 /* Write out dependencies according to the selected format (which is
500 only Make at the moment). */
501 /* Really we should be opening fp here. */
503 void
504 deps_write (const cpp_reader *pfile, FILE *fp, unsigned int colmax)
506 make_write (pfile, fp, colmax);
509 /* Write out a a filepath for P1689R5 output. */
511 static void
512 p1689r5_write_filepath (const char *name, FILE *fp)
514 if (cpp_valid_utf8_p (name, strlen (name)))
516 fputc ('"', fp);
517 for (const char* c = name; *c; c++)
519 // Escape control characters.
520 if (ISCNTRL (*c))
521 fprintf (fp, "\\u%04x", *c);
522 // JSON escape characters.
523 else if (*c == '"' || *c == '\\')
525 fputc ('\\', fp);
526 fputc (*c, fp);
528 // Everything else.
529 else
530 fputc (*c, fp);
532 fputc ('"', fp);
534 else
536 // TODO: print an error
540 /* Write a JSON array from a `vec` for P1689R5 output.
542 In P1689R5, all array values are filepaths. */
544 static void
545 p1689r5_write_vec (const mkdeps::vec<const char *> &vec, FILE *fp)
547 for (unsigned ix = 0; ix != vec.size (); ix++)
549 p1689r5_write_filepath (vec[ix], fp);
550 if (ix < vec.size () - 1)
551 fputc (',', fp);
552 fputc ('\n', fp);
556 /* Write out the P1689R5 format using the module dependency tracking
557 information gathered while scanning and/or compiling.
559 Ideally this (and the above `p1689r5_` functions) would use `gcc/json.h`,
560 but since this is `libcpp`, we cannot use `gcc/` code.
562 TODO: move `json.h` to libiberty. */
564 void
565 deps_write_p1689r5 (const struct mkdeps *d, FILE *fp)
567 fputs ("{\n", fp);
569 fputs ("\"rules\": [\n", fp);
570 fputs ("{\n", fp);
572 if (d->primary_output)
574 fputs ("\"primary-output\": ", fp);
575 p1689r5_write_filepath (d->primary_output, fp);
576 fputs (",\n", fp);
579 if (d->fdeps_targets.size ())
581 fputs ("\"outputs\": [\n", fp);
582 p1689r5_write_vec (d->fdeps_targets, fp);
583 fputs ("],\n", fp);
586 if (d->module_name)
588 fputs ("\"provides\": [\n", fp);
589 fputs ("{\n", fp);
591 fputs ("\"logical-name\": ", fp);
592 p1689r5_write_filepath (d->module_name, fp);
593 fputs (",\n", fp);
595 fprintf (fp, "\"is-interface\": %s\n", d->is_exported ? "true" : "false");
597 // TODO: header-unit information
599 fputs ("}\n", fp);
600 fputs ("],\n", fp);
603 fputs ("\"requires\": [\n", fp);
604 for (size_t i = 0; i < d->modules.size (); i++)
606 if (i != 0)
607 fputs (",\n", fp);
608 fputs ("{\n", fp);
610 fputs ("\"logical-name\": ", fp);
611 p1689r5_write_filepath (d->modules[i], fp);
612 fputs ("\n", fp);
614 // TODO: header-unit information
616 fputs ("}\n", fp);
618 fputs ("]\n", fp);
620 fputs ("}\n", fp);
622 fputs ("],\n", fp);
624 fputs ("\"version\": 0,\n", fp);
625 fputs ("\"revision\": 0\n", fp);
627 fputs ("}\n", fp);
630 /* Write out a deps buffer to a file, in a form that can be read back
631 with deps_restore. Returns nonzero on error, in which case the
632 error number will be in errno. */
635 deps_save (class mkdeps *deps, FILE *f)
637 unsigned int i;
638 size_t size;
640 /* The cppreader structure contains makefile dependences. Write out this
641 structure. */
643 /* The number of dependences. */
644 size = deps->deps.size ();
645 if (fwrite (&size, sizeof (size), 1, f) != 1)
646 return -1;
648 /* The length of each dependence followed by the string. */
649 for (i = 0; i < deps->deps.size (); i++)
651 size = strlen (deps->deps[i]);
652 if (fwrite (&size, sizeof (size), 1, f) != 1)
653 return -1;
654 if (fwrite (deps->deps[i], size, 1, f) != 1)
655 return -1;
658 return 0;
661 /* Read back dependency information written with deps_save into
662 the deps sizefer. The third argument may be NULL, in which case
663 the dependency information is just skipped, or it may be a filename,
664 in which case that filename is skipped. */
667 deps_restore (class mkdeps *deps, FILE *fd, const char *self)
669 size_t size;
670 char *buf = NULL;
671 size_t buf_size = 0;
673 /* Number of dependences. */
674 if (fread (&size, sizeof (size), 1, fd) != 1)
675 return -1;
677 /* The length of each dependence string, followed by the string. */
678 for (unsigned i = size; i--;)
680 /* Read in # bytes in string. */
681 if (fread (&size, sizeof (size), 1, fd) != 1)
682 return -1;
684 if (size >= buf_size)
686 buf_size = size + 512;
687 buf = XRESIZEVEC (char, buf, buf_size);
689 if (fread (buf, 1, size, fd) != size)
691 XDELETEVEC (buf);
692 return -1;
694 buf[size] = 0;
696 /* Generate makefile dependencies from .pch if -nopch-deps. */
697 if (self != NULL && filename_cmp (buf, self) != 0)
698 deps_add_dep (deps, buf);
701 XDELETEVEC (buf);
702 return 0;