#!/usr/bin/env python2 import argparse import os import sys import logging from vapi_c_gen import CField, CEnum, CStruct, CSimpleType, CStructType,\ CMessage, json_to_c_header_name from vapi_json_parser import JsonParser class CppField(CField): pass class CppStruct(CStruct): pass class CppEnum(CEnum): pass class CppSimpleType (CSimpleType): pass class CppStructType (CStructType, CppStruct): pass class CppMessage (CMessage): def get_swap_to_be_template_instantiation(self): return "\n".join([ "template <> inline void vapi_swap_to_be<%s>(%s *msg)" % (self.get_c_name(), self.get_c_name()), "{", " %s(msg);" % self.get_swap_to_be_func_name(), "}", ]) def get_swap_to_host_template_instantiation(self): return "\n".join([ "template <> inline void vapi_swap_to_host<%s>(%s *msg)" % (self.get_c_name(), self.get_c_name()), "{", " %s(msg);" % self.get_swap_to_host_func_name(), "}", ]) def get_alloc_template_instantiation(self): return "\n".join([ "template <> inline %s* vapi_alloc<%s%s>" "(Connection &con%s)" % (self.get_c_name(), self.get_c_name(), ", size_t" * len(self.get_alloc_vla_param_names()), "".join([", size_t %s" % n for n in self.get_alloc_vla_param_names()]) ), "{", " %s* result = %s(con.vapi_ctx%s);" % (self.get_c_name(), self.get_alloc_func_name(), "".join([", %s" % n for n in self.get_alloc_vla_param_names()])), "#if VAPI_CPP_DEBUG_LEAKS", " con.on_shm_data_alloc(result);", "#endif", " return result;", "}", ]) def get_cpp_name(self): return "%s%s" % (self.name[0].upper(), self.name[1:]) def get_req_template_name(self): if self.reply_is_stream: template = "Dump" else: template = "Request" return "%s<%s, %s%s>" % ( template, self.get_c_name(), self.reply.get_c_name(), "".join([", size_t"] * len(self.get_alloc_vla_param_names())) ) def get_req_template_instantiation(self): return "template class %s;" % self.get_req_template_name() def get_type_alias(self): return "using %s = %s;" % ( self.get_cpp_name(), self.get_req_template_name()) def get_reply_template_name(self): return "Msg<%s>" % (self.get_c_name()) def get_reply_type_alias(self): return "using %s = %s;" % ( self.get_cpp_name(), self.get_reply_template_name()) def get_msg_class_instantiation(self): return "template class Msg<%s>;" % self.get_c_name() def get_get_msg_id_t_instantiation(self): return "\n".join([ ("template <> inline vapi_msg_id_t vapi_get_msg_id_t<%s>()" % self.get_c_name()), "{", " return ::%s; " % self.get_msg_id_name(), "}", "", ("template <> inline vapi_msg_id_t " "vapi_get_msg_id_t<Msg<%s>>()" % self.get_c_name()), "{", " return ::%s; " % self.get_msg_id_name(), "}", ]) def get_cpp_constructor(self): return '\n'.join([ ('static void __attribute__((constructor)) ' '__vapi_cpp_constructor_%s()' % self.name), '{', (' vapi::vapi_msg_set_msg_id<%s>(%s);' % ( self.get_c_name(), self.get_msg_id_name())), '}', ]) def gen_json_header(parser, logger, j, io, gen_h_prefix, add_debug_comments): logger.info("Generating header `%s'" % io.name) orig_stdout = sys.stdout sys.stdout = io d, f = os.path.split(j) include_guard = "__included_hpp_%s" % ( f.replace(".", "_").replace("/", "_").replace("-", "_")) print("#ifndef %s" % include_guard) print("#define %s" % include_guard) print("") print("#include <vapi/vapi.hpp>") print("#include <%s%s>" % (gen_h_prefix, json_to_c_header_name(f))) print("") print("namespace vapi {") print("") for m in parser.messages_by_json[j].values(): # utility functions need to go first, otherwise internal instantiation # causes headaches ... if add_debug_comments: print("/* m.get_swap_to_be_template_instantiation() */") print("%s" % m.get_swap_to_be_template_instantiation()) print("") if add_debug_comments: print("/* m.get_swap_to_host_template_instantiation() */") print("%s" % m.get_swap_to_host_template_instantiation()) print("") if add_debug_comments: print("/* m.get_get_msg_id_t_instantiation() */") print("%s" % m.get_get_msg_id_t_instantiation()) print("") if add_debug_comments: print("/* m.get_cpp_constructor() */") print("%s" % m.get_cpp_constructor()) print("") if not m.is_reply and not m.is_event: if add_debug_comments: print("/* m.get_alloc_template_instantiation() */") print("%s" % m.get_alloc_template_instantiation()) print("") if add_debug_comments: print("/* m.get_msg_class_instantiation() */") print("%s" % m.get_msg_class_instantiation()) print("") if m.is_reply or m.is_event: if add_debug_comments: print("/* m.get_reply_type_alias() */") print("%s" % m.get_reply_type_alias()) continue if add_debug_comments: print("/* m.get_req_template_instantiation() */") print("%s" % m.get_req_template_instantiation()) print("") if add_debug_comments: print("/* m.get_type_alias() */") print("%s" % m.get_type_alias()) print("") print("}") # namespace vapi print("#endif") sys.stdout = orig_stdout def json_to_cpp_header_name(json_name): if json_name.endswith(".json"): return "%s.vapi.hpp" % os.path.splitext(json_name)[0] raise Exception("Unexpected json name `%s'!" % json_name) def gen_cpp_headers(parser, logger, prefix, gen_h_prefix, remove_path, add_debug_comments=False): if prefix == "" or prefix is None: prefix = "" else: prefix = "%s/" % prefix if gen_h_prefix is None: gen_h_prefix = "" else: gen_h_prefix = "%s/" % gen_h_prefix for j in parser.json_files: if remove_path: d, f = os.path.split(j) else: f = j with open('%s%s' % (prefix, json_to_cpp_header_name(f)), "w") as io: gen_json_header(parser, logger, j, io, gen_h_prefix, add_debug_comments) if __name__ == '__main__': try: verbose = int(os.getenv("V", 0)) except: verbose = 0 if verbose >= 2: log_level = 10 elif verbose == 1: log_level = 20 else: log_level = 40 logging.basicConfig(stream=sys.stdout, level=log_level) logger = logging.getLogger("VAPI CPP GEN") logger.setLevel(log_level) argparser = argparse.ArgumentParser(description="VPP C++ API generator") argparser.add_argument('files', metavar='api-file', action='append', type=str, help='json api file' '(may be specified multiple times)') argparser.add_argument('--prefix', action='store', default=None, help='path prefix') argparser.add_argument('--gen-h-prefix', action='store', default=None, help='generated C header prefix') argparser.add_argument('--remove-path', action='store_true', help='remove path from filename') args = argparser.parse_args() jsonparser = JsonParser(logger, args.files, simple_type_class=CppSimpleType, struct_type_class=CppStructType, field_class=CppField, enum_class=CppEnum, message_class=CppMessage) gen_cpp_headers(jsonparser, logger, args.prefix, args.gen_h_prefix, args.remove_path) for e in jsonparser.exceptions: logger.warning(e)