1 | /* Process target.def to create initialization macros definition in |
2 | target-hooks-def.h and documentation in target-hooks.texi. |
3 | Copyright (C) 2009-2023 Free Software Foundation, Inc. |
4 | |
5 | This file is part of GCC. |
6 | |
7 | GCC is free software; you can redistribute it and/or modify it under |
8 | the terms of the GNU General Public License as published by the Free |
9 | Software Foundation; either version 3, or (at your option) any later |
10 | version. |
11 | |
12 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or |
14 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
15 | for more details. |
16 | |
17 | You should have received a copy of the GNU General Public License |
18 | along with GCC; see the file COPYING3. If not see |
19 | <http://www.gnu.org/licenses/>. */ |
20 | #include "bconfig.h" |
21 | #include "system.h" |
22 | #include "errors.h" |
23 | |
24 | struct hook_desc { const char *doc, *type, *name, *param, *init, *docname; }; |
25 | static struct hook_desc hook_array[] = { |
26 | #define HOOK_VECTOR_1(NAME, FRAGMENT) \ |
27 | { 0, 0, #NAME, 0, 0, HOOK_TYPE }, |
28 | #define DEFHOOKPOD(NAME, DOC, TYPE, INIT) \ |
29 | { DOC, #TYPE, HOOK_PREFIX #NAME, 0, #INIT, HOOK_TYPE }, |
30 | #define DEFHOOK(NAME, DOC, TYPE, PARAMS, INIT) \ |
31 | { DOC, #TYPE, HOOK_PREFIX #NAME, #PARAMS, #INIT, HOOK_TYPE }, |
32 | #define DEFHOOK_UNDOC(NAME, DOC, TYPE, PARAMS, INIT) \ |
33 | { "*", #TYPE, HOOK_PREFIX #NAME, #PARAMS, #INIT, HOOK_TYPE }, |
34 | #include "target.def" |
35 | #include "c-family/c-target.def" |
36 | #include "common/common-target.def" |
37 | #include "d/d-target.def" |
38 | #include "rust/rust-target.def" |
39 | #undef DEFHOOK |
40 | }; |
41 | |
42 | /* Return an upper-case copy of IN. */ |
43 | static char * |
44 | upstrdup (const char *in) |
45 | { |
46 | char *p, *ret = xstrdup (in); |
47 | for (p = ret; *p; p++) |
48 | *p = TOUPPER (*p); |
49 | return ret; |
50 | } |
51 | |
52 | /* Struct for 'start hooks' which start a sequence of consecutive hooks |
53 | that are defined in target.def and to be documented in tm.texi. */ |
54 | struct s_hook |
55 | { |
56 | char *name; |
57 | int pos; |
58 | }; |
59 | |
60 | static hashval_t |
61 | s_hook_hash (const void *p) |
62 | { |
63 | const struct s_hook *s_hook = (const struct s_hook *)p; |
64 | return htab_hash_string (s_hook->name); |
65 | } |
66 | |
67 | static int |
68 | s_hook_eq_p (const void *p1, const void *p2) |
69 | { |
70 | return (strcmp (s1: ((const struct s_hook *) p1)->name, |
71 | s2: ((const struct s_hook *) p2)->name) == 0); |
72 | } |
73 | |
74 | /* Read the documentation file with name IN_FNAME, perform substitutions |
75 | to incorporate information from hook_array, and emit the result on stdout. |
76 | Hooks defined with DEFHOOK / DEFHOOKPOD are emitted at the place of a |
77 | matching @hook in the input file; if there is no matching @hook, the |
78 | hook is emitted after the hook that precedes it in target.def . |
79 | Usually, the emitted hook documentation starts with the hook |
80 | signature, followed by the string from the doc field. |
81 | The documentation is bracketed in @deftypefn / @deftypevr and a matching |
82 | @end. |
83 | While emitting the doc field, an @findex entry is added |
84 | to the affected paragraph. |
85 | If the doc field starts with '*', the leading '*' is stripped, and the doc |
86 | field is otherwise emitted unaltered; no function signature/ |
87 | @deftypefn/deftypevr/@end is emitted. |
88 | In particular, a doc field of "*" means not to emit any ocumentation for |
89 | this target.def / hook_array entry at all (there might be documentation |
90 | for this hook in the file named IN_FNAME, though). |
91 | A doc field of 0 is used to append the hook signature after the previous |
92 | hook's signture, so that one description can be used for a group of hooks. |
93 | When the doc field is "", @deftypefn/@deftypevr and the hook signature |
94 | is emitted, but not the matching @end. This allows all the free-form |
95 | documentation to be placed in IN_FNAME, to work around GPL/GFDL |
96 | licensing incompatibility issues. */ |
97 | static void |
98 | emit_documentation (const char *in_fname) |
99 | { |
100 | int i, j; |
101 | char buf[1000]; |
102 | htab_t start_hooks = htab_create (99, s_hook_hash, s_hook_eq_p, (htab_del) 0); |
103 | FILE *f; |
104 | |
105 | /* Enter all the start hooks in start_hooks. */ |
106 | f = fopen (in_fname, "r" ); |
107 | if (!f) |
108 | { |
109 | perror (s: "" ); |
110 | fatal ("Couldn't open input file" ); |
111 | } |
112 | while (fscanf (stream: f, format: "%*[^@]" ), buf[0] = '\0', |
113 | fscanf (stream: f, format: "@%5[^ \n]" , buf) != EOF) |
114 | { |
115 | void **p; |
116 | struct s_hook *shp; |
117 | |
118 | if (strcmp (s1: buf, s2: "hook" ) != 0) |
119 | continue; |
120 | buf[0] = '\0'; |
121 | fscanf (stream: f, format: "%999s" , buf); |
122 | shp = XNEW (struct s_hook); |
123 | shp->name = upstrdup (in: buf); |
124 | shp->pos = -1; |
125 | p = htab_find_slot (start_hooks, shp, INSERT); |
126 | if (*p != HTAB_EMPTY_ENTRY) |
127 | fatal ("Duplicate placement for hook %s\n" , shp->name); |
128 | *(struct s_hook **) p = shp; |
129 | } |
130 | fclose (stream: f); |
131 | /* For each hook in hook_array, if it is a start hook, store its position. */ |
132 | for (i = 0; i < (int) (ARRAY_SIZE (hook_array)); i++) |
133 | { |
134 | struct s_hook sh, *shp; |
135 | void *p; |
136 | |
137 | if (!hook_array[i].doc || strcmp (s1: hook_array[i].doc, s2: "*" ) == 0) |
138 | continue; |
139 | sh.name = upstrdup (in: hook_array[i].name); |
140 | p = htab_find (start_hooks, &sh); |
141 | if (p) |
142 | { |
143 | shp = (struct s_hook *) p; |
144 | if (shp->pos >= 0) |
145 | fatal ("Duplicate hook %s\n" , sh.name); |
146 | shp->pos = i; |
147 | } |
148 | else |
149 | fatal ("No place specified to document hook %s\n" , sh.name); |
150 | free (ptr: sh.name); |
151 | } |
152 | /* Copy input file to stdout, substituting @hook directives with the |
153 | corresponding hook documentation sequences. */ |
154 | f = fopen (in_fname, "r" ); |
155 | if (!f) |
156 | { |
157 | perror (s: "" ); |
158 | fatal ("Couldn't open input file" ); |
159 | } |
160 | for (;;) |
161 | { |
162 | struct s_hook sh, *shp; |
163 | int c = getc (f); |
164 | char *name; |
165 | |
166 | if (c == EOF) |
167 | break; |
168 | if (c != '@') |
169 | { |
170 | putchar (c); |
171 | continue; |
172 | } |
173 | buf[0] = '\0'; |
174 | fscanf (stream: f, format: "%5[^ \n]" , buf); |
175 | if (strcmp (s1: buf, s2: "hook" ) != 0) |
176 | { |
177 | printf (format: "@%s" , buf); |
178 | continue; |
179 | } |
180 | fscanf (stream: f, format: "%999s" , buf); |
181 | sh.name = name = upstrdup (in: buf); |
182 | shp = (struct s_hook *) htab_find (start_hooks, &sh); |
183 | if (!shp || shp->pos < 0) |
184 | fatal ("No documentation for hook %s\n" , sh.name); |
185 | i = shp->pos; |
186 | do |
187 | { |
188 | const char *q, *e; |
189 | const char *deftype; |
190 | const char *doc, *p_end; |
191 | |
192 | /* A leading '*' means to output the documentation string without |
193 | further processing. */ |
194 | if (*hook_array[i].doc == '*') |
195 | printf (format: "%s" , hook_array[i].doc + 1); |
196 | else |
197 | { |
198 | if (i != shp->pos) |
199 | printf (format: "\n\n" ); |
200 | |
201 | /* Print header. Function-valued hooks have a parameter list, |
202 | unlike POD-valued ones. */ |
203 | deftype = hook_array[i].param ? "deftypefn" : "deftypevr" ; |
204 | printf (format: "@%s {%s} " , deftype, hook_array[i].docname); |
205 | if (strchr (s: hook_array[i].type, c: ' ')) |
206 | printf (format: "{%s}" , hook_array[i].type); |
207 | else |
208 | printf (format: "%s" , hook_array[i].type); |
209 | printf (format: " %s" , name); |
210 | if (hook_array[i].param) |
211 | { |
212 | /* Print the parameter list, with the parameter names |
213 | enclosed in @var{}. */ |
214 | printf (format: " " ); |
215 | for (q = hook_array[i].param; (e = strpbrk (s: q, accept: " *,)" )); |
216 | q = e + 1) |
217 | /* Type names like 'int' are followed by a space, sometimes |
218 | also by '*'. 'void' should appear only in "(void)". */ |
219 | if (*e == ' ' || *e == '*' || *q == '(') |
220 | printf (format: "%.*s" , (int) (e - q + 1), q); |
221 | else |
222 | printf (format: "@var{%.*s}%c" , (int) (e - q), q, *e); |
223 | } |
224 | /* POD-valued hooks sometimes come in groups with common |
225 | documentation.*/ |
226 | for (j = i + 1; |
227 | j < (int) (ARRAY_SIZE (hook_array)) |
228 | && hook_array[j].doc == 0 && hook_array[j].type; j++) |
229 | { |
230 | char *namex = upstrdup (in: hook_array[j].name); |
231 | |
232 | printf (format: "\n@%sx {%s} {%s} %s" , |
233 | deftype, hook_array[j].docname, |
234 | hook_array[j].type, namex); |
235 | } |
236 | if (hook_array[i].doc[0]) |
237 | { |
238 | printf (format: "\n" ); |
239 | /* Print each documentation paragraph in turn. */ |
240 | for (doc = hook_array[i].doc; *doc; doc = p_end) |
241 | { |
242 | /* Find paragraph end. */ |
243 | p_end = strstr (haystack: doc, needle: "\n\n" ); |
244 | p_end = (p_end ? p_end + 2 : doc + strlen (s: doc)); |
245 | printf (format: "%.*s" , (int) (p_end - doc), doc); |
246 | } |
247 | printf (format: "\n@end %s" , deftype); |
248 | } |
249 | } |
250 | if (++i >= (int) (ARRAY_SIZE (hook_array)) || !hook_array[i].doc) |
251 | break; |
252 | free (ptr: name); |
253 | sh.name = name = upstrdup (in: hook_array[i].name); |
254 | } |
255 | while (!htab_find (start_hooks, &sh)); |
256 | free (ptr: name); |
257 | } |
258 | } |
259 | |
260 | /* Emit #defines to stdout (this will be redirected to generate |
261 | target-hook-def.h) which set target hooks initializer macros |
262 | to their default values. These should only be emitted for hooks |
263 | whose type is given by DOCNAME. */ |
264 | static void |
265 | emit_init_macros (const char *docname) |
266 | { |
267 | int i; |
268 | const int MAX_NEST = 2; |
269 | int print_nest, nest = 0; |
270 | |
271 | for (print_nest = 0; print_nest <= MAX_NEST; print_nest++) |
272 | { |
273 | for (i = 0; i < (int) (ARRAY_SIZE (hook_array)); i++) |
274 | { |
275 | char *name = upstrdup (in: hook_array[i].name); |
276 | |
277 | if (strcmp (s1: hook_array[i].docname, s2: docname) != 0) |
278 | continue; |
279 | |
280 | if (!hook_array[i].type) |
281 | { |
282 | if (*name) |
283 | { |
284 | if (nest && nest == print_nest) |
285 | printf (format: " %s, \\\n" , name); |
286 | nest++; |
287 | if (nest > MAX_NEST) |
288 | fatal ("Unexpected nesting of %s\n" , name); |
289 | if (nest == print_nest) |
290 | printf (format: "\n#define %s \\\n { \\\n" , name); |
291 | } |
292 | else |
293 | { |
294 | if (nest == print_nest) |
295 | printf (format: " }\n" ); |
296 | nest--; |
297 | } |
298 | continue; |
299 | } |
300 | if (print_nest == 0) |
301 | { |
302 | /* Output default definitions of target hooks. */ |
303 | printf (format: "#ifndef %s\n#define %s %s\n#endif\n" , |
304 | name, name, hook_array[i].init); |
305 | } |
306 | if (nest == print_nest) |
307 | printf (format: " %s, \\\n" , name); |
308 | } |
309 | } |
310 | } |
311 | |
312 | int |
313 | main (int argc, char **argv) |
314 | { |
315 | progname = "genhooks" ; |
316 | |
317 | if (argc >= 3) |
318 | emit_documentation (in_fname: argv[2]); |
319 | else |
320 | emit_init_macros (docname: argv[1]); |
321 | return 0; |
322 | } |
323 | |