X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=generator%2Fnanopb_generator.py;h=6e5ebaf9317d2a31bdea54e535962008d62abf54;hb=ffe4aff87cc3a15863c09aa808adf2381c8f2fb7;hp=62ee41bf14487fc8d4630dbe993a48817323bf14;hpb=7af48e550f084aed929bb7db99070cb8f5d08210;p=apps%2Fagl-service-can-low-level.git diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py index 62ee41bf..6e5ebaf9 100755 --- a/generator/nanopb_generator.py +++ b/generator/nanopb_generator.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals '''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.''' -nanopb_version = "nanopb-0.3.5-dev" +nanopb_version = "nanopb-0.3.8-dev" import sys import re @@ -197,12 +197,37 @@ class Enum: result += ' %s;' % self.names + result += '\n#define _%s_MIN %s' % (self.names, self.values[0][0]) + result += '\n#define _%s_MAX %s' % (self.names, self.values[-1][0]) + result += '\n#define _%s_ARRAYSIZE ((%s)(%s+1))' % (self.names, self.names, self.values[-1][0]) + if not self.options.long_names: # Define the long names always so that enum value references # from other files work properly. for i, x in enumerate(self.values): result += '\n#define %s %s' % (self.value_longnames[i], x[0]) + if self.options.enum_to_string: + result += '\nconst char *%s_name(%s v);\n' % (self.names, self.names) + + return result + + def enum_to_string_definition(self): + if not self.options.enum_to_string: + return "" + + result = 'const char *%s_name(%s v) {\n' % (self.names, self.names) + result += ' switch (v) {\n' + + for ((enumname, _), strname) in zip(self.values, self.value_longnames): + # Strip off the leading type name from the string value. + strval = str(strname)[len(str(self.names)) + 1:] + result += ' case %s: return "%s";\n' % (enumname, strval) + + result += ' }\n' + result += ' return "unknown";\n' + result += '}\n' + return result class FieldMaxSize: @@ -213,7 +238,7 @@ class FieldMaxSize: self.worst = worst self.worst_field = field_name - self.checks = checks + self.checks = list(checks) def extend(self, extend, field_name = None): self.worst = max(self.worst, extend.worst) @@ -237,6 +262,11 @@ class Field: self.enc_size = None self.ctype = None + self.inline = None + if field_options.type == nanopb_pb2.FT_INLINE: + field_options.type = nanopb_pb2.FT_STATIC + self.inline = nanopb_pb2.FT_INLINE + # Parse field options if field_options.HasField("max_size"): self.max_size = field_options.max_size @@ -249,16 +279,18 @@ class Field: # Check field rules, i.e. required/optional/repeated. can_be_static = True - if desc.label == FieldD.LABEL_REQUIRED: - self.rules = 'REQUIRED' - elif desc.label == FieldD.LABEL_OPTIONAL: - self.rules = 'OPTIONAL' - elif desc.label == FieldD.LABEL_REPEATED: + if desc.label == FieldD.LABEL_REPEATED: self.rules = 'REPEATED' if self.max_count is None: can_be_static = False else: self.array_decl = '[%d]' % self.max_count + elif field_options.proto3: + self.rules = 'SINGULAR' + elif desc.label == FieldD.LABEL_REQUIRED: + self.rules = 'REQUIRED' + elif desc.label == FieldD.LABEL_OPTIONAL: + self.rules = 'OPTIONAL' else: raise NotImplementedError(desc.label) @@ -315,7 +347,12 @@ class Field: elif desc.type == FieldD.TYPE_BYTES: self.pbtype = 'BYTES' if self.allocation == 'STATIC': - self.ctype = self.struct_name + self.name + 't' + # Inline STATIC for BYTES is like STATIC for STRING. + if self.inline: + self.ctype = 'pb_byte_t' + self.array_decl += '[%d]' % self.max_size + else: + self.ctype = self.struct_name + self.name + 't' self.enc_size = varint_max_size(self.max_size) + self.max_size elif self.allocation == 'POINTER': self.ctype = 'pb_bytes_array_t' @@ -355,7 +392,7 @@ class Field: def types(self): '''Return definitions for any special types this field might need.''' - if self.pbtype == 'BYTES' and self.allocation == 'STATIC': + if self.pbtype == 'BYTES' and self.allocation == 'STATIC' and not self.inline: result = 'typedef PB_BYTES_ARRAY_T(%d) %s;\n' % (self.max_size, self.ctype) else: result = '' @@ -384,7 +421,10 @@ class Field: if self.pbtype == 'STRING': inner_init = '""' elif self.pbtype == 'BYTES': - inner_init = '{0, {0}}' + if self.inline: + inner_init = '{0}' + else: + inner_init = '{0, {0}}' elif self.pbtype in ('ENUM', 'UENUM'): inner_init = '(%s)0' % self.ctype else: @@ -396,9 +436,15 @@ class Field: elif self.pbtype == 'BYTES': data = ['0x%02x' % ord(c) for c in self.default] if len(data) == 0: - inner_init = '{0, {0}}' + if self.inline: + inner_init = '{0}' + else: + inner_init = '{0, {0}}' else: - inner_init = '{%d, {%s}}' % (len(data), ','.join(data)) + if self.inline: + inner_init = '{%s}' % ','.join(data) + else: + inner_init = '{%d, {%s}}' % (len(data), ','.join(data)) elif self.pbtype in ['FIXED32', 'UINT32']: inner_init = str(self.default) + 'u' elif self.pbtype in ['FIXED64', 'UINT64']: @@ -450,6 +496,8 @@ class Field: elif self.pbtype == 'BYTES': if self.allocation != 'STATIC': return None # Not implemented + if self.inline: + array_decl = '[%d]' % self.max_size if declaration_only: return 'extern const %s %s_default%s;' % (ctype, self.struct_name + self.name, array_decl) @@ -461,9 +509,10 @@ class Field: identifier = '%s_%s_tag' % (self.struct_name, self.name) return '#define %-40s %d\n' % (identifier, self.tag) - def pb_field_t(self, prev_field_name): + def pb_field_t(self, prev_field_name, union_index = None): '''Return the pb_field_t initializer to use in the constant array. - prev_field_name is the name of the previous field or None. + prev_field_name is the name of the previous field or None. For OneOf + unions, union_index is the index of this field inside the OneOf. ''' if self.rules == 'ONEOF': @@ -477,8 +526,15 @@ class Field: result += '%3d, ' % self.tag result += '%-8s, ' % self.pbtype result += '%s, ' % self.rules - result += '%-8s, ' % self.allocation - result += '%s, ' % ("FIRST" if not prev_field_name else "OTHER") + result += '%-8s, ' % (self.allocation if not self.inline else "INLINE") + + if union_index is not None and union_index > 0: + result += 'UNION, ' + elif prev_field_name is None: + result += 'FIRST, ' + else: + result += 'OTHER, ' + result += '%s, ' % self.struct_name result += '%s, ' % self.name result += '%s, ' % (prev_field_name or self.name) @@ -503,8 +559,8 @@ class Field: '''Determine if this field needs 16bit or 32bit pb_field_t structure to compile properly. Returns numeric value or a C-expression for assert.''' check = [] - if self.pbtype == 'MESSAGE': - if self.rules == 'REPEATED' and self.allocation == 'STATIC': + if self.pbtype == 'MESSAGE' and self.allocation == 'STATIC': + if self.rules == 'REPEATED': check.append('pb_membersize(%s, %s[0])' % (self.struct_name, self.name)) elif self.rules == 'ONEOF': if self.anonymous: @@ -513,6 +569,9 @@ class Field: check.append('pb_membersize(%s, %s.%s)' % (self.struct_name, self.union_name, self.name)) else: check.append('pb_membersize(%s, %s)' % (self.struct_name, self.name)) + elif self.pbtype == 'BYTES' and self.allocation == 'STATIC': + if self.max_size > 251: + check.append('pb_membersize(%s, %s)' % (self.struct_name, self.name)) return FieldMaxSize([self.tag, self.max_size, self.max_count], check, @@ -590,6 +649,7 @@ class ExtensionRange(Field): self.default = None self.max_size = 0 self.max_count = 0 + self.inline = None def __str__(self): return ' pb_extension_t *extensions;' @@ -667,6 +727,7 @@ class OneOf(Field): self.default = None self.rules = 'ONEOF' self.anonymous = False + self.inline = None def add_field(self, field): if field.allocation == 'CALLBACK': @@ -714,8 +775,10 @@ class OneOf(Field): return ''.join([f.tags() for f in self.fields]) def pb_field_t(self, prev_field_name): - result = ',\n'.join([f.pb_field_t(prev_field_name) for f in self.fields]) - return result + parts = [] + for union_index, field in enumerate(self.fields): + parts.append(field.pb_field_t(prev_field_name, union_index)) + return ',\n'.join(parts) def get_last_field_name(self): if self.anonymous: @@ -730,10 +793,11 @@ class OneOf(Field): return largest def encoded_size(self, dependencies): + '''Returns the size of the largest oneof field.''' largest = EncodedSize(0) for f in self.fields: size = EncodedSize(f.encoded_size(dependencies)) - if size is None: + if size.value is None: return None elif size.symbols: return None # Cannot resolve maximum of symbols @@ -808,9 +872,10 @@ class Message: if not self.ordered_fields: # Empty structs are not allowed in C standard. # Therefore add a dummy field if an empty message occurs. - result += ' uint8_t dummy_field;' + result += ' char dummy_field;' result += '\n'.join([str(f) for f in self.ordered_fields]) + result += '\n/* @@protoc_insertion_point(struct:%s) */' % self.name result += '\n}' if self.packed: @@ -1039,7 +1104,10 @@ class ProtoFile: else: yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime()) - symbol = make_identifier(headername) + if self.fdesc.package: + symbol = make_identifier(self.fdesc.package + '_' + headername) + else: + symbol = make_identifier(headername) yield '#ifndef PB_%s_INCLUDED\n' % symbol yield '#define PB_%s_INCLUDED\n' % symbol try: @@ -1054,6 +1122,8 @@ class ProtoFile: yield options.genformat % (noext + options.extension + '.h') yield '\n' + yield '/* @@protoc_insertion_point(includes) */\n' + yield '#if PB_PROTO_HEADER_VERSION != 30\n' yield '#error Regenerate this file with the current version of nanopb generator.\n' yield '#endif\n' @@ -1111,9 +1181,11 @@ class ProtoFile: yield '/* Maximum encoded size of messages (where known) */\n' for msg in self.messages: msize = msg.encoded_size(self.dependencies) + identifier = '%s_size' % msg.name if msize is not None: - identifier = '%s_size' % msg.name yield '#define %-40s %s\n' % (identifier, msize) + else: + yield '/* %s depends on runtime parameters */\n' % identifier yield '\n' yield '/* Message IDs (where set with "msgid" option) */\n' @@ -1148,6 +1220,7 @@ class ProtoFile: yield '#endif\n' # End of header + yield '/* @@protoc_insertion_point(eof) */\n' yield '\n#endif\n' def generate_source(self, headername, options): @@ -1160,6 +1233,7 @@ class ProtoFile: yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime()) yield options.genformat % (headername) yield '\n' + yield '/* @@protoc_insertion_point(includes) */\n' yield '#if PB_PROTO_HEADER_VERSION != 30\n' yield '#error Regenerate this file with the current version of nanopb generator.\n' @@ -1177,6 +1251,9 @@ class ProtoFile: for ext in self.extensions: yield ext.extension_def() + '\n' + for enum in self.enums: + yield enum.enum_to_string_definition() + '\n' + # Add checks for numeric limits if self.messages: largest_msg = max(self.messages, key = lambda m: m.count_required_fields()) @@ -1252,6 +1329,7 @@ class ProtoFile: yield 'PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES)\n' yield '\n' + yield '/* @@protoc_insertion_point(eof) */\n' # --------------------------------------------------------------------------- # Options parsing for the .proto files @@ -1305,6 +1383,9 @@ def get_nanopb_suboptions(subdesc, options, name): new_options = nanopb_pb2.NanoPBOptions() new_options.CopyFrom(options) + if hasattr(subdesc, 'syntax') and subdesc.syntax == "proto3": + new_options.proto3 = True + # Handle options defined in a separate file dotname = '.'.join(name.parts) for namemask, options in Globals.separate_options: @@ -1356,6 +1437,9 @@ optparser.add_option("-f", "--options-file", dest="options_file", metavar="FILE" optparser.add_option("-I", "--options-path", dest="options_path", metavar="DIR", action="append", default = [], help="Search for .options files additionally in this path") +optparser.add_option("-D", "--output-dir", dest="output_dir", + metavar="OUTPUTDIR", default=None, + help="Output directory of .pb.h and .pb.c files") optparser.add_option("-Q", "--generated-include-format", dest="genformat", metavar="FORMAT", default='#include "%s"\n', help="Set format string to use for including other .pb.h files. [default: %default]") @@ -1472,17 +1556,29 @@ def main_cli(): if options.quiet: options.verbose = False - Globals.verbose_options = options.verbose + if options.output_dir and not os.path.exists(options.output_dir): + optparser.print_help() + sys.stderr.write("\noutput_dir does not exist: %s\n" % options.output_dir) + sys.exit(1) + + Globals.verbose_options = options.verbose for filename in filenames: results = process_file(filename, None, options) + base_dir = options.output_dir or '' + to_write = [ + (os.path.join(base_dir, results['headername']), results['headerdata']), + (os.path.join(base_dir, results['sourcename']), results['sourcedata']), + ] + if not options.quiet: - sys.stderr.write("Writing to " + results['headername'] + " and " - + results['sourcename'] + "\n") + paths = " and ".join([x[0] for x in to_write]) + sys.stderr.write("Writing to %s\n" % paths) - open(results['headername'], 'w').write(results['headerdata']) - open(results['sourcename'], 'w').write(results['sourcedata']) + for path, data in to_write: + with open(path, 'w') as f: + f.write(data) def main_plugin(): '''Main function when invoked as a protoc plugin.''' @@ -1546,4 +1642,3 @@ if __name__ == '__main__': main_plugin() else: main_cli() -