tests docs: update python3 venv packages
[vpp.git] / src / vpp-api / vapi / vapi_c_gen.py
1 #!/usr/bin/env python3
2
3 import argparse
4 import inspect
5 import os
6 import sys
7 import logging
8 from vapi_json_parser import (
9     Field,
10     Struct,
11     Enum,
12     Union,
13     Message,
14     JsonParser,
15     SimpleType,
16     StructType,
17     Alias,
18 )
19
20
21 class CField(Field):
22     def get_c_name(self):
23         return "vapi_type_%s" % self.name
24
25     def get_c_def(self):
26         if self.type.get_c_name() == "vl_api_string_t":
27             if self.len:
28                 return "u8 %s[%d];" % (self.name, self.len)
29             else:
30                 return "vl_api_string_t %s;" % (self.name)
31         else:
32             if self.len is not None and type(self.len) != dict:
33                 return "%s %s[%d];" % (self.type.get_c_name(), self.name, self.len)
34             else:
35                 return "%s %s;" % (self.type.get_c_name(), self.name)
36
37     def get_swap_to_be_code(self, struct, var):
38         if self.len is not None and type(self.len) != dict:
39             if self.len > 0:
40                 return (
41                     "do { unsigned i; for (i = 0; i < %d; ++i) { %s } }"
42                     " while(0);"
43                     % (self.len, self.type.get_swap_to_be_code(struct, "%s[i]" % var))
44                 )
45             else:
46                 if self.nelem_field.needs_byte_swap():
47                     nelem_field = "%s(%s%s)" % (
48                         self.nelem_field.type.get_swap_to_host_func_name(),
49                         struct,
50                         self.nelem_field.name,
51                     )
52                 else:
53                     nelem_field = "%s%s" % (struct, self.nelem_field.name)
54                 return (
55                     "do { unsigned i; for (i = 0; i < %s; ++i) { %s } }"
56                     " while(0);"
57                     % (
58                         nelem_field,
59                         self.type.get_swap_to_be_code(struct, "%s[i]" % var),
60                     )
61                 )
62         return self.type.get_swap_to_be_code(struct, "%s" % var)
63
64     def get_swap_to_host_code(self, struct, var):
65         if self.len is not None and type(self.len) != dict:
66             if self.len > 0:
67                 return (
68                     "do { unsigned i; for (i = 0; i < %d; ++i) { %s } }"
69                     " while(0);"
70                     % (self.len, self.type.get_swap_to_host_code(struct, "%s[i]" % var))
71                 )
72             else:
73                 # nelem_field already swapped to host here...
74                 return (
75                     "do { unsigned i; for (i = 0; i < %s%s; ++i) { %s } }"
76                     " while(0);"
77                     % (
78                         struct,
79                         self.nelem_field.name,
80                         self.type.get_swap_to_host_code(struct, "%s[i]" % var),
81                     )
82                 )
83         return self.type.get_swap_to_host_code(struct, "%s" % var)
84
85     def needs_byte_swap(self):
86         return self.type.needs_byte_swap()
87
88     def get_vla_field_length_name(self, path):
89         return "%s_%s_array_size" % ("_".join(path), self.name)
90
91     def get_alloc_vla_param_names(self, path):
92         if self.is_vla():
93             result = [self.get_vla_field_length_name(path)]
94         else:
95             result = []
96         if self.type.has_vla():
97             t = self.type.get_alloc_vla_param_names(path + [self.name])
98             result.extend(t)
99         return result
100
101     def get_vla_calc_size_code(self, prefix, path):
102         if self.is_vla():
103             result = [
104                 "sizeof(%s.%s[0]) * %s"
105                 % (
106                     ".".join([prefix] + path),
107                     self.name,
108                     self.get_vla_field_length_name(path),
109                 )
110             ]
111         else:
112             result = []
113         if self.type.has_vla():
114             t = self.type.get_vla_calc_size_code(prefix, path + [self.name])
115             result.extend(t)
116         return result
117
118     def get_vla_assign_code(self, prefix, path):
119         result = []
120         if self.is_vla():
121             result.append(
122                 "%s.%s = %s"
123                 % (
124                     ".".join([prefix] + path),
125                     self.nelem_field.name,
126                     self.get_vla_field_length_name(path),
127                 )
128             )
129         if self.type.has_vla():
130             t = self.type.get_vla_assign_code(prefix, path + [self.name])
131             result.extend(t)
132         return result
133
134
135 class CAlias(CField):
136     def get_c_name(self):
137         return "vapi_type_%s" % self.name
138
139     def get_c_def(self):
140         if self.len is not None:
141             return "typedef %s vapi_type_%s[%d];" % (
142                 self.type.get_c_name(),
143                 self.name,
144                 self.len,
145             )
146         else:
147             return "typedef %s vapi_type_%s;" % (self.type.get_c_name(), self.name)
148
149
150 class CStruct(Struct):
151     def get_c_def(self):
152         return "\n".join(
153             [
154                 "typedef struct __attribute__((__packed__)) {\n%s"
155                 % ("\n".join(["  %s" % x.get_c_def() for x in self.fields])),
156                 "} %s;" % self.get_c_name(),
157             ]
158         )
159
160     def get_vla_assign_code(self, prefix, path):
161         return [
162             x
163             for f in self.fields
164             if f.has_vla()
165             for x in f.get_vla_assign_code(prefix, path)
166         ]
167
168     def get_alloc_vla_param_names(self, path):
169         return [
170             x
171             for f in self.fields
172             if f.has_vla()
173             for x in f.get_alloc_vla_param_names(path)
174         ]
175
176     def get_vla_calc_size_code(self, prefix, path):
177         return [
178             x
179             for f in self.fields
180             if f.has_vla()
181             for x in f.get_vla_calc_size_code(prefix, path)
182         ]
183
184
185 class CSimpleType(SimpleType):
186     swap_to_be_dict = {
187         "i16": "htobe16",
188         "u16": "htobe16",
189         "i32": "htobe32",
190         "u32": "htobe32",
191         "i64": "htobe64",
192         "u64": "htobe64",
193     }
194
195     swap_to_host_dict = {
196         "i16": "be16toh",
197         "u16": "be16toh",
198         "i32": "be32toh",
199         "u32": "be32toh",
200         "i64": "be64toh",
201         "u64": "be64toh",
202     }
203
204     __packed = "__attribute__((packed))"
205     pack_dict = {
206         "i8": __packed,
207         "u8": __packed,
208         "i16": __packed,
209         "u16": __packed,
210     }
211
212     def get_c_name(self):
213         return self.name
214
215     def get_swap_to_be_func_name(self):
216         return self.swap_to_be_dict[self.name]
217
218     def get_swap_to_host_func_name(self):
219         return self.swap_to_host_dict[self.name]
220
221     def get_packed_string(self):
222         return self.pack_dict[self.name]
223
224     def get_swap_to_be_code(self, struct, var, cast=None):
225         x = "%s%s" % (struct, var)
226         return "%s = %s%s(%s);" % (
227             x,
228             "(%s)" % cast if cast else "",
229             self.get_swap_to_be_func_name(),
230             x,
231         )
232
233     def get_swap_to_host_code(self, struct, var, cast=None):
234         x = "%s%s" % (struct, var)
235         return "%s = %s%s(%s);" % (
236             x,
237             "(%s)" % cast if cast else "",
238             self.get_swap_to_host_func_name(),
239             x,
240         )
241
242     def needs_byte_swap(self):
243         try:
244             self.get_swap_to_host_func_name()
245             return True
246         except KeyError:
247             pass
248         return False
249
250     def get_packed(self):
251         return self.pack_dict.get(self.name, "")
252
253
254 class CEnum(Enum):
255     def get_c_name(self):
256         return "vapi_enum_%s" % self.name
257
258     def get_c_def(self):
259         return "typedef enum {\n%s\n} %s %s;" % (
260             "\n".join(["  %s = %s," % (i, j) for i, j in self.value_pairs]),
261             self.type.get_packed(),
262             self.get_c_name(),
263         )
264
265     def needs_byte_swap(self):
266         return self.type.needs_byte_swap()
267
268     def get_swap_to_be_code(self, struct, var):
269         return self.type.get_swap_to_be_code(struct, var, self.get_c_name())
270
271     def get_swap_to_host_code(self, struct, var):
272         return self.type.get_swap_to_host_code(struct, var, self.get_c_name())
273
274
275 class CUnion(Union):
276     def get_c_name(self):
277         return "vapi_union_%s" % self.name
278
279     def get_c_def(self):
280         return "typedef union {\n%s\n} %s;" % (
281             "\n".join(["  %s %s;" % (i.get_c_name(), j) for i, j in self.type_pairs]),
282             self.get_c_name(),
283         )
284
285     def needs_byte_swap(self):
286         return False
287
288
289 class CStructType(StructType, CStruct):
290     def get_c_name(self):
291         return "vapi_type_%s" % self.name
292
293     def get_swap_to_be_func_name(self):
294         return "%s_hton" % self.get_c_name()
295
296     def get_swap_to_host_func_name(self):
297         return "%s_ntoh" % self.get_c_name()
298
299     def get_swap_to_be_func_decl(self):
300         return "void %s(%s *msg)" % (self.get_swap_to_be_func_name(), self.get_c_name())
301
302     def get_swap_to_be_func_def(self):
303         return "%s\n{\n%s\n}" % (
304             self.get_swap_to_be_func_decl(),
305             "\n".join(
306                 [
307                     "  %s" % p.get_swap_to_be_code("msg->", "%s" % p.name)
308                     for p in self.fields
309                     if p.needs_byte_swap()
310                 ]
311             ),
312         )
313
314     def get_swap_to_host_func_decl(self):
315         return "void %s(%s *msg)" % (
316             self.get_swap_to_host_func_name(),
317             self.get_c_name(),
318         )
319
320     def get_swap_to_host_func_def(self):
321         return "%s\n{\n%s\n}" % (
322             self.get_swap_to_host_func_decl(),
323             "\n".join(
324                 [
325                     "  %s" % p.get_swap_to_host_code("msg->", "%s" % p.name)
326                     for p in self.fields
327                     if p.needs_byte_swap()
328                 ]
329             ),
330         )
331
332     def get_swap_to_be_code(self, struct, var):
333         return "%s(&%s%s);" % (self.get_swap_to_be_func_name(), struct, var)
334
335     def get_swap_to_host_code(self, struct, var):
336         return "%s(&%s%s);" % (self.get_swap_to_host_func_name(), struct, var)
337
338     def needs_byte_swap(self):
339         for f in self.fields:
340             if f.needs_byte_swap():
341                 return True
342         return False
343
344
345 class CMessage(Message):
346     def __init__(self, logger, definition, json_parser):
347         super(CMessage, self).__init__(logger, definition, json_parser)
348         self.payload_members = [
349             "  %s" % p.get_c_def() for p in self.fields if p.type != self.header
350         ]
351
352     def has_payload(self):
353         return len(self.payload_members) > 0
354
355     def get_msg_id_name(self):
356         return "vapi_msg_id_%s" % self.name
357
358     def get_c_name(self):
359         return "vapi_msg_%s" % self.name
360
361     def get_payload_struct_name(self):
362         return "vapi_payload_%s" % self.name
363
364     def get_alloc_func_name(self):
365         return "vapi_alloc_%s" % self.name
366
367     def get_alloc_vla_param_names(self):
368         return [
369             x
370             for f in self.fields
371             if f.has_vla()
372             for x in f.get_alloc_vla_param_names([])
373         ]
374
375     def get_alloc_func_decl(self):
376         return "%s* %s(struct vapi_ctx_s *ctx%s)" % (
377             self.get_c_name(),
378             self.get_alloc_func_name(),
379             "".join([", size_t %s" % n for n in self.get_alloc_vla_param_names()]),
380         )
381
382     def get_alloc_func_def(self):
383         extra = []
384         if self.header.has_field("client_index"):
385             extra.append("  msg->header.client_index = vapi_get_client_index(ctx);")
386         if self.header.has_field("context"):
387             extra.append("  msg->header.context = 0;")
388         return "\n".join(
389             [
390                 "%s" % self.get_alloc_func_decl(),
391                 "{",
392                 "  %s *msg = NULL;" % self.get_c_name(),
393                 "  const size_t size = sizeof(%s)%s;"
394                 % (
395                     self.get_c_name(),
396                     "".join(
397                         [
398                             " + %s" % x
399                             for f in self.fields
400                             if f.has_vla()
401                             for x in f.get_vla_calc_size_code("msg->payload", [])
402                         ]
403                     ),
404                 ),
405                 "  /* cast here required to play nicely with C++ world ... */",
406                 "  msg = (%s*)vapi_msg_alloc(ctx, size);" % self.get_c_name(),
407                 "  if (!msg) {",
408                 "    return NULL;",
409                 "  }",
410             ]
411             + extra
412             + [
413                 "  msg->header._vl_msg_id = vapi_lookup_vl_msg_id(ctx, %s);"
414                 % self.get_msg_id_name(),
415                 "".join(
416                     [
417                         "  %s;\n" % line
418                         for f in self.fields
419                         if f.has_vla()
420                         for line in f.get_vla_assign_code("msg->payload", [])
421                     ]
422                 ),
423                 "  return msg;",
424                 "}",
425             ]
426         )
427
428     def get_calc_msg_size_func_name(self):
429         return "vapi_calc_%s_msg_size" % self.name
430
431     def get_calc_msg_size_func_decl(self):
432         return "uword %s(%s *msg)" % (
433             self.get_calc_msg_size_func_name(),
434             self.get_c_name(),
435         )
436
437     def get_calc_msg_size_func_def(self):
438         return "\n".join(
439             [
440                 "%s" % self.get_calc_msg_size_func_decl(),
441                 "{",
442                 "  return sizeof(*msg)%s;"
443                 % "".join(
444                     [
445                         "+ msg->payload.%s * sizeof(msg->payload.%s[0])"
446                         % (f.nelem_field.name, f.name)
447                         for f in self.fields
448                         if f.nelem_field is not None
449                     ]
450                 ),
451                 "}",
452             ]
453         )
454
455     def get_verify_msg_size_func_name(self):
456         return f"vapi_verify_{self.name}_msg_size"
457
458     def get_verify_msg_size_func_decl(self):
459         return "int %s(%s *msg, uword buf_size)" % (
460             self.get_verify_msg_size_func_name(),
461             self.get_c_name(),
462         )
463
464     def get_verify_msg_size_func_def(self):
465         return inspect.cleandoc(
466             f"""
467             {self.get_verify_msg_size_func_decl()}
468             {{
469               if (sizeof({self.get_c_name()}) > buf_size)
470                 {{
471                   VAPI_ERR("Truncated '{self.name}' msg received, received %lu"
472                     "bytes, expected %lu bytes.", buf_size,
473                     sizeof({self.get_c_name()}));
474                   return -1;
475                 }}
476               if ({self.get_calc_msg_size_func_name()}(msg) > buf_size)
477                 {{
478                   VAPI_ERR("Truncated '{self.name}' msg received, received %lu"
479                     "bytes, expected %lu bytes.", buf_size,
480                     sizeof({self.get_calc_msg_size_func_name()}));
481                   return -1;
482                 }}
483               return 0;
484             }}
485         """
486         )
487
488     def get_c_def(self):
489         if self.has_payload():
490             return "\n".join(
491                 [
492                     "typedef struct __attribute__ ((__packed__)) {",
493                     "%s " % "\n".join(self.payload_members),
494                     "} %s;" % self.get_payload_struct_name(),
495                     "",
496                     "typedef struct __attribute__ ((__packed__)) {",
497                     (
498                         "  %s %s;" % (self.header.get_c_name(), self.fields[0].name)
499                         if self.header is not None
500                         else ""
501                     ),
502                     "  %s payload;" % self.get_payload_struct_name(),
503                     "} %s;" % self.get_c_name(),
504                 ]
505             )
506         else:
507             return "\n".join(
508                 [
509                     "typedef struct __attribute__ ((__packed__)) {",
510                     (
511                         "  %s %s;" % (self.header.get_c_name(), self.fields[0].name)
512                         if self.header is not None
513                         else ""
514                     ),
515                     "} %s;" % self.get_c_name(),
516                 ]
517             )
518
519     def get_swap_payload_to_host_func_name(self):
520         return "%s_payload_ntoh" % self.get_c_name()
521
522     def get_swap_payload_to_be_func_name(self):
523         return "%s_payload_hton" % self.get_c_name()
524
525     def get_swap_payload_to_host_func_decl(self):
526         return "void %s(%s *payload)" % (
527             self.get_swap_payload_to_host_func_name(),
528             self.get_payload_struct_name(),
529         )
530
531     def get_swap_payload_to_be_func_decl(self):
532         return "void %s(%s *payload)" % (
533             self.get_swap_payload_to_be_func_name(),
534             self.get_payload_struct_name(),
535         )
536
537     def get_swap_payload_to_be_func_def(self):
538         return "%s\n{\n%s\n}" % (
539             self.get_swap_payload_to_be_func_decl(),
540             "\n".join(
541                 [
542                     "  %s" % p.get_swap_to_be_code("payload->", "%s" % p.name)
543                     for p in self.fields
544                     if p.needs_byte_swap() and p.type != self.header
545                 ]
546             ),
547         )
548
549     def get_swap_payload_to_host_func_def(self):
550         return "%s\n{\n%s\n}" % (
551             self.get_swap_payload_to_host_func_decl(),
552             "\n".join(
553                 [
554                     "  %s" % p.get_swap_to_host_code("payload->", "%s" % p.name)
555                     for p in self.fields
556                     if p.needs_byte_swap() and p.type != self.header
557                 ]
558             ),
559         )
560
561     def get_swap_to_host_func_name(self):
562         return "%s_ntoh" % self.get_c_name()
563
564     def get_swap_to_be_func_name(self):
565         return "%s_hton" % self.get_c_name()
566
567     def get_swap_to_host_func_decl(self):
568         return "void %s(%s *msg)" % (
569             self.get_swap_to_host_func_name(),
570             self.get_c_name(),
571         )
572
573     def get_swap_to_be_func_decl(self):
574         return "void %s(%s *msg)" % (self.get_swap_to_be_func_name(), self.get_c_name())
575
576     def get_swap_to_be_func_def(self):
577         return "\n".join(
578             [
579                 "%s" % self.get_swap_to_be_func_decl(),
580                 "{",
581                 (
582                     '  VAPI_DBG("Swapping `%s\'@%%p to big endian", msg);'
583                     % self.get_c_name()
584                 ),
585                 "  %s(&msg->header);" % self.header.get_swap_to_be_func_name()
586                 if self.header is not None
587                 else "",
588                 "  %s(&msg->payload);" % self.get_swap_payload_to_be_func_name()
589                 if self.has_payload()
590                 else "",
591                 "}",
592             ]
593         )
594
595     def get_swap_to_host_func_def(self):
596         return "\n".join(
597             [
598                 "%s" % self.get_swap_to_host_func_decl(),
599                 "{",
600                 (
601                     '  VAPI_DBG("Swapping `%s\'@%%p to host byte order", msg);'
602                     % self.get_c_name()
603                 ),
604                 "  %s(&msg->header);" % self.header.get_swap_to_host_func_name()
605                 if self.header is not None
606                 else "",
607                 "  %s(&msg->payload);" % self.get_swap_payload_to_host_func_name()
608                 if self.has_payload()
609                 else "",
610                 "}",
611             ]
612         )
613
614     def get_op_func_name(self):
615         return "vapi_%s" % self.name
616
617     def get_op_func_decl(self):
618         if self.reply.has_payload():
619             return "vapi_error_e %s(%s)" % (
620                 self.get_op_func_name(),
621                 ",\n  ".join(
622                     [
623                         "struct vapi_ctx_s *ctx",
624                         "%s *msg" % self.get_c_name(),
625                         "vapi_error_e (*callback)(struct vapi_ctx_s *ctx",
626                         "                         void *callback_ctx",
627                         "                         vapi_error_e rv",
628                         "                         bool is_last",
629                         "                         %s *reply)"
630                         % self.reply.get_payload_struct_name(),
631                         "void *callback_ctx",
632                     ]
633                 ),
634             )
635         else:
636             return "vapi_error_e %s(%s)" % (
637                 self.get_op_func_name(),
638                 ",\n  ".join(
639                     [
640                         "struct vapi_ctx_s *ctx",
641                         "%s *msg" % self.get_c_name(),
642                         "vapi_error_e (*callback)(struct vapi_ctx_s *ctx",
643                         "                         void *callback_ctx",
644                         "                         vapi_error_e rv",
645                         "                         bool is_last)",
646                         "void *callback_ctx",
647                     ]
648                 ),
649             )
650
651     def get_op_func_def(self):
652         return "\n".join(
653             [
654                 "%s" % self.get_op_func_decl(),
655                 "{",
656                 "  if (!msg || !callback) {",
657                 "    return VAPI_EINVAL;",
658                 "  }",
659                 "  if (vapi_is_nonblocking(ctx) && vapi_requests_full(ctx)) {",
660                 "    return VAPI_EAGAIN;",
661                 "  }",
662                 "  vapi_error_e rv;",
663                 "  if (VAPI_OK != (rv = vapi_producer_lock (ctx))) {",
664                 "    return rv;",
665                 "  }",
666                 "  u32 req_context = vapi_gen_req_context(ctx);",
667                 "  msg->header.context = req_context;",
668                 "  %s(msg);" % self.get_swap_to_be_func_name(),
669                 (
670                     "  if (VAPI_OK == (rv = vapi_send_with_control_ping "
671                     "(ctx, msg, req_context))) {"
672                     if self.reply_is_stream
673                     else "  if (VAPI_OK == (rv = vapi_send (ctx, msg))) {"
674                 ),
675                 (
676                     "    vapi_store_request(ctx, req_context, %s, "
677                     "(vapi_cb_t)callback, callback_ctx);"
678                     % ("true" if self.reply_is_stream else "false")
679                 ),
680                 "    if (VAPI_OK != vapi_producer_unlock (ctx)) {",
681                 "      abort (); /* this really shouldn't happen */",
682                 "    }",
683                 "    if (vapi_is_nonblocking(ctx)) {",
684                 "      rv = VAPI_OK;",
685                 "    } else {",
686                 "      rv = vapi_dispatch(ctx);",
687                 "    }",
688                 "  } else {",
689                 "    %s(msg);" % self.get_swap_to_host_func_name(),
690                 "    if (VAPI_OK != vapi_producer_unlock (ctx)) {",
691                 "      abort (); /* this really shouldn't happen */",
692                 "    }",
693                 "  }",
694                 "  return rv;",
695                 "}",
696                 "",
697             ]
698         )
699
700     def get_event_cb_func_decl(self):
701         if not self.is_reply and not self.is_event:
702             raise Exception("Cannot register event callback for non-reply message")
703         if self.has_payload():
704             return "\n".join(
705                 [
706                     "void vapi_set_%s_event_cb (" % self.get_c_name(),
707                     "  struct vapi_ctx_s *ctx, ",
708                     (
709                         "  vapi_error_e (*callback)(struct vapi_ctx_s *ctx, "
710                         "void *callback_ctx, %s *payload),"
711                         % self.get_payload_struct_name()
712                     ),
713                     "  void *callback_ctx)",
714                 ]
715             )
716         else:
717             return "\n".join(
718                 [
719                     "void vapi_set_%s_event_cb (" % self.get_c_name(),
720                     "  struct vapi_ctx_s *ctx, ",
721                     "  vapi_error_e (*callback)(struct vapi_ctx_s *ctx, "
722                     "void *callback_ctx),",
723                     "  void *callback_ctx)",
724                 ]
725             )
726
727     def get_event_cb_func_def(self):
728         if not self.is_reply and not self.is_event:
729             raise Exception("Cannot register event callback for non-reply function")
730         return "\n".join(
731             [
732                 "%s" % self.get_event_cb_func_decl(),
733                 "{",
734                 (
735                     "  vapi_set_event_cb(ctx, %s, (vapi_event_cb)callback, "
736                     "callback_ctx);" % self.get_msg_id_name()
737                 ),
738                 "}",
739             ]
740         )
741
742     def get_c_metadata_struct_name(self):
743         return "__vapi_metadata_%s" % self.name
744
745     def get_c_constructor(self):
746         has_context = False
747         if self.header is not None:
748             has_context = self.header.has_field("context")
749         return "\n".join(
750             [
751                 "static void __attribute__((constructor)) __vapi_constructor_%s()"
752                 % self.name,
753                 "{",
754                 '  static const char name[] = "%s";' % self.name,
755                 '  static const char name_with_crc[] = "%s_%s";'
756                 % (self.name, self.crc[2:]),
757                 "  static vapi_message_desc_t %s = {"
758                 % self.get_c_metadata_struct_name(),
759                 "    name,",
760                 "    sizeof(name) - 1,",
761                 "    name_with_crc,",
762                 "    sizeof(name_with_crc) - 1,",
763                 "    true," if has_context else "    false,",
764                 "    offsetof(%s, context)," % self.header.get_c_name()
765                 if has_context
766                 else "    0,",
767                 ("    offsetof(%s, payload)," % self.get_c_name())
768                 if self.has_payload()
769                 else "    VAPI_INVALID_MSG_ID,",
770                 "    (verify_msg_size_fn_t)%s," % self.get_verify_msg_size_func_name(),
771                 "    (generic_swap_fn_t)%s," % self.get_swap_to_be_func_name(),
772                 "    (generic_swap_fn_t)%s," % self.get_swap_to_host_func_name(),
773                 "    VAPI_INVALID_MSG_ID,",
774                 "  };",
775                 "",
776                 "  %s = vapi_register_msg(&%s);"
777                 % (self.get_msg_id_name(), self.get_c_metadata_struct_name()),
778                 '  VAPI_DBG("Assigned msg id %%d to %s", %s);'
779                 % (self.name, self.get_msg_id_name()),
780                 "}",
781             ]
782         )
783
784
785 def emit_definition(parser, json_file, emitted, o):
786     if o in emitted:
787         return
788     if o.name in ("msg_header1_t", "msg_header2_t"):
789         return
790     if hasattr(o, "depends"):
791         for x in o.depends:
792             emit_definition(parser, json_file, emitted, x)
793     if hasattr(o, "reply"):
794         emit_definition(parser, json_file, emitted, o.reply)
795     if hasattr(o, "get_c_def"):
796         if (
797             o not in parser.enums_by_json[json_file]
798             and o not in parser.types_by_json[json_file]
799             and o not in parser.unions_by_json[json_file]
800             and o.name not in parser.messages_by_json[json_file]
801             and o not in parser.aliases_by_json[json_file]
802         ):
803             return
804         guard = "defined_%s" % o.get_c_name()
805         print("#ifndef %s" % guard)
806         print("#define %s" % guard)
807         print("%s" % o.get_c_def())
808         print("")
809         function_attrs = "static inline "
810         if o.name in parser.messages_by_json[json_file]:
811             if o.has_payload():
812                 print("%s%s" % (function_attrs, o.get_swap_payload_to_be_func_def()))
813                 print("")
814                 print("%s%s" % (function_attrs, o.get_swap_payload_to_host_func_def()))
815                 print("")
816             print("%s%s" % (function_attrs, o.get_swap_to_be_func_def()))
817             print("")
818             print("%s%s" % (function_attrs, o.get_swap_to_host_func_def()))
819             print("")
820             print("%s%s" % (function_attrs, o.get_calc_msg_size_func_def()))
821             print("")
822             print("%s%s" % (function_attrs, o.get_verify_msg_size_func_def()))
823             if not o.is_reply and not o.is_event:
824                 print("")
825                 print("%s%s" % (function_attrs, o.get_alloc_func_def()))
826                 print("")
827                 print("%s%s" % (function_attrs, o.get_op_func_def()))
828             print("")
829             print("%s" % o.get_c_constructor())
830             if o.is_reply or o.is_event:
831                 print("")
832                 print("%s%s;" % (function_attrs, o.get_event_cb_func_def()))
833         elif hasattr(o, "get_swap_to_be_func_def"):
834             print("%s%s" % (function_attrs, o.get_swap_to_be_func_def()))
835             print("")
836             print("%s%s" % (function_attrs, o.get_swap_to_host_func_def()))
837         print("#endif")
838         print("")
839     emitted.append(o)
840
841
842 def gen_json_unified_header(parser, logger, j, io, name):
843     d, f = os.path.split(j)
844     logger.info("Generating header `%s'" % name)
845     orig_stdout = sys.stdout
846     sys.stdout = io
847     include_guard = "__included_%s" % (
848         j.replace(".", "_")
849         .replace("/", "_")
850         .replace("-", "_")
851         .replace("+", "_")
852         .replace("@", "_")
853     )
854     print("#ifndef %s" % include_guard)
855     print("#define %s" % include_guard)
856     print("")
857     print("#include <stdlib.h>")
858     print("#include <stddef.h>")
859     print("#include <arpa/inet.h>")
860     print("#include <vapi/vapi_internal.h>")
861     print("#include <vapi/vapi.h>")
862     print("#include <vapi/vapi_dbg.h>")
863     print("")
864     print("#ifdef __cplusplus")
865     print('extern "C" {')
866     print("#endif")
867     if name == "memclnt.api.vapi.h":
868         print("")
869         print(
870             "static inline vapi_error_e vapi_send_with_control_ping "
871             "(vapi_ctx_t ctx, void * msg, u32 context);"
872         )
873     elif name == "vlib.api.vapi.h":
874         print("#include <vapi/memclnt.api.vapi.h>")
875     else:
876         print("#include <vapi/vlib.api.vapi.h>")
877     print("")
878     for m in parser.messages_by_json[j].values():
879         print("extern vapi_msg_id_t %s;" % m.get_msg_id_name())
880     print("")
881     print(
882         "#define DEFINE_VAPI_MSG_IDS_%s\\"
883         % f.replace(".", "_").replace("/", "_").replace("-", "_").upper()
884     )
885     print(
886         "\\\n".join(
887             [
888                 "  vapi_msg_id_t %s;" % m.get_msg_id_name()
889                 for m in parser.messages_by_json[j].values()
890             ]
891         )
892     )
893     print("")
894     print("")
895     emitted = []
896     for e in parser.enums_by_json[j]:
897         emit_definition(parser, j, emitted, e)
898     for u in parser.unions_by_json[j]:
899         emit_definition(parser, j, emitted, u)
900     for t in parser.types_by_json[j]:
901         emit_definition(parser, j, emitted, t)
902     for a in parser.aliases_by_json[j]:
903         emit_definition(parser, j, emitted, a)
904     for m in parser.messages_by_json[j].values():
905         emit_definition(parser, j, emitted, m)
906
907     print("")
908
909     if name == "vlib.api.vapi.h":
910         vapi_send_with_control_ping_function = """
911 static inline vapi_error_e
912 vapi_send_with_control_ping (vapi_ctx_t ctx, void *msg, u32 context)
913 {
914   vapi_msg_control_ping *ping = vapi_alloc_control_ping (ctx);
915   if (!ping)
916     {
917       return VAPI_ENOMEM;
918     }
919   ping->header.context = context;
920   vapi_msg_control_ping_hton (ping);
921   return vapi_send2 (ctx, msg, ping);
922 }
923 """
924         print("%s" % vapi_send_with_control_ping_function)
925         print("")
926
927     print("#ifdef __cplusplus")
928     print("}")
929     print("#endif")
930     print("")
931     print("#endif")
932     sys.stdout = orig_stdout
933
934
935 def json_to_c_header_name(json_name):
936     if json_name.endswith(".json"):
937         return "%s.vapi.h" % os.path.splitext(json_name)[0]
938     raise Exception("Unexpected json name `%s'!" % json_name)
939
940
941 def gen_c_unified_headers(parser, logger, prefix, remove_path):
942     if prefix == "" or prefix is None:
943         prefix = ""
944     else:
945         prefix = "%s/" % prefix
946     for j in parser.json_files:
947         if remove_path:
948             d, f = os.path.split(j)
949         else:
950             f = j
951         with open("%s%s" % (prefix, json_to_c_header_name(f)), "w") as io:
952             gen_json_unified_header(parser, logger, j, io, json_to_c_header_name(f))
953
954
955 if __name__ == "__main__":
956     try:
957         verbose = int(os.getenv("V", 0))
958     except:
959         verbose = 0
960
961     if verbose >= 2:
962         log_level = 10
963     elif verbose == 1:
964         log_level = 20
965     else:
966         log_level = 40
967
968     logging.basicConfig(stream=sys.stdout, level=log_level)
969     logger = logging.getLogger("VAPI C GEN")
970     logger.setLevel(log_level)
971
972     argparser = argparse.ArgumentParser(description="VPP C API generator")
973     argparser.add_argument(
974         "files",
975         metavar="api-file",
976         action="append",
977         type=str,
978         help="json api file" "(may be specified multiple times)",
979     )
980     argparser.add_argument("--prefix", action="store", default=None, help="path prefix")
981     argparser.add_argument(
982         "--remove-path", action="store_true", help="remove path from filename"
983     )
984     args = argparser.parse_args()
985
986     jsonparser = JsonParser(
987         logger,
988         args.files,
989         simple_type_class=CSimpleType,
990         enum_class=CEnum,
991         union_class=CUnion,
992         struct_type_class=CStructType,
993         field_class=CField,
994         message_class=CMessage,
995         alias_class=CAlias,
996     )
997
998     # not using the model of having separate generated header and code files
999     # with generated symbols present in shared library (per discussion with
1000     # Damjan), to avoid symbol version issues in .so
1001     # gen_c_headers_and_code(jsonparser, logger, args.prefix)
1002
1003     gen_c_unified_headers(jsonparser, logger, args.prefix, args.remove_path)
1004
1005     for e in jsonparser.exceptions:
1006         logger.warning(e)