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