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