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