drop python3 dependency
[vpp.git] / src / vpp-api / vapi / vapi_cpp_gen.py
1 #!/usr/bin/env python2
2
3 import argparse
4 import os
5 import sys
6 import logging
7 from vapi_c_gen import CField, CStruct, CSimpleType, CStructType, CMessage, \
8     json_to_c_header_name
9 from vapi_json_parser import JsonParser
10
11
12 class CppField(CField):
13     def __init__(
14             self,
15             field_name,
16             field_type,
17             array_len=None,
18             nelem_field=None):
19         super(CppField, self).__init__(
20             field_name, field_type, array_len, nelem_field)
21
22
23 class CppStruct(CStruct):
24     def __init__(self, name, fields):
25         super(CppStruct, self).__init__(name, fields)
26
27
28 class CppSimpleType (CSimpleType):
29     def __init__(self, name):
30         super(CppSimpleType, self).__init__(name)
31
32
33 class CppStructType (CStructType, CppStruct):
34     def __init__(self, definition, typedict, field_class):
35         super(CppStructType, self).__init__(definition, typedict, field_class)
36
37
38 class CppMessage (CMessage):
39     def __init__(self, logger, definition, typedict,
40                  struct_type_class, simple_type_class, field_class):
41         super(CppMessage, self).__init__(
42             logger, definition, typedict, struct_type_class,
43             simple_type_class, field_class)
44
45     def get_swap_to_be_template_instantiation(self):
46         return "\n".join([
47             "template <> inline void vapi_swap_to_be<%s>(%s *msg)" %
48             (self.get_c_name(), self.get_c_name()),
49             "{",
50             "  %s(msg);" % self.get_swap_to_be_func_name(),
51             "}",
52         ])
53
54     def get_swap_to_host_template_instantiation(self):
55         return "\n".join([
56             "template <> inline void vapi_swap_to_host<%s>(%s *msg)" %
57             (self.get_c_name(), self.get_c_name()),
58             "{",
59             "  %s(msg);" % self.get_swap_to_host_func_name(),
60             "}",
61         ])
62
63     def get_alloc_template_instantiation(self):
64         return "\n".join([
65             "template <> inline %s* vapi_alloc<%s%s>"
66             "(Connection &con%s)" %
67             (self.get_c_name(), self.get_c_name(),
68                 ", size_t" * len(self.get_alloc_vla_param_names()),
69                 "".join([", size_t %s" % n for n in
70                          self.get_alloc_vla_param_names()])
71              ),
72             "{",
73             "  %s* result = %s(con.vapi_ctx%s);" %
74             (self.get_c_name(), self.get_alloc_func_name(),
75                 "".join([", %s" % n
76                          for n in self.get_alloc_vla_param_names()])),
77             "#if VAPI_CPP_DEBUG_LEAKS",
78             "  con.on_shm_data_alloc(result);",
79             "#endif",
80             "  return result;",
81             "}",
82         ])
83
84     def get_cpp_name(self):
85         return "%s%s" % (self.name[0].upper(), self.name[1:])
86
87     def get_req_template_name(self):
88         if self.is_dump():
89             template = "Dump"
90         else:
91             template = "Request"
92
93         return "%s<%s, %s%s>" % (
94             template,
95             self.get_c_name(),
96             self.reply.get_c_name(),
97             "".join([", size_t"] * len(self.get_alloc_vla_param_names()))
98         )
99
100     def get_req_template_instantiation(self):
101         return "template class %s;" % self.get_req_template_name()
102
103     def get_type_alias(self):
104         return "using %s = %s;" % (
105             self.get_cpp_name(), self.get_req_template_name())
106
107     def get_reply_template_name(self):
108         return "Msg<%s>" % (self.get_c_name())
109
110     def get_reply_type_alias(self):
111         return "using %s = %s;" % (
112             self.get_cpp_name(), self.get_reply_template_name())
113
114     def get_msg_class_instantiation(self):
115         return "template class Msg<%s>;" % self.get_c_name()
116
117     def get_get_msg_id_t_instantiation(self):
118         return "\n".join([
119             ("template <> inline vapi_msg_id_t vapi_get_msg_id_t<%s>()"
120                 % self.get_c_name()),
121             "{",
122             "  return ::%s; " % self.get_msg_id_name(),
123             "}",
124             "",
125             ("template <> inline vapi_msg_id_t "
126              "vapi_get_msg_id_t<Msg<%s>>()" % self.get_c_name()),
127             "{",
128             "  return ::%s; " % self.get_msg_id_name(),
129             "}",
130         ])
131
132     def get_cpp_constructor(self):
133         return '\n'.join([
134             ('static void __attribute__((constructor)) '
135              '__vapi_cpp_constructor_%s()'
136              % self.name),
137             '{',
138             ('  vapi::vapi_msg_set_msg_id<%s>(%s);' % (
139                 self.get_c_name(), self.get_msg_id_name())),
140             '}',
141         ])
142
143
144 def gen_json_header(parser, logger, j, io, gen_h_prefix, add_debug_comments):
145     logger.info("Generating header `%s'" % io.name)
146     orig_stdout = sys.stdout
147     sys.stdout = io
148     include_guard = "__included_hpp_%s" % (
149         j.replace(".", "_").replace("/", "_").replace("-", "_"))
150     print("#ifndef %s" % include_guard)
151     print("#define %s" % include_guard)
152     print("")
153     print("#include <vapi/vapi.hpp>")
154     print("#include <%s%s>" % (gen_h_prefix, json_to_c_header_name(j)))
155     print("")
156     print("namespace vapi {")
157     print("")
158     for m in parser.messages_by_json[j].values():
159         # utility functions need to go first, otherwise internal instantiation
160         # causes headaches ...
161         if add_debug_comments:
162             print("/* m.get_swap_to_be_template_instantiation() */")
163         print("%s" % m.get_swap_to_be_template_instantiation())
164         print("")
165         if add_debug_comments:
166             print("/* m.get_swap_to_host_template_instantiation() */")
167         print("%s" % m.get_swap_to_host_template_instantiation())
168         print("")
169         if add_debug_comments:
170             print("/* m.get_get_msg_id_t_instantiation() */")
171         print("%s" % m.get_get_msg_id_t_instantiation())
172         print("")
173         if add_debug_comments:
174             print("/* m.get_cpp_constructor() */")
175         print("%s" % m.get_cpp_constructor())
176         print("")
177         if not m.is_reply():
178             if add_debug_comments:
179                 print("/* m.get_alloc_template_instantiation() */")
180             print("%s" % m.get_alloc_template_instantiation())
181             print("")
182         if add_debug_comments:
183             print("/* m.get_msg_class_instantiation() */")
184         print("%s" % m.get_msg_class_instantiation())
185         print("")
186         if m.is_reply():
187             if add_debug_comments:
188                 print("/* m.get_reply_type_alias() */")
189             print("%s" % m.get_reply_type_alias())
190             continue
191         if add_debug_comments:
192             print("/* m.get_req_template_instantiation() */")
193         print("%s" % m.get_req_template_instantiation())
194         print("")
195         if add_debug_comments:
196             print("/* m.get_type_alias() */")
197         print("%s" % m.get_type_alias())
198         print("")
199     print("}")  # namespace vapi
200
201     print("#endif")
202     sys.stdout = orig_stdout
203
204
205 def json_to_cpp_header_name(json_name):
206     if json_name.endswith(".json"):
207         return "%s.vapi.hpp" % os.path.splitext(json_name)[0]
208     raise Exception("Unexpected json name `%s'!" % json_name)
209
210
211 def gen_cpp_headers(parser, logger, prefix, gen_h_prefix,
212                     add_debug_comments=False):
213     if prefix == "" or prefix is None:
214         prefix = ""
215     else:
216         prefix = "%s/" % prefix
217     if gen_h_prefix is None:
218         gen_h_prefix = ""
219     else:
220         gen_h_prefix = "%s/" % gen_h_prefix
221     for j in parser.json_files:
222         with open('%s%s' % (prefix, json_to_cpp_header_name(j)), "w") as io:
223             gen_json_header(parser, logger, j, io,
224                             gen_h_prefix, add_debug_comments)
225
226
227 if __name__ == '__main__':
228     try:
229         verbose = int(os.getenv("V", 0))
230     except:
231         verbose = 0
232
233     if verbose >= 2:
234         log_level = 10
235     elif verbose == 1:
236         log_level = 20
237     else:
238         log_level = 40
239
240     logging.basicConfig(stream=sys.stdout, level=log_level)
241     logger = logging.getLogger("VAPI CPP GEN")
242     logger.setLevel(log_level)
243
244     argparser = argparse.ArgumentParser(description="VPP C++ API generator")
245     argparser.add_argument('files', metavar='api-file', action='append',
246                            type=str, help='json api file'
247                            '(may be specified multiple times)')
248     argparser.add_argument('--prefix', action='store', default=None,
249                            help='path prefix')
250     argparser.add_argument('--gen-h-prefix', action='store', default=None,
251                            help='generated C header prefix')
252     args = argparser.parse_args()
253
254     jsonparser = JsonParser(logger, args.files,
255                             simple_type_class=CppSimpleType,
256                             struct_type_class=CppStructType,
257                             field_class=CppField,
258                             message_class=CppMessage)
259
260     gen_cpp_headers(jsonparser, logger, args.prefix, args.gen_h_prefix)
261
262     for e in jsonparser.exceptions:
263         logger.error(e)