blob: 33744a3d58c0f8394197dbdeb36556ad5109812a [file] [log] [blame]
#!/usr/bin/env python3
import argparse
import os
import sys
import logging
from vapi_c_gen import (
CField,
CEnum,
CStruct,
CSimpleType,
CStructType,
CMessage,
json_to_c_header_name,
CAlias,
)
from vapi_json_parser import JsonParser
class CppField(CField):
pass
class CppStruct(CStruct):
pass
class CppEnum(CEnum):
pass
class CppAlias(CAlias):
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("-", "_").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,
alias_class=CppAlias,
)
gen_cpp_headers(
jsonparser, logger, args.prefix, args.gen_h_prefix, args.remove_path
)
for e in jsonparser.exceptions:
logger.warning(e)