#!/usr/bin/python
'''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
-nanopb_version = "nanopb-0.2.4-dev"
+nanopb_version = "nanopb-0.2.5-dev"
+
+import sys
try:
- import google.protobuf.descriptor_pb2 as descriptor
+ import google, distutils.util # bbfreeze seems to need these
import google.protobuf.text_format as text_format
except:
- print
- print "*************************************************************"
- print "*** Could not import the Google protobuf Python libraries ***"
- print "*** Try installing package 'python-protobuf' or similar. ***"
- print "*************************************************************"
- print
+ sys.stderr.write('''
+ *************************************************************
+ *** Could not import the Google protobuf Python libraries ***
+ *** Try installing package 'python-protobuf' or similar. ***
+ *************************************************************
+ ''' + '\n')
raise
try:
- import nanopb_pb2
+ import proto.nanopb_pb2 as nanopb_pb2
+ import proto.descriptor_pb2 as descriptor
except:
- print
- print "***************************************************************"
- print "*** Could not import the precompiled nanopb_pb2.py. ***"
- print "*** Run 'make' in the 'generator' folder to update the file.***"
- print "***************************************************************"
- print
+ sys.stderr.write('''
+ ********************************************************************
+ *** Failed to import the protocol definitions for generator. ***
+ *** You have to run 'make' in the nanopb/generator/proto folder. ***
+ ********************************************************************
+ ''' + '\n')
raise
-
-
-
-
# ---------------------------------------------------------------------------
# Generation of single fields
# ---------------------------------------------------------------------------
FieldD.TYPE_FIXED32: ('uint32_t', 'FIXED32', 4),
FieldD.TYPE_FIXED64: ('uint64_t', 'FIXED64', 8),
FieldD.TYPE_FLOAT: ('float', 'FLOAT', 4),
- FieldD.TYPE_INT32: ('int32_t', 'INT32', 5),
+ FieldD.TYPE_INT32: ('int32_t', 'INT32', 10),
FieldD.TYPE_INT64: ('int64_t', 'INT64', 10),
FieldD.TYPE_SFIXED32: ('int32_t', 'SFIXED32', 4),
FieldD.TYPE_SFIXED64: ('int64_t', 'SFIXED64', 8),
else:
raise NotImplementedError(desc.label)
+ # Check if the field can be implemented with static allocation
+ # i.e. whether the data size is known.
+ if desc.type == FieldD.TYPE_STRING and self.max_size is None:
+ can_be_static = False
+
+ if desc.type == FieldD.TYPE_BYTES and self.max_size is None:
+ can_be_static = False
+
+ # Decide how the field data will be allocated
+ 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_STATIC:
+ self.allocation = 'STATIC'
+ elif field_options.type == nanopb_pb2.FT_POINTER:
+ self.allocation = 'POINTER'
+ elif field_options.type == nanopb_pb2.FT_CALLBACK:
+ self.allocation = 'CALLBACK'
+ else:
+ raise NotImplementedError(field_options.type)
+
# Decide the C data type to use in the struct.
if datatypes.has_key(desc.type):
self.ctype, self.pbtype, self.enc_size = datatypes[desc.type]
self.enc_size = 5 # protoc rejects enum values > 32 bits
elif desc.type == FieldD.TYPE_STRING:
self.pbtype = 'STRING'
- if self.max_size is None:
- can_be_static = False
- else:
+ self.ctype = 'char'
+ if self.allocation == 'STATIC':
self.ctype = 'char'
self.array_decl += '[%d]' % self.max_size
self.enc_size = varint_max_size(self.max_size) + self.max_size
elif desc.type == FieldD.TYPE_BYTES:
self.pbtype = 'BYTES'
- if self.max_size is None:
- can_be_static = False
- else:
+ if self.allocation == 'STATIC':
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_ptr_t'
elif desc.type == FieldD.TYPE_MESSAGE:
self.pbtype = 'MESSAGE'
self.ctype = self.submsgname = names_from_type_name(desc.type_name)
else:
raise NotImplementedError(desc.type)
- 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_STATIC:
- self.allocation = 'STATIC'
- elif field_options.type == nanopb_pb2.FT_CALLBACK:
- self.allocation = 'CALLBACK'
- self.ctype = 'pb_callback_t'
- self.array_decl = ''
- else:
- raise NotImplementedError(field_options.type)
-
def __cmp__(self, other):
return cmp(self.tag, other.tag)
def __str__(self):
- if self.rules == 'OPTIONAL' and self.allocation == 'STATIC':
- result = ' bool has_' + self.name + ';\n'
- elif self.rules == 'REPEATED' and self.allocation == 'STATIC':
- result = ' size_t ' + self.name + '_count;\n'
+ result = ''
+ if self.allocation == 'POINTER':
+ if self.rules == 'REPEATED':
+ result += ' size_t ' + self.name + '_count;\n'
+
+ if self.pbtype == 'MESSAGE':
+ # Use struct definition, so recursive submessages are possible
+ result += ' struct _%s *%s;' % (self.ctype, self.name)
+ elif self.rules == 'REPEATED' and self.pbtype == 'STRING':
+ # String arrays need to be defined as pointers to pointers
+ result += ' %s **%s;' % (self.ctype, self.name)
+ else:
+ result += ' %s *%s;' % (self.ctype, self.name)
+ elif self.allocation == 'CALLBACK':
+ result += ' pb_callback_t %s;' % self.name
else:
- result = ''
- result += ' %s %s%s;' % (self.ctype, self.name, self.array_decl)
+ if self.rules == 'OPTIONAL' and self.allocation == 'STATIC':
+ result += ' bool has_' + self.name + ';\n'
+ elif self.rules == 'REPEATED' and self.allocation == 'STATIC':
+ result += ' size_t ' + self.name + '_count;\n'
+ result += ' %s %s%s;' % (self.ctype, self.name, self.array_decl)
return result
def types(self):
result = ' PB_FIELD2(%3d, ' % self.tag
result += '%-8s, ' % self.pbtype
result += '%s, ' % self.rules
- result += '%s, ' % self.allocation
+ result += '%-8s, ' % self.allocation
result += '%s, ' % ("FIRST" if not prev_field_name else "OTHER")
result += '%s, ' % self.struct_name
result += '%s, ' % self.name
self.skip = False
self.rules = 'OPTEXT'
+ def tags(self):
+ '''Return the #define for the tag number of this field.'''
+ identifier = '%s_tag' % self.fullname
+ return '#define %-40s %d\n' % (identifier, self.tag)
+
def extension_decl(self):
'''Declaration of the extension type in the .pb.h file'''
if self.skip:
for msg in sort_dependencies(messages):
for field in msg.fields:
yield field.tags()
+ for extension in extensions:
+ yield extension.tags()
yield '\n'
yield '/* Struct field encoding specification for nanopb */\n'
def main_plugin():
'''Main function when invoked as a protoc plugin.'''
- import plugin_pb2
+ import sys
+ if sys.platform == "win32":
+ import os, msvcrt
+ # Set stdin and stdout to binary mode
+ msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
+ msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
+
+ import proto.plugin_pb2 as plugin_pb2
data = sys.stdin.read()
request = plugin_pb2.CodeGeneratorRequest.FromString(data)
if __name__ == '__main__':
# Check if we are running as a plugin under protoc
- if 'protoc-gen-' in sys.argv[0]:
+ if 'protoc-gen-' in sys.argv[0] or '--protoc-plugin' in sys.argv:
main_plugin()
else:
main_cli()