mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice-common
synced 2025-12-25 12:28:08 +00:00
- Remove "u" prefix from strings; - Raising strings as exception is not valid anymore; - Convert generators to list where necessary; - traceback.print_exc accept a limit as first argument, not a file (this even for Python 2). Signed-off-by: Frediano Ziglio <freddy77@gmail.com>
367 lines
13 KiB
Python
Executable File
367 lines
13 KiB
Python
Executable File
#!/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
|
|
|
|
|
|
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 */")
|
|
|
|
def write_content(dest_file, content, keep_identical_file):
|
|
if keep_identical_file:
|
|
try:
|
|
with open(dest_file, 'rb') as f:
|
|
old_content = f.read()
|
|
|
|
if content == old_content:
|
|
print("No changes to %s" % dest_file)
|
|
return
|
|
|
|
except IOError:
|
|
pass
|
|
|
|
with open(dest_file, 'wb') as f:
|
|
f.write(bytes(content, 'UTF-8'))
|
|
|
|
print("Wrote %s" % dest_file)
|
|
|
|
|
|
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("--suffix", dest="suffix",
|
|
help="set public symbol suffix", default="")
|
|
parser.add_option("--license", dest="license",
|
|
help="license to use for generated file(s) (LGPL/BSD)", default="LGPL")
|
|
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()
|
|
|
|
if len(args) == 0:
|
|
parser.error("No protocol file specified")
|
|
|
|
if len(args) == 1:
|
|
parser.error("No destination file specified")
|
|
|
|
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()
|
|
if options.generate_header:
|
|
filename = os.path.splitext(dest_file)[0] + '.h'
|
|
writer.header.set_option("dest_file", filename)
|
|
else:
|
|
writer.header.set_option("dest_file", dest_file)
|
|
writer.set_option("source", os.path.basename(proto_file))
|
|
|
|
if options.license == "LGPL":
|
|
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/>.
|
|
*/
|
|
|
|
"""
|
|
elif options.license == "BSD":
|
|
license = """/*
|
|
Copyright (C) 2013 Red Hat, Inc.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are
|
|
met:
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in
|
|
the documentation and/or other materials provided with the
|
|
distribution.
|
|
* Neither the name of the copyright holder nor the names of its
|
|
contributors may be used to endorse or promote products derived
|
|
from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
|
|
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
"""
|
|
else:
|
|
print("Invalid license specified: %s" % options.license, file=sys.stderr)
|
|
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('type %s' % t, file=sys.stderr)
|
|
print(writer.getvalue(), file=sys.stderr)
|
|
traceback.print_exc(file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
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 */")
|
|
writer.write(license)
|
|
writer.header.writeln("/* this is a file autogenerated by spice_codegen.py */")
|
|
writer.header.write(license)
|
|
if not options.generate_enums:
|
|
writer.writeln("#include <config.h>")
|
|
|
|
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("Must specify client and/or server", file=sys.stderr)
|
|
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("Must specify client and/or server", file=sys.stderr)
|
|
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()
|
|
write_content(dest_file, content, options.keep_identical_file)
|
|
if options.generate_header:
|
|
content = writer.header.getvalue()
|
|
filename = writer.header.options["dest_file"]
|
|
write_content(filename, content, options.keep_identical_file)
|
|
sys.exit(0)
|