Add new C API
[vpp.git] / src / vpp-api / vapi / vapi_c_gen.py
1 #!/usr/bin/env python3
2
3 import argparse
4 import os
5 import sys
6 import logging
7 from vapi_json_parser import Field, Struct, Message, JsonParser,\
8     SimpleType, StructType
9
10
11 class CField(Field):
12     def __init__(
13             self,
14             field_name,
15             field_type,
16             array_len=None,
17             nelem_field=None):
18         super().__init__(field_name, field_type, array_len, nelem_field)
19
20     def get_c_def(self):
21         if self.len is not None:
22             return "%s %s[%d]" % (self.type.get_c_name(), self.name, self.len)
23         else:
24             return "%s %s" % (self.type.get_c_name(), self.name)
25
26     def get_swap_to_be_code(self, struct, var):
27         if self.len is not None:
28             if self.len > 0:
29                 return "do { int i; for (i = 0; i < %d; ++i) { %s } }"\
30                     " while(0);" % (
31                         self.len,
32                         self.type.get_swap_to_be_code(struct, "%s[i]" % var))
33             else:
34                 if self.nelem_field.needs_byte_swap():
35                     nelem_field = "%s(%s%s)" % (
36                         self.nelem_field.type.get_swap_to_host_func_name(),
37                         struct, self.nelem_field.name)
38                 else:
39                     nelem_field = "%s%s" % (struct, self.nelem_field.name)
40                 return (
41                     "do { int i; for (i = 0; i < %s; ++i) { %s } }"
42                     " while(0);" %
43                     (nelem_field, self.type.get_swap_to_be_code(
44                         struct, "%s[i]" % var)))
45         return self.type.get_swap_to_be_code(struct, "%s" % var)
46
47     def get_swap_to_host_code(self, struct, var):
48         if self.len is not None:
49             if self.len > 0:
50                 return "do { int i; for (i = 0; i < %d; ++i) { %s } }"\
51                     " while(0);" % (
52                         self.len,
53                         self.type.get_swap_to_host_code(struct, "%s[i]" % var))
54             else:
55                 # nelem_field already swapped to host here...
56                 return (
57                     "do { int i; for (i = 0; i < %s%s; ++i) { %s } }"
58                     " while(0);" %
59                     (struct, self.nelem_field.name,
60                      self.type.get_swap_to_host_code(
61                          struct, "%s[i]" % var)))
62         return self.type.get_swap_to_host_code(struct, "%s" % var)
63
64     def needs_byte_swap(self):
65         return self.type.needs_byte_swap()
66
67
68 class CStruct(Struct):
69     def __init__(self, name, fields):
70         super().__init__(name, fields)
71
72     def get_c_def(self):
73         return "\n".join([
74             "typedef struct __attribute__((__packed__)) {",
75             "%s;" % ";\n".join(["  %s" % x.get_c_def()
76                                 for x in self.fields]),
77             "} %s;" % self.get_c_name()])
78
79
80 class CSimpleType (SimpleType):
81
82     swap_to_be_dict = {
83         'i16': 'htobe16', 'u16': 'htobe16',
84         'i32': 'htobe32', 'u32': 'htobe32',
85         'i64': 'htobe64', 'u64': 'htobe64',
86     }
87
88     swap_to_host_dict = {
89         'i16': 'be16toh', 'u16': 'be16toh',
90         'i32': 'be32toh', 'u32': 'be32toh',
91         'i64': 'be64toh', 'u64': 'be64toh',
92     }
93
94     def __init__(self, name):
95         super().__init__(name)
96
97     def get_c_name(self):
98         return self.name
99
100     def get_swap_to_be_func_name(self):
101         return self.swap_to_be_dict[self.name]
102
103     def get_swap_to_host_func_name(self):
104         return self.swap_to_host_dict[self.name]
105
106     def get_swap_to_be_code(self, struct, var):
107         x = "%s%s" % (struct, var)
108         return "%s = %s(%s);" % (x, self.get_swap_to_be_func_name(), x)
109
110     def get_swap_to_host_code(self, struct, var):
111         x = "%s%s" % (struct, var)
112         return "%s = %s(%s);" % (x, self.get_swap_to_host_func_name(), x)
113
114     def needs_byte_swap(self):
115         try:
116             self.get_swap_to_host_func_name()
117             return True
118         except:
119             pass
120         return False
121
122
123 class CStructType (StructType, CStruct):
124     def __init__(self, definition, typedict, field_class):
125         super().__init__(definition, typedict, field_class)
126
127     def get_c_name(self):
128         return "vapi_type_%s" % self.name
129
130     def get_swap_to_be_func_name(self):
131         return "%s_hton" % self.get_c_name()
132
133     def get_swap_to_host_func_name(self):
134         return "%s_ntoh" % self.get_c_name()
135
136     def get_swap_to_be_func_decl(self):
137         return "void %s(%s *msg)" % (
138             self.get_swap_to_be_func_name(), self.get_c_name())
139
140     def get_swap_to_be_func_def(self):
141         return "%s\n{\n%s\n}" % (
142             self.get_swap_to_be_func_decl(),
143             "\n".join([
144                 "  %s" % p.get_swap_to_be_code("msg->", "%s" % p.name)
145                 for p in self.fields if p.needs_byte_swap()]),
146         )
147
148     def get_swap_to_host_func_decl(self):
149         return "void %s(%s *msg)" % (
150             self.get_swap_to_host_func_name(), self.get_c_name())
151
152     def get_swap_to_host_func_def(self):
153         return "%s\n{\n%s\n}" % (
154             self.get_swap_to_host_func_decl(),
155             "\n".join([
156                 "  %s" % p.get_swap_to_host_code("msg->", "%s" % p.name)
157                 for p in self.fields if p.needs_byte_swap()]),
158         )
159
160     def get_swap_to_be_code(self, struct, var):
161         return "%s(&%s%s);" % (self.get_swap_to_be_func_name(), struct, var)
162
163     def get_swap_to_host_code(self, struct, var):
164         return "%s(&%s%s);" % (self.get_swap_to_host_func_name(), struct, var)
165
166     def needs_byte_swap(self):
167         for f in self.fields:
168             if f.needs_byte_swap():
169                 return True
170         return False
171
172
173 class CMessage (Message):
174     def __init__(self, logger, definition, typedict,
175                  struct_type_class, simple_type_class, field_class):
176         super().__init__(logger, definition, typedict, struct_type_class,
177                          simple_type_class, field_class)
178         self.payload_members = [
179             "  %s" % p.get_c_def()
180             for p in self.fields
181             if p.type != self.header
182         ]
183
184     def has_payload(self):
185         return len(self.payload_members) > 0
186
187     def get_msg_id_name(self):
188         return "vapi_msg_id_%s" % self.name
189
190     def get_c_name(self):
191         return "vapi_msg_%s" % self.name
192
193     def get_payload_struct_name(self):
194         return "vapi_payload_%s" % self.name
195
196     def get_alloc_func_vla_field_length_name(self, field):
197         return "%s_array_size" % field.name
198
199     def get_alloc_func_name(self):
200         return "vapi_alloc_%s" % self.name
201
202     def get_alloc_func_decl(self):
203         return "%s* %s(struct vapi_ctx_s *ctx%s)" % (
204             self.get_c_name(),
205             self.get_alloc_func_name(),
206             "".join([", size_t %s" %
207                      self.get_alloc_func_vla_field_length_name(f)
208                      for f in self.fields
209                      if f.nelem_field is not None]))
210
211     def get_alloc_func_def(self):
212         extra = []
213         if self.header.has_field('client_index'):
214             extra.append(
215                 "  msg->header.client_index = vapi_get_client_index(ctx);")
216         if self.header.has_field('context'):
217             extra.append("  msg->header.context = 0;")
218         return "\n".join([
219             "%s" % self.get_alloc_func_decl(),
220             "{",
221             "  %s *msg = NULL;" % self.get_c_name(),
222             "  const size_t size = sizeof(%s)%s;" % (
223                 self.get_c_name(),
224                 "".join([
225                     " + sizeof(msg->payload.%s[0]) * %s" % (
226                         f.name,
227                         self.get_alloc_func_vla_field_length_name(f))
228                     for f in self.fields
229                     if f.nelem_field is not None
230                 ])),
231             "  msg = vapi_msg_alloc(ctx, size);",
232             "  if (!msg) {",
233             "    return NULL;",
234             "  }",
235         ] + extra + [
236             "  msg->header._vl_msg_id = vapi_lookup_vl_msg_id(ctx, %s);" %
237             self.get_msg_id_name(),
238             "\n".join(["  msg->payload.%s = %s;" % (
239                 f.nelem_field.name,
240                 self.get_alloc_func_vla_field_length_name(f))
241                 for f in self.fields
242                 if f.nelem_field is not None]),
243             "  return msg;",
244             "}"])
245
246     def get_calc_msg_size_func_name(self):
247         return "vapi_calc_%s_msg_size" % self.name
248
249     def get_calc_msg_size_func_decl(self):
250         return "uword %s(%s *msg)" % (
251             self.get_calc_msg_size_func_name(),
252             self.get_c_name())
253
254     def get_calc_msg_size_func_def(self):
255         return "\n".join([
256             "%s" % self.get_calc_msg_size_func_decl(),
257             "{",
258             "  return sizeof(*msg)%s;" %
259             "".join(["+ msg->payload.%s * sizeof(msg->payload.%s[0])" % (
260                     f.nelem_field.name,
261                     f.name)
262                 for f in self.fields
263                 if f.nelem_field is not None
264             ]),
265             "}",
266         ])
267
268     def get_c_def(self):
269         if self.has_payload():
270             return "\n".join([
271                 "typedef struct __attribute__ ((__packed__)) {",
272                 "%s; " %
273                 ";\n".join(self.payload_members),
274                 "} %s;" % self.get_payload_struct_name(),
275                 "",
276                 "typedef struct __attribute__ ((__packed__)) {",
277                 ("  %s %s;" % (self.header.get_c_name(),
278                                self.fields[0].name)
279                     if self.header is not None else ""),
280                 "  %s payload;" % self.get_payload_struct_name(),
281                 "} %s;" % self.get_c_name(), ])
282         else:
283             return "\n".join([
284                 "typedef struct __attribute__ ((__packed__)) {",
285                 ("  %s %s;" % (self.header.get_c_name(),
286                                self.fields[0].name)
287                     if self.header is not None else ""),
288                 "} %s;" % self.get_c_name(), ])
289
290     def get_swap_payload_to_host_func_name(self):
291         return "%s_payload_ntoh" % self.get_c_name()
292
293     def get_swap_payload_to_be_func_name(self):
294         return "%s_payload_hton" % self.get_c_name()
295
296     def get_swap_payload_to_host_func_decl(self):
297         return "void %s(%s *payload)" % (
298             self.get_swap_payload_to_host_func_name(),
299             self.get_payload_struct_name())
300
301     def get_swap_payload_to_be_func_decl(self):
302         return "void %s(%s *payload)" % (
303             self.get_swap_payload_to_be_func_name(),
304             self.get_payload_struct_name())
305
306     def get_swap_payload_to_be_func_def(self):
307         return "%s\n{\n%s\n}" % (
308             self.get_swap_payload_to_be_func_decl(),
309             "\n".join([
310                 "  %s" % p.get_swap_to_be_code("payload->", "%s" % p.name)
311                 for p in self.fields
312                 if p.needs_byte_swap() and p.type != self.header]),
313         )
314
315     def get_swap_payload_to_host_func_def(self):
316         return "%s\n{\n%s\n}" % (
317             self.get_swap_payload_to_host_func_decl(),
318             "\n".join([
319                 "  %s" % p.get_swap_to_host_code("payload->", "%s" % p.name)
320                 for p in self.fields
321                 if p.needs_byte_swap() and p.type != self.header]),
322         )
323
324     def get_swap_to_host_func_name(self):
325         return "%s_ntoh" % self.get_c_name()
326
327     def get_swap_to_be_func_name(self):
328         return "%s_hton" % self.get_c_name()
329
330     def get_swap_to_host_func_decl(self):
331         return "void %s(%s *msg)" % (
332             self.get_swap_to_host_func_name(), self.get_c_name())
333
334     def get_swap_to_be_func_decl(self):
335         return "void %s(%s *msg)" % (
336             self.get_swap_to_be_func_name(), self.get_c_name())
337
338     def get_swap_to_be_func_def(self):
339         return "\n".join([
340             "%s" % self.get_swap_to_be_func_decl(),
341             "{",
342             ("  VAPI_DBG(\"Swapping `%s'@%%p to big endian\", msg);" %
343                 self.get_c_name()),
344             "  %s(&msg->header);" % self.header.get_swap_to_be_func_name()
345             if self.header is not None else "",
346             "  %s(&msg->payload);" % self.get_swap_payload_to_be_func_name()
347             if self.has_payload() else "",
348             "}",
349         ])
350
351     def get_swap_to_host_func_def(self):
352         return "\n".join([
353             "%s" % self.get_swap_to_host_func_decl(),
354             "{",
355             ("  VAPI_DBG(\"Swapping `%s'@%%p to host byte order\", msg);" %
356                 self.get_c_name()),
357             "  %s(&msg->header);" % self.header.get_swap_to_host_func_name()
358             if self.header is not None else "",
359             "  %s(&msg->payload);" % self.get_swap_payload_to_host_func_name()
360             if self.has_payload() else "",
361             "}",
362         ])
363
364     def get_op_func_name(self):
365         return "vapi_%s" % self.name
366
367     def get_op_func_decl(self):
368         if self.reply.has_payload():
369             return "vapi_error_e %s(%s)" % (
370                 self.get_op_func_name(),
371                 ",\n  ".join([
372                     'struct vapi_ctx_s *ctx',
373                     '%s *msg' % self.get_c_name(),
374                     'vapi_error_e (*callback)(struct vapi_ctx_s *ctx',
375                     '                         void *callback_ctx',
376                     '                         vapi_error_e rv',
377                     '                         bool is_last',
378                     '                         %s *reply)' %
379                     self.reply.get_payload_struct_name(),
380                     'void *callback_ctx',
381                 ])
382             )
383         else:
384             return "vapi_error_e %s(%s)" % (
385                 self.get_op_func_name(),
386                 ",\n  ".join([
387                     'struct vapi_ctx_s *ctx',
388                     '%s *msg' % self.get_c_name(),
389                     'vapi_error_e (*callback)(struct vapi_ctx_s *ctx',
390                     '                         void *callback_ctx',
391                     '                         vapi_error_e rv',
392                     '                         bool is_last)',
393                     'void *callback_ctx',
394                 ])
395             )
396
397     def get_op_func_def(self):
398         return "\n".join([
399             "%s" % self.get_op_func_decl(),
400             "{",
401             "  if (!msg || !callback) {",
402             "    return VAPI_EINVAL;",
403             "  }",
404             "  if (vapi_is_nonblocking(ctx) && vapi_requests_full(ctx)) {",
405             "    return VAPI_EAGAIN;",
406             "  }",
407             "  vapi_error_e rv;",
408             "  if (VAPI_OK != (rv = vapi_producer_lock (ctx))) {",
409             "    return rv;",
410             "  }",
411             "  u32 req_context = vapi_gen_req_context(ctx);",
412             "  msg->header.context = req_context;",
413             "  %s(msg);" % self.get_swap_to_be_func_name(),
414             ("  if (VAPI_OK == (rv = vapi_send_with_control_ping "
415                 "(ctx, msg, req_context))) {"
416                 if self.is_dump() else
417                 "  if (VAPI_OK == (rv = vapi_send (ctx, msg))) {"
418              ),
419             ("    vapi_store_request(ctx, req_context, %s, "
420              "(vapi_cb_t)callback, callback_ctx);" %
421              ("true" if self.is_dump() else "false")),
422             "    if (VAPI_OK != vapi_producer_unlock (ctx)) {",
423             "      abort (); /* this really shouldn't happen */",
424             "    }",
425             "    if (vapi_is_nonblocking(ctx)) {",
426             "      rv = VAPI_OK;",
427             "    } else {",
428             "      rv = vapi_dispatch(ctx);",
429             "    }",
430             "  } else {",
431             "    %s(msg);" % self.get_swap_to_host_func_name(),
432             "    if (VAPI_OK != vapi_producer_unlock (ctx)) {",
433             "      abort (); /* this really shouldn't happen */",
434             "    }",
435             "  }",
436             "  return rv;",
437             "}",
438             "",
439         ])
440
441     def get_event_cb_func_decl(self):
442         if not self.is_reply():
443             raise Exception(
444                 "Cannot register event callback for non-reply function")
445         if self.has_payload():
446             return "\n".join([
447                 "void vapi_set_%s_event_cb (" %
448                 self.get_c_name(),
449                 "  struct vapi_ctx_s *ctx, ",
450                 ("  vapi_error_e (*callback)(struct vapi_ctx_s *ctx, "
451                  "void *callback_ctx, %s *payload)," %
452                  self.get_payload_struct_name()),
453                 "  void *callback_ctx)",
454             ])
455         else:
456             return "\n".join([
457                 "void vapi_set_%s_event_cb (" %
458                 self.get_c_name(),
459                 "  struct vapi_ctx_s *ctx, ",
460                 "  vapi_error_e (*callback)(struct vapi_ctx_s *ctx, "
461                 "void *callback_ctx),",
462                 "  void *callback_ctx)",
463             ])
464
465     def get_event_cb_func_def(self):
466         if not self.is_reply():
467             raise Exception(
468                 "Cannot register event callback for non-reply function")
469         return "\n".join([
470             "%s" % self.get_event_cb_func_decl(),
471             "{",
472             ("  vapi_set_event_cb(ctx, %s, (vapi_event_cb)callback, "
473              "callback_ctx);" %
474              self.get_msg_id_name()),
475             "}"])
476
477     def get_c_metadata_struct_name(self):
478         return "__vapi_metadata_%s" % self.name
479
480     def get_c_constructor(self):
481         has_context = False
482         if self.header is not None:
483             has_context = self.header.has_field('context')
484         return '\n'.join([
485             'static void __attribute__((constructor)) __vapi_constructor_%s()'
486             % self.name,
487             '{',
488             '  static const char name[] = "%s";' % self.name,
489             '  static const char name_with_crc[] = "%s_%s";'
490             % (self.name, self.crc[2:]),
491             '  static vapi_message_desc_t %s = {' %
492             self.get_c_metadata_struct_name(),
493             '    name,',
494             '    sizeof(name) - 1,',
495             '    name_with_crc,',
496             '    sizeof(name_with_crc) - 1,',
497             '    true,' if has_context else '    false,',
498             '    offsetof(%s, context),' % self.header.get_c_name()
499             if has_context else '    0,',
500             ('    offsetof(%s, payload),' % self.get_c_name())
501             if self.has_payload() else '-1,',
502             '    sizeof(%s),' % self.get_c_name(),
503             '    (generic_swap_fn_t)%s,' % self.get_swap_to_be_func_name(),
504             '    (generic_swap_fn_t)%s,' % self.get_swap_to_host_func_name(),
505             '    ~0,',
506             '  };',
507             '',
508             '  %s = vapi_register_msg(&%s);' %
509             (self.get_msg_id_name(), self.get_c_metadata_struct_name()),
510             '  VAPI_DBG("Assigned msg id %%d to %s", %s);' %
511             (self.name, self.get_msg_id_name()),
512             '}',
513         ])
514
515
516 vapi_send_with_control_ping = """
517 static inline vapi_error_e
518 vapi_send_with_control_ping (vapi_ctx_t ctx, void *msg, u32 context)
519 {
520   vapi_msg_control_ping *ping = vapi_alloc_control_ping (ctx);
521   if (!ping)
522     {
523       return VAPI_ENOMEM;
524     }
525   ping->header.context = context;
526   vapi_msg_control_ping_hton (ping);
527   return vapi_send2 (ctx, msg, ping);
528 }
529 """
530
531
532 def gen_json_header(parser, logger, j, io):
533     logger.info("Generating header `%s'" % io.name)
534     orig_stdout = sys.stdout
535     sys.stdout = io
536     include_guard = "__included_%s" % (
537         j.replace(".", "_").replace("/", "_").replace("-", "_"))
538     print("#ifndef %s" % include_guard)
539     print("#define %s" % include_guard)
540     print("")
541     print("#include <vapi_internal.h>")
542     print("")
543     if io.name == "vpe.api.vapi.h":
544         print("static inline vapi_error_e vapi_send_with_control_ping "
545               "(vapi_ctx_t ctx, void * msg, u32 context);")
546         print("")
547     for m in parser.messages_by_json[j].values():
548         print("extern vapi_msg_id_t %s;" % m.get_msg_id_name())
549     print("")
550     for t in parser.types_by_json[j].values():
551         try:
552             print("%s" % t.get_c_def())
553             print("")
554         except:
555             pass
556     for t in parser.types_by_json[j].values():
557         print("%s;" % t.get_swap_to_be_func_decl())
558         print("")
559         print("%s;" % t.get_swap_to_host_func_decl())
560         print("")
561     for m in parser.messages_by_json[j].values():
562         print("%s" % m.get_c_def())
563         print("")
564     for m in parser.messages_by_json[j].values():
565         if not m.is_reply():
566             print("%s;" % m.get_alloc_func_decl())
567             print("")
568             print("%s;" % m.get_op_func_decl())
569         if m.has_payload():
570             print("%s;" % m.get_swap_payload_to_be_func_decl())
571             print("")
572             print("%s;" % m.get_swap_payload_to_host_func_decl())
573             print("")
574         print("%s;" % m.get_calc_msg_size_func_decl())
575         print("")
576         print("%s;" % m.get_swap_to_host_func_decl())
577         print("")
578         print("%s;" % m.get_swap_to_be_func_decl())
579         print("")
580     for m in parser.messages_by_json[j].values():
581         if not m.is_reply():
582             continue
583         print("%s;" % m.get_event_cb_func_decl())
584         print("")
585
586     if io.name == "vpe.api.vapi.h":
587         print("%s" % vapi_send_with_control_ping)
588         print("")
589
590     print("#endif")
591     sys.stdout = orig_stdout
592
593
594 def gen_json_code(parser, logger, j, io):
595     logger.info("Generating code `%s'" % io.name)
596     orig_stdout = sys.stdout
597     sys.stdout = io
598     print("#include <%s>" % json_to_header_name(j))
599     print("#include <stdlib.h>")
600     print("#include <stddef.h>")
601     print("#include <arpa/inet.h>")
602     print("#include <vapi_internal.h>")
603     print("#include <vapi_dbg.h>")
604     print("")
605     for t in parser.types_by_json[j].values():
606         print("%s" % t.get_swap_to_be_func_def())
607         print("")
608         print("%s" % t.get_swap_to_host_func_def())
609         print("")
610     for m in parser.messages_by_json[j].values():
611         if m.has_payload():
612             print("%s" % m.get_swap_payload_to_be_func_def())
613             print("")
614             print("%s" % m.get_swap_payload_to_host_func_def())
615             print("")
616         print("%s" % m.get_calc_msg_size_func_def())
617         print("")
618         print("%s" % m.get_swap_to_be_func_def())
619         print("")
620         print("%s" % m.get_swap_to_host_func_def())
621         print("")
622     for m in parser.messages_by_json[j].values():
623         if m.is_reply():
624             continue
625         print("%s" % m.get_alloc_func_def())
626         print("")
627         print("%s" % m.get_op_func_def())
628         print("")
629     print("")
630     for m in parser.messages_by_json[j].values():
631         print("%s" % m.get_c_constructor())
632         print("")
633     print("")
634     for m in parser.messages_by_json[j].values():
635         if not m.is_reply():
636             continue
637         print("%s;" % m.get_event_cb_func_def())
638         print("")
639     print("")
640     for m in parser.messages_by_json[j].values():
641         print("vapi_msg_id_t %s;" % m.get_msg_id_name())
642     sys.stdout = orig_stdout
643
644
645 def gen_json_unified_header(parser, logger, j, io):
646     logger.info("Generating header `%s'" % io.name)
647     orig_stdout = sys.stdout
648     sys.stdout = io
649     include_guard = "__included_%s" % (
650         j.replace(".", "_").replace("/", "_").replace("-", "_"))
651     print("#ifndef %s" % include_guard)
652     print("#define %s" % include_guard)
653     print("")
654     print("#include <vapi_internal.h>")
655     print("#include <vapi.h>")
656     print("#include <stdlib.h>")
657     print("#include <stddef.h>")
658     print("#include <arpa/inet.h>")
659     print("#include <vapi_dbg.h>")
660     if io.name == "vpe.api.vapi.h":
661         print("")
662         print("static inline vapi_error_e vapi_send_with_control_ping "
663               "(vapi_ctx_t ctx, void * msg, u32 context);")
664     else:
665         print("#include <vpe.api.vapi.h>")
666     print("")
667     for m in parser.messages_by_json[j].values():
668         print("extern vapi_msg_id_t %s;" % m.get_msg_id_name())
669     print("")
670     print("#define DEFINE_VAPI_MSG_IDS_%s\\" %
671           j.replace(".", "_").replace("/", "_").replace("-", "_").upper())
672     print("\\\n".join([
673         "  vapi_msg_id_t %s;" % m.get_msg_id_name()
674         for m in parser.messages_by_json[j].values()
675     ]))
676     print("")
677     print("")
678     for t in parser.types_by_json[j].values():
679         try:
680             print("%s" % t.get_c_def())
681             print("")
682         except:
683             pass
684     for m in parser.messages_by_json[j].values():
685         print("%s" % m.get_c_def())
686         print("")
687
688     print("")
689     function_attrs = "static inline "
690     for t in parser.types_by_json[j].values():
691         print("%s%s" % (function_attrs, t.get_swap_to_be_func_def()))
692         print("")
693         print("%s%s" % (function_attrs, t.get_swap_to_host_func_def()))
694         print("")
695     for m in parser.messages_by_json[j].values():
696         if m.has_payload():
697             print("%s%s" % (function_attrs,
698                             m.get_swap_payload_to_be_func_def()))
699             print("")
700             print("%s%s" % (function_attrs,
701                             m.get_swap_payload_to_host_func_def()))
702             print("")
703         print("%s%s" % (function_attrs, m.get_calc_msg_size_func_def()))
704         print("")
705         print("%s%s" % (function_attrs, m.get_swap_to_be_func_def()))
706         print("")
707         print("%s%s" % (function_attrs, m.get_swap_to_host_func_def()))
708         print("")
709     for m in parser.messages_by_json[j].values():
710         if m.is_reply():
711             continue
712         print("%s%s" % (function_attrs, m.get_alloc_func_def()))
713         print("")
714         print("%s%s" % (function_attrs, m.get_op_func_def()))
715         print("")
716     print("")
717     for m in parser.messages_by_json[j].values():
718         print("%s" % m.get_c_constructor())
719         print("")
720     print("")
721     for m in parser.messages_by_json[j].values():
722         if not m.is_reply():
723             continue
724         print("%s%s;" % (function_attrs, m.get_event_cb_func_def()))
725         print("")
726     print("")
727
728     if io.name == "vpe.api.vapi.h":
729         print("%s" % vapi_send_with_control_ping)
730         print("")
731
732     print("#endif")
733     sys.stdout = orig_stdout
734
735
736 def json_to_header_name(json_name):
737     if json_name.endswith(".json"):
738         return "%s.vapi.h" % os.path.splitext(json_name)[0]
739     raise Exception("Unexpected json name `%s'!" % json_name)
740
741
742 def json_to_code_name(json_name):
743     if json_name.endswith(".json"):
744         return "%s.vapi.c" % os.path.splitext(json_name)[0]
745     raise Exception("Unexpected json name `%s'!" % json_name)
746
747
748 def gen_c_headers_and_code(parser, logger, prefix):
749     if prefix == "" or prefix is None:
750         prefix = ""
751     else:
752         prefix = "%s/" % prefix
753     for j in parser.json_files:
754         with open('%s%s' % (prefix, json_to_header_name(j)), "w") as io:
755             gen_json_header(parser, logger, j, io)
756         with open('%s%s' % (prefix, json_to_code_name(j)), "w") as io:
757             gen_json_code(parser, logger, j, io)
758
759
760 def gen_c_unified_headers(parser, logger, prefix):
761     if prefix == "" or prefix is None:
762         prefix = ""
763     else:
764         prefix = "%s/" % prefix
765     for j in parser.json_files:
766         with open('%s%s' % (prefix, json_to_header_name(j)), "w") as io:
767             gen_json_unified_header(parser, logger, j, io)
768
769
770 if __name__ == '__main__':
771     try:
772         verbose = int(os.getenv("V", 0))
773     except:
774         verbose = 0
775
776     if verbose >= 2:
777         log_level = 10
778     elif verbose == 1:
779         log_level = 20
780     else:
781         log_level = 40
782
783     logging.basicConfig(stream=sys.stdout, level=log_level)
784     logger = logging.getLogger("VAPI C GEN")
785     logger.setLevel(log_level)
786
787     argparser = argparse.ArgumentParser(description="VPP JSON API parser")
788     argparser.add_argument('files', metavar='api-file', action='append',
789                            type=str, help='json api file'
790                            '(may be specified multiple times)')
791     argparser.add_argument('--prefix', action='store', default=None,
792                            help='path prefix')
793     args = argparser.parse_args()
794
795     jsonparser = JsonParser(logger, args.files,
796                             simple_type_class=CSimpleType,
797                             struct_type_class=CStructType,
798                             field_class=CField,
799                             message_class=CMessage)
800
801     # not using the model of having separate generated header and code files
802     # with generated symbols present in shared library (per discussion with
803     # Damjan), to avoid symbol version issues in .so
804     # gen_c_headers_and_code(jsonparser, logger, args.prefix)
805
806     gen_c_unified_headers(jsonparser, logger, args.prefix)
807
808     for e in jsonparser.exceptions:
809         logger.error(e)