summaryrefslogtreecommitdiffstats
path: root/spice_codegen.py
diff options
context:
space:
mode:
authorMarc-André Lureau <marcandre.lureau@gmail.com>2016-03-10 15:18:06 +0100
committerMarc-André Lureau <marcandre.lureau@redhat.com>2016-03-10 16:01:36 +0100
commit1cd26b87c10870ea9bfb65b4640d2e318b45aafb (patch)
treed6488289140a7082bea97ea12ab2eaa551f401e1 /spice_codegen.py
parentf06f699d7817354a532f091f46044720f9045887 (diff)
downloadspice-common-1cd26b87c10870ea9bfb65b4640d2e318b45aafb.tar.gz
spice-common-1cd26b87c10870ea9bfb65b4640d2e318b45aafb.tar.xz
spice-common-1cd26b87c10870ea9bfb65b4640d2e318b45aafb.zip
Revert "Remove files moved to spice-protocol"
This reverts commit 7665dcf1bb2fa0b16b3d0015b28d7f5912664c3f. Also revert the related build-sys changes to fix the build. codegen generated code depends on spice-common code (marshaller, messages etc), it makes more sense to keep the generator along this. Otherwise a newer protocol release will fail to build older projects. *.proto files are required as well, since it generates code that parent modules depend on unconditionnaly. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Diffstat (limited to 'spice_codegen.py')
-rwxr-xr-xspice_codegen.py275
1 files changed, 275 insertions, 0 deletions
diff --git a/spice_codegen.py b/spice_codegen.py
new file mode 100755
index 0000000..569cccc
--- /dev/null
+++ b/spice_codegen.py
@@ -0,0 +1,275 @@
+#!/usr/bin/env python
+
+import os
+import sys
+from optparse import OptionParser
+import traceback
+from python_modules import spice_parser
+from python_modules import ptypes
+from python_modules import codegen
+from python_modules import demarshal
+from python_modules import marshal
+import six
+
+def write_channel_enums(writer, channel, client, describe):
+ messages = list(filter(lambda m : m.channel == channel, \
+ channel.client_messages if client else channel.server_messages))
+ if len(messages) == 0:
+ return
+ if client:
+ prefix = [ "MSGC" ]
+ else:
+ prefix = [ "MSG" ]
+ if channel.member_name:
+ prefix.append(channel.member_name.upper())
+ if not describe:
+ writer.begin_block("enum")
+ else:
+ writer.begin_block("static const value_string %s_vs[] = " % (codegen.prefix_underscore_lower(*[x.lower() for x in prefix])))
+ i = 0
+ prefix.append(None) # To be replaced with name
+ for m in messages:
+ prefix[-1] = m.name.upper()
+ enum = codegen.prefix_underscore_upper(*prefix)
+ if describe:
+ writer.writeln("{ %s, \"%s %s\" }," % (enum, "Client" if client else "Server", m.name.upper()))
+ else:
+ if m.value == i:
+ writer.writeln("%s," % enum)
+ i = i + 1
+ else:
+ writer.writeln("%s = %s," % (enum, m.value))
+ i = m.value + 1
+ if describe:
+ writer.writeln("{ 0, NULL }");
+ else:
+ if channel.member_name:
+ prefix[-1] = prefix[-2]
+ prefix[-2] = "END"
+ writer.newline()
+ writer.writeln("%s" % (codegen.prefix_underscore_upper(*prefix)))
+ writer.end_block(semicolon=True)
+ writer.newline()
+
+def write_channel_type_enum(writer, describe=False):
+ i = 0
+ if describe:
+ writer.begin_block("static const value_string channel_types_vs[] =")
+ else:
+ writer.begin_block("enum")
+ for c in proto.channels:
+ enum = codegen.prefix_underscore_upper("CHANNEL", c.name.upper())
+ if describe:
+ writer.writeln("{ %s, \"%s\" }," % (enum, c.name.upper()))
+ else:
+ if c.value == i:
+ writer.writeln("%s," % enum)
+ i = i + 1
+ else:
+ writer.writeln("%s = %s," % (enum, c.value))
+ i = c.value + 1
+ writer.newline()
+ if describe:
+ writer.writeln("{ 0, NULL }")
+ else:
+ writer.writeln("SPICE_END_CHANNEL")
+ writer.end_block(semicolon=True)
+ writer.newline()
+
+
+def write_enums(writer, describe=False):
+ writer.writeln("#ifndef _H_SPICE_ENUMS")
+ writer.writeln("#define _H_SPICE_ENUMS")
+ writer.newline()
+
+ # Define enums
+ for t in ptypes.get_named_types():
+ if isinstance(t, ptypes.EnumBaseType):
+ t.c_define(writer)
+ if describe:
+ t.c_describe(writer)
+
+ write_channel_type_enum(writer)
+ if (describe):
+ write_channel_type_enum(writer, True)
+
+ for c in ptypes.get_named_types():
+ if not isinstance(c, ptypes.ChannelType):
+ continue
+ write_channel_enums(writer, c, False, False)
+ if describe:
+ write_channel_enums(writer, c, False, describe)
+ write_channel_enums(writer, c, True, False)
+ if describe:
+ write_channel_enums(writer, c, True, describe)
+
+ writer.writeln("#endif /* _H_SPICE_ENUMS */")
+
+parser = OptionParser(usage="usage: %prog [options] <protocol_file> <destination file>")
+parser.add_option("-e", "--generate-enums",
+ action="store_true", dest="generate_enums", default=False,
+ help="Generate enums")
+parser.add_option("-w", "--generate-wireshark-dissector",
+ action="store_true", dest="generate_dissector", default=False,
+ help="Generate Wireshark dissector definitions")
+parser.add_option("-d", "--generate-demarshallers",
+ action="store_true", dest="generate_demarshallers", default=False,
+ help="Generate demarshallers")
+parser.add_option("-m", "--generate-marshallers",
+ action="store_true", dest="generate_marshallers", default=False,
+ help="Generate message marshallers")
+parser.add_option("-P", "--private-marshallers",
+ action="store_true", dest="private_marshallers", default=False,
+ help="Generate private message marshallers")
+parser.add_option("-M", "--generate-struct-marshaller",
+ action="append", dest="struct_marshallers",
+ help="Generate struct marshallers")
+parser.add_option("-a", "--assert-on-error",
+ action="store_true", dest="assert_on_error", default=False,
+ help="Assert on error")
+parser.add_option("-H", "--header",
+ action="store_true", dest="header", default=False,
+ help="Generate header")
+parser.add_option("-p", "--print-error",
+ action="store_true", dest="print_error", default=False,
+ help="Print errors")
+parser.add_option("-s", "--server",
+ action="store_true", dest="server", default=False,
+ help="Print errors")
+parser.add_option("-c", "--client",
+ action="store_true", dest="client", default=False,
+ help="Print errors")
+parser.add_option("-k", "--keep-identical-file",
+ action="store_true", dest="keep_identical_file", default=False,
+ help="Print errors")
+parser.add_option("-i", "--include",
+ action="append", dest="includes", metavar="FILE",
+ help="Include FILE in generated code")
+parser.add_option("--prefix", dest="prefix",
+ help="set public symbol prefix", default="")
+parser.add_option("--ptrsize", dest="ptrsize",
+ help="set default pointer size", default="4")
+
+(options, args) = parser.parse_args()
+
+if len(args) == 0:
+ parser.error("No protocol file specified")
+
+if len(args) == 1:
+ parser.error("No destination file specified")
+
+ptypes.default_pointer_size = int(options.ptrsize)
+
+proto_file = args[0]
+dest_file = args[1]
+proto = spice_parser.parse(proto_file)
+
+if proto == None:
+ exit(1)
+
+codegen.set_prefix(proto.name)
+writer = codegen.CodeWriter()
+writer.header = codegen.CodeWriter()
+writer.set_option("source", os.path.basename(proto_file))
+
+license = """/*
+ Copyright (C) 2013 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+"""
+
+writer.public_prefix = options.prefix
+
+writer.writeln("/* this is a file autogenerated by spice_codegen.py */")
+writer.write(license)
+writer.header.writeln("/* this is a file autogenerated by spice_codegen.py */")
+writer.header.write(license)
+if not options.header and not options.generate_enums:
+ writer.writeln("#ifdef HAVE_CONFIG_H")
+ writer.writeln("#include <config.h>")
+ writer.writeln("#endif")
+
+if options.assert_on_error:
+ writer.set_option("assert_on_error")
+
+if options.print_error:
+ writer.set_option("print_error")
+
+if options.includes:
+ for i in options.includes:
+ writer.header.writeln('#include <%s>' % i)
+ writer.writeln('#include <%s>' % i)
+
+if options.generate_enums or options.generate_dissector:
+ write_enums(writer, options.generate_dissector)
+
+if options.generate_demarshallers:
+ if not options.server and not options.client:
+ print >> sys.stderr, "Must specify client and/or server"
+ sys.exit(1)
+ demarshal.write_includes(writer)
+
+ if options.server:
+ demarshal.write_protocol_parser(writer, proto, False)
+ if options.client:
+ demarshal.write_protocol_parser(writer, proto, True)
+
+if options.generate_marshallers or (options.struct_marshallers and len(options.struct_marshallers) > 0):
+ marshal.write_includes(writer)
+
+if options.generate_marshallers:
+ if not options.server and not options.client:
+ print >> sys.stderr, "Must specify client and/or server"
+ sys.exit(1)
+ if options.server:
+ marshal.write_protocol_marshaller(writer, proto, False, options.private_marshallers)
+ if options.client:
+ marshal.write_protocol_marshaller(writer, proto, True, options.private_marshallers)
+
+if options.struct_marshallers:
+ for structname in options.struct_marshallers:
+ t = ptypes.lookup_type(structname)
+ marshal.write_marshal_ptr_function(writer, t, False)
+
+if options.generate_marshallers or (options.struct_marshallers and len(options.struct_marshallers) > 0):
+ marshal.write_trailer(writer)
+
+if options.header:
+ content = writer.header.getvalue()
+else:
+ content = writer.getvalue()
+if options.keep_identical_file:
+ try:
+ f = open(dest_file, 'rb')
+ old_content = f.read()
+ f.close()
+
+ if content == old_content:
+ six.print_("No changes to %s" % dest_file)
+ sys.exit(0)
+
+ except IOError:
+ pass
+
+f = open(dest_file, 'wb')
+if six.PY3:
+ f.write(bytes(content, 'UTF-8'))
+else:
+ f.write(content)
+f.close()
+
+six.print_("Wrote %s" % dest_file)
+sys.exit(0)