[Spice-devel] [PATCH spice-common 4/6] codegen: Allows to generate C declarations automatically

Frediano Ziglio fziglio at redhat.com
Thu Feb 21 10:38:05 UTC 2019


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

Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
---
 python_modules/ptypes.py | 64 ++++++++++++++++++++++++++++++++++++++++
 spice_codegen.py         | 47 +++++++++++++++++++++++++++++
 2 files changed, 111 insertions(+)

diff --git a/python_modules/ptypes.py b/python_modules/ptypes.py
index 7dca78d..64c198e 100644
--- a/python_modules/ptypes.py
+++ b/python_modules/ptypes.py
@@ -72,6 +72,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([
@@ -485,6 +487,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"): # TODO len
+            return writer.writeln('%s *%s;' % (self.c_type(), name))
+        if member.has_attr("ptr_array"): # TODO where the length is stored? overflow?
+            return writer.writeln('%s *%s[0];' % (self.c_type(), name))
+        if member.has_end_attr() or self.is_remaining_length(): # TODO len
+            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)
@@ -529,6 +551,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 = {}
@@ -624,6 +655,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
@@ -747,6 +786,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:
@@ -857,6 +907,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)
diff --git a/spice_codegen.py b/spice_codegen.py
index 4664740..66f99a5 100755
--- a/spice_codegen.py
+++ b/spice_codegen.py
@@ -177,6 +177,8 @@ parser.add_option("--license", dest="license",
                   help="license to use for generated file(s) (LGPL/BSD)", default="LGPL")
 parser.add_option("--generated-header", dest="generated_header", metavar="FILE",
                   help="Name of the file to generate 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()
 
@@ -259,6 +261,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_prefix = options.prefix
+    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_prefix = options.prefix
 
 writer.writeln("/* this is a file autogenerated by spice_codegen.py */")
-- 
2.20.1



More information about the Spice-devel mailing list