api: split vl_api_prefix into two
[vpp.git] / src / tools / vppapigen / vppapigen_c.py
1 # C generation
2 import datetime
3 import os
4 import time
5 import sys
6 from io import StringIO
7
8 datestring = datetime.datetime.utcfromtimestamp(
9     int(os.environ.get('SOURCE_DATE_EPOCH', time.time())))
10 input_filename = 'inputfil'
11 top_boilerplate = '''\
12 /*
13  * VLIB API definitions {datestring}
14  * Input file: {input_filename}
15  * Automatically generated: please edit the input file NOT this file!
16  */
17
18 #include <stdbool.h>
19 #if defined(vl_msg_id)||defined(vl_union_id) \\
20     || defined(vl_printfun) ||defined(vl_endianfun) \\
21     || defined(vl_api_version)||defined(vl_typedefs) \\
22     || defined(vl_msg_name)||defined(vl_msg_name_crc_list) \\
23     || defined(vl_api_version_tuple)
24 /* ok, something was selected */
25 #else
26 #warning no content included from {input_filename}
27 #endif
28
29 #define VL_API_PACKED(x) x __attribute__ ((packed))
30 '''
31
32 bottom_boilerplate = '''\
33 /****** API CRC (whole file) *****/
34
35 #ifdef vl_api_version
36 vl_api_version({input_filename}, {file_crc:#08x})
37
38 #endif
39 '''
40
41
42 def msg_ids(s):
43     output = '''\
44
45 /****** Message ID / handler enum ******/
46
47 #ifdef vl_msg_id
48 '''
49
50     for t in s['Define']:
51         output += "vl_msg_id(VL_API_%s, vl_api_%s_t_handler)\n" % \
52                   (t.name.upper(), t.name)
53     output += "#endif"
54
55     return output
56
57
58 def msg_names(s):
59     output = '''\
60
61 /****** Message names ******/
62
63 #ifdef vl_msg_name
64 '''
65
66     for t in s['Define']:
67         dont_trace = 0 if t.dont_trace else 1
68         output += "vl_msg_name(vl_api_%s_t, %d)\n" % (t.name, dont_trace)
69     output += "#endif"
70
71     return output
72
73
74 def msg_name_crc_list(s, suffix):
75     output = '''\
76
77 /****** Message name, crc list ******/
78
79 #ifdef vl_msg_name_crc_list
80 '''
81     output += "#define foreach_vl_msg_name_crc_%s " % suffix
82
83     for t in s['Define']:
84         output += "\\\n_(VL_API_%s, %s, %08x) " % \
85                    (t.name.upper(), t.name, t.crc)
86     output += "\n#endif"
87
88     return output
89
90
91 def api2c(fieldtype):
92     mappingtable = {'string': 'vl_api_string_t', }
93     if fieldtype in mappingtable:
94         return mappingtable[fieldtype]
95     return fieldtype
96
97
98 def typedefs(objs, filename):
99     name = filename.replace('.', '_')
100     output = '''\
101
102
103 /****** Typedefs ******/
104
105 #ifdef vl_typedefs
106 #ifndef included_{module}_typedef
107 #define included_{module}_typedef
108 '''
109     output = output.format(module=name)
110
111     for o in objs:
112         tname = o.__class__.__name__
113         if tname == 'Using':
114             if 'length' in o.alias:
115                 output +=  'typedef %s vl_api_%s_t[%s];\n' % (o.alias['type'], o.name, o.alias['length'])
116             else:
117                 output += 'typedef %s vl_api_%s_t;\n' % (o.alias['type'], o.name)
118         elif tname == 'Enum':
119             if o.enumtype == 'u32':
120                 output += "typedef enum {\n"
121             else:
122                 output += "typedef enum __attribute__((__packed__)) {\n"
123
124             for b in o.block:
125                 output += "    %s = %s,\n" % (b[0], b[1])
126             output += '} vl_api_%s_t;\n' % o.name
127             if o.enumtype != 'u32':
128                 size1 = 'sizeof(vl_api_%s_t)' % o.name
129                 size2 = 'sizeof(%s)' % o.enumtype
130                 err_str = 'size of API enum %s is wrong' % o.name
131                 output += ('STATIC_ASSERT(%s == %s, "%s");\n'
132                            % (size1, size2, err_str))
133         else:
134             if tname == 'Union':
135                 output += "typedef VL_API_PACKED(union _vl_api_%s {\n" % o.name
136             else:
137                 output += ("typedef VL_API_PACKED(struct _vl_api_%s {\n"
138                            % o.name)
139             for b in o.block:
140                 if b.type == 'Option':
141                     continue
142                 if b.type == 'Field':
143                     output += "    %s %s;\n" % (api2c(b.fieldtype),
144                                                 b.fieldname)
145                 elif b.type == 'Array':
146                     if b.lengthfield:
147                         output += "    %s %s[0];\n" % (api2c(b.fieldtype),
148                                                        b.fieldname)
149                     else:
150                         # Fixed length strings decay to nul terminated u8
151                         if b.fieldtype == 'string':
152                             if b.modern_vla:
153                                 output += ('    {} {};\n'
154                                            .format(api2c(b.fieldtype),
155                                                    b.fieldname))
156                             else:
157                                 output += ('    u8 {}[{}];\n'
158                                            .format(b.fieldname, b.length))
159                         else:
160                             output += ("    %s %s[%s];\n" %
161                                        (api2c(b.fieldtype), b.fieldname,
162                                         b.length))
163                 else:
164                     raise ValueError("Error in processing type {} for {}"
165                                      .format(b, o.name))
166
167             output += '}) vl_api_%s_t;\n' % o.name
168
169     output += "\n#endif"
170     output += "\n#endif\n\n"
171
172     return output
173
174
175 format_strings = {'u8': '%u',
176                   'bool': '%u',
177                   'i8': '%d',
178                   'u16': '%u',
179                   'i16': '%d',
180                   'u32': '%u',
181                   'i32': '%ld',
182                   'u64': '%llu',
183                   'i64': '%llu',
184                   'f64': '%.2f'}
185
186 noprint_fields = {'_vl_msg_id': None,
187                   'client_index': None,
188                   'context': None}
189
190
191 class Printfun():
192     _dispatch = {}
193
194     def __init__(self, stream):
195         self.stream = stream
196
197     def print_string(self, o, stream):
198         write = stream.write
199         if o.modern_vla:
200             write('    if (vl_api_string_len(&a->{f}) > 0) {{\n'
201                   .format(f=o.fieldname))
202             write('        s = format(s, "\\n%U{f}: %.*s", '
203                   'format_white_space, indent, '
204                   'vl_api_string_len(&a->{f}) - 1, '
205                   'vl_api_from_api_string(&a->{f}));\n'.format(f=o.fieldname))
206             write('    } else {\n')
207             write('        s = format(s, "\\n%U{f}:", '
208                   'format_white_space, indent);\n'.format(f=o.fieldname))
209             write('    }\n')
210         else:
211             write('    s = format(s, "\\n%U{f}: %s", '
212                   'format_white_space, indent, a->{f});\n'
213                   .format(f=o.fieldname))
214
215     def print_field(self, o, stream):
216         write = stream.write
217         if o.fieldname in noprint_fields:
218             return
219         if o.fieldtype in format_strings:
220             f = format_strings[o.fieldtype]
221             write('    s = format(s, "\\n%U{n}: {f}", '
222                   'format_white_space, indent, a->{n});\n'
223                   .format(n=o.fieldname, f=f))
224         else:
225             write('    s = format(s, "\\n%U{n}: %U", '
226                   'format_white_space, indent, '
227                   'format_{t}, &a->{n}, indent);\n'
228                   .format(n=o.fieldname, t=o.fieldtype))
229
230     _dispatch['Field'] = print_field
231
232     def print_array(self, o, stream):
233         write = stream.write
234
235         forloop = '''\
236     for (i = 0; i < {lfield}; i++) {{
237         s = format(s, "\\n%U{n}: %U",
238                    format_white_space, indent, format_{t}, &a->{n}[i], indent);
239     }}
240 '''
241
242         forloop_format = '''\
243     for (i = 0; i < {lfield}; i++) {{
244         s = format(s, "\\n%U{n}: {t}",
245                    format_white_space, indent, a->{n}[i]);
246     }}
247 '''
248
249         if o.fieldtype == 'string':
250             return self.print_string(o, stream)
251
252         if o.fieldtype == 'u8':
253             if o.lengthfield:
254                 write('    s = format(s, "\\n%U{n}: %U", format_white_space, '
255                       'indent, format_hex_bytes, a->{n}, a->{lfield});\n'
256                       .format(n=o.fieldname, lfield=o.lengthfield))
257             else:
258                 write('    s = format(s, "\\n%U{n}: %U", format_white_space, '
259                       'indent, format_hex_bytes, a, {lfield});\n'
260                       .format(n=o.fieldname, lfield=o.length))
261             return
262
263         lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
264         if o.fieldtype in format_strings:
265             write(forloop_format.format(lfield=lfield,
266                                         t=format_strings[o.fieldtype],
267                                         n=o.fieldname))
268         else:
269             write(forloop.format(lfield=lfield, t=o.fieldtype, n=o.fieldname))
270
271     _dispatch['Array'] = print_array
272
273     def print_alias(self, k, v, stream):
274         write = stream.write
275         if ('length' in v.alias and v.alias['length'] and
276                 v.alias['type'] == 'u8'):
277             write('    return format(s, "%U", format_hex_bytes, a, {});\n'
278                   .format(v.alias['length']))
279         elif v.alias['type'] in format_strings:
280             write('    return format(s, "{}", *a);\n'
281                   .format(format_strings[v.alias['type']]))
282         else:
283             write('    return format(s, "{} (print not implemented)");\n'
284                   .format(k))
285
286     def print_enum(self, o, stream):
287         write = stream.write
288         write("    switch(*a) {\n")
289         for b in o:
290             write("    case %s:\n" % b[1])
291             write('        return format(s, "{}");\n'.format(b[0]))
292         write('    }\n')
293
294     _dispatch['Enum'] = print_enum
295
296     def print_obj(self, o, stream):
297         write = stream.write
298
299         if o.type in self._dispatch:
300             self._dispatch[o.type](self, o, stream)
301         else:
302             write('    s = format(s, "\\n{} {} {} (print not implemented");\n'
303                   .format(o.type, o.fieldtype, o.fieldname))
304
305
306 def printfun(objs, stream, modulename):
307     write = stream.write
308
309     h = '''\
310 /****** Print functions *****/
311 #ifdef vl_printfun
312 #ifndef included_{module}_printfun
313 #define included_{module}_printfun
314
315 #ifdef LP64
316 #define _uword_fmt \"%lld\"
317 #define _uword_cast (long long)
318 #else
319 #define _uword_fmt \"%ld\"
320 #define _uword_cast long
321 #endif
322
323 '''
324
325     signature = '''\
326 static inline void *vl_api_{name}_t_print (vl_api_{name}_t *a, void *handle)
327 {{
328     u8 *s = 0;
329     u32 indent __attribute__((unused)) = 2;
330     int i __attribute__((unused));
331 '''
332
333     h = h.format(module=modulename)
334     write(h)
335
336     pp = Printfun(stream)
337     for t in objs:
338         if t.manual_print:
339             write("/***** manual: vl_api_%s_t_print  *****/\n\n" % t.name)
340             continue
341         write(signature.format(name=t.name))
342         write('    /* Message definition: vl_api_{}_t: */\n'.format(t.name))
343         write("    s = format(s, \"vl_api_%s_t:\");\n" % t.name)
344         for o in t.block:
345             pp.print_obj(o, stream)
346         write('    vec_add1(s, 0);\n')
347         write('    vl_print (handle, (char *)s);\n')
348         write('    vec_free (s);\n')
349         write('    return handle;\n')
350         write('}\n\n')
351
352     write("\n#endif")
353     write("\n#endif /* vl_printfun */\n")
354
355     return ''
356
357
358 def printfun_types(objs, stream, modulename):
359     write = stream.write
360     pp = Printfun(stream)
361
362     h = '''\
363 /****** Print functions *****/
364 #ifdef vl_printfun
365 #ifndef included_{module}_printfun_types
366 #define included_{module}_printfun_types
367
368 '''
369     h = h.format(module=modulename)
370     write(h)
371
372     signature = '''\
373 static inline u8 *format_vl_api_{name}_t (u8 *s, va_list * args)
374 {{
375     vl_api_{name}_t *a = va_arg (*args, vl_api_{name}_t *);
376     u32 indent __attribute__((unused)) = va_arg (*args, u32);
377     int i __attribute__((unused));
378     indent += 2;
379 '''
380
381     for t in objs:
382         if t.__class__.__name__ == 'Enum':
383             write(signature.format(name=t.name))
384             pp.print_enum(t.block, stream)
385             write('    return s;\n')
386             write('}\n\n')
387             continue
388
389         if t.manual_print:
390             write("/***** manual: vl_api_%s_t_print  *****/\n\n" % t.name)
391             continue
392
393         if t.__class__.__name__ == 'Using':
394             write(signature.format(name=t.name))
395             pp.print_alias(t.name, t, stream)
396             write('}\n\n')
397             continue
398
399         write(signature.format(name=t.name))
400         for o in t.block:
401             pp.print_obj(o, stream)
402
403         write('    return s;\n')
404         write('}\n\n')
405
406     write("\n#endif")
407     write("\n#endif /* vl_printfun_types */\n")
408
409
410 def imports(imports):
411     output = '/* Imported API files */\n'
412     output += '#ifndef vl_api_version\n'
413
414     for i in imports:
415         s = i.filename.replace('plugins/', '')
416         output += '#include <{}.h>\n'.format(s)
417     output += '#endif\n'
418     return output
419
420
421 endian_strings = {
422     'u16': 'clib_net_to_host_u16',
423     'u32': 'clib_net_to_host_u32',
424     'u64': 'clib_net_to_host_u64',
425     'i16': 'clib_net_to_host_u16',
426     'i32': 'clib_net_to_host_u32',
427     'i64': 'clib_net_to_host_u64',
428     'f64': 'clib_net_to_host_u64',
429 }
430
431
432 def endianfun_array(o):
433     forloop = '''\
434     for (i = 0; i < {length}; i++) {{
435         a->{name}[i] = {format}(a->{name}[i]);
436     }}
437 '''
438
439     forloop_format = '''\
440     for (i = 0; i < {length}; i++) {{
441         {type}_endian(&a->{name}[i]);
442     }}
443 '''
444
445     output = ''
446     if o.fieldtype == 'u8' or o.fieldtype == 'string':
447         output += '    /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
448     else:
449         lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
450         if o.fieldtype in endian_strings:
451             output += (forloop
452                        .format(length=lfield,
453                                format=endian_strings[o.fieldtype],
454                                name=o.fieldname))
455         else:
456             output += (forloop_format
457                        .format(length=lfield, type=o.fieldtype,
458                                name=o.fieldname))
459     return output
460
461
462 def endianfun_obj(o):
463     output = ''
464     if o.type == 'Array':
465         return endianfun_array(o)
466     elif o.type != 'Field':
467         output += ('    s = format(s, "\\n{} {} {} (print not implemented");\n'
468                    .format(o.type, o.fieldtype, o.fieldname))
469         return output
470     if o.fieldtype in endian_strings:
471         output += ('    a->{name} = {format}(a->{name});\n'
472                    .format(name=o.fieldname,
473                            format=endian_strings[o.fieldtype]))
474     elif o.fieldtype.startswith('vl_api_'):
475         output += ('    {type}_endian(&a->{name});\n'
476                    .format(type=o.fieldtype, name=o.fieldname))
477     else:
478         output += '    /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
479
480     return output
481
482
483 def endianfun(objs, modulename):
484     output = '''\
485
486 /****** Endian swap functions *****/\n\
487 #ifdef vl_endianfun
488 #ifndef included_{module}_endianfun
489 #define included_{module}_endianfun
490
491 #undef clib_net_to_host_uword
492 #ifdef LP64
493 #define clib_net_to_host_uword clib_net_to_host_u64
494 #else
495 #define clib_net_to_host_uword clib_net_to_host_u32
496 #endif
497
498 '''
499     output = output.format(module=modulename)
500
501     signature = '''\
502 static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
503 {{
504     int i __attribute__((unused));
505 '''
506
507     for t in objs:
508         if t.__class__.__name__ == 'Enum':
509             output += signature.format(name=t.name)
510             if t.enumtype in endian_strings:
511                 output += ('    *a = {}(*a);\n'
512                            .format(endian_strings[t.enumtype]))
513             else:
514                 output += ('    /* a->{name} = a->{name} (no-op) */\n'
515                            .format(name=t.name))
516
517             output += '}\n\n'
518             continue
519
520         if t.manual_endian:
521             output += "/***** manual: vl_api_%s_t_endian  *****/\n\n" % t.name
522             continue
523
524
525         if t.__class__.__name__ == 'Using':
526             output += signature.format(name=t.name)
527             if ('length' in t.alias and t.alias['length'] and
528                     t.alias['type'] == 'u8'):
529                 output += ('    /* a->{name} = a->{name} (no-op) */\n'
530                            .format(name=t.name))
531             elif t.alias['type'] in format_strings:
532                 output += ('    *a = {}(*a);\n'
533                            .format(endian_strings[t.alias['type']]))
534             else:
535                 output += '    /* Not Implemented yet {} */'.format(t.name)
536             output += '}\n\n'
537             continue
538
539         output += signature.format(name=t.name)
540
541         for o in t.block:
542             output += endianfun_obj(o)
543         output += '}\n\n'
544
545     output += "\n#endif"
546     output += "\n#endif /* vl_endianfun */\n\n"
547
548     return output
549
550
551 def version_tuple(s, module):
552     output = '''\
553 /****** Version tuple *****/
554
555 #ifdef vl_api_version_tuple
556
557 '''
558     if 'version' in s['Option']:
559         v = s['Option']['version']
560         (major, minor, patch) = v.split('.')
561         output += "vl_api_version_tuple(%s, %s, %s, %s)\n" % \
562                   (module, major, minor, patch)
563
564     output += "\n#endif /* vl_api_version_tuple */\n\n"
565
566     return output
567
568
569 #
570 # Plugin entry point
571 #
572 def run(input_filename, s):
573     stream = StringIO()
574     basename = os.path.basename(input_filename)
575     filename, file_extension = os.path.splitext(basename)
576     modulename = filename.replace('.', '_')
577
578     output = top_boilerplate.format(datestring=datestring,
579                                     input_filename=basename)
580     output += imports(s['Import'])
581     output += msg_ids(s)
582     output += msg_names(s)
583     output += msg_name_crc_list(s, filename)
584     output += typedefs(s['types'] + s['Define'], filename + file_extension)
585     printfun_types(s['types'], stream, modulename)
586     printfun(s['Define'], stream, modulename)
587     output += stream.getvalue()
588     output += endianfun(s['types'] + s['Define'], modulename)
589     output += version_tuple(s, basename)
590     output += bottom_boilerplate.format(input_filename=basename,
591                                         file_crc=s['file_crc'])
592
593     return output