1#!/usr/bin/env python 2 3import os 4import sys 5from optparse import OptionParser 6import traceback 7from python_modules import spice_parser 8from python_modules import ptypes 9from python_modules import codegen 10from python_modules import demarshal 11from python_modules import marshal 12import six 13 14def write_channel_enums(writer, channel, client, describe): 15 messages = list(filter(lambda m : m.channel == channel, \ 16 channel.client_messages if client else channel.server_messages)) 17 if len(messages) == 0: 18 return 19 if client: 20 prefix = [ "MSGC" ] 21 else: 22 prefix = [ "MSG" ] 23 if channel.member_name: 24 prefix.append(channel.member_name.upper()) 25 if not describe: 26 writer.begin_block("enum") 27 else: 28 writer.begin_block("static const value_string %s_vs[] = " % (codegen.prefix_underscore_lower(*[x.lower() for x in prefix]))) 29 i = 0 30 prefix.append(None) # To be replaced with name 31 for m in messages: 32 prefix[-1] = m.name.upper() 33 enum = codegen.prefix_underscore_upper(*prefix) 34 if describe: 35 writer.writeln("{ %s, \"%s %s\" }," % (enum, "Client" if client else "Server", m.name.upper())) 36 else: 37 if m.value == i: 38 writer.writeln("%s," % enum) 39 i = i + 1 40 else: 41 writer.writeln("%s = %s," % (enum, m.value)) 42 i = m.value + 1 43 if describe: 44 writer.writeln("{ 0, NULL }"); 45 else: 46 if channel.member_name: 47 prefix[-1] = prefix[-2] 48 prefix[-2] = "END" 49 writer.newline() 50 writer.writeln("%s" % (codegen.prefix_underscore_upper(*prefix))) 51 writer.end_block(semicolon=True) 52 writer.newline() 53 54def write_channel_type_enum(writer, describe=False): 55 i = 0 56 if describe: 57 writer.begin_block("static const value_string channel_types_vs[] =") 58 else: 59 writer.begin_block("enum") 60 for c in proto.channels: 61 enum = codegen.prefix_underscore_upper("CHANNEL", c.name.upper()) 62 if describe: 63 writer.writeln("{ %s, \"%s\" }," % (enum, c.name.upper())) 64 else: 65 if c.value == i: 66 writer.writeln("%s," % enum) 67 i = i + 1 68 else: 69 writer.writeln("%s = %s," % (enum, c.value)) 70 i = c.value + 1 71 writer.newline() 72 if describe: 73 writer.writeln("{ 0, NULL }") 74 else: 75 writer.writeln("SPICE_END_CHANNEL") 76 writer.end_block(semicolon=True) 77 writer.newline() 78 79 80def write_enums(writer, describe=False): 81 writer.writeln("#ifndef _H_SPICE_ENUMS") 82 writer.writeln("#define _H_SPICE_ENUMS") 83 writer.newline() 84 85 # Define enums 86 for t in ptypes.get_named_types(): 87 if isinstance(t, ptypes.EnumBaseType): 88 t.c_define(writer) 89 if describe: 90 t.c_describe(writer) 91 92 write_channel_type_enum(writer) 93 if (describe): 94 write_channel_type_enum(writer, True) 95 96 for c in ptypes.get_named_types(): 97 if not isinstance(c, ptypes.ChannelType): 98 continue 99 write_channel_enums(writer, c, False, False) 100 if describe: 101 write_channel_enums(writer, c, False, describe) 102 write_channel_enums(writer, c, True, False) 103 if describe: 104 write_channel_enums(writer, c, True, describe) 105 106 writer.writeln("#endif /* _H_SPICE_ENUMS */") 107 108def write_content(dest_file, content, keep_identical_file): 109 if keep_identical_file: 110 try: 111 f = open(dest_file, 'rb') 112 old_content = f.read() 113 f.close() 114 115 if content == old_content: 116 six.print_("No changes to %s" % dest_file) 117 return 118 119 except IOError: 120 pass 121 122 f = open(dest_file, 'wb') 123 if six.PY3: 124 f.write(bytes(content, 'UTF-8')) 125 else: 126 f.write(content) 127 f.close() 128 129 six.print_("Wrote %s" % dest_file) 130 131 132parser = OptionParser(usage="usage: %prog [options] <protocol_file> <destination file>") 133parser.add_option("-e", "--generate-enums", 134 action="store_true", dest="generate_enums", default=False, 135 help="Generate enums") 136parser.add_option("-w", "--generate-wireshark-dissector", 137 action="store_true", dest="generate_dissector", default=False, 138 help="Generate Wireshark dissector definitions") 139parser.add_option("-d", "--generate-demarshallers", 140 action="store_true", dest="generate_demarshallers", default=False, 141 help="Generate demarshallers") 142parser.add_option("-m", "--generate-marshallers", 143 action="store_true", dest="generate_marshallers", default=False, 144 help="Generate message marshallers") 145parser.add_option("-P", "--private-marshallers", 146 action="store_true", dest="private_marshallers", default=False, 147 help="Generate private message marshallers") 148parser.add_option("-M", "--generate-struct-marshaller", 149 action="append", dest="struct_marshallers", 150 help="Generate struct marshallers") 151parser.add_option("-a", "--assert-on-error", 152 action="store_true", dest="assert_on_error", default=False, 153 help="Assert on error") 154parser.add_option("-H", "--header", 155 action="store_true", dest="header", default=False, 156 help="Generate header") 157parser.add_option("-p", "--print-error", 158 action="store_true", dest="print_error", default=False, 159 help="Print errors") 160parser.add_option("-s", "--server", 161 action="store_true", dest="server", default=False, 162 help="Print errors") 163parser.add_option("-c", "--client", 164 action="store_true", dest="client", default=False, 165 help="Print errors") 166parser.add_option("-k", "--keep-identical-file", 167 action="store_true", dest="keep_identical_file", default=False, 168 help="Print errors") 169parser.add_option("-i", "--include", 170 action="append", dest="includes", metavar="FILE", 171 help="Include FILE in generated code") 172parser.add_option("--suffix", dest="suffix", 173 help="set public symbol suffix", default="") 174parser.add_option("--license", dest="license", 175 help="license to use for generated file(s) (LGPL/BSD)", default="LGPL") 176parser.add_option("--generate-header", 177 action="store_true", dest="generate_header", default=False, 178 help="Generate also the header") 179parser.add_option("--generated-declaration-file", dest="generated_declaration_file", metavar="FILE", 180 help="Name of the file to generate declarations") 181 182(options, args) = parser.parse_args() 183 184if len(args) == 0: 185 parser.error("No protocol file specified") 186 187if len(args) == 1: 188 parser.error("No destination file specified") 189 190proto_file = args[0] 191dest_file = args[1] 192proto = spice_parser.parse(proto_file) 193 194if proto == None: 195 exit(1) 196 197codegen.set_prefix(proto.name) 198writer = codegen.CodeWriter() 199writer.header = codegen.CodeWriter() 200if options.generate_header: 201 filename = os.path.splitext(dest_file)[0] + '.h' 202 writer.header.set_option("dest_file", filename) 203else: 204 writer.header.set_option("dest_file", dest_file) 205writer.set_option("source", os.path.basename(proto_file)) 206 207if options.license == "LGPL": 208 license = """/* 209 Copyright (C) 2013 Red Hat, Inc. 210 211 This library is free software; you can redistribute it and/or 212 modify it under the terms of the GNU Lesser General Public 213 License as published by the Free Software Foundation; either 214 version 2.1 of the License, or (at your option) any later version. 215 216 This library is distributed in the hope that it will be useful, 217 but WITHOUT ANY WARRANTY; without even the implied warranty of 218 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 219 Lesser General Public License for more details. 220 221 You should have received a copy of the GNU Lesser General Public 222 License along with this library; if not, see <http://www.gnu.org/licenses/>. 223*/ 224 225""" 226elif options.license == "BSD": 227 license = """/* 228 Copyright (C) 2013 Red Hat, Inc. 229 230 Redistribution and use in source and binary forms, with or without 231 modification, are permitted provided that the following conditions are 232 met: 233 234 * Redistributions of source code must retain the above copyright 235 notice, this list of conditions and the following disclaimer. 236 * Redistributions in binary form must reproduce the above copyright 237 notice, this list of conditions and the following disclaimer in 238 the documentation and/or other materials provided with the 239 distribution. 240 * Neither the name of the copyright holder nor the names of its 241 contributors may be used to endorse or promote products derived 242 from this software without specific prior written permission. 243 244 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS 245 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 246 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 247 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 248 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 249 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 250 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 251 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 252 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 253 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 254 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 255*/ 256 257""" 258else: 259 print >> sys.stderr, "Invalid license specified: %s" % options.license 260 sys.exit(1) 261 262all_structures = {} 263def generate_declaration(t, writer_top): 264 writer = codegen.CodeWriter() 265 try: 266 c_type = t.c_type() 267 t.generate_c_declaration(writer) 268 value = writer.getvalue().strip() 269 if not value: 270 return 271 if c_type in all_structures: 272 assert all_structures[c_type] == value, """Structure %s redefinition 273previous: 274%s 275--- 276current: 277%s 278---""" % (c_type, all_structures[c_type], value) 279 else: 280 all_structures[c_type] = value 281 t.generate_c_declaration(writer_top) 282 except: 283 print >> sys.stderr, 'type %s' % t 284 print >> sys.stderr, writer.getvalue() 285 traceback.print_exc(sys.stderr) 286 287def generate_declarations(): 288 writer = codegen.CodeWriter() 289 writer.public_suffix = options.suffix 290 writer.write(license) 291 292 # all types 293 for t in ptypes.get_named_types(): 294 if isinstance(t, ptypes.StructType): 295 generate_declaration(t, writer) 296 if isinstance(t, ptypes.ChannelType): 297 for m in t.client_messages + t.server_messages: 298 generate_declaration(m.message_type, writer) 299 300 content = writer.getvalue() 301 write_content(options.generated_declaration_file, content, 302 options.keep_identical_file) 303 304if options.generated_declaration_file: 305 generate_declarations() 306 307writer.public_suffix = options.suffix 308 309writer.writeln("/* this is a file autogenerated by spice_codegen.py */") 310writer.write(license) 311writer.header.writeln("/* this is a file autogenerated by spice_codegen.py */") 312writer.header.write(license) 313if not options.generate_enums: 314 writer.writeln("#ifdef HAVE_CONFIG_H") 315 writer.writeln("#include <config.h>") 316 writer.writeln("#endif") 317 318if options.assert_on_error: 319 writer.set_option("assert_on_error") 320 321if options.print_error: 322 writer.set_option("print_error") 323 324if options.includes: 325 for i in options.includes: 326 writer.header.writeln('#include "%s"' % i) 327 writer.writeln('#include "%s"' % i) 328 329if options.generate_enums or options.generate_dissector: 330 write_enums(writer, options.generate_dissector) 331 332if options.generate_demarshallers: 333 if not options.server and not options.client: 334 print >> sys.stderr, "Must specify client and/or server" 335 sys.exit(1) 336 demarshal.write_includes(writer) 337 338 if options.server: 339 demarshal.write_protocol_parser(writer, proto, False) 340 if options.client: 341 demarshal.write_protocol_parser(writer, proto, True) 342 343if options.generate_marshallers or (options.struct_marshallers and len(options.struct_marshallers) > 0): 344 marshal.write_includes(writer) 345 346if options.generate_marshallers: 347 if not options.server and not options.client: 348 print >> sys.stderr, "Must specify client and/or server" 349 sys.exit(1) 350 if options.server: 351 marshal.write_protocol_marshaller(writer, proto, False, options.private_marshallers) 352 if options.client: 353 marshal.write_protocol_marshaller(writer, proto, True, options.private_marshallers) 354 355if options.struct_marshallers: 356 for structname in options.struct_marshallers: 357 t = ptypes.lookup_type(structname) 358 marshal.write_marshal_ptr_function(writer, t, False) 359 360if options.generate_marshallers or (options.struct_marshallers and len(options.struct_marshallers) > 0): 361 marshal.write_trailer(writer) 362 363if options.header: 364 content = writer.header.getvalue() 365else: 366 content = writer.getvalue() 367write_content(dest_file, content, options.keep_identical_file) 368if options.generate_header: 369 content = writer.header.getvalue() 370 filename = writer.header.options["dest_file"] 371 write_content(filename, content, options.keep_identical_file) 372sys.exit(0) 373