#!/usr/bin/python
'''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
-nanopb_version = "nanopb-0.3.2-dev"
+nanopb_version = "nanopb-0.3.3-dev"
import sys
+import re
try:
# Add some dummy imports to keep packaging tools happy.
result = 'typedef enum _%s {\n' % self.names
result += ',\n'.join([" %s = %d" % x for x in self.values])
result += '\n} %s;' % self.names
+
+ 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])
+
return result
class Field:
self.name = oneof_desc.name
self.ctype = 'union'
self.fields = []
+ self.allocation = 'ONEOF'
+ self.default = None
+ self.rules = 'ONEOF'
def add_field(self, field):
if field.allocation == 'CALLBACK':
raise Exception("Callback fields inside of oneof are not supported"
- + " (field %s)" % field.fullname)
+ + " (field %s)" % field.name)
field.union_name = self.name
field.rules = 'ONEOF'
return '\n'.join([f.tags() for f in self.fields])
def pb_field_t(self, prev_field_name):
- prev_field_name = prev_field_name or self.name
result = ',\n'.join([f.pb_field_t(prev_field_name) for f in self.fields])
return result
return max([f.largest_field_value() for f in self.fields])
def encoded_size(self, allmsgs):
- return max([f.encoded_size(allmsgs) for f in self.fields])
+ largest = EncodedSize(0)
+ for f in self.fields:
+ size = f.encoded_size(allmsgs)
+ if size is None:
+ return None
+ elif size.symbols:
+ return None # Cannot resolve maximum of symbols
+ elif size.value > largest.value:
+ largest = size
+
+ return largest
# ---------------------------------------------------------------------------
# Generation of messages (structures)
def __init__(self, names, desc, message_options):
self.name = names
self.fields = []
- self.oneofs = []
+ self.oneofs = {}
+ no_unions = []
+
+ if message_options.msgid:
+ self.msgid = message_options.msgid
if hasattr(desc, 'oneof_decl'):
- for f in desc.oneof_decl:
- oneof = OneOf(self.name, f)
- self.oneofs.append(oneof)
- self.fields.append(oneof)
+ for i, f in enumerate(desc.oneof_decl):
+ oneof_options = get_nanopb_suboptions(desc, message_options, self.name + f.name)
+ if oneof_options.no_unions:
+ no_unions.append(i) # No union, but add fields normally
+ elif oneof_options.type == nanopb_pb2.FT_IGNORE:
+ pass # No union and skip fields also
+ else:
+ oneof = OneOf(self.name, f)
+ self.oneofs[i] = oneof
+ self.fields.append(oneof)
for f in desc.field:
field_options = get_nanopb_suboptions(f, message_options, self.name + f.name)
continue
field = Field(self.name, f, field_options)
- if hasattr(f, 'oneof_index') and f.HasField('oneof_index'):
- self.oneofs[f.oneof_index].add_field(field)
+ if (hasattr(f, 'oneof_index') and
+ f.HasField('oneof_index') and
+ f.oneof_index not in no_unions):
+ if f.oneof_index in self.oneofs:
+ self.oneofs[f.oneof_index].add_field(field)
else:
self.fields.append(field)
'''Returns number of required fields inside this message'''
count = 0
for f in self.fields:
- if f not in self.oneofs:
+ if not isinstance(f, OneOf):
if f.rules == 'REQUIRED':
count += 1
return count
def count_all_fields(self):
count = 0
for f in self.fields:
- if f in self.oneofs:
+ if isinstance(f, OneOf):
count += len(f.fields)
else:
count += 1
if message_options.skip_message:
continue
-
+
messages.append(Message(names, message, message_options))
for enum in message.enum_type:
enum_options = get_nanopb_suboptions(enum, message_options, names + enum.name)
identifier = '%s_size' % msg.name
yield '#define %-40s %s\n' % (identifier, msize)
yield '\n'
+
+ yield '/* Message IDs (where set with "msgid" option) */\n'
+ yield '#ifdef PB_MSGID\n'
+ for msg in messages:
+ if hasattr(msg,'msgid'):
+ yield '#define PB_MSG_%d %s\n' % (msg.msgid, msg.name)
+ yield '\n'
+
+ symbol = make_identifier(headername.split('.')[0])
+ yield '#define %s_MESSAGES \\\n' % symbol
+
+ for msg in messages:
+ m = "-1"
+ msize = msg.encoded_size(messages)
+ if msize is not None:
+ m = msize
+ if hasattr(msg,'msgid'):
+ yield '\tPB_MSG(%d,%s,%s) \\\n' % (msg.msgid, m, msg.name)
+ yield '\n'
+
+ for msg in messages:
+ if hasattr(msg,'msgid'):
+ yield '#define %s_msgid %d\n' % (msg.name, msg.msgid)
+ yield '\n'
+
+ yield '#endif\n\n'
+
+
yield '#ifdef __cplusplus\n'
yield '} /* extern "C" */\n'
yield '#endif\n'
[(namemask, options), ...]
'''
results = []
- for line in infile:
+ data = infile.read()
+ data = re.sub('/\*.*?\*/', '', data, flags = re.MULTILINE)
+ data = re.sub('//.*?$', '', data, flags = re.MULTILINE)
+ data = re.sub('#.*?$', '', data, flags = re.MULTILINE)
+ for i, line in enumerate(data.split('\n')):
line = line.strip()
- if not line or line.startswith('//') or line.startswith('#'):
+ if not line:
continue
parts = line.split(None, 1)
+
+ if len(parts) < 2:
+ sys.stderr.write("%s:%d: " % (infile.name, i + 1) +
+ "Option lines should have space between field name and options. " +
+ "Skipping line: '%s'\n" % line)
+ continue
+
opts = nanopb_pb2.NanoPBOptions()
- text_format.Merge(parts[1], opts)
+
+ try:
+ text_format.Merge(parts[1], opts)
+ except Exception, e:
+ sys.stderr.write("%s:%d: " % (infile.name, i + 1) +
+ "Unparseable option line: '%s'. " % line +
+ "Error: %s\n" % str(e))
+ continue
results.append((parts[0], opts))
return results