7 from vapi_json_parser import Field, Struct, Enum, Union, Message, JsonParser,\
13 if self.len is not None:
14 return "%s %s[%d]" % (self.type.get_c_name(), self.name, self.len)
16 return "%s %s" % (self.type.get_c_name(), self.name)
18 def get_swap_to_be_code(self, struct, var):
19 if self.len is not None:
21 return "do { unsigned i; for (i = 0; i < %d; ++i) { %s } }"\
24 self.type.get_swap_to_be_code(struct, "%s[i]" % var))
26 if self.nelem_field.needs_byte_swap():
27 nelem_field = "%s(%s%s)" % (
28 self.nelem_field.type.get_swap_to_host_func_name(),
29 struct, self.nelem_field.name)
31 nelem_field = "%s%s" % (struct, self.nelem_field.name)
33 "do { unsigned i; for (i = 0; i < %s; ++i) { %s } }"
35 (nelem_field, self.type.get_swap_to_be_code(
36 struct, "%s[i]" % var)))
37 return self.type.get_swap_to_be_code(struct, "%s" % var)
39 def get_swap_to_host_code(self, struct, var):
40 if self.len is not None:
42 return "do { unsigned i; for (i = 0; i < %d; ++i) { %s } }"\
45 self.type.get_swap_to_host_code(struct, "%s[i]" % var))
47 # nelem_field already swapped to host here...
49 "do { unsigned i; for (i = 0; i < %s%s; ++i) { %s } }"
51 (struct, self.nelem_field.name,
52 self.type.get_swap_to_host_code(
53 struct, "%s[i]" % var)))
54 return self.type.get_swap_to_host_code(struct, "%s" % var)
56 def needs_byte_swap(self):
57 return self.type.needs_byte_swap()
60 class CStruct(Struct):
63 "typedef struct __attribute__((__packed__)) {\n%s;" % (
64 ";\n".join([" %s" % x.get_c_def()
65 for x in self.fields])),
66 "} %s;" % self.get_c_name()])
69 class CSimpleType (SimpleType):
72 'i16': 'htobe16', 'u16': 'htobe16',
73 'i32': 'htobe32', 'u32': 'htobe32',
74 'i64': 'htobe64', 'u64': 'htobe64',
78 'i16': 'be16toh', 'u16': 'be16toh',
79 'i32': 'be32toh', 'u32': 'be32toh',
80 'i64': 'be64toh', 'u64': 'be64toh',
86 def get_swap_to_be_func_name(self):
87 return self.swap_to_be_dict[self.name]
89 def get_swap_to_host_func_name(self):
90 return self.swap_to_host_dict[self.name]
92 def get_swap_to_be_code(self, struct, var, cast=None):
93 x = "%s%s" % (struct, var)
94 return "%s = %s%s(%s);" % (x,
95 "(%s)" % cast if cast else "",
96 self.get_swap_to_be_func_name(), x)
98 def get_swap_to_host_code(self, struct, var, cast=None):
99 x = "%s%s" % (struct, var)
100 return "%s = %s%s(%s);" % (x,
101 "(%s)" % cast if cast else "",
102 self.get_swap_to_host_func_name(), x)
104 def needs_byte_swap(self):
106 self.get_swap_to_host_func_name()
114 def get_c_name(self):
115 return "vapi_enum_%s" % self.name
118 return "typedef enum {\n%s\n} %s;" % (
119 "\n".join([" %s = %s," % (i, j) for i, j in self.value_pairs]),
123 def needs_byte_swap(self):
124 return self.type.needs_byte_swap()
126 def get_swap_to_be_code(self, struct, var):
127 return self.type.get_swap_to_be_code(struct, var, self.get_c_name())
129 def get_swap_to_host_code(self, struct, var):
130 return self.type.get_swap_to_host_code(struct, var, self.get_c_name())
134 def get_c_name(self):
135 return "vapi_union_%s" % self.name
138 return "typedef union {\n%s\n} %s;" % (
139 "\n".join([" %s %s;" % (i.get_c_name(), j)
140 for i, j in self.type_pairs]),
144 def needs_byte_swap(self):
148 class CStructType (StructType, CStruct):
149 def get_c_name(self):
150 return "vapi_type_%s" % self.name
152 def get_swap_to_be_func_name(self):
153 return "%s_hton" % self.get_c_name()
155 def get_swap_to_host_func_name(self):
156 return "%s_ntoh" % self.get_c_name()
158 def get_swap_to_be_func_decl(self):
159 return "void %s(%s *msg)" % (
160 self.get_swap_to_be_func_name(), self.get_c_name())
162 def get_swap_to_be_func_def(self):
163 return "%s\n{\n%s\n}" % (
164 self.get_swap_to_be_func_decl(),
166 " %s" % p.get_swap_to_be_code("msg->", "%s" % p.name)
167 for p in self.fields if p.needs_byte_swap()]),
170 def get_swap_to_host_func_decl(self):
171 return "void %s(%s *msg)" % (
172 self.get_swap_to_host_func_name(), self.get_c_name())
174 def get_swap_to_host_func_def(self):
175 return "%s\n{\n%s\n}" % (
176 self.get_swap_to_host_func_decl(),
178 " %s" % p.get_swap_to_host_code("msg->", "%s" % p.name)
179 for p in self.fields if p.needs_byte_swap()]),
182 def get_swap_to_be_code(self, struct, var):
183 return "%s(&%s%s);" % (self.get_swap_to_be_func_name(), struct, var)
185 def get_swap_to_host_code(self, struct, var):
186 return "%s(&%s%s);" % (self.get_swap_to_host_func_name(), struct, var)
188 def needs_byte_swap(self):
189 for f in self.fields:
190 if f.needs_byte_swap():
195 class CMessage (Message):
196 def __init__(self, logger, definition, json_parser):
197 super(CMessage, self).__init__(logger, definition, json_parser)
198 self.payload_members = [
199 " %s" % p.get_c_def()
201 if p.type != self.header
204 def has_payload(self):
205 return len(self.payload_members) > 0
207 def get_msg_id_name(self):
208 return "vapi_msg_id_%s" % self.name
210 def get_c_name(self):
211 return "vapi_msg_%s" % self.name
213 def get_payload_struct_name(self):
214 return "vapi_payload_%s" % self.name
216 def get_alloc_func_vla_field_length_name(self, field):
217 return "%s_array_size" % field.name
219 def get_alloc_func_name(self):
220 return "vapi_alloc_%s" % self.name
222 def get_alloc_vla_param_names(self):
223 return [self.get_alloc_func_vla_field_length_name(f)
225 if f.nelem_field is not None]
227 def get_alloc_func_decl(self):
228 return "%s* %s(struct vapi_ctx_s *ctx%s)" % (
230 self.get_alloc_func_name(),
231 "".join([", size_t %s" % n for n in
232 self.get_alloc_vla_param_names()]))
234 def get_alloc_func_def(self):
236 if self.header.has_field('client_index'):
238 " msg->header.client_index = vapi_get_client_index(ctx);")
239 if self.header.has_field('context'):
240 extra.append(" msg->header.context = 0;")
242 "%s" % self.get_alloc_func_decl(),
244 " %s *msg = NULL;" % self.get_c_name(),
245 " const size_t size = sizeof(%s)%s;" % (
248 " + sizeof(msg->payload.%s[0]) * %s" % (
250 self.get_alloc_func_vla_field_length_name(f))
252 if f.nelem_field is not None
254 " /* cast here required to play nicely with C++ world ... */",
255 " msg = (%s*)vapi_msg_alloc(ctx, size);" % self.get_c_name(),
260 " msg->header._vl_msg_id = vapi_lookup_vl_msg_id(ctx, %s);" %
261 self.get_msg_id_name(),
262 "\n".join([" msg->payload.%s = %s;" % (
264 self.get_alloc_func_vla_field_length_name(f))
266 if f.nelem_field is not None]),
270 def get_calc_msg_size_func_name(self):
271 return "vapi_calc_%s_msg_size" % self.name
273 def get_calc_msg_size_func_decl(self):
274 return "uword %s(%s *msg)" % (
275 self.get_calc_msg_size_func_name(),
278 def get_calc_msg_size_func_def(self):
280 "%s" % self.get_calc_msg_size_func_decl(),
282 " return sizeof(*msg)%s;" %
283 "".join(["+ msg->payload.%s * sizeof(msg->payload.%s[0])" % (
287 if f.nelem_field is not None
293 if self.has_payload():
295 "typedef struct __attribute__ ((__packed__)) {",
297 ";\n".join(self.payload_members),
298 "} %s;" % self.get_payload_struct_name(),
300 "typedef struct __attribute__ ((__packed__)) {",
301 (" %s %s;" % (self.header.get_c_name(),
303 if self.header is not None else ""),
304 " %s payload;" % self.get_payload_struct_name(),
305 "} %s;" % self.get_c_name(), ])
308 "typedef struct __attribute__ ((__packed__)) {",
309 (" %s %s;" % (self.header.get_c_name(),
311 if self.header is not None else ""),
312 "} %s;" % self.get_c_name(), ])
314 def get_swap_payload_to_host_func_name(self):
315 return "%s_payload_ntoh" % self.get_c_name()
317 def get_swap_payload_to_be_func_name(self):
318 return "%s_payload_hton" % self.get_c_name()
320 def get_swap_payload_to_host_func_decl(self):
321 return "void %s(%s *payload)" % (
322 self.get_swap_payload_to_host_func_name(),
323 self.get_payload_struct_name())
325 def get_swap_payload_to_be_func_decl(self):
326 return "void %s(%s *payload)" % (
327 self.get_swap_payload_to_be_func_name(),
328 self.get_payload_struct_name())
330 def get_swap_payload_to_be_func_def(self):
331 return "%s\n{\n%s\n}" % (
332 self.get_swap_payload_to_be_func_decl(),
334 " %s" % p.get_swap_to_be_code("payload->", "%s" % p.name)
336 if p.needs_byte_swap() and p.type != self.header]),
339 def get_swap_payload_to_host_func_def(self):
340 return "%s\n{\n%s\n}" % (
341 self.get_swap_payload_to_host_func_decl(),
343 " %s" % p.get_swap_to_host_code("payload->", "%s" % p.name)
345 if p.needs_byte_swap() and p.type != self.header]),
348 def get_swap_to_host_func_name(self):
349 return "%s_ntoh" % self.get_c_name()
351 def get_swap_to_be_func_name(self):
352 return "%s_hton" % self.get_c_name()
354 def get_swap_to_host_func_decl(self):
355 return "void %s(%s *msg)" % (
356 self.get_swap_to_host_func_name(), self.get_c_name())
358 def get_swap_to_be_func_decl(self):
359 return "void %s(%s *msg)" % (
360 self.get_swap_to_be_func_name(), self.get_c_name())
362 def get_swap_to_be_func_def(self):
364 "%s" % self.get_swap_to_be_func_decl(),
366 (" VAPI_DBG(\"Swapping `%s'@%%p to big endian\", msg);" %
368 " %s(&msg->header);" % self.header.get_swap_to_be_func_name()
369 if self.header is not None else "",
370 " %s(&msg->payload);" % self.get_swap_payload_to_be_func_name()
371 if self.has_payload() else "",
375 def get_swap_to_host_func_def(self):
377 "%s" % self.get_swap_to_host_func_decl(),
379 (" VAPI_DBG(\"Swapping `%s'@%%p to host byte order\", msg);" %
381 " %s(&msg->header);" % self.header.get_swap_to_host_func_name()
382 if self.header is not None else "",
383 " %s(&msg->payload);" % self.get_swap_payload_to_host_func_name()
384 if self.has_payload() else "",
388 def get_op_func_name(self):
389 return "vapi_%s" % self.name
391 def get_op_func_decl(self):
392 if self.reply.has_payload():
393 return "vapi_error_e %s(%s)" % (
394 self.get_op_func_name(),
396 'struct vapi_ctx_s *ctx',
397 '%s *msg' % self.get_c_name(),
398 'vapi_error_e (*callback)(struct vapi_ctx_s *ctx',
399 ' void *callback_ctx',
403 self.reply.get_payload_struct_name(),
404 'void *callback_ctx',
408 return "vapi_error_e %s(%s)" % (
409 self.get_op_func_name(),
411 'struct vapi_ctx_s *ctx',
412 '%s *msg' % self.get_c_name(),
413 'vapi_error_e (*callback)(struct vapi_ctx_s *ctx',
414 ' void *callback_ctx',
417 'void *callback_ctx',
421 def get_op_func_def(self):
423 "%s" % self.get_op_func_decl(),
425 " if (!msg || !callback) {",
426 " return VAPI_EINVAL;",
428 " if (vapi_is_nonblocking(ctx) && vapi_requests_full(ctx)) {",
429 " return VAPI_EAGAIN;",
432 " if (VAPI_OK != (rv = vapi_producer_lock (ctx))) {",
435 " u32 req_context = vapi_gen_req_context(ctx);",
436 " msg->header.context = req_context;",
437 " %s(msg);" % self.get_swap_to_be_func_name(),
438 (" if (VAPI_OK == (rv = vapi_send_with_control_ping "
439 "(ctx, msg, req_context))) {"
440 if self.reply_is_stream else
441 " if (VAPI_OK == (rv = vapi_send (ctx, msg))) {"
443 (" vapi_store_request(ctx, req_context, %s, "
444 "(vapi_cb_t)callback, callback_ctx);" %
445 ("true" if self.reply_is_stream else "false")),
446 " if (VAPI_OK != vapi_producer_unlock (ctx)) {",
447 " abort (); /* this really shouldn't happen */",
449 " if (vapi_is_nonblocking(ctx)) {",
452 " rv = vapi_dispatch(ctx);",
455 " %s(msg);" % self.get_swap_to_host_func_name(),
456 " if (VAPI_OK != vapi_producer_unlock (ctx)) {",
457 " abort (); /* this really shouldn't happen */",
465 def get_event_cb_func_decl(self):
466 if not self.is_reply and not self.is_event:
468 "Cannot register event callback for non-reply message")
469 if self.has_payload():
471 "void vapi_set_%s_event_cb (" %
473 " struct vapi_ctx_s *ctx, ",
474 (" vapi_error_e (*callback)(struct vapi_ctx_s *ctx, "
475 "void *callback_ctx, %s *payload)," %
476 self.get_payload_struct_name()),
477 " void *callback_ctx)",
481 "void vapi_set_%s_event_cb (" %
483 " struct vapi_ctx_s *ctx, ",
484 " vapi_error_e (*callback)(struct vapi_ctx_s *ctx, "
485 "void *callback_ctx),",
486 " void *callback_ctx)",
489 def get_event_cb_func_def(self):
490 if not self.is_reply and not self.is_event:
492 "Cannot register event callback for non-reply function")
494 "%s" % self.get_event_cb_func_decl(),
496 (" vapi_set_event_cb(ctx, %s, (vapi_event_cb)callback, "
498 self.get_msg_id_name()),
501 def get_c_metadata_struct_name(self):
502 return "__vapi_metadata_%s" % self.name
504 def get_c_constructor(self):
506 if self.header is not None:
507 has_context = self.header.has_field('context')
509 'static void __attribute__((constructor)) __vapi_constructor_%s()'
512 ' static const char name[] = "%s";' % self.name,
513 ' static const char name_with_crc[] = "%s_%s";'
514 % (self.name, self.crc[2:]),
515 ' static vapi_message_desc_t %s = {' %
516 self.get_c_metadata_struct_name(),
518 ' sizeof(name) - 1,',
520 ' sizeof(name_with_crc) - 1,',
521 ' true,' if has_context else ' false,',
522 ' offsetof(%s, context),' % self.header.get_c_name()
523 if has_context else ' 0,',
524 (' offsetof(%s, payload),' % self.get_c_name())
525 if self.has_payload() else ' VAPI_INVALID_MSG_ID,',
526 ' sizeof(%s),' % self.get_c_name(),
527 ' (generic_swap_fn_t)%s,' % self.get_swap_to_be_func_name(),
528 ' (generic_swap_fn_t)%s,' % self.get_swap_to_host_func_name(),
529 ' VAPI_INVALID_MSG_ID,',
532 ' %s = vapi_register_msg(&%s);' %
533 (self.get_msg_id_name(), self.get_c_metadata_struct_name()),
534 ' VAPI_DBG("Assigned msg id %%d to %s", %s);' %
535 (self.name, self.get_msg_id_name()),
540 vapi_send_with_control_ping = """
541 static inline vapi_error_e
542 vapi_send_with_control_ping (vapi_ctx_t ctx, void *msg, u32 context)
544 vapi_msg_control_ping *ping = vapi_alloc_control_ping (ctx);
549 ping->header.context = context;
550 vapi_msg_control_ping_hton (ping);
551 return vapi_send2 (ctx, msg, ping);
556 def emit_definition(parser, json_file, emitted, o, logger):
559 logger.debug("emit definition for %s" % o)
560 if o.name in ("msg_header1_t", "msg_header2_t"):
562 if hasattr(o, "depends"):
564 emit_definition(parser, json_file, emitted, x, logger)
565 if hasattr(o, "reply"):
566 emit_definition(parser, json_file, emitted, o.reply, logger)
567 if hasattr(o, "get_c_def"):
568 if (o not in parser.enums_by_json[json_file] and
569 o not in parser.types_by_json[json_file] and
570 o not in parser.unions_by_json[json_file] and
571 o.name not in parser.messages_by_json[json_file]):
573 logger.debug("writing def")
574 guard = "defined_%s" % o.get_c_name()
575 print("#ifndef %s" % guard)
576 print("#define %s" % guard)
577 print("%s" % o.get_c_def())
579 function_attrs = "static inline "
580 if o.name in parser.messages_by_json[json_file]:
581 logger.debug("o is %s" % o.name)
583 logger.debug("o has payload ")
584 print("%s%s" % (function_attrs,
585 o.get_swap_payload_to_be_func_def()))
587 print("%s%s" % (function_attrs,
588 o.get_swap_payload_to_host_func_def()))
590 print("%s%s" % (function_attrs, o.get_swap_to_be_func_def()))
592 print("%s%s" % (function_attrs, o.get_swap_to_host_func_def()))
594 print("%s%s" % (function_attrs, o.get_calc_msg_size_func_def()))
596 if not o.is_reply and not o.is_event:
597 print("%s%s" % (function_attrs, o.get_alloc_func_def()))
599 print("%s%s" % (function_attrs, o.get_op_func_def()))
601 print("%s" % o.get_c_constructor())
603 if o.is_reply and not o.is_event:
604 print("%s%s;" % (function_attrs, o.get_event_cb_func_def()))
606 elif hasattr(o, "get_swap_to_be_func_def"):
607 print("%s%s" % (function_attrs, o.get_swap_to_be_func_def()))
609 print("%s%s" % (function_attrs, o.get_swap_to_host_func_def()))
615 def gen_json_unified_header(parser, logger, j, io, name):
616 d, f = os.path.split(j)
617 logger.info("Generating header `%s'" % name)
618 orig_stdout = sys.stdout
620 include_guard = "__included_%s" % (
621 j.replace(".", "_").replace("/", "_").replace("-", "_"))
622 print("#ifndef %s" % include_guard)
623 print("#define %s" % include_guard)
625 print("#include <stdlib.h>")
626 print("#include <stddef.h>")
627 print("#include <arpa/inet.h>")
628 print("#include <vapi/vapi_internal.h>")
629 print("#include <vapi/vapi.h>")
630 print("#include <vapi/vapi_dbg.h>")
632 print("#ifdef __cplusplus")
633 print("extern \"C\" {")
635 if name == "vpe.api.vapi.h":
637 print("static inline vapi_error_e vapi_send_with_control_ping "
638 "(vapi_ctx_t ctx, void * msg, u32 context);")
640 print("#include <vapi/vpe.api.vapi.h>")
642 for m in parser.messages_by_json[j].values():
643 print("extern vapi_msg_id_t %s;" % m.get_msg_id_name())
645 print("#define DEFINE_VAPI_MSG_IDS_%s\\" %
646 f.replace(".", "_").replace("/", "_").replace("-", "_").upper())
648 " vapi_msg_id_t %s;" % m.get_msg_id_name()
649 for m in parser.messages_by_json[j].values()
654 for e in parser.enums_by_json[j]:
655 emit_definition(parser, j, emitted, e, logger)
656 for u in parser.unions_by_json[j]:
657 emit_definition(parser, j, emitted, u, logger)
658 for t in parser.types_by_json[j]:
659 emit_definition(parser, j, emitted, t, logger)
660 for m in parser.messages_by_json[j].values():
661 emit_definition(parser, j, emitted, m, logger)
665 if name == "vpe.api.vapi.h":
666 print("%s" % vapi_send_with_control_ping)
669 print("#ifdef __cplusplus")
674 sys.stdout = orig_stdout
677 def json_to_c_header_name(json_name):
678 if json_name.endswith(".json"):
679 return "%s.vapi.h" % os.path.splitext(json_name)[0]
680 raise Exception("Unexpected json name `%s'!" % json_name)
683 def gen_c_unified_headers(parser, logger, prefix, remove_path):
684 if prefix == "" or prefix is None:
687 prefix = "%s/" % prefix
688 for j in parser.json_files:
690 d, f = os.path.split(j)
693 with open('%s%s' % (prefix, json_to_c_header_name(f)), "w") as io:
694 gen_json_unified_header(
695 parser, logger, j, io, json_to_c_header_name(f))
698 if __name__ == '__main__':
700 verbose = int(os.getenv("V", 0))
711 logging.basicConfig(stream=sys.stdout, level=log_level)
712 logger = logging.getLogger("VAPI C GEN")
713 logger.setLevel(log_level)
715 argparser = argparse.ArgumentParser(description="VPP C API generator")
716 argparser.add_argument('files', metavar='api-file', action='append',
717 type=str, help='json api file'
718 '(may be specified multiple times)')
719 argparser.add_argument('--prefix', action='store', default=None,
721 argparser.add_argument('--remove-path', action='store_true',
722 help='remove path from filename')
723 args = argparser.parse_args()
725 jsonparser = JsonParser(logger, args.files,
726 simple_type_class=CSimpleType,
729 struct_type_class=CStructType,
731 message_class=CMessage)
733 # not using the model of having separate generated header and code files
734 # with generated symbols present in shared library (per discussion with
735 # Damjan), to avoid symbol version issues in .so
736 # gen_c_headers_and_code(jsonparser, logger, args.prefix)
738 gen_c_unified_headers(jsonparser, logger, args.prefix, args.remove_path)
740 for e in jsonparser.exceptions: