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