X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=generator%2Fnanopb_generator.py;h=bffdb353c7daff7b9a27d2ec085d3a9996f0be85;hb=bb5dc04584f4e147cf3c892ab004ecd36ba7642b;hp=6ce91cf48d002b387807fa9aba9dd13bb4a42d97;hpb=9e0ee92f0a42ce2c5c9d4bf4f1d7d822caf1c561;p=apps%2Fagl-service-can-low-level.git diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index 6ce91cf4..bffdb353 100644 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -1,4 +1,5 @@ '''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.''' +nanopb_version = "nanopb-0.1.9-dev" try: import google.protobuf.descriptor_pb2 as descriptor @@ -31,7 +32,7 @@ except: # Generation of single fields # --------------------------------------------------------------------------- - +import time import os.path # Values are tuple (c type, pb ltype) @@ -72,6 +73,9 @@ class Names: else: raise ValueError("Name parts should be of type str") + def __eq__(self, other): + return isinstance(other, Names) and self.parts == other.parts + def names_from_type_name(type_name): '''Parse Names() from FieldDescriptorProto type_name''' if type_name[0] != '.': @@ -79,10 +83,18 @@ def names_from_type_name(type_name): return Names(type_name[1:].split('.')) class Enum: - def __init__(self, names, desc): + def __init__(self, names, desc, enum_options): '''desc is EnumDescriptorProto''' + + self.options = enum_options self.names = names + desc.name - self.values = [(self.names + x.name, x.number) for x in desc.value] + + if enum_options.long_names: + self.values = [(self.names + x.name, x.number) for x in desc.value] + else: + self.values = [(names + x.name, x.number) for x in desc.value] + + self.value_longnames = [self.names + x.name for x in desc.value] def __str__(self): result = 'typedef enum _%s {\n' % self.names @@ -91,7 +103,7 @@ class Enum: return result class Field: - def __init__(self, struct_name, desc): + def __init__(self, struct_name, desc, field_options): '''desc is FieldDescriptorProto''' self.tag = desc.number self.struct_name = struct_name @@ -101,28 +113,27 @@ class Field: self.max_count = None self.array_decl = "" - # Parse nanopb-specific field options - if desc.options.HasExtension(nanopb_pb2.nanopb): - ext = desc.options.Extensions[nanopb_pb2.nanopb] - if ext.HasField("max_size"): - self.max_size = ext.max_size - if ext.HasField("max_count"): - self.max_count = ext.max_count + # Parse field options + if field_options.HasField("max_size"): + self.max_size = field_options.max_size + + if field_options.HasField("max_count"): + self.max_count = field_options.max_count if desc.HasField('default_value'): self.default = desc.default_value - + # Decide HTYPE # HTYPE is the high-order nibble of nanopb field description, # defining whether value is required/optional/repeated. - is_callback = False + can_be_static = True if desc.label == FieldD.LABEL_REQUIRED: self.htype = 'PB_HTYPE_REQUIRED' elif desc.label == FieldD.LABEL_OPTIONAL: self.htype = 'PB_HTYPE_OPTIONAL' elif desc.label == FieldD.LABEL_REPEATED: if self.max_count is None: - is_callback = True + can_be_static = False else: self.htype = 'PB_HTYPE_ARRAY' self.array_decl = '[%d]' % self.max_count @@ -143,14 +154,14 @@ class Field: elif desc.type == FieldD.TYPE_STRING: self.ltype = 'PB_LTYPE_STRING' if self.max_size is None: - is_callback = True + can_be_static = False else: self.ctype = 'char' self.array_decl += '[%d]' % self.max_size elif desc.type == FieldD.TYPE_BYTES: self.ltype = 'PB_LTYPE_BYTES' if self.max_size is None: - is_callback = True + can_be_static = False else: self.ctype = self.struct_name + self.name + 't' elif desc.type == FieldD.TYPE_MESSAGE: @@ -159,7 +170,16 @@ class Field: else: raise NotImplementedError(desc.type) - if is_callback: + if field_options.type == nanopb_pb2.FT_DEFAULT: + if can_be_static: + field_options.type = nanopb_pb2.FT_STATIC + else: + field_options.type = nanopb_pb2.FT_CALLBACK + + if field_options.type == nanopb_pb2.FT_STATIC and not can_be_static: + raise Exception("Field %s is defined as static, but max_size or max_count is not given." % self.name) + + if field_options.type == nanopb_pb2.FT_CALLBACK: self.htype = 'PB_HTYPE_CALLBACK' self.ctype = 'pb_callback_t' self.array_decl = '' @@ -284,9 +304,9 @@ class Field: class Message: - def __init__(self, names, desc): + def __init__(self, names, desc, message_options): self.name = names - self.fields = [Field(self.name, f) for f in desc.field] + self.fields = [Field(self.name, f, get_nanopb_suboptions(f, message_options)) for f in desc.field] self.ordered_fields = self.fields[:] self.ordered_fields.sort() @@ -356,7 +376,7 @@ def iterate_messages(desc, names = Names()): for x in iterate_messages(submsg, sub_names): yield x -def parse_file(fdesc): +def parse_file(fdesc, file_options): '''Takes a FileDescriptorProto and returns tuple (enum, messages).''' enums = [] @@ -368,12 +388,24 @@ def parse_file(fdesc): base_name = Names() for enum in fdesc.enum_type: - enums.append(Enum(base_name, enum)) + enum_options = get_nanopb_suboptions(enum, file_options) + enums.append(Enum(base_name, enum, enum_options)) for names, message in iterate_messages(fdesc, base_name): - messages.append(Message(names, message)) + message_options = get_nanopb_suboptions(message, file_options) + messages.append(Message(names, message, message_options)) for enum in message.enum_type: - enums.append(Enum(names, enum)) + enum_options = get_nanopb_suboptions(enum, message_options) + enums.append(Enum(names, enum, enum_options)) + + # Fix field default values where enum short names are used. + for enum in enums: + if not enum.options.long_names: + for message in messages: + for field in message.fields: + if field.default in enum.value_longnames: + idx = enum.value_longnames.index(field.default) + field.default = enum.values[idx][0] return enums, messages @@ -414,6 +446,7 @@ def generate_header(dependencies, headername, enums, messages): ''' yield '/* Automatically generated nanopb header */\n' + yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime()) symbol = headername.replace('.', '_').upper() yield '#ifndef _PB_%s_\n' % symbol @@ -423,7 +456,10 @@ def generate_header(dependencies, headername, enums, messages): for dependency in dependencies: noext = os.path.splitext(dependency)[0] yield '#include "%s.pb.h"\n' % noext - yield '\n' + + yield '#ifdef __cplusplus\n' + yield 'extern "C" {\n' + yield '#endif\n\n' yield '/* Enum definitions */\n' for enum in enums: @@ -457,7 +493,9 @@ def generate_header(dependencies, headername, enums, messages): worst = 0 worst_field = '' checks = [] + checks_msgnames = [] for msg in messages: + checks_msgnames.append(msg.name) for field in msg.fields: status = field.largest_field_value() if isinstance(status, (str, unicode)): @@ -475,7 +513,8 @@ def generate_header(dependencies, headername, enums, messages): yield '#error Field descriptor for %s is too large. Define PB_FIELD_16BIT to fix this.\n' % worst_field else: assertion = ' && '.join(str(c) + ' < 256' for c in checks) - yield 'STATIC_ASSERT((%s), YOU_MUST_DEFINE_PB_FIELD_16BIT)\n' % assertion + msgs = '_'.join(str(n) for n in checks_msgnames) + yield 'STATIC_ASSERT((%s), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_%s)\n'%(assertion,msgs) yield '#endif\n\n' if worst > 65535 or checks: @@ -484,9 +523,14 @@ def generate_header(dependencies, headername, enums, messages): yield '#error Field descriptor for %s is too large. Define PB_FIELD_32BIT to fix this.\n' % worst_field else: assertion = ' && '.join(str(c) + ' < 65536' for c in checks) - yield 'STATIC_ASSERT((%s), YOU_MUST_DEFINE_PB_FIELD_32BIT)\n' % assertion + msgs = '_'.join(str(n) for n in checks_msgnames) + yield 'STATIC_ASSERT((%s), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_%s)\n'%(assertion,msgs) yield '#endif\n' + yield '\n#ifdef __cplusplus\n' + yield '} /* extern "C" */\n' + yield '#endif\n' + # End of header yield '\n#endif\n' @@ -494,6 +538,7 @@ def generate_source(headername, enums, messages): '''Generate content for a source file.''' yield '/* Automatically generated nanopb constant definitions */\n' + yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime()) yield '#include "%s"\n\n' % headername for msg in messages: @@ -513,6 +558,7 @@ def generate_source(headername, enums, messages): import sys import os.path from optparse import OptionParser +import google.protobuf.text_format as text_format optparser = OptionParser( usage = "Usage: nanopb_generator.py [options] file.pb ...", @@ -522,6 +568,32 @@ optparser.add_option("-x", dest="exclude", metavar="FILE", action="append", defa help="Exclude file from generated #include list.") optparser.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False, help="Don't print anything except errors.") +optparser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False, + help="Print more information.") +optparser.add_option("-s", dest="settings", metavar="OPTION:VALUE", action="append", default=[], + help="Set generator option (max_size, max_count etc.).") + +def get_nanopb_suboptions(subdesc, options): + '''Get copy of options, and merge information from subdesc.''' + new_options = nanopb_pb2.NanoPBOptions() + new_options.CopyFrom(options) + + if isinstance(subdesc.options, descriptor.FieldOptions): + ext_type = nanopb_pb2.nanopb + elif isinstance(subdesc.options, descriptor.FileOptions): + ext_type = nanopb_pb2.nanopb_fileopt + elif isinstance(subdesc.options, descriptor.MessageOptions): + ext_type = nanopb_pb2.nanopb_msgopt + elif isinstance(subdesc.options, descriptor.EnumOptions): + ext_type = nanopb_pb2.nanopb_enumopt + else: + raise Exception("Unknown options type") + + if subdesc.options.HasExtension(ext_type): + ext = subdesc.options.Extensions[ext_type] + new_options.MergeFrom(ext) + + return new_options def process(filenames, options): '''Process the files given on the command line.''' @@ -530,10 +602,24 @@ def process(filenames, options): optparser.print_help() return False + if options.quiet: + options.verbose = False + + toplevel_options = nanopb_pb2.NanoPBOptions() + for s in options.settings: + text_format.Merge(s, toplevel_options) + for filename in filenames: data = open(filename, 'rb').read() fdesc = descriptor.FileDescriptorSet.FromString(data) - enums, messages = parse_file(fdesc.file[0]) + + file_options = get_nanopb_suboptions(fdesc.file[0], toplevel_options) + + if options.verbose: + print "Options for " + filename + ":" + print text_format.MessageToString(file_options) + + enums, messages = parse_file(fdesc.file[0], file_options) noext = os.path.splitext(filename)[0] headername = noext + '.pb.h' @@ -545,7 +631,7 @@ def process(filenames, options): # List of .proto files that should not be included in the C header file # even if they are mentioned in the source .proto. - excludes = ['nanopb.proto', 'google/protobuf/descriptor.proto'] + excludes = ['nanopb.proto', 'google/protobuf/descriptor.proto'] + options.exclude dependencies = [d for d in fdesc.file[0].dependency if d not in excludes] header = open(headername, 'w')