codegen: Allows to generate C declarations automatically

Allows to specify a @declare attribute for messages and structure
that can generate the needed C structures.

Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
Acked-by: Christophe Fergeau <cfergeau@redhat.com>
This commit is contained in:
Frediano Ziglio 2019-02-18 16:16:21 +00:00
parent dac34baaab
commit 3cd3886b27
2 changed files with 111 additions and 0 deletions

View File

@ -70,6 +70,8 @@ valid_attributes=set([
'zero',
# this attribute does not exist on the network, fill just structure with the value
'virtual',
# generate C structure declarations from protocol definition
'declare',
])
attributes_with_arguments=set([
@ -483,6 +485,26 @@ class ArrayType(Type):
def c_type(self):
return self.element_type.c_type()
def generate_c_declaration(self, writer, member):
name = member.name
if member.has_attr("chunk"):
return writer.writeln('SpiceChunks *%s;' % name)
if member.has_attr("as_ptr"):
len_var = member.attributes["as_ptr"][0]
writer.writeln('uint32_t %s;' % len_var)
return writer.writeln('%s *%s;' % (self.c_type(), name))
if member.has_attr("to_ptr"):
return writer.writeln('%s *%s;' % (self.c_type(), name))
if member.has_attr("ptr_array"):
return writer.writeln('%s *%s[0];' % (self.c_type(), name))
if member.has_end_attr() or self.is_remaining_length():
return writer.writeln('%s %s[0];' % (self.c_type(), name))
if self.is_constant_length():
return writer.writeln('%s %s[%s];' % (self.c_type(), name, self.size))
if self.is_identifier_length():
return writer.writeln('%s *%s;' % (self.c_type(), name))
raise NotImplementedError('unknown array %s' % str(self))
class PointerType(Type):
def __init__(self, target_type):
Type.__init__(self)
@ -517,6 +539,15 @@ class PointerType(Type):
def get_num_pointers(self):
return 1
def generate_c_declaration(self, writer, member):
target_type = self.target_type
is_array = target_type.is_array()
if not is_array or target_type.is_identifier_length():
writer.writeln("%s *%s;" % (target_type.c_type(), member.name))
return
raise NotImplementedError('Some pointers to array declarations are not implemented %s' %
member)
class Containee:
def __init__(self):
self.attributes = {}
@ -612,6 +643,14 @@ class Member(Containee):
names = [prefix + "_" + name for name in names]
return names
def generate_c_declaration(self, writer):
if self.has_attr("zero"):
return
if self.is_pointer() or self.is_array():
self.member_type.generate_c_declaration(writer, self)
return
return writer.writeln("%s %s;" % (self.member_type.c_type(), self.name))
class SwitchCase:
def __init__(self, values, member):
self.values = values
@ -735,6 +774,17 @@ class Switch(Containee):
names = names + c.get_pointer_names(marshalled)
return names
def generate_c_declaration(self, writer):
if self.has_attr("anon") and len(self.cases) == 1:
self.cases[0].member.generate_c_declaration(writer)
return
writer.writeln('union {')
writer.indent()
for m in self.cases:
m.member.generate_c_declaration(writer)
writer.unindent()
writer.writeln('} %s;' % self.name)
class ContainerType(Type):
def is_fixed_sizeof(self):
for m in self.members:
@ -845,6 +895,20 @@ class ContainerType(Type):
return member
def generate_c_declaration(self, writer):
if not self.has_attr('declare'):
return
name = self.c_type()
writer.writeln('typedef struct %s {' % name)
writer.indent()
for m in self.members:
m.generate_c_declaration(writer)
if len(self.members) == 0:
# make sure generated structure are not empty
writer.writeln("char dummy[0];")
writer.unindent()
writer.writeln('} %s;' % name).newline()
class StructType(ContainerType):
def __init__(self, name, members, attribute_list):
Type.__init__(self)

View File

@ -176,6 +176,8 @@ parser.add_option("--license", dest="license",
parser.add_option("--generate-header",
action="store_true", dest="generate_header", default=False,
help="Generate also the header")
parser.add_option("--generated-declaration-file", dest="generated_declaration_file", metavar="FILE",
help="Name of the file to generate declarations")
(options, args) = parser.parse_args()
@ -257,6 +259,51 @@ else:
print >> sys.stderr, "Invalid license specified: %s" % options.license
sys.exit(1)
all_structures = {}
def generate_declaration(t, writer_top):
writer = codegen.CodeWriter()
try:
c_type = t.c_type()
t.generate_c_declaration(writer)
value = writer.getvalue().strip()
if not value:
return
if c_type in all_structures:
assert all_structures[c_type] == value, """Structure %s redefinition
previous:
%s
---
current:
%s
---""" % (c_type, all_structures[c_type], value)
else:
all_structures[c_type] = value
t.generate_c_declaration(writer_top)
except:
print >> sys.stderr, 'type %s' % t
print >> sys.stderr, writer.getvalue()
traceback.print_exc(sys.stderr)
def generate_declarations():
writer = codegen.CodeWriter()
writer.public_suffix = options.suffix
writer.write(license)
# all types
for t in ptypes.get_named_types():
if isinstance(t, ptypes.StructType):
generate_declaration(t, writer)
if isinstance(t, ptypes.ChannelType):
for m in t.client_messages + t.server_messages:
generate_declaration(m.message_type, writer)
content = writer.getvalue()
write_content(options.generated_declaration_file, content,
options.keep_identical_file)
if options.generated_declaration_file:
generate_declarations()
writer.public_suffix = options.suffix
writer.writeln("/* this is a file autogenerated by spice_codegen.py */")