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

Christophe Fergeau cfergeau at redhat.com
Thu Mar 7 15:35:19 UTC 2019


On Sun, Mar 03, 2019 at 07:10:25PM +0000, Frediano Ziglio wrote:
> 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))

These TODO are worrying, but only the has_end_attr() one seems to be run
at the moment, the others seem to be dead code?

Looks good otherwise,

Acked-by: Christophe Fergeau <cfergeau at redhat.com>

> +        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
> 
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/spice-devel
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <https://lists.freedesktop.org/archives/spice-devel/attachments/20190307/ac297c95/attachment-0001.sig>


More information about the Spice-devel mailing list