blob: 4369dd86690d66d037d26c4b1bec72f518b98f27 [file] [log] [blame]
Ole Troandf87f802020-11-18 19:17:48 +01001#
2# Copyright (c) 2020 Cisco and/or its affiliates.
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at:
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14#
15
16#
17# Provide two classes FromJSON and TOJSON that converts between JSON and VPP's
18# binary API format
19#
20
21'''
22This module creates C code for core VPP, VPP plugins and client side VAT and
23VAT2 tests.
24'''
25
Ole Troan9d420872017-10-12 13:06:35 +020026import datetime
27import os
Nirmoy Dasc9f40222018-06-28 10:18:43 +020028import time
Ole Troan33a58172019-09-04 09:12:29 +020029import sys
30from io import StringIO
Ole Troan2a1ca782019-09-19 01:08:30 +020031import shutil
Ole Troan9d420872017-10-12 13:06:35 +020032
Paul Vinciguerra9046e442020-11-20 23:10:09 -050033process_imports = False
34
Ole Troandf87f802020-11-18 19:17:48 +010035###############################################################################
36class ToJSON():
37 '''Class to generate functions converting from VPP binary API to JSON.'''
38 _dispatch = {}
39 noprint_fields = {'_vl_msg_id': None,
40 'client_index': None,
41 'context': None}
42 is_number = {'u8': None,
43 'i8': None,
44 'u16': None,
45 'i16': None,
46 'u32': None,
47 'i32': None,
48 'u64': None,
49 'i64': None,
50 'f64': None,
51 }
52
53 def __init__(self, module, types, defines, imported_types, stream):
54 self.stream = stream
55 self.module = module
56 self.defines = defines
57 self.types = types
58 self.types_hash = {'vl_api_'+d.name+'_t':
59 d for d in types + imported_types}
60 self.defines_hash = {d.name: d for d in defines}
61
62 def header(self):
63 '''Output the top boilerplate.'''
64 write = self.stream.write
65 write('#ifndef included_{}_api_tojson_h\n'.format(self.module))
66 write('#define included_{}_api_tojson_h\n'.format(self.module))
67 write('#include <vppinfra/cJSON.h>\n\n')
68 write('#include <vat2/jsonconvert.h>\n\n')
69
70 def footer(self):
71 '''Output the bottom boilerplate.'''
72 write = self.stream.write
73 write('#endif\n')
74
75 def get_json_func(self, t):
76 '''Given the type, returns the function to use to create a
77 cJSON object'''
78 vt_type = None
79 try:
80 vt = self.types_hash[t]
81 if vt.type == 'Using' and 'length' not in vt.alias:
82 vt_type = vt.alias['type']
83 except KeyError:
84 vt = t
85
86 if t in self.is_number or vt_type in self.is_number:
87 return 'cJSON_AddNumberToObject', '', False
88 if t == 'bool':
89 return 'cJSON_AddBoolToObject', '', False
90
91 # Lookup type name check if it's enum
92 if vt.type == 'Enum':
93 return '{t}_tojson'.format(t=t), '', True
94 return '{t}_tojson'.format(t=t), '&', True
95
96 def get_json_array_func(self, t):
97 '''Given a type returns the function to create a cJSON object
98 for arrays.'''
99 if t in self.is_number:
100 return 'cJSON_CreateNumber', ''
101 if t == 'bool':
102 return 'cJSON_CreateBool', ''
103 return '{t}_tojson'.format(t=t), '&'
104
105 def print_string(self, o):
106 '''Create cJSON object from vl_api_string_t'''
107 write = self.stream.write
108 if o.modern_vla:
109 write(' vl_api_string_cJSON_AddToObject(o, "{n}", &a->{n});\n'
110 .format(n=o.fieldname))
111 else:
112
113 write(' cJSON_AddStringToObject(o, "{n}", (char *)a->{n});\n'
114 .format(n=o.fieldname))
115
116 def print_field(self, o):
117 '''Called for every field in a typedef or define.'''
118 write = self.stream.write
119 if o.fieldname in self.noprint_fields:
120 return
121
122 f, p, newobj = self.get_json_func(o.fieldtype)
123
124 if newobj:
125 write(' cJSON_AddItemToObject(o, "{n}", {f}({p}a->{n}));\n'
126 .format(f=f, p=p, n=o.fieldname))
127 else:
128 write(' {f}(o, "{n}", {p}a->{n});\n'
129 .format(f=f, p=p, n=o.fieldname))
130
131 _dispatch['Field'] = print_field
132
133 def print_array(self, o):
134 '''Converts a VPP API array to cJSON array.'''
135 write = self.stream.write
136
137 forloop = '''\
138 {{
139 int i;
140 cJSON *array = cJSON_AddArrayToObject(o, "{n}");
141 for (i = 0; i < {lfield}; i++) {{
142 cJSON_AddItemToArray(array, {f}({p}a->{n}[i]));
143 }}
144 }}
145'''
146
147 if o.fieldtype == 'string':
148 self.print_string(o)
149 return
150
151 lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
152 if o.fieldtype == 'u8':
153 write(' {\n')
154 # What is length field doing here?
155 write(' u8 *s = format(0, "0x%U", format_hex_bytes, '
156 '&a->{n}, {lfield});\n'
157 .format(n=o.fieldname, lfield=lfield))
158 write(' cJSON_AddStringToObject(o, "{n}", (char *)s);\n'
159 .format(n=o.fieldname))
160 write(' vec_free(s);\n')
161 write(' }\n')
162 return
163
164 f, p = self.get_json_array_func(o.fieldtype)
165 write(forloop.format(lfield=lfield,
166 t=o.fieldtype,
167 n=o.fieldname,
168 f=f,
169 p=p))
170
171 _dispatch['Array'] = print_array
172
173 def print_enum(self, o):
174 '''Create cJSON object (string) for VPP API enum'''
175 write = self.stream.write
176 write('static inline cJSON *vl_api_{name}_t_tojson '
177 '(vl_api_{name}_t a) {{\n'.format(name=o.name))
178
179 write(" switch(a) {\n")
180 for b in o.block:
181 write(" case %s:\n" % b[1])
182 write(' return cJSON_CreateString("{}");\n'.format(b[0]))
183 write(' default: return cJSON_CreateString("Invalid ENUM");\n')
184 write(' }\n')
185 write(' return 0;\n')
186 write('}\n')
187
188 _dispatch['Enum'] = print_enum
189
190 def print_typedef(self, o):
191 '''Create cJSON (dictionary) object from VPP API typedef'''
192 write = self.stream.write
193 write('static inline cJSON *vl_api_{name}_t_tojson '
194 '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
195 write(' cJSON *o = cJSON_CreateObject();\n')
196
197 for t in o.block:
198 self._dispatch[t.type](self, t)
199
200 write(' return o;\n')
201 write('}\n')
202
203 def print_define(self, o):
204 '''Create cJSON (dictionary) object from VPP API define'''
205 write = self.stream.write
206 write('static inline cJSON *vl_api_{name}_t_tojson '
207 '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
208 write(' cJSON *o = cJSON_CreateObject();\n')
209 write(' cJSON_AddStringToObject(o, "_msgname", "{}");\n'
210 .format(o.name))
211
212 for t in o.block:
213 self._dispatch[t.type](self, t)
214
215 write(' return o;\n')
216 write('}\n')
217
218 def print_using(self, o):
219 '''Create cJSON (dictionary) object from VPP API aliased type'''
220 if o.manual_print:
221 return
222
223 write = self.stream.write
224 write('static inline cJSON *vl_api_{name}_t_tojson '
225 '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
226
227 write(' u8 *s = format(0, "%U", format_vl_api_{}_t, a);\n'
228 .format(o.name))
229 write(' cJSON *o = cJSON_CreateString((char *)s);\n')
230 write(' vec_free(s);\n')
231 write(' return o;\n')
232 write('}\n')
233
234 _dispatch['Typedef'] = print_typedef
235 _dispatch['Define'] = print_define
236 _dispatch['Using'] = print_using
237 _dispatch['Union'] = print_typedef
238
239 def generate_function(self, t):
240 '''Main entry point'''
241 write = self.stream.write
242 if t.manual_print:
243 write('/* Manual print {} */\n'.format(t.name))
244 return
245 self._dispatch[t.type](self, t)
246
247 def generate_types(self):
248 '''Main entry point'''
249 for t in self.types:
250 self.generate_function(t)
251
252 def generate_defines(self):
253 '''Main entry point'''
254 for t in self.defines:
255 self.generate_function(t)
256
257
258class FromJSON():
259 '''
260 Parse JSON objects into VPP API binary message structures.
261 '''
262 _dispatch = {}
263 noprint_fields = {'_vl_msg_id': None,
264 'client_index': None,
265 'context': None}
266 is_number = {'u8': None,
267 'i8': None,
268 'u16': None,
269 'i16': None,
270 'u32': None,
271 'i32': None,
272 'u64': None,
273 'i64': None,
274 'f64': None,
275 }
276
277 def __init__(self, module, types, defines, imported_types, stream):
278 self.stream = stream
279 self.module = module
280 self.defines = defines
281 self.types = types
282 self.types_hash = {'vl_api_'+d.name+'_t':
283 d for d in types + imported_types}
284 self.defines_hash = {d.name: d for d in defines}
285
286 def header(self):
287 '''Output the top boilerplate.'''
288 write = self.stream.write
289 write('#ifndef included_{}_api_fromjson_h\n'.format(self.module))
290 write('#define included_{}_api_fromjson_h\n'.format(self.module))
291 write('#include <vppinfra/cJSON.h>\n\n')
292 write('#include <vat2/jsonconvert.h>\n\n')
293
294 def is_base_type(self, t):
295 '''Check if a type is one of the VPP API base types'''
296 if t in self.is_number:
297 return True
298 if t == 'bool':
299 return True
300 return False
301
302 def footer(self):
303 '''Output the bottom boilerplate.'''
304 write = self.stream.write
305 write('#endif\n')
306
307 def print_string(self, o, toplevel=False):
308 '''Convert JSON string to vl_api_string_t'''
309 write = self.stream.write
310
311 msgvar = "a" if toplevel else "mp"
312 msgsize = "l" if toplevel else "*len"
313
314 if o.modern_vla:
315 write(' char *p = cJSON_GetStringValue(item);\n')
316 write(' size_t plen = strlen(p);\n')
317 write(' {msgvar} = realloc({msgvar}, {msgsize} + plen);\n'
318 .format(msgvar=msgvar, msgsize=msgsize))
319 write(' vl_api_c_string_to_api_string(p, (void *){msgvar} + '
320 '{msgsize} - sizeof(vl_api_string_t));\n'
321 .format(msgvar=msgvar, msgsize=msgsize))
322 write(' {msgsize} += plen;\n'.format(msgsize=msgsize))
323 else:
324 write(' strncpy_s((char *)a->{n}, sizeof(a->{n}), '
325 'cJSON_GetStringValue(item), sizeof(a->{n}) - 1);\n'
326 .format(n=o.fieldname))
327
328 def print_field(self, o, toplevel=False):
329 '''Called for every field in a typedef or define.'''
330 write = self.stream.write
331 write(' // start field {}\n'.format(o.fieldname))
332 if o.fieldname in self.noprint_fields:
333 return
334 is_bt = self.is_base_type(o.fieldtype)
335 t = 'vl_api_{}'.format(o.fieldtype) if is_bt else o.fieldtype
336
337 msgvar = "a" if toplevel else "mp"
338 msgsize = "&l" if toplevel else "len"
339
340 if is_bt:
341 write(' vl_api_{t}_fromjson(item, &a->{n});\n'
342 .format(t=o.fieldtype, n=o.fieldname))
343 else:
344 write(' {msgvar} = {t}_fromjson({msgvar}, '
345 '{msgsize}, item, &a->{n});\n'
346 .format(t=t, n=o.fieldname, msgvar=msgvar, msgsize=msgsize))
347 write(' if (!{msgvar}) return 0;\n'.format(msgvar=msgvar))
348
349 write(' // end field {}\n'.format(o.fieldname))
350
351 _dispatch['Field'] = print_field
352
353 def print_array(self, o, toplevel=False):
354 '''Convert JSON array to VPP API array'''
355 write = self.stream.write
356
357 forloop = '''\
358 {{
359 int i;
360 cJSON *array = cJSON_GetObjectItem(o, "{n}");
361 int size = cJSON_GetArraySize(array);
362 if (size != {lfield}) return 0;
363 for (i = 0; i < size; i++) {{
364 cJSON *e = cJSON_GetArrayItem(array, i);
365 {call}
366 }}
367 }}
368'''
369 forloop_vla = '''\
370 {{
371 int i;
372 cJSON *array = cJSON_GetObjectItem(o, "{n}");
373 int size = cJSON_GetArraySize(array);
374 {lfield} = size;
375 {msgvar} = realloc({msgvar}, {msgsize} + sizeof({t}) * size);
376 {t} *d = (void *){msgvar} + {msgsize};
377 {msgsize} += sizeof({t}) * size;
378 for (i = 0; i < size; i++) {{
379 cJSON *e = cJSON_GetArrayItem(array, i);
380 {call}
381 }}
382 }}
383'''
384 t = o.fieldtype
385 if o.fieldtype == 'string':
386 self.print_string(o, toplevel)
387 return
388
389 lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
390 msgvar = "a" if toplevel else "mp"
391 msgsize = "l" if toplevel else "*len"
392
393 if o.fieldtype == 'u8':
394 if o.lengthfield:
395 write(' s = u8string_fromjson(o, "{}");\n'
396 .format(o.fieldname))
397 write(' if (!s) return 0;\n')
398 write(' {} = vec_len(s);\n'.format(lfield))
399
400 write(' {msgvar} = realloc({msgvar}, {msgsize} + '
401 'vec_len(s));\n'.format(msgvar=msgvar, msgsize=msgsize))
402 write(' memcpy((void *){msgvar} + {msgsize}, s, '
403 'vec_len(s));\n'.format(msgvar=msgvar, msgsize=msgsize))
404 write(' {msgsize} += vec_len(s);\n'.format(msgsize=msgsize))
405
406 write(' vec_free(s);\n')
407 else:
408 write(' u8string_fromjson2(o, "{n}", a->{n});\n'
409 .format(n=o.fieldname))
410 return
411
412 is_bt = self.is_base_type(o.fieldtype)
413
414 if o.lengthfield:
415 if is_bt:
416 call = ('vl_api_{t}_fromjson(e, &d[i]);'
417 .format(t=o.fieldtype))
418 else:
419 call = ('{t}_fromjson({msgvar}, len, e, &d[i]); '
420 .format(t=o.fieldtype, msgvar=msgvar))
421 write(forloop_vla.format(lfield=lfield,
422 t=o.fieldtype,
423 n=o.fieldname,
424 call=call,
425 msgvar=msgvar,
426 msgsize=msgsize))
427 else:
428 if is_bt:
429 call = ('vl_api_{t}_fromjson(e, &a->{n}[i]);'
430 .format(t=t, n=o.fieldname))
431 else:
432 call = ('a = {}_fromjson({}, len, e, &a->{}[i]);'
433 .format(t, msgvar, o.fieldname))
434 write(forloop.format(lfield=lfield,
435 t=t,
436 n=o.fieldname,
437 call=call,
438 msgvar=msgvar,
439 msgsize=msgsize))
440
441 _dispatch['Array'] = print_array
442
443 def print_enum(self, o):
444 '''Convert to JSON enum(string) to VPP API enum (int)'''
445 write = self.stream.write
446 write('static inline void *vl_api_{n}_t_fromjson '
447 '(void *mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n'
448 .format(n=o.name))
449 write(' char *p = cJSON_GetStringValue(o);\n')
450 for b in o.block:
451 write(' if (strcmp(p, "{}") == 0) {{*a = {}; return mp;}}\n'
452 .format(b[0], b[1]))
453 write(' return 0;\n')
454 write('}\n')
455
456 _dispatch['Enum'] = print_enum
457
458 def print_typedef(self, o):
459 '''Convert from JSON object to VPP API binary representation'''
460 write = self.stream.write
461
462 write('static inline void *vl_api_{name}_t_fromjson (void *mp, '
463 'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
464 .format(name=o.name))
465 write(' cJSON *item __attribute__ ((unused));\n')
466 write(' u8 *s __attribute__ ((unused));\n')
467 for t in o.block:
468 if t.type == 'Field' and t.is_lengthfield:
469 continue
470 write(' item = cJSON_GetObjectItem(o, "{}");\n'
471 .format(t.fieldname))
472 write(' if (!item) return 0;\n')
473
474 self._dispatch[t.type](self, t)
475
476 write(' return mp;\n')
477 write('}\n')
478
479 def print_union(self, o):
480 '''Convert JSON object to VPP API binary union'''
481 write = self.stream.write
482
483 write('static inline void *vl_api_{name}_t_fromjson (void *mp, '
484 'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
485 .format(name=o.name))
486 write(' cJSON *item __attribute__ ((unused));\n')
487 write(' u8 *s __attribute__ ((unused));\n')
488 for t in o.block:
489 if t.type == 'Field' and t.is_lengthfield:
490 continue
491 write(' item = cJSON_GetObjectItem(o, "{}");\n'
492 .format(t.fieldname))
493 write(' if (item) {\n')
494 self._dispatch[t.type](self, t)
495 write(' };\n')
496 write(' return mp;\n')
497 write('}\n')
498
499 def print_define(self, o):
500 '''Convert JSON object to VPP API message'''
501 write = self.stream.write
502 write('static inline vl_api_{name}_t *vl_api_{name}_t_fromjson '
503 '(cJSON *o, int *len) {{\n'.format(name=o.name))
504 write(' cJSON *item __attribute__ ((unused));\n')
505 write(' u8 *s __attribute__ ((unused));\n')
506 write(' int l = sizeof(vl_api_{}_t);\n'.format(o.name))
507 write(' vl_api_{}_t *a = malloc(l);\n'.format(o.name))
508
509 for t in o.block:
510 if t.fieldname in self.noprint_fields:
511 continue
512 if t.type == 'Field' and t.is_lengthfield:
513 continue
514 write(' // processing {}: {} {}\n'
515 .format(o.name, t.fieldtype, t.fieldname))
516
517 write(' item = cJSON_GetObjectItem(o, "{}");\n'
518 .format(t.fieldname))
519 write(' if (!item) return 0;\n')
520 self._dispatch[t.type](self, t, toplevel=True)
521 write('\n')
522
523 write('\n')
524 write(' *len = l;\n')
525 write(' return a;\n')
526 write('}\n')
527
528 def print_using(self, o):
529 '''Convert JSON field to VPP type alias'''
530 write = self.stream.write
531
532 if o.manual_print:
533 return
534
535 t = o.using
536 write('static inline void *vl_api_{name}_t_fromjson (void *mp, '
537 'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
538 .format(name=o.name))
539 if 'length' in o.alias:
540 if t.fieldtype != 'u8':
541 raise ValueError("Error in processing type {} for {}"
542 .format(t.fieldtype, o.name))
543 write(' vl_api_u8_string_fromjson(o, (u8 *)a, {});\n'
544 .format(o.alias['length']))
545 else:
546 write(' vl_api_{t}_fromjson(o, ({t} *)a);\n'
547 .format(t=t.fieldtype))
548
549 write(' return mp;\n')
550 write('}\n')
551
552 _dispatch['Typedef'] = print_typedef
553 _dispatch['Define'] = print_define
554 _dispatch['Using'] = print_using
555 _dispatch['Union'] = print_union
556
557 def generate_function(self, t):
558 '''Main entry point'''
559 write = self.stream.write
560 if t.manual_print:
561 write('/* Manual print {} */\n'.format(t.name))
562 return
563 self._dispatch[t.type](self, t)
564
565 def generate_types(self):
566 '''Main entry point'''
567 for t in self.types:
568 self.generate_function(t)
569
570 def generate_defines(self):
571 '''Main entry point'''
572 for t in self.defines:
573 self.generate_function(t)
574
575
576def generate_tojson(s, modulename, stream):
577 '''Generate all functions to convert from API to JSON'''
578 write = stream.write
579
580 write('/* Imported API files */\n')
581 for i in s['Import']:
582 f = i.filename.replace('plugins/', '')
583 write('#include <{}_tojson.h>\n'.format(f))
584
585 pp = ToJSON(modulename, s['types'], s['Define'], s['imported']['types'],
586 stream)
587 pp.header()
588 pp.generate_types()
589 pp.generate_defines()
590 pp.footer()
591 return ''
592
593
594def generate_fromjson(s, modulename, stream):
595 '''Generate all functions to convert from JSON to API'''
596 write = stream.write
597 write('/* Imported API files */\n')
598 for i in s['Import']:
599 f = i.filename.replace('plugins/', '')
600 write('#include <{}_fromjson.h>\n'.format(f))
601
602 pp = FromJSON(modulename, s['types'], s['Define'], s['imported']['types'],
603 stream)
604 pp.header()
605 pp.generate_types()
606 pp.generate_defines()
607 pp.footer()
608
609 return ''
610
611###############################################################################
612
613
614DATESTRING = datetime.datetime.utcfromtimestamp(
Ole Troan413f4a52018-11-28 11:36:05 +0100615 int(os.environ.get('SOURCE_DATE_EPOCH', time.time())))
Ole Troandf87f802020-11-18 19:17:48 +0100616TOP_BOILERPLATE = '''\
Ole Troan9d420872017-10-12 13:06:35 +0200617/*
618 * VLIB API definitions {datestring}
619 * Input file: {input_filename}
620 * Automatically generated: please edit the input file NOT this file!
621 */
622
Ole Troan288e0932019-05-29 12:30:05 +0200623#include <stdbool.h>
Ole Troan9d420872017-10-12 13:06:35 +0200624#if defined(vl_msg_id)||defined(vl_union_id) \\
625 || defined(vl_printfun) ||defined(vl_endianfun) \\
626 || defined(vl_api_version)||defined(vl_typedefs) \\
627 || defined(vl_msg_name)||defined(vl_msg_name_crc_list) \\
628 || defined(vl_api_version_tuple)
629/* ok, something was selected */
630#else
631#warning no content included from {input_filename}
632#endif
633
634#define VL_API_PACKED(x) x __attribute__ ((packed))
635'''
636
Ole Troandf87f802020-11-18 19:17:48 +0100637BOTTOM_BOILERPLATE = '''\
Ole Troan9d420872017-10-12 13:06:35 +0200638/****** API CRC (whole file) *****/
639
640#ifdef vl_api_version
641vl_api_version({input_filename}, {file_crc:#08x})
642
643#endif
644'''
645
646
647def msg_ids(s):
Ole Troandf87f802020-11-18 19:17:48 +0100648 '''Generate macro to map API message id to handler'''
Ole Troan9d420872017-10-12 13:06:35 +0200649 output = '''\
650
651/****** Message ID / handler enum ******/
652
653#ifdef vl_msg_id
654'''
655
Ole Troan2c2feab2018-04-24 00:02:37 -0400656 for t in s['Define']:
Ole Troan9d420872017-10-12 13:06:35 +0200657 output += "vl_msg_id(VL_API_%s, vl_api_%s_t_handler)\n" % \
658 (t.name.upper(), t.name)
659 output += "#endif"
660
661 return output
662
663
664def msg_names(s):
Ole Troandf87f802020-11-18 19:17:48 +0100665 '''Generate calls to name mapping macro'''
Ole Troan9d420872017-10-12 13:06:35 +0200666 output = '''\
667
668/****** Message names ******/
669
670#ifdef vl_msg_name
671'''
672
Ole Troan2c2feab2018-04-24 00:02:37 -0400673 for t in s['Define']:
Ole Troan9d420872017-10-12 13:06:35 +0200674 dont_trace = 0 if t.dont_trace else 1
675 output += "vl_msg_name(vl_api_%s_t, %d)\n" % (t.name, dont_trace)
676 output += "#endif"
677
678 return output
679
680
681def msg_name_crc_list(s, suffix):
Ole Troandf87f802020-11-18 19:17:48 +0100682 '''Generate list of names to CRC mappings'''
Ole Troan9d420872017-10-12 13:06:35 +0200683 output = '''\
684
685/****** Message name, crc list ******/
686
687#ifdef vl_msg_name_crc_list
688'''
689 output += "#define foreach_vl_msg_name_crc_%s " % suffix
690
Ole Troan2c2feab2018-04-24 00:02:37 -0400691 for t in s['Define']:
Ole Troan9d420872017-10-12 13:06:35 +0200692 output += "\\\n_(VL_API_%s, %s, %08x) " % \
693 (t.name.upper(), t.name, t.crc)
694 output += "\n#endif"
695
696 return output
697
698
Ole Troan413f4a52018-11-28 11:36:05 +0100699def api2c(fieldtype):
Ole Troandf87f802020-11-18 19:17:48 +0100700 '''Map between API type names and internal VPP type names'''
Ole Troan413f4a52018-11-28 11:36:05 +0100701 mappingtable = {'string': 'vl_api_string_t', }
702 if fieldtype in mappingtable:
703 return mappingtable[fieldtype]
704 return fieldtype
705
706
Ole Troan2a1ca782019-09-19 01:08:30 +0200707def typedefs(filename):
Ole Troandf87f802020-11-18 19:17:48 +0100708 '''Include in the main files to the types file'''
Ole Troan2a1ca782019-09-19 01:08:30 +0200709 output = '''\
Ole Troan9d420872017-10-12 13:06:35 +0200710
711/****** Typedefs ******/
712
713#ifdef vl_typedefs
Ole Troan2a1ca782019-09-19 01:08:30 +0200714#include "{include}.api_types.h"
715#endif
716'''.format(include=filename)
Ole Troan9d420872017-10-12 13:06:35 +0200717 return output
718
719
Ole Troandf87f802020-11-18 19:17:48 +0100720FORMAT_STRINGS = {'u8': '%u',
Ole Troan33a58172019-09-04 09:12:29 +0200721 'bool': '%u',
Ole Troan9d420872017-10-12 13:06:35 +0200722 'i8': '%d',
723 'u16': '%u',
724 'i16': '%d',
725 'u32': '%u',
726 'i32': '%ld',
727 'u64': '%llu',
Paul Vinciguerrab307c712019-11-23 10:13:39 -0500728 'i64': '%lld',
Ole Troan33a58172019-09-04 09:12:29 +0200729 'f64': '%.2f'}
730
Ole Troan9d420872017-10-12 13:06:35 +0200731
Ole Troan33a58172019-09-04 09:12:29 +0200732class Printfun():
Ole Troandf87f802020-11-18 19:17:48 +0100733 '''Functions for pretty printing VPP API messages'''
Ole Troan33a58172019-09-04 09:12:29 +0200734 _dispatch = {}
Ole Troandf87f802020-11-18 19:17:48 +0100735 noprint_fields = {'_vl_msg_id': None,
736 'client_index': None,
737 'context': None}
Ole Troan33a58172019-09-04 09:12:29 +0200738
739 def __init__(self, stream):
740 self.stream = stream
741
Ole Troandf87f802020-11-18 19:17:48 +0100742 @staticmethod
743 def print_string(o, stream):
744 '''Pretty print a vl_api_string_t'''
Ole Troan33a58172019-09-04 09:12:29 +0200745 write = stream.write
746 if o.modern_vla:
747 write(' if (vl_api_string_len(&a->{f}) > 0) {{\n'
748 .format(f=o.fieldname))
Jakub Grajciar2dbee932020-02-07 11:30:26 +0100749 write(' s = format(s, "\\n%U{f}: %U", '
Ole Troan33a58172019-09-04 09:12:29 +0200750 'format_white_space, indent, '
Jakub Grajciar2dbee932020-02-07 11:30:26 +0100751 'vl_api_format_string, (&a->{f}));\n'.format(f=o.fieldname))
Ole Troan33a58172019-09-04 09:12:29 +0200752 write(' } else {\n')
753 write(' s = format(s, "\\n%U{f}:", '
754 'format_white_space, indent);\n'.format(f=o.fieldname))
755 write(' }\n')
756 else:
757 write(' s = format(s, "\\n%U{f}: %s", '
758 'format_white_space, indent, a->{f});\n'
759 .format(f=o.fieldname))
760
761 def print_field(self, o, stream):
Ole Troandf87f802020-11-18 19:17:48 +0100762 '''Pretty print API field'''
Ole Troan33a58172019-09-04 09:12:29 +0200763 write = stream.write
Ole Troandf87f802020-11-18 19:17:48 +0100764 if o.fieldname in self.noprint_fields:
Ole Troan33a58172019-09-04 09:12:29 +0200765 return
Ole Troandf87f802020-11-18 19:17:48 +0100766 if o.fieldtype in FORMAT_STRINGS:
767 f = FORMAT_STRINGS[o.fieldtype]
Ole Troan33a58172019-09-04 09:12:29 +0200768 write(' s = format(s, "\\n%U{n}: {f}", '
769 'format_white_space, indent, a->{n});\n'
770 .format(n=o.fieldname, f=f))
771 else:
772 write(' s = format(s, "\\n%U{n}: %U", '
773 'format_white_space, indent, '
774 'format_{t}, &a->{n}, indent);\n'
775 .format(n=o.fieldname, t=o.fieldtype))
776
777 _dispatch['Field'] = print_field
778
779 def print_array(self, o, stream):
Ole Troandf87f802020-11-18 19:17:48 +0100780 '''Pretty print API array'''
Ole Troan33a58172019-09-04 09:12:29 +0200781 write = stream.write
782
783 forloop = '''\
784 for (i = 0; i < {lfield}; i++) {{
785 s = format(s, "\\n%U{n}: %U",
786 format_white_space, indent, format_{t}, &a->{n}[i], indent);
787 }}
788'''
789
790 forloop_format = '''\
791 for (i = 0; i < {lfield}; i++) {{
792 s = format(s, "\\n%U{n}: {t}",
793 format_white_space, indent, a->{n}[i]);
794 }}
795'''
796
797 if o.fieldtype == 'string':
Ole Troandf87f802020-11-18 19:17:48 +0100798 self.print_string(o, stream)
799 return
Ole Troan33a58172019-09-04 09:12:29 +0200800
801 if o.fieldtype == 'u8':
802 if o.lengthfield:
803 write(' s = format(s, "\\n%U{n}: %U", format_white_space, '
804 'indent, format_hex_bytes, a->{n}, a->{lfield});\n'
805 .format(n=o.fieldname, lfield=o.lengthfield))
806 else:
807 write(' s = format(s, "\\n%U{n}: %U", format_white_space, '
808 'indent, format_hex_bytes, a, {lfield});\n'
809 .format(n=o.fieldname, lfield=o.length))
810 return
811
812 lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
Ole Troandf87f802020-11-18 19:17:48 +0100813 if o.fieldtype in FORMAT_STRINGS:
Ole Troan33a58172019-09-04 09:12:29 +0200814 write(forloop_format.format(lfield=lfield,
Ole Troandf87f802020-11-18 19:17:48 +0100815 t=FORMAT_STRINGS[o.fieldtype],
Ole Troan33a58172019-09-04 09:12:29 +0200816 n=o.fieldname))
817 else:
818 write(forloop.format(lfield=lfield, t=o.fieldtype, n=o.fieldname))
819
820 _dispatch['Array'] = print_array
821
Ole Troandf87f802020-11-18 19:17:48 +0100822 @staticmethod
823 def print_alias(k, v, stream):
824 '''Pretty print type alias'''
Ole Troan33a58172019-09-04 09:12:29 +0200825 write = stream.write
826 if ('length' in v.alias and v.alias['length'] and
827 v.alias['type'] == 'u8'):
828 write(' return format(s, "%U", format_hex_bytes, a, {});\n'
829 .format(v.alias['length']))
Ole Troandf87f802020-11-18 19:17:48 +0100830 elif v.alias['type'] in FORMAT_STRINGS:
Ole Troan33a58172019-09-04 09:12:29 +0200831 write(' return format(s, "{}", *a);\n'
Ole Troandf87f802020-11-18 19:17:48 +0100832 .format(FORMAT_STRINGS[v.alias['type']]))
Ole Troan33a58172019-09-04 09:12:29 +0200833 else:
Ole Troan75761b92019-09-11 17:49:08 +0200834 write(' return format(s, "{} (print not implemented)");\n'
Ole Troan33a58172019-09-04 09:12:29 +0200835 .format(k))
836
Ole Troandf87f802020-11-18 19:17:48 +0100837 @staticmethod
838 def print_enum(o, stream):
839 '''Pretty print API enum'''
Ole Troan33a58172019-09-04 09:12:29 +0200840 write = stream.write
841 write(" switch(*a) {\n")
842 for b in o:
843 write(" case %s:\n" % b[1])
844 write(' return format(s, "{}");\n'.format(b[0]))
845 write(' }\n')
846
847 _dispatch['Enum'] = print_enum
848
849 def print_obj(self, o, stream):
Ole Troandf87f802020-11-18 19:17:48 +0100850 '''Entry point'''
Ole Troan33a58172019-09-04 09:12:29 +0200851 write = stream.write
852
853 if o.type in self._dispatch:
854 self._dispatch[o.type](self, o, stream)
855 else:
856 write(' s = format(s, "\\n{} {} {} (print not implemented");\n'
857 .format(o.type, o.fieldtype, o.fieldname))
858
859
860def printfun(objs, stream, modulename):
Ole Troandf87f802020-11-18 19:17:48 +0100861 '''Main entry point for pretty print function generation'''
Ole Troan33a58172019-09-04 09:12:29 +0200862 write = stream.write
863
864 h = '''\
Ole Troan9d420872017-10-12 13:06:35 +0200865/****** Print functions *****/
866#ifdef vl_printfun
Ole Troan33a58172019-09-04 09:12:29 +0200867#ifndef included_{module}_printfun
868#define included_{module}_printfun
Ole Troan9d420872017-10-12 13:06:35 +0200869
870#ifdef LP64
871#define _uword_fmt \"%lld\"
872#define _uword_cast (long long)
873#else
874#define _uword_fmt \"%ld\"
875#define _uword_cast long
876#endif
877
878'''
Ole Troan33a58172019-09-04 09:12:29 +0200879
880 signature = '''\
881static inline void *vl_api_{name}_t_print (vl_api_{name}_t *a, void *handle)
882{{
883 u8 *s = 0;
884 u32 indent __attribute__((unused)) = 2;
885 int i __attribute__((unused));
886'''
887
888 h = h.format(module=modulename)
889 write(h)
890
891 pp = Printfun(stream)
892 for t in objs:
893 if t.manual_print:
894 write("/***** manual: vl_api_%s_t_print *****/\n\n" % t.name)
895 continue
896 write(signature.format(name=t.name))
897 write(' /* Message definition: vl_api_{}_t: */\n'.format(t.name))
898 write(" s = format(s, \"vl_api_%s_t:\");\n" % t.name)
899 for o in t.block:
900 pp.print_obj(o, stream)
901 write(' vec_add1(s, 0);\n')
902 write(' vl_print (handle, (char *)s);\n')
903 write(' vec_free (s);\n')
904 write(' return handle;\n')
905 write('}\n\n')
906
907 write("\n#endif")
908 write("\n#endif /* vl_printfun */\n")
909
910 return ''
911
912
Ole Troan75761b92019-09-11 17:49:08 +0200913def printfun_types(objs, stream, modulename):
Ole Troandf87f802020-11-18 19:17:48 +0100914 '''Pretty print API types'''
Ole Troan33a58172019-09-04 09:12:29 +0200915 write = stream.write
916 pp = Printfun(stream)
917
918 h = '''\
919/****** Print functions *****/
920#ifdef vl_printfun
921#ifndef included_{module}_printfun_types
922#define included_{module}_printfun_types
923
924'''
925 h = h.format(module=modulename)
926 write(h)
927
928 signature = '''\
929static inline u8 *format_vl_api_{name}_t (u8 *s, va_list * args)
930{{
931 vl_api_{name}_t *a = va_arg (*args, vl_api_{name}_t *);
932 u32 indent __attribute__((unused)) = va_arg (*args, u32);
933 int i __attribute__((unused));
934 indent += 2;
935'''
936
Ole Troan2c2feab2018-04-24 00:02:37 -0400937 for t in objs:
938 if t.__class__.__name__ == 'Enum':
Ole Troan33a58172019-09-04 09:12:29 +0200939 write(signature.format(name=t.name))
940 pp.print_enum(t.block, stream)
941 write(' return s;\n')
942 write('}\n\n')
Ole Troan2c2feab2018-04-24 00:02:37 -0400943 continue
Ole Troan33a58172019-09-04 09:12:29 +0200944
Ole Troan9d420872017-10-12 13:06:35 +0200945 if t.manual_print:
Ole Troan33a58172019-09-04 09:12:29 +0200946 write("/***** manual: vl_api_%s_t_print *****/\n\n" % t.name)
Ole Troan9d420872017-10-12 13:06:35 +0200947 continue
Ole Troan9d420872017-10-12 13:06:35 +0200948
Ole Troan75761b92019-09-11 17:49:08 +0200949 if t.__class__.__name__ == 'Using':
950 write(signature.format(name=t.name))
951 pp.print_alias(t.name, t, stream)
952 write('}\n\n')
953 continue
954
Ole Troan33a58172019-09-04 09:12:29 +0200955 write(signature.format(name=t.name))
Ole Troan9d420872017-10-12 13:06:35 +0200956 for o in t.block:
Ole Troan33a58172019-09-04 09:12:29 +0200957 pp.print_obj(o, stream)
Ole Troan9d420872017-10-12 13:06:35 +0200958
Ole Troan33a58172019-09-04 09:12:29 +0200959 write(' return s;\n')
960 write('}\n\n')
Ole Troan9d420872017-10-12 13:06:35 +0200961
Ole Troan33a58172019-09-04 09:12:29 +0200962 write("\n#endif")
963 write("\n#endif /* vl_printfun_types */\n")
Ole Troan9d420872017-10-12 13:06:35 +0200964
Ole Troan33a58172019-09-04 09:12:29 +0200965
Ole Troandf87f802020-11-18 19:17:48 +0100966def generate_imports(imports):
967 '''Add #include matching the API import statements'''
Ole Troan33a58172019-09-04 09:12:29 +0200968 output = '/* Imported API files */\n'
969 output += '#ifndef vl_api_version\n'
970
971 for i in imports:
972 s = i.filename.replace('plugins/', '')
973 output += '#include <{}.h>\n'.format(s)
974 output += '#endif\n'
Ole Troan9d420872017-10-12 13:06:35 +0200975 return output
976
977
Ole Troandf87f802020-11-18 19:17:48 +0100978ENDIAN_STRINGS = {
Ole Troan9d420872017-10-12 13:06:35 +0200979 'u16': 'clib_net_to_host_u16',
980 'u32': 'clib_net_to_host_u32',
981 'u64': 'clib_net_to_host_u64',
Paul Vinciguerra1c200dc2019-11-19 23:30:53 -0500982 'i16': 'clib_net_to_host_i16',
983 'i32': 'clib_net_to_host_i32',
984 'i64': 'clib_net_to_host_i64',
985 'f64': 'clib_net_to_host_f64',
Ole Troan9d420872017-10-12 13:06:35 +0200986}
987
988
Ole Troan33a58172019-09-04 09:12:29 +0200989def endianfun_array(o):
Ole Troandf87f802020-11-18 19:17:48 +0100990 '''Generate endian functions for arrays'''
Ole Troan33a58172019-09-04 09:12:29 +0200991 forloop = '''\
992 for (i = 0; i < {length}; i++) {{
993 a->{name}[i] = {format}(a->{name}[i]);
994 }}
995'''
996
997 forloop_format = '''\
998 for (i = 0; i < {length}; i++) {{
999 {type}_endian(&a->{name}[i]);
1000 }}
1001'''
1002
1003 output = ''
1004 if o.fieldtype == 'u8' or o.fieldtype == 'string':
1005 output += ' /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
1006 else:
1007 lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
Ole Troandf87f802020-11-18 19:17:48 +01001008 if o.fieldtype in ENDIAN_STRINGS:
Ole Troan33a58172019-09-04 09:12:29 +02001009 output += (forloop
1010 .format(length=lfield,
Ole Troandf87f802020-11-18 19:17:48 +01001011 format=ENDIAN_STRINGS[o.fieldtype],
Ole Troan33a58172019-09-04 09:12:29 +02001012 name=o.fieldname))
1013 else:
1014 output += (forloop_format
1015 .format(length=lfield, type=o.fieldtype,
1016 name=o.fieldname))
1017 return output
1018
Ole Troandf87f802020-11-18 19:17:48 +01001019
1020NO_ENDIAN_CONVERSION = {'client_index': None}
1021
Ole Troan33a58172019-09-04 09:12:29 +02001022
1023def endianfun_obj(o):
Ole Troandf87f802020-11-18 19:17:48 +01001024 '''Generate endian conversion function for type'''
Ole Troan33a58172019-09-04 09:12:29 +02001025 output = ''
1026 if o.type == 'Array':
1027 return endianfun_array(o)
Ole Troandf87f802020-11-18 19:17:48 +01001028 if o.type != 'Field':
Ole Troan33a58172019-09-04 09:12:29 +02001029 output += (' s = format(s, "\\n{} {} {} (print not implemented");\n'
1030 .format(o.type, o.fieldtype, o.fieldname))
1031 return output
Ole Troandf87f802020-11-18 19:17:48 +01001032 if o.fieldname in NO_ENDIAN_CONVERSION:
Ole Troane796a182020-05-18 11:14:05 +02001033 output += ' /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
1034 return output
Ole Troandf87f802020-11-18 19:17:48 +01001035 if o.fieldtype in ENDIAN_STRINGS:
Ole Troan33a58172019-09-04 09:12:29 +02001036 output += (' a->{name} = {format}(a->{name});\n'
1037 .format(name=o.fieldname,
Ole Troandf87f802020-11-18 19:17:48 +01001038 format=ENDIAN_STRINGS[o.fieldtype]))
Ole Troan33a58172019-09-04 09:12:29 +02001039 elif o.fieldtype.startswith('vl_api_'):
1040 output += (' {type}_endian(&a->{name});\n'
1041 .format(type=o.fieldtype, name=o.fieldname))
1042 else:
1043 output += ' /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
1044
1045 return output
1046
1047
Ole Troan75761b92019-09-11 17:49:08 +02001048def endianfun(objs, modulename):
Ole Troandf87f802020-11-18 19:17:48 +01001049 '''Main entry point for endian function generation'''
Ole Troan9d420872017-10-12 13:06:35 +02001050 output = '''\
1051
1052/****** Endian swap functions *****/\n\
1053#ifdef vl_endianfun
Ole Troan33a58172019-09-04 09:12:29 +02001054#ifndef included_{module}_endianfun
1055#define included_{module}_endianfun
Ole Troan9d420872017-10-12 13:06:35 +02001056
1057#undef clib_net_to_host_uword
1058#ifdef LP64
1059#define clib_net_to_host_uword clib_net_to_host_u64
1060#else
1061#define clib_net_to_host_uword clib_net_to_host_u32
1062#endif
1063
1064'''
Ole Troan33a58172019-09-04 09:12:29 +02001065 output = output.format(module=modulename)
1066
1067 signature = '''\
1068static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
1069{{
1070 int i __attribute__((unused));
1071'''
1072
Ole Troan2c2feab2018-04-24 00:02:37 -04001073 for t in objs:
1074 if t.__class__.__name__ == 'Enum':
Ole Troan33a58172019-09-04 09:12:29 +02001075 output += signature.format(name=t.name)
Ole Troandf87f802020-11-18 19:17:48 +01001076 if t.enumtype in ENDIAN_STRINGS:
Ole Troan33a58172019-09-04 09:12:29 +02001077 output += (' *a = {}(*a);\n'
Ole Troandf87f802020-11-18 19:17:48 +01001078 .format(ENDIAN_STRINGS[t.enumtype]))
Ole Troan33a58172019-09-04 09:12:29 +02001079 else:
1080 output += (' /* a->{name} = a->{name} (no-op) */\n'
1081 .format(name=t.name))
1082
1083 output += '}\n\n'
Ole Troan2c2feab2018-04-24 00:02:37 -04001084 continue
Ole Troan33a58172019-09-04 09:12:29 +02001085
Ole Troan9d420872017-10-12 13:06:35 +02001086 if t.manual_endian:
1087 output += "/***** manual: vl_api_%s_t_endian *****/\n\n" % t.name
1088 continue
Ole Troan33a58172019-09-04 09:12:29 +02001089
Ole Troan75761b92019-09-11 17:49:08 +02001090 if t.__class__.__name__ == 'Using':
1091 output += signature.format(name=t.name)
1092 if ('length' in t.alias and t.alias['length'] and
1093 t.alias['type'] == 'u8'):
1094 output += (' /* a->{name} = a->{name} (no-op) */\n'
1095 .format(name=t.name))
Ole Troandf87f802020-11-18 19:17:48 +01001096 elif t.alias['type'] in FORMAT_STRINGS:
Ole Troan75761b92019-09-11 17:49:08 +02001097 output += (' *a = {}(*a);\n'
Ole Troandf87f802020-11-18 19:17:48 +01001098 .format(ENDIAN_STRINGS[t.alias['type']]))
Ole Troan75761b92019-09-11 17:49:08 +02001099 else:
1100 output += ' /* Not Implemented yet {} */'.format(t.name)
1101 output += '}\n\n'
1102 continue
1103
Ole Troan33a58172019-09-04 09:12:29 +02001104 output += signature.format(name=t.name)
Ole Troan9d420872017-10-12 13:06:35 +02001105
1106 for o in t.block:
Ole Troan33a58172019-09-04 09:12:29 +02001107 output += endianfun_obj(o)
Ole Troan9d420872017-10-12 13:06:35 +02001108 output += '}\n\n'
Ole Troan33a58172019-09-04 09:12:29 +02001109
1110 output += "\n#endif"
Ole Troan9d420872017-10-12 13:06:35 +02001111 output += "\n#endif /* vl_endianfun */\n\n"
1112
1113 return output
1114
1115
1116def version_tuple(s, module):
Ole Troandf87f802020-11-18 19:17:48 +01001117 '''Generate semantic version string'''
Ole Troan9d420872017-10-12 13:06:35 +02001118 output = '''\
1119/****** Version tuple *****/
1120
1121#ifdef vl_api_version_tuple
1122
1123'''
Ole Troan2c2feab2018-04-24 00:02:37 -04001124 if 'version' in s['Option']:
1125 v = s['Option']['version']
Ole Troan9d420872017-10-12 13:06:35 +02001126 (major, minor, patch) = v.split('.')
1127 output += "vl_api_version_tuple(%s, %s, %s, %s)\n" % \
1128 (module, major, minor, patch)
1129
1130 output += "\n#endif /* vl_api_version_tuple */\n\n"
1131
1132 return output
1133
1134
Ole Troan2a1ca782019-09-19 01:08:30 +02001135def generate_include_enum(s, module, stream):
Ole Troandf87f802020-11-18 19:17:48 +01001136 '''Generate <name>.api_enum.h'''
Ole Troan2a1ca782019-09-19 01:08:30 +02001137 write = stream.write
1138
Ole Troandf87f802020-11-18 19:17:48 +01001139 if 'Define' in s:
Ole Troan2a1ca782019-09-19 01:08:30 +02001140 write('typedef enum {\n')
1141 for t in s['Define']:
1142 write(' VL_API_{},\n'.format(t.name.upper()))
Ole Troan3d812672020-09-15 10:53:34 +02001143 write(' VL_MSG_{}_LAST\n'.format(module.upper()))
Ole Troan2a1ca782019-09-19 01:08:30 +02001144 write('}} vl_api_{}_enum_t;\n'.format(module))
1145
Paul Vinciguerra7c8803d2019-11-21 17:16:18 -05001146
Ole Troandf87f802020-11-18 19:17:48 +01001147def generate_include_counters(s, stream):
1148 '''Include file for the counter data model types.'''
Ole Troan148c7b72020-10-07 18:05:37 +02001149 write = stream.write
1150
1151 for counters in s:
1152 csetname = counters.name
1153 write('typedef enum {\n')
1154 for c in counters.block:
1155 write(' {}_ERROR_{},\n'
1156 .format(csetname.upper(), c['name'].upper()))
1157 write(' {}_N_ERROR\n'.format(csetname.upper()))
1158 write('}} vl_counter_{}_enum_t;\n'.format(csetname))
1159
Ole Troan148c7b72020-10-07 18:05:37 +02001160 write('extern vl_counter_t {}_error_counters[];\n'.format(csetname))
1161
Ole Troandf87f802020-11-18 19:17:48 +01001162
Ole Troan2a1ca782019-09-19 01:08:30 +02001163def generate_include_types(s, module, stream):
Ole Troandf87f802020-11-18 19:17:48 +01001164 '''Generate separate API _types file.'''
Ole Troan2a1ca782019-09-19 01:08:30 +02001165 write = stream.write
1166
1167 write('#ifndef included_{module}_api_types_h\n'.format(module=module))
1168 write('#define included_{module}_api_types_h\n'.format(module=module))
1169
Ole Troanf92bfb12020-02-28 13:45:42 +01001170 if 'version' in s['Option']:
1171 v = s['Option']['version']
1172 (major, minor, patch) = v.split('.')
Ole Troandf87f802020-11-18 19:17:48 +01001173 write('#define VL_API_{m}_API_VERSION_MAJOR {v}\n'
1174 .format(m=module.upper(), v=major))
1175 write('#define VL_API_{m}_API_VERSION_MINOR {v}\n'
1176 .format(m=module.upper(), v=minor))
1177 write('#define VL_API_{m}_API_VERSION_PATCH {v}\n'
1178 .format(m=module.upper(), v=patch))
Ole Troanf92bfb12020-02-28 13:45:42 +01001179
Ole Troandf87f802020-11-18 19:17:48 +01001180 if 'Import' in s:
Ole Troan2a1ca782019-09-19 01:08:30 +02001181 write('/* Imported API files */\n')
1182 for i in s['Import']:
1183 filename = i.filename.replace('plugins/', '')
1184 write('#include <{}_types.h>\n'.format(filename))
1185
1186 for o in s['types'] + s['Define']:
1187 tname = o.__class__.__name__
1188 if tname == 'Using':
1189 if 'length' in o.alias:
Ole Troandf87f802020-11-18 19:17:48 +01001190 write('typedef %s vl_api_%s_t[%s];\n' %
1191 (o.alias['type'], o.name, o.alias['length']))
Ole Troan2a1ca782019-09-19 01:08:30 +02001192 else:
1193 write('typedef %s vl_api_%s_t;\n' % (o.alias['type'], o.name))
1194 elif tname == 'Enum':
1195 if o.enumtype == 'u32':
1196 write("typedef enum {\n")
1197 else:
1198 write("typedef enum __attribute__((packed)) {\n")
1199
1200 for b in o.block:
1201 write(" %s = %s,\n" % (b[0], b[1]))
1202 write('} vl_api_%s_t;\n' % o.name)
1203 if o.enumtype != 'u32':
1204 size1 = 'sizeof(vl_api_%s_t)' % o.name
1205 size2 = 'sizeof(%s)' % o.enumtype
1206 err_str = 'size of API enum %s is wrong' % o.name
1207 write('STATIC_ASSERT(%s == %s, "%s");\n'
1208 % (size1, size2, err_str))
1209 else:
1210 if tname == 'Union':
Ole Troandf87f802020-11-18 19:17:48 +01001211 write("typedef union __attribute__ ((packed)) _vl_api_%s {\n"
1212 % o.name)
Ole Troan2a1ca782019-09-19 01:08:30 +02001213 else:
1214 write(("typedef struct __attribute__ ((packed)) _vl_api_%s {\n")
Ole Troandf87f802020-11-18 19:17:48 +01001215 % o.name)
Ole Troan2a1ca782019-09-19 01:08:30 +02001216 for b in o.block:
1217 if b.type == 'Option':
1218 continue
1219 if b.type == 'Field':
Ole Troandf87f802020-11-18 19:17:48 +01001220 write(" %s %s;\n" % (api2c(b.fieldtype),
1221 b.fieldname))
Ole Troan2a1ca782019-09-19 01:08:30 +02001222 elif b.type == 'Array':
1223 if b.lengthfield:
Ole Troandf87f802020-11-18 19:17:48 +01001224 write(" %s %s[0];\n" % (api2c(b.fieldtype),
1225 b.fieldname))
Ole Troan2a1ca782019-09-19 01:08:30 +02001226 else:
1227 # Fixed length strings decay to nul terminated u8
1228 if b.fieldtype == 'string':
1229 if b.modern_vla:
1230 write(' {} {};\n'
1231 .format(api2c(b.fieldtype),
1232 b.fieldname))
1233 else:
1234 write(' u8 {}[{}];\n'
1235 .format(b.fieldname, b.length))
1236 else:
1237 write(" %s %s[%s];\n" %
1238 (api2c(b.fieldtype), b.fieldname,
1239 b.length))
1240 else:
1241 raise ValueError("Error in processing type {} for {}"
1242 .format(b, o.name))
1243
1244 write('} vl_api_%s_t;\n' % o.name)
1245
Ole Troan3f2d5712019-12-07 00:39:49 +01001246 for t in s['Define']:
1247 write('#define VL_API_{ID}_CRC "{n}_{crc:08x}"\n'
1248 .format(n=t.name, ID=t.name.upper(), crc=t.crc))
1249
Ole Troan2a1ca782019-09-19 01:08:30 +02001250 write("\n#endif\n")
1251
1252
Ole Troan148c7b72020-10-07 18:05:37 +02001253def generate_c_boilerplate(services, defines, counters, file_crc,
1254 module, stream):
Ole Troandf87f802020-11-18 19:17:48 +01001255 '''VPP side plugin.'''
Ole Troan2a1ca782019-09-19 01:08:30 +02001256 write = stream.write
Ole Troan148c7b72020-10-07 18:05:37 +02001257 define_hash = {d.name: d for d in defines}
Ole Troan2a1ca782019-09-19 01:08:30 +02001258
1259 hdr = '''\
1260#define vl_endianfun /* define message structures */
1261#include "{module}.api.h"
1262#undef vl_endianfun
1263
1264/* instantiate all the print functions we know about */
1265#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1266#define vl_printfun
1267#include "{module}.api.h"
1268#undef vl_printfun
1269
1270'''
1271
1272 write(hdr.format(module=module))
1273 write('static u16\n')
1274 write('setup_message_id_table (void) {\n')
Dave Barach39d69112019-11-27 11:42:13 -05001275 write(' api_main_t *am = my_api_main;\n')
Ole Troane796a182020-05-18 11:14:05 +02001276 write(' vl_msg_api_msg_config_t c;\n')
Ole Troandf87f802020-11-18 19:17:48 +01001277 write(' u16 msg_id_base = vl_msg_api_get_msg_ids ("{}_{crc:08x}", '
1278 'VL_MSG_{m}_LAST);\n'
Ole Troan3d812672020-09-15 10:53:34 +02001279 .format(module, crc=file_crc, m=module.upper()))
Ole Troan2a1ca782019-09-19 01:08:30 +02001280
Ole Troan2a1ca782019-09-19 01:08:30 +02001281 for d in defines:
1282 write(' vl_msg_api_add_msg_name_crc (am, "{n}_{crc:08x}",\n'
1283 ' VL_API_{ID} + msg_id_base);\n'
1284 .format(n=d.name, ID=d.name.upper(), crc=d.crc))
1285 for s in services:
Ole Troane796a182020-05-18 11:14:05 +02001286 d = define_hash[s.caller]
Ole Troandf87f802020-11-18 19:17:48 +01001287 write(' c = (vl_msg_api_msg_config_t) '
1288 ' {{.id = VL_API_{ID} + msg_id_base,\n'
1289 ' .name = "{n}",\n'
1290 ' .handler = vl_api_{n}_t_handler,\n'
1291 ' .cleanup = vl_noop_handler,\n'
1292 ' .endian = vl_api_{n}_t_endian,\n'
1293 ' .print = vl_api_{n}_t_print,\n'
1294 ' .is_autoendian = 0}};\n'
Ole Troan2a1ca782019-09-19 01:08:30 +02001295 .format(n=s.caller, ID=s.caller.upper()))
Ole Troane796a182020-05-18 11:14:05 +02001296 write(' vl_msg_api_config (&c);\n')
Ole Troanbad67922020-08-24 12:22:01 +02001297 try:
1298 d = define_hash[s.reply]
Ole Troandf87f802020-11-18 19:17:48 +01001299 write(' c = (vl_msg_api_msg_config_t) '
1300 '{{.id = VL_API_{ID} + msg_id_base,\n'
1301 ' .name = "{n}",\n'
1302 ' .handler = 0,\n'
1303 ' .cleanup = vl_noop_handler,\n'
1304 ' .endian = vl_api_{n}_t_endian,\n'
1305 ' .print = vl_api_{n}_t_print,\n'
1306 ' .is_autoendian = 0}};\n'
Ole Troanbad67922020-08-24 12:22:01 +02001307 .format(n=s.reply, ID=s.reply.upper()))
1308 write(' vl_msg_api_config (&c);\n')
1309 except KeyError:
1310 pass
Ole Troan2a1ca782019-09-19 01:08:30 +02001311
1312 write(' return msg_id_base;\n')
1313 write('}\n')
1314
Ole Troan148c7b72020-10-07 18:05:37 +02001315 severity = {'error': 'VL_COUNTER_SEVERITY_ERROR',
1316 'info': 'VL_COUNTER_SEVERITY_INFO',
1317 'warn': 'VL_COUNTER_SEVERITY_WARN'}
1318
1319 for cnt in counters:
1320 csetname = cnt.name
Ole Troan148c7b72020-10-07 18:05:37 +02001321 write('vl_counter_t {}_error_counters[] = {{\n'.format(csetname))
1322 for c in cnt.block:
1323 write(' {\n')
1324 write(' .name = "{}",\n'.format(c['name']))
1325 write(' .desc = "{}",\n'.format(c['description']))
1326 write(' .severity = {},\n'.format(severity[c['severity']]))
1327 write(' },\n')
1328 write('};\n')
Ole Troan2a1ca782019-09-19 01:08:30 +02001329
Ole Troandf87f802020-11-18 19:17:48 +01001330
1331def generate_c_test_boilerplate(services, defines, file_crc, module, plugin,
1332 stream):
1333 '''Generate code for legacy style VAT. To be deleted.'''
Ole Troan2a1ca782019-09-19 01:08:30 +02001334 write = stream.write
1335
Ole Troandf87f802020-11-18 19:17:48 +01001336 define_hash = {d.name: d for d in defines}
Ole Troan2a1ca782019-09-19 01:08:30 +02001337
1338 hdr = '''\
Ole Troandf87f802020-11-18 19:17:48 +01001339#define vl_endianfun /* define message structures */
Ole Troan2a1ca782019-09-19 01:08:30 +02001340#include "{module}.api.h"
1341#undef vl_endianfun
1342
1343/* instantiate all the print functions we know about */
1344#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1345#define vl_printfun
1346#include "{module}.api.h"
1347#undef vl_printfun
1348
1349'''
1350
1351 write(hdr.format(module=module))
1352 for s in services:
1353 try:
1354 d = define_hash[s.reply]
Ole Troandf87f802020-11-18 19:17:48 +01001355 except KeyError:
Ole Troan2a1ca782019-09-19 01:08:30 +02001356 continue
1357 if d.manual_print:
Ole Troandf87f802020-11-18 19:17:48 +01001358 write('/*\n'
1359 ' * Manual definition requested for: \n'
1360 ' * vl_api_{n}_t_handler()\n'
1361 ' */\n'
Ole Troan2a1ca782019-09-19 01:08:30 +02001362 .format(n=s.reply))
1363 continue
1364 if not define_hash[s.caller].autoreply:
Ole Troandf87f802020-11-18 19:17:48 +01001365 write('/* Generation not supported (vl_api_{n}_t_handler()) */\n'
Ole Troan2a1ca782019-09-19 01:08:30 +02001366 .format(n=s.reply))
1367 continue
Paul Vinciguerra7c8803d2019-11-21 17:16:18 -05001368 write('#ifndef VL_API_{n}_T_HANDLER\n'.format(n=s.reply.upper()))
Ole Troan2a1ca782019-09-19 01:08:30 +02001369 write('static void\n')
Ole Troandf87f802020-11-18 19:17:48 +01001370 write('vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n'
1371 .format(n=s.reply))
Ole Troan2a1ca782019-09-19 01:08:30 +02001372 write(' vat_main_t * vam = {}_test_main.vat_main;\n'.format(module))
1373 write(' i32 retval = ntohl(mp->retval);\n')
1374 write(' if (vam->async_mode) {\n')
1375 write(' vam->async_errors += (retval < 0);\n')
1376 write(' } else {\n')
1377 write(' vam->retval = retval;\n')
1378 write(' vam->result_ready = 1;\n')
1379 write(' }\n')
1380 write('}\n')
Ole Troan3ae9f5a2019-10-09 12:39:32 +02001381 write('#endif\n')
Ole Troan2a1ca782019-09-19 01:08:30 +02001382
Ole Troanb126ebc2019-10-07 16:22:00 +02001383 for e in s.events:
1384 if define_hash[e].manual_print:
1385 continue
1386 write('static void\n')
1387 write('vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n'.format(n=e))
1388 write(' vl_print(0, "{n} event called:");\n'.format(n=e))
1389 write(' vl_api_{n}_t_print(mp, 0);\n'.format(n=e))
1390 write('}\n')
1391
Ole Troan2a1ca782019-09-19 01:08:30 +02001392 write('static void\n')
1393 write('setup_message_id_table (vat_main_t * vam, u16 msg_id_base) {\n')
1394 for s in services:
Ole Troandf87f802020-11-18 19:17:48 +01001395 write(' vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, '
1396 ' "{n}",\n'
1397 ' vl_api_{n}_t_handler, '
1398 ' vl_noop_handler,\n'
1399 ' vl_api_{n}_t_endian, '
1400 ' vl_api_{n}_t_print,\n'
Ole Troan2a1ca782019-09-19 01:08:30 +02001401 ' sizeof(vl_api_{n}_t), 1);\n'
1402 .format(n=s.reply, ID=s.reply.upper()))
Ole Troandf87f802020-11-18 19:17:48 +01001403 write(' hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'
1404 .format(n=s.caller))
Ole Troan2a1ca782019-09-19 01:08:30 +02001405 try:
1406 write(' hash_set_mem (vam->help_by_name, "{n}", "{help}");\n'
Ole Troandf87f802020-11-18 19:17:48 +01001407 .format(n=s.caller,
1408 help=define_hash[s.caller].options['vat_help']))
1409 except KeyError:
Ole Troan2a1ca782019-09-19 01:08:30 +02001410 pass
1411
Ole Troanb126ebc2019-10-07 16:22:00 +02001412 # Events
1413 for e in s.events:
Ole Troandf87f802020-11-18 19:17:48 +01001414 write(' vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, '
1415 ' "{n}",\n'
1416 ' vl_api_{n}_t_handler, '
1417 ' vl_noop_handler,\n'
1418 ' vl_api_{n}_t_endian, '
1419 ' vl_api_{n}_t_print,\n'
Ole Troanb126ebc2019-10-07 16:22:00 +02001420 ' sizeof(vl_api_{n}_t), 1);\n'
1421 .format(n=e, ID=e.upper()))
1422
Ole Troan2a1ca782019-09-19 01:08:30 +02001423 write('}\n')
Ole Troan3d812672020-09-15 10:53:34 +02001424 if plugin:
1425 write('clib_error_t * vat_plugin_register (vat_main_t *vam)\n')
1426 else:
Ole Troandf87f802020-11-18 19:17:48 +01001427 write('clib_error_t * vat_{}_plugin_register (vat_main_t *vam)\n'
1428 .format(module))
Ole Troan2a1ca782019-09-19 01:08:30 +02001429 write('{\n')
1430 write(' {n}_test_main_t * mainp = &{n}_test_main;\n'.format(n=module))
1431 write(' mainp->vat_main = vam;\n')
Ole Troandf87f802020-11-18 19:17:48 +01001432 write(' mainp->msg_id_base = vl_client_get_first_plugin_msg_id '
1433 ' ("{n}_{crc:08x}");\n'
Ole Troan2a1ca782019-09-19 01:08:30 +02001434 .format(n=module, crc=file_crc))
1435 write(' if (mainp->msg_id_base == (u16) ~0)\n')
Ole Troandf87f802020-11-18 19:17:48 +01001436 write(' return clib_error_return (0, "{} plugin not loaded...");\n'
1437 .format(module))
Ole Troan2a1ca782019-09-19 01:08:30 +02001438 write(' setup_message_id_table (vam, mainp->msg_id_base);\n')
Ole Troan709dad32019-10-09 14:38:14 +02001439 write('#ifdef VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE\n')
1440 write(' VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE(vam);\n')
1441 write('#endif\n')
Ole Troan2a1ca782019-09-19 01:08:30 +02001442 write(' return 0;\n')
1443 write('}\n')
1444
Ole Troandf87f802020-11-18 19:17:48 +01001445
1446def apifunc(func):
1447 '''Check if a method is generated already.'''
1448 def _f(module, d, processed, *args):
1449 if d.name in processed:
1450 return None
1451 processed[d.name] = True
1452 return func(module, d, *args)
1453 return _f
1454
1455
1456def c_test_api_service(s, dump, stream):
1457 '''Generate JSON code for a service.'''
1458 write = stream.write
1459
1460 req_reply_template = '''\
1461static cJSON *
1462api_{n} (cJSON *o)
1463{{
1464 vl_api_{n}_t *mp;
1465 int len;
1466 if (!o) return 0;
1467 mp = vl_api_{n}_t_fromjson(o, &len);
1468 if (!mp) {{
1469 fprintf(stderr, "Failed converting JSON to API\\n");
1470 return 0;
1471 }}
1472
1473 mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1474 vl_api_{n}_t_endian(mp);
1475 vac_write((char *)mp, len);
1476 free(mp);
1477
1478 /* Read reply */
1479 char *p;
1480 int l;
1481 vac_read(&p, &l, 5); // XXX: Fix timeout
1482 // XXX Will fail in case of event received. Do loop
1483 if (ntohs(*((u16 *)p)) != vac_get_msg_index(VL_API_{R}_CRC)) {{
1484 fprintf(stderr, "Mismatched reply\\n");
1485 return 0;
1486 }}
1487 vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1488 vl_api_{r}_t_endian(rmp);
1489 return vl_api_{r}_t_tojson(rmp);
1490}}
1491
1492'''
1493 dump_details_template = '''\
1494static cJSON *
1495api_{n} (cJSON *o)
1496{{
1497 u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1498 int len;
1499 if (!o) return 0;
1500 vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1501 if (!mp) {{
1502 fprintf(stderr, "Failed converting JSON to API\\n");
1503 return 0;
1504 }}
1505 mp->_vl_msg_id = msg_id;
1506 vl_api_{n}_t_endian(mp);
1507 vac_write((char *)mp, len);
1508 free(mp);
1509
1510 vat2_control_ping(123); // FIX CONTEXT
1511 cJSON *reply = cJSON_CreateArray();
1512
1513 u16 ping_reply_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_REPLY_CRC);
1514 u16 details_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1515
1516 while (1) {{
1517 /* Read reply */
1518 char *p;
1519 int l;
1520 vac_read(&p, &l, 5); // XXX: Fix timeout
1521
1522 /* Message can be one of [_details, control_ping_reply
1523 * or unrelated event]
1524 */
1525 u16 reply_msg_id = ntohs(*((u16 *)p));
1526 if (reply_msg_id == ping_reply_msg_id) {{
1527 break;
1528 }}
1529
1530 if (reply_msg_id == details_msg_id) {{
1531 vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1532 vl_api_{r}_t_endian(rmp);
1533 cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1534 }}
1535 }}
1536 return reply;
1537}}
1538
1539'''
1540 gets_details_reply_template = '''\
1541static cJSON *
1542api_{n} (cJSON *o)
1543{{
1544 u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1545 int len = 0;
1546 if (!o) return 0;
1547 vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1548 if (!mp) {{
1549 fprintf(stderr, "Failed converting JSON to API\\n");
1550 return 0;
1551 }}
1552 mp->_vl_msg_id = msg_id;
1553
1554 vl_api_{n}_t_endian(mp);
1555 vac_write((char *)mp, len);
1556 free(mp);
1557
1558 cJSON *reply = cJSON_CreateArray();
1559
1560 u16 reply_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1561 u16 details_msg_id = vac_get_msg_index(VL_API_{D}_CRC);
1562
1563 while (1) {{
1564 /* Read reply */
1565 char *p;
1566 int l;
1567 vac_read(&p, &l, 5); // XXX: Fix timeout
1568
1569 /* Message can be one of [_details, control_ping_reply
1570 * or unrelated event]
1571 */
1572 u16 msg_id = ntohs(*((u16 *)p));
1573 if (msg_id == reply_msg_id) {{
1574 vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1575 vl_api_{r}_t_endian(rmp);
1576 cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1577 break;
1578 }}
1579
1580 if (msg_id == details_msg_id) {{
1581 vl_api_{d}_t *rmp = (vl_api_{d}_t *)p;
1582 vl_api_{d}_t_endian(rmp);
1583 cJSON_AddItemToArray(reply, vl_api_{d}_t_tojson(rmp));
1584 }}
1585 }}
1586 return reply;
1587}}
1588
1589'''
1590
1591 if dump:
1592 if s.stream_message:
1593 write(gets_details_reply_template
1594 .format(n=s.caller, r=s.reply, N=s.caller.upper(),
1595 R=s.reply.upper(), d=s.stream_message,
1596 D=s.stream_message.upper()))
1597 else:
1598 write(dump_details_template.format(n=s.caller, r=s.reply,
1599 N=s.caller.upper(),
1600 R=s.reply.upper()))
1601 else:
1602 write(req_reply_template.format(n=s.caller, r=s.reply,
1603 N=s.caller.upper(),
1604 R=s.reply.upper()))
1605
1606
1607def generate_c_test2_boilerplate(services, defines, module, stream):
1608 '''Generate code for VAT2 plugin.'''
1609 write = stream.write
1610
1611 define_hash = {d.name: d for d in defines}
1612 # replies = {}
1613
1614 hdr = '''\
1615#include <vlibapi/api.h>
1616#include <vlibmemory/api.h>
1617#include <vppinfra/error.h>
1618#include <vnet/ip/ip_format_fns.h>
1619#include <vnet/ethernet/ethernet_format_fns.h>
1620
1621#define vl_typedefs /* define message structures */
1622#include <vpp/api/vpe_all_api_h.h>
1623#undef vl_typedefs
1624
1625#include "{module}.api_enum.h"
1626#include "{module}.api_types.h"
1627
1628#define vl_endianfun /* define message structures */
1629#include "{module}.api.h"
1630#undef vl_endianfun
1631
1632#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1633#define vl_printfun
1634#include "{module}.api.h"
1635#undef vl_printfun
1636
1637#include "{module}.api_tojson.h"
1638#include "{module}.api_fromjson.h"
1639#include <vpp-api/client/vppapiclient.h>
1640
1641#include <vat2/vat2_helpers.h>
1642
1643'''
1644
1645 write(hdr.format(module=module))
1646
1647 for s in services:
1648 if s.reply not in define_hash:
1649 continue
1650 c_test_api_service(s, s.stream, stream)
1651
1652 write('void vat2_register_function(char *, cJSON * (*)(cJSON *));\n')
1653 # write('__attribute__((constructor))')
1654 write('clib_error_t *\n')
1655 write('vat2_register_plugin (void) {\n')
1656 for s in services:
1657 write(' vat2_register_function("{n}", api_{n});\n'
1658 .format(n=s.caller))
1659 write(' return 0;\n')
1660 write('}\n')
1661
1662
Ole Troan9d420872017-10-12 13:06:35 +02001663#
1664# Plugin entry point
1665#
Ole Troandf87f802020-11-18 19:17:48 +01001666def run(args, apifilename, s):
1667 '''Main plugin entry point.'''
Ole Troan33a58172019-09-04 09:12:29 +02001668 stream = StringIO()
Ole Troan2a1ca782019-09-19 01:08:30 +02001669
1670 if not args.outputdir:
1671 sys.stderr.write('Missing --outputdir argument')
1672 return None
1673
Ole Troandf87f802020-11-18 19:17:48 +01001674 basename = os.path.basename(apifilename)
1675 filename, _ = os.path.splitext(basename)
Ole Troan33a58172019-09-04 09:12:29 +02001676 modulename = filename.replace('.', '_')
Ole Troan2a1ca782019-09-19 01:08:30 +02001677 filename_enum = os.path.join(args.outputdir + '/' + basename + '_enum.h')
1678 filename_types = os.path.join(args.outputdir + '/' + basename + '_types.h')
1679 filename_c = os.path.join(args.outputdir + '/' + basename + '.c')
1680 filename_c_test = os.path.join(args.outputdir + '/' + basename + '_test.c')
Ole Troandf87f802020-11-18 19:17:48 +01001681 filename_c_test2 = (os.path.join(args.outputdir + '/' + basename +
1682 '_test2.c'))
1683 filename_c_tojson = (os.path.join(args.outputdir +
1684 '/' + basename + '_tojson.h'))
1685 filename_c_fromjson = (os.path.join(args.outputdir + '/' +
1686 basename + '_fromjson.h'))
Ole Troan2a1ca782019-09-19 01:08:30 +02001687
1688 # Generate separate types file
1689 st = StringIO()
1690 generate_include_types(s, modulename, st)
Ole Troandf87f802020-11-18 19:17:48 +01001691 with open(filename_types, 'w') as fd:
1692 st.seek(0)
1693 shutil.copyfileobj(st, fd)
Ole Troan2a1ca782019-09-19 01:08:30 +02001694 st.close()
1695
1696 # Generate separate enum file
1697 st = StringIO()
Ole Troan148c7b72020-10-07 18:05:37 +02001698 st.write('#ifndef included_{}_api_enum_h\n'.format(modulename))
1699 st.write('#define included_{}_api_enum_h\n'.format(modulename))
Ole Troan2a1ca782019-09-19 01:08:30 +02001700 generate_include_enum(s, modulename, st)
Ole Troandf87f802020-11-18 19:17:48 +01001701 generate_include_counters(s['Counters'], st)
Ole Troan148c7b72020-10-07 18:05:37 +02001702 st.write('#endif\n')
Ole Troandf87f802020-11-18 19:17:48 +01001703 with open(filename_enum, 'w') as fd:
1704 st.seek(0)
1705 shutil.copyfileobj(st, fd)
Ole Troan2a1ca782019-09-19 01:08:30 +02001706 st.close()
1707
1708 # Generate separate C file
1709 st = StringIO()
Ole Troan148c7b72020-10-07 18:05:37 +02001710 generate_c_boilerplate(s['Service'], s['Define'], s['Counters'],
1711 s['file_crc'], modulename, st)
Ole Troandf87f802020-11-18 19:17:48 +01001712 with open(filename_c, 'w') as fd:
1713 st.seek(0)
Ole Troan2a1ca782019-09-19 01:08:30 +02001714 shutil.copyfileobj(st, fd)
1715 st.close()
1716
1717 # Generate separate C test file
Ole Troan2a1ca782019-09-19 01:08:30 +02001718 st = StringIO()
Ole Troandf87f802020-11-18 19:17:48 +01001719 plugin = bool('plugin' in apifilename)
1720 generate_c_test_boilerplate(s['Service'], s['Define'],
1721 s['file_crc'],
Ole Troan3d812672020-09-15 10:53:34 +02001722 modulename, plugin, st)
Ole Troandf87f802020-11-18 19:17:48 +01001723 with open(filename_c_test, 'w') as fd:
1724 st.seek(0)
Ole Troan2a1ca782019-09-19 01:08:30 +02001725 shutil.copyfileobj(st, fd)
1726 st.close()
Ole Troan33a58172019-09-04 09:12:29 +02001727
Ole Troandf87f802020-11-18 19:17:48 +01001728 # Fully autogenerated VATv2 C test file
1729 st = StringIO()
1730 generate_c_test2_boilerplate(s['Service'], s['Define'],
1731 modulename, st)
1732 with open(filename_c_test2, 'w') as fd:
1733 st.seek(0)
1734 shutil.copyfileobj(st, fd)
1735 st.close() #
1736
1737 # Generate separate JSON file
1738 st = StringIO()
1739 generate_tojson(s, modulename, st)
1740 with open(filename_c_tojson, 'w') as fd:
1741 st.seek(0)
1742 shutil.copyfileobj(st, fd)
1743 st.close()
1744 st = StringIO()
1745 generate_fromjson(s, modulename, st)
1746 with open(filename_c_fromjson, 'w') as fd:
1747 st.seek(0)
1748 shutil.copyfileobj(st, fd)
1749 st.close()
1750
1751 output = TOP_BOILERPLATE.format(datestring=DATESTRING,
Ole Troan9d420872017-10-12 13:06:35 +02001752 input_filename=basename)
Ole Troandf87f802020-11-18 19:17:48 +01001753 output += generate_imports(s['Import'])
Ole Troan9d420872017-10-12 13:06:35 +02001754 output += msg_ids(s)
1755 output += msg_names(s)
1756 output += msg_name_crc_list(s, filename)
Ole Troan2a1ca782019-09-19 01:08:30 +02001757 output += typedefs(modulename)
Ole Troan75761b92019-09-11 17:49:08 +02001758 printfun_types(s['types'], stream, modulename)
Ole Troan33a58172019-09-04 09:12:29 +02001759 printfun(s['Define'], stream, modulename)
1760 output += stream.getvalue()
Ole Troan2a1ca782019-09-19 01:08:30 +02001761 stream.close()
Ole Troan75761b92019-09-11 17:49:08 +02001762 output += endianfun(s['types'] + s['Define'], modulename)
Ole Troan9d420872017-10-12 13:06:35 +02001763 output += version_tuple(s, basename)
Ole Troandf87f802020-11-18 19:17:48 +01001764 output += BOTTOM_BOILERPLATE.format(input_filename=basename,
Ole Troan8dbfb432019-04-24 14:31:18 +02001765 file_crc=s['file_crc'])
Ole Troan9d420872017-10-12 13:06:35 +02001766
1767 return output