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