vapi: support services
[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                     {self.get_calc_msg_size_func_name()}(msg));
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         stream_param_lines = []
619         if self.has_stream_msg:
620             stream_param_lines = [
621                 "vapi_error_e (*details_callback)(struct vapi_ctx_s *ctx",
622                 "                                 void *callback_ctx",
623                 "                                 vapi_error_e rv",
624                 "                                 bool is_last",
625                 "                                 %s *details)"
626                 % self.stream_msg.get_payload_struct_name(),
627                 "void *details_callback_ctx",
628             ]
629
630         return "vapi_error_e %s(%s)" % (
631             self.get_op_func_name(),
632             ",\n  ".join(
633                 [
634                     "struct vapi_ctx_s *ctx",
635                     "%s *msg" % self.get_c_name(),
636                     "vapi_error_e (*reply_callback)(struct vapi_ctx_s *ctx",
637                     "                               void *callback_ctx",
638                     "                               vapi_error_e rv",
639                     "                               bool is_last",
640                     "                               %s *reply)"
641                     % self.reply.get_payload_struct_name(),
642                 ]
643                 + [
644                     "void *reply_callback_ctx",
645                 ]
646                 + stream_param_lines
647             ),
648         )
649
650     def get_op_func_def(self):
651         param_check_lines = ["  if (!msg || !reply_callback) {"]
652         store_request_lines = [
653             "    vapi_store_request(ctx, req_context, %s, %s, "
654             % (
655                 self.reply.get_msg_id_name(),
656                 "VAPI_REQUEST_DUMP" if self.reply_is_stream else "VAPI_REQUEST_REG",
657             ),
658             "                       (vapi_cb_t)reply_callback, reply_callback_ctx);",
659         ]
660         if self.has_stream_msg:
661             param_check_lines = [
662                 "  if (!msg || !reply_callback || !details_callback) {"
663             ]
664             store_request_lines = [
665                 f"    vapi_store_request(ctx, req_context, {self.stream_msg.get_msg_id_name()}, VAPI_REQUEST_STREAM, ",
666                 "                       (vapi_cb_t)details_callback, details_callback_ctx);",
667                 f"    vapi_store_request(ctx, req_context, {self.reply.get_msg_id_name()}, VAPI_REQUEST_REG, ",
668                 "                       (vapi_cb_t)reply_callback, reply_callback_ctx);",
669             ]
670
671         return "\n".join(
672             [
673                 "%s" % self.get_op_func_decl(),
674                 "{",
675             ]
676             + param_check_lines
677             + [
678                 "    return VAPI_EINVAL;",
679                 "  }",
680                 "  if (vapi_is_nonblocking(ctx) && vapi_requests_full(ctx)) {",
681                 "    return VAPI_EAGAIN;",
682                 "  }",
683                 "  vapi_error_e rv;",
684                 "  if (VAPI_OK != (rv = vapi_producer_lock (ctx))) {",
685                 "    return rv;",
686                 "  }",
687                 "  u32 req_context = vapi_gen_req_context(ctx);",
688                 "  msg->header.context = req_context;",
689                 "  %s(msg);" % self.get_swap_to_be_func_name(),
690                 (
691                     "  if (VAPI_OK == (rv = vapi_send_with_control_ping "
692                     "(ctx, msg, req_context))) {"
693                     if (self.reply_is_stream and not self.has_stream_msg)
694                     else "  if (VAPI_OK == (rv = vapi_send (ctx, msg))) {"
695                 ),
696             ]
697             + store_request_lines
698             + [
699                 "    if (VAPI_OK != vapi_producer_unlock (ctx)) {",
700                 "      abort (); /* this really shouldn't happen */",
701                 "    }",
702                 "    if (vapi_is_nonblocking(ctx)) {",
703                 "      rv = VAPI_OK;",
704                 "    } else {",
705                 "      rv = vapi_dispatch(ctx);",
706                 "    }",
707                 "  } else {",
708                 "    %s(msg);" % self.get_swap_to_host_func_name(),
709                 "    if (VAPI_OK != vapi_producer_unlock (ctx)) {",
710                 "      abort (); /* this really shouldn't happen */",
711                 "    }",
712                 "  }",
713                 "  return rv;",
714                 "}",
715                 "",
716             ]
717         )
718
719     def get_event_cb_func_decl(self):
720         if not self.is_reply and not self.is_event:
721             raise Exception("Cannot register event callback for non-reply message")
722         if self.has_payload():
723             return "\n".join(
724                 [
725                     "void vapi_set_%s_event_cb (" % self.get_c_name(),
726                     "  struct vapi_ctx_s *ctx, ",
727                     (
728                         "  vapi_error_e (*callback)(struct vapi_ctx_s *ctx, "
729                         "void *callback_ctx, %s *payload),"
730                         % self.get_payload_struct_name()
731                     ),
732                     "  void *callback_ctx)",
733                 ]
734             )
735         else:
736             return "\n".join(
737                 [
738                     "void vapi_set_%s_event_cb (" % self.get_c_name(),
739                     "  struct vapi_ctx_s *ctx, ",
740                     "  vapi_error_e (*callback)(struct vapi_ctx_s *ctx, "
741                     "void *callback_ctx),",
742                     "  void *callback_ctx)",
743                 ]
744             )
745
746     def get_event_cb_func_def(self):
747         if not self.is_reply and not self.is_event:
748             raise Exception("Cannot register event callback for non-reply function")
749         return "\n".join(
750             [
751                 "%s" % self.get_event_cb_func_decl(),
752                 "{",
753                 (
754                     "  vapi_set_event_cb(ctx, %s, (vapi_event_cb)callback, "
755                     "callback_ctx);" % self.get_msg_id_name()
756                 ),
757                 "}",
758             ]
759         )
760
761     def get_c_metadata_struct_name(self):
762         return "__vapi_metadata_%s" % self.name
763
764     def get_c_constructor(self):
765         has_context = False
766         if self.header is not None:
767             has_context = self.header.has_field("context")
768         return "\n".join(
769             [
770                 "static void __attribute__((constructor)) __vapi_constructor_%s()"
771                 % self.name,
772                 "{",
773                 '  static const char name[] = "%s";' % self.name,
774                 '  static const char name_with_crc[] = "%s_%s";'
775                 % (self.name, self.crc[2:]),
776                 "  static vapi_message_desc_t %s = {"
777                 % self.get_c_metadata_struct_name(),
778                 "    name,",
779                 "    sizeof(name) - 1,",
780                 "    name_with_crc,",
781                 "    sizeof(name_with_crc) - 1,",
782                 "    true," if has_context else "    false,",
783                 "    offsetof(%s, context)," % self.header.get_c_name()
784                 if has_context
785                 else "    0,",
786                 ("    offsetof(%s, payload)," % self.get_c_name())
787                 if self.has_payload()
788                 else "    VAPI_INVALID_MSG_ID,",
789                 "    (verify_msg_size_fn_t)%s," % self.get_verify_msg_size_func_name(),
790                 "    (generic_swap_fn_t)%s," % self.get_swap_to_be_func_name(),
791                 "    (generic_swap_fn_t)%s," % self.get_swap_to_host_func_name(),
792                 "    VAPI_INVALID_MSG_ID,",
793                 "  };",
794                 "",
795                 "  %s = vapi_register_msg(&%s);"
796                 % (self.get_msg_id_name(), self.get_c_metadata_struct_name()),
797                 '  VAPI_DBG("Assigned msg id %%d to %s", %s);'
798                 % (self.name, self.get_msg_id_name()),
799                 "}",
800             ]
801         )
802
803
804 def emit_definition(parser, json_file, emitted, o):
805     if o in emitted:
806         return
807     if o.name in ("msg_header1_t", "msg_header2_t"):
808         return
809     if hasattr(o, "depends"):
810         for x in o.depends:
811             emit_definition(parser, json_file, emitted, x)
812     if hasattr(o, "reply"):
813         emit_definition(parser, json_file, emitted, o.reply)
814     if hasattr(o, "stream_msg"):
815         emit_definition(parser, json_file, emitted, o.stream_msg)
816     if hasattr(o, "get_c_def"):
817         if (
818             o not in parser.enums_by_json[json_file]
819             and o not in parser.types_by_json[json_file]
820             and o not in parser.unions_by_json[json_file]
821             and o.name not in parser.messages_by_json[json_file]
822             and o not in parser.aliases_by_json[json_file]
823         ):
824             return
825         guard = "defined_%s" % o.get_c_name()
826         print("#ifndef %s" % guard)
827         print("#define %s" % guard)
828         print("%s" % o.get_c_def())
829         print("")
830         function_attrs = "static inline "
831         if o.name in parser.messages_by_json[json_file]:
832             if o.has_payload():
833                 print("%s%s" % (function_attrs, o.get_swap_payload_to_be_func_def()))
834                 print("")
835                 print("%s%s" % (function_attrs, o.get_swap_payload_to_host_func_def()))
836                 print("")
837             print("%s%s" % (function_attrs, o.get_swap_to_be_func_def()))
838             print("")
839             print("%s%s" % (function_attrs, o.get_swap_to_host_func_def()))
840             print("")
841             print("%s%s" % (function_attrs, o.get_calc_msg_size_func_def()))
842             print("")
843             print("%s%s" % (function_attrs, o.get_verify_msg_size_func_def()))
844             if not o.is_reply and not o.is_event and not o.is_stream:
845                 print("")
846                 print("%s%s" % (function_attrs, o.get_alloc_func_def()))
847                 print("")
848                 print("%s%s" % (function_attrs, o.get_op_func_def()))
849             print("")
850             print("%s" % o.get_c_constructor())
851             if (o.is_reply or o.is_event) and not o.is_stream:
852                 print("")
853                 print("%s%s;" % (function_attrs, o.get_event_cb_func_def()))
854         elif hasattr(o, "get_swap_to_be_func_def"):
855             print("%s%s" % (function_attrs, o.get_swap_to_be_func_def()))
856             print("")
857             print("%s%s" % (function_attrs, o.get_swap_to_host_func_def()))
858         print("#endif")
859         print("")
860     emitted.append(o)
861
862
863 def gen_json_unified_header(parser, logger, j, io, name):
864     d, f = os.path.split(j)
865     logger.info("Generating header `%s'" % name)
866     orig_stdout = sys.stdout
867     sys.stdout = io
868     include_guard = "__included_%s" % (
869         j.replace(".", "_")
870         .replace("/", "_")
871         .replace("-", "_")
872         .replace("+", "_")
873         .replace("@", "_")
874     )
875     print("#ifndef %s" % include_guard)
876     print("#define %s" % include_guard)
877     print("")
878     print("#include <stdlib.h>")
879     print("#include <stddef.h>")
880     print("#include <arpa/inet.h>")
881     print("#include <vapi/vapi_internal.h>")
882     print("#include <vapi/vapi.h>")
883     print("#include <vapi/vapi_dbg.h>")
884     print("")
885     print("#ifdef __cplusplus")
886     print('extern "C" {')
887     print("#endif")
888     if name == "memclnt.api.vapi.h":
889         print("")
890         print(
891             "static inline vapi_error_e vapi_send_with_control_ping "
892             "(vapi_ctx_t ctx, void * msg, u32 context);"
893         )
894     elif name == "vlib.api.vapi.h":
895         print("#include <vapi/memclnt.api.vapi.h>")
896     else:
897         print("#include <vapi/vlib.api.vapi.h>")
898     print("")
899     for m in parser.messages_by_json[j].values():
900         print("extern vapi_msg_id_t %s;" % m.get_msg_id_name())
901     print("")
902     print(
903         "#define DEFINE_VAPI_MSG_IDS_%s\\"
904         % f.replace(".", "_").replace("/", "_").replace("-", "_").upper()
905     )
906     print(
907         "\\\n".join(
908             [
909                 "  vapi_msg_id_t %s;" % m.get_msg_id_name()
910                 for m in parser.messages_by_json[j].values()
911             ]
912         )
913     )
914     print("")
915     print("")
916     emitted = []
917     for e in parser.enums_by_json[j]:
918         emit_definition(parser, j, emitted, e)
919     for u in parser.unions_by_json[j]:
920         emit_definition(parser, j, emitted, u)
921     for t in parser.types_by_json[j]:
922         emit_definition(parser, j, emitted, t)
923     for a in parser.aliases_by_json[j]:
924         emit_definition(parser, j, emitted, a)
925     for m in parser.messages_by_json[j].values():
926         emit_definition(parser, j, emitted, m)
927
928     print("")
929
930     if name == "vlib.api.vapi.h":
931         vapi_send_with_control_ping_function = """
932 static inline vapi_error_e
933 vapi_send_with_control_ping (vapi_ctx_t ctx, void *msg, u32 context)
934 {
935   vapi_msg_control_ping *ping = vapi_alloc_control_ping (ctx);
936   if (!ping)
937     {
938       return VAPI_ENOMEM;
939     }
940   ping->header.context = context;
941   vapi_msg_control_ping_hton (ping);
942   return vapi_send2 (ctx, msg, ping);
943 }
944 """
945         print("%s" % vapi_send_with_control_ping_function)
946         print("")
947
948     print("#ifdef __cplusplus")
949     print("}")
950     print("#endif")
951     print("")
952     print("#endif")
953     sys.stdout = orig_stdout
954
955
956 def json_to_c_header_name(json_name):
957     if json_name.endswith(".json"):
958         return "%s.vapi.h" % os.path.splitext(json_name)[0]
959     raise Exception("Unexpected json name `%s'!" % json_name)
960
961
962 def gen_c_unified_headers(parser, logger, prefix, remove_path):
963     if prefix == "" or prefix is None:
964         prefix = ""
965     else:
966         prefix = "%s/" % prefix
967     for j in parser.json_files:
968         if remove_path:
969             d, f = os.path.split(j)
970         else:
971             f = j
972         with open("%s%s" % (prefix, json_to_c_header_name(f)), "w") as io:
973             gen_json_unified_header(parser, logger, j, io, json_to_c_header_name(f))
974
975
976 if __name__ == "__main__":
977     try:
978         verbose = int(os.getenv("V", 0))
979     except:
980         verbose = 0
981
982     if verbose >= 2:
983         log_level = 10
984     elif verbose == 1:
985         log_level = 20
986     else:
987         log_level = 40
988
989     logging.basicConfig(stream=sys.stdout, level=log_level)
990     logger = logging.getLogger("VAPI C GEN")
991     logger.setLevel(log_level)
992
993     argparser = argparse.ArgumentParser(description="VPP C API generator")
994     argparser.add_argument(
995         "files",
996         metavar="api-file",
997         action="append",
998         type=str,
999         help="json api file" "(may be specified multiple times)",
1000     )
1001     argparser.add_argument("--prefix", action="store", default=None, help="path prefix")
1002     argparser.add_argument(
1003         "--remove-path", action="store_true", help="remove path from filename"
1004     )
1005     args = argparser.parse_args()
1006
1007     jsonparser = JsonParser(
1008         logger,
1009         args.files,
1010         simple_type_class=CSimpleType,
1011         enum_class=CEnum,
1012         union_class=CUnion,
1013         struct_type_class=CStructType,
1014         field_class=CField,
1015         message_class=CMessage,
1016         alias_class=CAlias,
1017     )
1018
1019     # not using the model of having separate generated header and code files
1020     # with generated symbols present in shared library (per discussion with
1021     # Damjan), to avoid symbol version issues in .so
1022     # gen_c_headers_and_code(jsonparser, logger, args.prefix)
1023
1024     gen_c_unified_headers(jsonparser, logger, args.prefix, args.remove_path)
1025
1026     for e in jsonparser.exceptions:
1027         logger.warning(e)