17ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen#!/usr/bin/python
27ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
37ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen'''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
47ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohennanopb_version = "nanopb-0.2.8-dev"
57ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
67ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenimport sys
77ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
87ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohentry:
97ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    # Add some dummy imports to keep packaging tools happy.
107ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    import google, distutils.util # bbfreeze seems to need these
117ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    import pkg_resources # pyinstaller / protobuf 2.5 seem to need these
127ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenexcept:
137ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    # Don't care, we will error out later if it is actually important.
147ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    pass
157ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
167ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohentry:
177ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    import google.protobuf.text_format as text_format
187ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    import google.protobuf.descriptor_pb2 as descriptor
197ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenexcept:
207ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    sys.stderr.write('''
217ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen         *************************************************************
227ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen         *** Could not import the Google protobuf Python libraries ***
237ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen         *** Try installing package 'python-protobuf' or similar.  ***
247ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen         *************************************************************
257ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    ''' + '\n')
267ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    raise
277ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
287ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohentry:
297ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    import proto.nanopb_pb2 as nanopb_pb2
307ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    import proto.plugin_pb2 as plugin_pb2
317ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenexcept:
327ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    sys.stderr.write('''
337ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen         ********************************************************************
347ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen         *** Failed to import the protocol definitions for generator.     ***
357ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen         *** You have to run 'make' in the nanopb/generator/proto folder. ***
367ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen         ********************************************************************
377ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    ''' + '\n')
387ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    raise
397ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
407ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen# ---------------------------------------------------------------------------
417ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen#                     Generation of single fields
427ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen# ---------------------------------------------------------------------------
437ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
447ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenimport time
457ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenimport os.path
467ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
477ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen# Values are tuple (c type, pb type, encoded size)
487ef855e462b9a18b7d330e4b40f350164a6ad9daEtan CohenFieldD = descriptor.FieldDescriptorProto
497ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendatatypes = {
507ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    FieldD.TYPE_BOOL:       ('bool',     'BOOL',        1),
517ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    FieldD.TYPE_DOUBLE:     ('double',   'DOUBLE',      8),
527ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    FieldD.TYPE_FIXED32:    ('uint32_t', 'FIXED32',     4),
537ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    FieldD.TYPE_FIXED64:    ('uint64_t', 'FIXED64',     8),
547ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    FieldD.TYPE_FLOAT:      ('float',    'FLOAT',       4),
557ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    FieldD.TYPE_INT32:      ('int32_t',  'INT32',      10),
567ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    FieldD.TYPE_INT64:      ('int64_t',  'INT64',      10),
577ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    FieldD.TYPE_SFIXED32:   ('int32_t',  'SFIXED32',    4),
587ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    FieldD.TYPE_SFIXED64:   ('int64_t',  'SFIXED64',    8),
597ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    FieldD.TYPE_SINT32:     ('int32_t',  'SINT32',      5),
607ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    FieldD.TYPE_SINT64:     ('int64_t',  'SINT64',     10),
617ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    FieldD.TYPE_UINT32:     ('uint32_t', 'UINT32',      5),
627ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    FieldD.TYPE_UINT64:     ('uint64_t', 'UINT64',     10)
637ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen}
647ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
657ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenclass Names:
667ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Keeps a set of nested names and formats them to C identifier.'''
677ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def __init__(self, parts = ()):
687ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if isinstance(parts, Names):
697ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            parts = parts.parts
707ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.parts = tuple(parts)
717ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
727ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def __str__(self):
737ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return '_'.join(self.parts)
747ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
757ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def __add__(self, other):
767ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if isinstance(other, (str, unicode)):
777ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            return Names(self.parts + (other,))
787ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif isinstance(other, tuple):
797ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            return Names(self.parts + other)
807ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        else:
817ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            raise ValueError("Name parts should be of type str")
827ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
837ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def __eq__(self, other):
847ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return isinstance(other, Names) and self.parts == other.parts
857ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
867ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef names_from_type_name(type_name):
877ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Parse Names() from FieldDescriptorProto type_name'''
887ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if type_name[0] != '.':
897ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        raise NotImplementedError("Lookup of non-absolute type names is not supported")
907ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    return Names(type_name[1:].split('.'))
917ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
927ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef varint_max_size(max_value):
937ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Returns the maximum number of bytes a varint can take when encoded.'''
947ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for i in range(1, 11):
957ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if (max_value >> (i * 7)) == 0:
967ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            return i
977ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    raise ValueError("Value too large for varint: " + str(max_value))
987ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
997ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenassert varint_max_size(0) == 1
1007ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenassert varint_max_size(127) == 1
1017ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenassert varint_max_size(128) == 2
1027ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1037ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenclass EncodedSize:
1047ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Class used to represent the encoded size of a field or a message.
1057ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    Consists of a combination of symbolic sizes and integer sizes.'''
1067ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def __init__(self, value = 0, symbols = []):
1077ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if isinstance(value, (str, Names)):
1087ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            symbols = [str(value)]
1097ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            value = 0
1107ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.value = value
1117ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.symbols = symbols
1127ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1137ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def __add__(self, other):
1147ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if isinstance(other, (int, long)):
1157ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            return EncodedSize(self.value + other, self.symbols)
1167ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif isinstance(other, (str, Names)):
1177ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            return EncodedSize(self.value, self.symbols + [str(other)])
1187ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif isinstance(other, EncodedSize):
1197ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            return EncodedSize(self.value + other.value, self.symbols + other.symbols)
1207ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        else:
1217ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            raise ValueError("Cannot add size: " + repr(other))
1227ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1237ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def __mul__(self, other):
1247ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if isinstance(other, (int, long)):
1257ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            return EncodedSize(self.value * other, [str(other) + '*' + s for s in self.symbols])
1267ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        else:
1277ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            raise ValueError("Cannot multiply size: " + repr(other))
1287ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1297ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def __str__(self):
1307ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if not self.symbols:
1317ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            return str(self.value)
1327ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        else:
1337ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            return '(' + str(self.value) + ' + ' + ' + '.join(self.symbols) + ')'
1347ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1357ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def upperlimit(self):
1367ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if not self.symbols:
1377ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            return self.value
1387ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        else:
1397ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            return 2**32 - 1
1407ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1417ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenclass Enum:
1427ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def __init__(self, names, desc, enum_options):
1437ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        '''desc is EnumDescriptorProto'''
1447ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1457ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.options = enum_options
1467ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.names = names + desc.name
1477ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1487ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if enum_options.long_names:
1497ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.values = [(self.names + x.name, x.number) for x in desc.value]
1507ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        else:
1517ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.values = [(names + x.name, x.number) for x in desc.value]
1527ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1537ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.value_longnames = [self.names + x.name for x in desc.value]
1547ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1557ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def __str__(self):
1567ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result = 'typedef enum _%s {\n' % self.names
1577ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += ',\n'.join(["    %s = %d" % x for x in self.values])
1587ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += '\n} %s;' % self.names
1597ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return result
1607ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1617ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenclass Field:
1627ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def __init__(self, struct_name, desc, field_options):
1637ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        '''desc is FieldDescriptorProto'''
1647ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.tag = desc.number
1657ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.struct_name = struct_name
1667ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.name = desc.name
1677ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.default = None
1687ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.max_size = None
1697ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.max_count = None
1707ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.array_decl = ""
1717ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.enc_size = None
1727ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.ctype = None
1737ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1747ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        # Parse field options
1757ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if field_options.HasField("max_size"):
1767ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.max_size = field_options.max_size
1777ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1787ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if field_options.HasField("max_count"):
1797ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.max_count = field_options.max_count
1807ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1817ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if desc.HasField('default_value'):
1827ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.default = desc.default_value
1837ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1847ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        # Check field rules, i.e. required/optional/repeated.
1857ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        can_be_static = True
1867ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if desc.label == FieldD.LABEL_REQUIRED:
1877ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.rules = 'REQUIRED'
1887ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif desc.label == FieldD.LABEL_OPTIONAL:
1897ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.rules = 'OPTIONAL'
1907ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif desc.label == FieldD.LABEL_REPEATED:
1917ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.rules = 'REPEATED'
1927ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if self.max_count is None:
1937ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                can_be_static = False
1947ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            else:
1957ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                self.array_decl = '[%d]' % self.max_count
1967ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        else:
1977ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            raise NotImplementedError(desc.label)
1987ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1997ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        # Check if the field can be implemented with static allocation
2007ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        # i.e. whether the data size is known.
2017ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if desc.type == FieldD.TYPE_STRING and self.max_size is None:
2027ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            can_be_static = False
2037ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
2047ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if desc.type == FieldD.TYPE_BYTES and self.max_size is None:
2057ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            can_be_static = False
2067ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
2077ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        # Decide how the field data will be allocated
2087ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if field_options.type == nanopb_pb2.FT_DEFAULT:
2097ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if can_be_static:
2107ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                field_options.type = nanopb_pb2.FT_STATIC
2117ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            else:
2127ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                field_options.type = nanopb_pb2.FT_CALLBACK
2137ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
2147ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if field_options.type == nanopb_pb2.FT_STATIC and not can_be_static:
2157ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            raise Exception("Field %s is defined as static, but max_size or "
2167ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                            "max_count is not given." % self.name)
2177ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
2187ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if field_options.type == nanopb_pb2.FT_STATIC:
2197ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.allocation = 'STATIC'
2207ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif field_options.type == nanopb_pb2.FT_POINTER:
2217ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.allocation = 'POINTER'
2227ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif field_options.type == nanopb_pb2.FT_CALLBACK:
2237ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.allocation = 'CALLBACK'
2247ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        else:
2257ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            raise NotImplementedError(field_options.type)
2267ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
2277ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        # Decide the C data type to use in the struct.
2287ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if datatypes.has_key(desc.type):
2297ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.ctype, self.pbtype, self.enc_size = datatypes[desc.type]
2307ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif desc.type == FieldD.TYPE_ENUM:
2317ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.pbtype = 'ENUM'
2327ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.ctype = names_from_type_name(desc.type_name)
2337ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if self.default is not None:
2347ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                self.default = self.ctype + self.default
2357ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.enc_size = 5 # protoc rejects enum values > 32 bits
2367ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif desc.type == FieldD.TYPE_STRING:
2377ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.pbtype = 'STRING'
2387ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.ctype = 'char'
2397ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if self.allocation == 'STATIC':
2407ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                self.ctype = 'char'
2417ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                self.array_decl += '[%d]' % self.max_size
2427ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                self.enc_size = varint_max_size(self.max_size) + self.max_size
2437ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif desc.type == FieldD.TYPE_BYTES:
2447ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.pbtype = 'BYTES'
2457ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if self.allocation == 'STATIC':
2467ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                self.ctype = self.struct_name + self.name + 't'
2477ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                self.enc_size = varint_max_size(self.max_size) + self.max_size
2487ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            elif self.allocation == 'POINTER':
2497ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                self.ctype = 'pb_bytes_array_t'
2507ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif desc.type == FieldD.TYPE_MESSAGE:
2517ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.pbtype = 'MESSAGE'
2527ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.ctype = self.submsgname = names_from_type_name(desc.type_name)
2537ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.enc_size = None # Needs to be filled in after the message type is available
2547ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        else:
2557ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            raise NotImplementedError(desc.type)
2567ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
2577ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def __cmp__(self, other):
2587ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return cmp(self.tag, other.tag)
2597ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
2607ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def __str__(self):
2617ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result = ''
2627ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if self.allocation == 'POINTER':
2637ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if self.rules == 'REPEATED':
2647ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                result += '    size_t ' + self.name + '_count;\n'
2657ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
2667ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if self.pbtype == 'MESSAGE':
2677ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                # Use struct definition, so recursive submessages are possible
2687ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                result += '    struct _%s *%s;' % (self.ctype, self.name)
2697ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            elif self.rules == 'REPEATED' and self.pbtype in ['STRING', 'BYTES']:
2707ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                # String/bytes arrays need to be defined as pointers to pointers
2717ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                result += '    %s **%s;' % (self.ctype, self.name)
2727ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            else:
2737ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                result += '    %s *%s;' % (self.ctype, self.name)
2747ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif self.allocation == 'CALLBACK':
2757ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result += '    pb_callback_t %s;' % self.name
2767ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        else:
2777ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if self.rules == 'OPTIONAL' and self.allocation == 'STATIC':
2787ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                result += '    bool has_' + self.name + ';\n'
2797ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            elif self.rules == 'REPEATED' and self.allocation == 'STATIC':
2807ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                result += '    size_t ' + self.name + '_count;\n'
2817ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result += '    %s %s%s;' % (self.ctype, self.name, self.array_decl)
2827ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return result
2837ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
2847ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def types(self):
2857ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        '''Return definitions for any special types this field might need.'''
2867ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if self.pbtype == 'BYTES' and self.allocation == 'STATIC':
2877ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result = 'typedef struct {\n'
2887ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result += '    size_t size;\n'
2897ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result += '    uint8_t bytes[%d];\n' % self.max_size
2907ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result += '} %s;\n' % self.ctype
2917ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        else:
2927ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result = None
2937ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return result
2947ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
2957ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def default_decl(self, declaration_only = False):
2967ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        '''Return definition for this field's default value.'''
2977ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if self.default is None:
2987ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            return None
2997ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
3007ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        ctype, default = self.ctype, self.default
3017ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        array_decl = ''
3027ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
3037ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if self.pbtype == 'STRING':
3047ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if self.allocation != 'STATIC':
3057ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                return None # Not implemented
3067ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
3077ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            array_decl = '[%d]' % self.max_size
3087ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            default = str(self.default).encode('string_escape')
3097ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            default = default.replace('"', '\\"')
3107ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            default = '"' + default + '"'
3117ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif self.pbtype == 'BYTES':
3127ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if self.allocation != 'STATIC':
3137ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                return None # Not implemented
3147ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
3157ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            data = self.default.decode('string_escape')
3167ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            data = ['0x%02x' % ord(c) for c in data]
3177ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            default = '{%d, {%s}}' % (len(data), ','.join(data))
3187ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif self.pbtype in ['FIXED32', 'UINT32']:
3197ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            default += 'u'
3207ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif self.pbtype in ['FIXED64', 'UINT64']:
3217ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            default += 'ull'
3227ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif self.pbtype in ['SFIXED64', 'INT64']:
3237ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            default += 'll'
3247ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
3257ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if declaration_only:
3267ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            return 'extern const %s %s_default%s;' % (ctype, self.struct_name + self.name, array_decl)
3277ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        else:
3287ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            return 'const %s %s_default%s = %s;' % (ctype, self.struct_name + self.name, array_decl, default)
3297ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
3307ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def tags(self):
3317ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        '''Return the #define for the tag number of this field.'''
3327ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        identifier = '%s_%s_tag' % (self.struct_name, self.name)
3337ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return '#define %-40s %d\n' % (identifier, self.tag)
3347ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
3357ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def pb_field_t(self, prev_field_name):
3367ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        '''Return the pb_field_t initializer to use in the constant array.
3377ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        prev_field_name is the name of the previous field or None.
3387ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        '''
3397ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result  = '    PB_FIELD2(%3d, ' % self.tag
3407ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += '%-8s, ' % self.pbtype
3417ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += '%s, ' % self.rules
3427ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += '%-8s, ' % self.allocation
3437ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += '%s, ' % ("FIRST" if not prev_field_name else "OTHER")
3447ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += '%s, ' % self.struct_name
3457ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += '%s, ' % self.name
3467ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += '%s, ' % (prev_field_name or self.name)
3477ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
3487ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if self.pbtype == 'MESSAGE':
3497ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result += '&%s_fields)' % self.submsgname
3507ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif self.default is None:
3517ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result += '0)'
3527ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif self.pbtype in ['BYTES', 'STRING'] and self.allocation != 'STATIC':
3537ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result += '0)' # Arbitrary size default values not implemented
3547ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif self.rules == 'OPTEXT':
3557ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result += '0)' # Default value for extensions is not implemented
3567ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        else:
3577ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result += '&%s_default)' % (self.struct_name + self.name)
3587ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
3597ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return result
3607ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
3617ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def largest_field_value(self):
3627ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        '''Determine if this field needs 16bit or 32bit pb_field_t structure to compile properly.
3637ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        Returns numeric value or a C-expression for assert.'''
3647ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if self.pbtype == 'MESSAGE':
3657ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if self.rules == 'REPEATED' and self.allocation == 'STATIC':
3667ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                return 'pb_membersize(%s, %s[0])' % (self.struct_name, self.name)
3677ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            else:
3687ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                return 'pb_membersize(%s, %s)' % (self.struct_name, self.name)
3697ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
3707ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return max(self.tag, self.max_size, self.max_count)
3717ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
3727ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def encoded_size(self, allmsgs):
3737ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        '''Return the maximum size that this field can take when encoded,
3747ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        including the field tag. If the size cannot be determined, returns
3757ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        None.'''
3767ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
3777ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if self.allocation != 'STATIC':
3787ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            return None
3797ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
3807ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if self.pbtype == 'MESSAGE':
3817ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            for msg in allmsgs:
3827ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                if msg.name == self.submsgname:
3837ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                    encsize = msg.encoded_size(allmsgs)
3847ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                    if encsize is None:
3857ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                        return None # Submessage size is indeterminate
3867ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
3877ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                    # Include submessage length prefix
3887ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                    encsize += varint_max_size(encsize.upperlimit())
3897ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                    break
3907ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            else:
3917ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                # Submessage cannot be found, this currently occurs when
3927ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                # the submessage type is defined in a different file.
3937ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                # Instead of direct numeric value, reference the size that
3947ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                # has been #defined in the other file.
3957ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                encsize = EncodedSize(self.submsgname + 'size')
3967ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
3977ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                # We will have to make a conservative assumption on the length
3987ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                # prefix size, though.
3997ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                encsize += 5
4007ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4017ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        elif self.enc_size is None:
4027ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            raise RuntimeError("Could not determine encoded size for %s.%s"
4037ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                               % (self.struct_name, self.name))
4047ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        else:
4057ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            encsize = EncodedSize(self.enc_size)
4067ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4077ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        encsize += varint_max_size(self.tag << 3) # Tag + wire type
4087ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4097ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if self.rules == 'REPEATED':
4107ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            # Decoders must be always able to handle unpacked arrays.
4117ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            # Therefore we have to reserve space for it, even though
4127ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            # we emit packed arrays ourselves.
4137ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            encsize *= self.max_count
4147ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4157ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return encsize
4167ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4177ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4187ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenclass ExtensionRange(Field):
4197ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def __init__(self, struct_name, range_start, field_options):
4207ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        '''Implements a special pb_extension_t* field in an extensible message
4217ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        structure. The range_start signifies the index at which the extensions
4227ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        start. Not necessarily all tags above this are extensions, it is merely
4237ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        a speed optimization.
4247ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        '''
4257ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.tag = range_start
4267ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.struct_name = struct_name
4277ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.name = 'extensions'
4287ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.pbtype = 'EXTENSION'
4297ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.rules = 'OPTIONAL'
4307ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.allocation = 'CALLBACK'
4317ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.ctype = 'pb_extension_t'
4327ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.array_decl = ''
4337ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.default = None
4347ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.max_size = 0
4357ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.max_count = 0
4367ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4377ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def __str__(self):
4387ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return '    pb_extension_t *extensions;'
4397ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4407ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def types(self):
4417ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return None
4427ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4437ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def tags(self):
4447ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return ''
4457ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4467ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def encoded_size(self, allmsgs):
4477ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        # We exclude extensions from the count, because they cannot be known
4487ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        # until runtime. Other option would be to return None here, but this
4497ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        # way the value remains useful if extensions are not used.
4507ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return EncodedSize(0)
4517ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4527ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenclass ExtensionField(Field):
4537ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def __init__(self, struct_name, desc, field_options):
4547ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.fullname = struct_name + desc.name
4557ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.extendee_name = names_from_type_name(desc.extendee)
4567ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        Field.__init__(self, self.fullname + 'struct', desc, field_options)
4577ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4587ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if self.rules != 'OPTIONAL':
4597ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.skip = True
4607ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        else:
4617ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.skip = False
4627ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            self.rules = 'OPTEXT'
4637ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4647ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def tags(self):
4657ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        '''Return the #define for the tag number of this field.'''
4667ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        identifier = '%s_tag' % self.fullname
4677ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return '#define %-40s %d\n' % (identifier, self.tag)
4687ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4697ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def extension_decl(self):
4707ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        '''Declaration of the extension type in the .pb.h file'''
4717ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if self.skip:
4727ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            msg = '/* Extension field %s was skipped because only "optional"\n' % self.fullname
4737ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            msg +='   type of extension fields is currently supported. */\n'
4747ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            return msg
4757ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4767ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return 'extern const pb_extension_type_t %s;\n' % self.fullname
4777ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4787ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def extension_def(self):
4797ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        '''Definition of the extension type in the .pb.c file'''
4807ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4817ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if self.skip:
4827ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            return ''
4837ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4847ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result  = 'typedef struct {\n'
4857ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += str(self)
4867ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += '\n} %s;\n\n' % self.struct_name
4877ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += ('static const pb_field_t %s_field = \n  %s;\n\n' %
4887ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                    (self.fullname, self.pb_field_t(None)))
4897ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += 'const pb_extension_type_t %s = {\n' % self.fullname
4907ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += '    NULL,\n'
4917ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += '    NULL,\n'
4927ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += '    &%s_field\n' % self.fullname
4937ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += '};\n'
4947ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return result
4957ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4967ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
4977ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen# ---------------------------------------------------------------------------
4987ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen#                   Generation of messages (structures)
4997ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen# ---------------------------------------------------------------------------
5007ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5017ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5027ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenclass Message:
5037ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def __init__(self, names, desc, message_options):
5047ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.name = names
5057ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.fields = []
5067ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5077ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        for f in desc.field:
5087ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            field_options = get_nanopb_suboptions(f, message_options, self.name + f.name)
5097ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if field_options.type != nanopb_pb2.FT_IGNORE:
5107ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                self.fields.append(Field(self.name, f, field_options))
5117ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5127ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if len(desc.extension_range) > 0:
5137ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            field_options = get_nanopb_suboptions(desc, message_options, self.name + 'extensions')
5147ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            range_start = min([r.start for r in desc.extension_range])
5157ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if field_options.type != nanopb_pb2.FT_IGNORE:
5167ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                self.fields.append(ExtensionRange(self.name, range_start, field_options))
5177ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5187ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.packed = message_options.packed_struct
5197ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.ordered_fields = self.fields[:]
5207ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        self.ordered_fields.sort()
5217ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5227ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def get_dependencies(self):
5237ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        '''Get list of type names that this structure refers to.'''
5247ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return [str(field.ctype) for field in self.fields]
5257ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5267ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def __str__(self):
5277ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result = 'typedef struct _%s {\n' % self.name
5287ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5297ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if not self.ordered_fields:
5307ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            # Empty structs are not allowed in C standard.
5317ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            # Therefore add a dummy field if an empty message occurs.
5327ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result += '    uint8_t dummy_field;'
5337ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5347ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += '\n'.join([str(f) for f in self.ordered_fields])
5357ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += '\n}'
5367ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5377ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if self.packed:
5387ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result += ' pb_packed'
5397ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5407ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += ' %s;' % self.name
5417ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5427ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if self.packed:
5437ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result = 'PB_PACKED_STRUCT_START\n' + result
5447ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result += '\nPB_PACKED_STRUCT_END'
5457ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5467ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return result
5477ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5487ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def types(self):
5497ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result = ""
5507ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        for field in self.fields:
5517ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            types = field.types()
5527ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if types is not None:
5537ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                result += types + '\n'
5547ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return result
5557ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5567ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def default_decl(self, declaration_only = False):
5577ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result = ""
5587ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        for field in self.fields:
5597ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            default = field.default_decl(declaration_only)
5607ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if default is not None:
5617ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                result += default + '\n'
5627ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return result
5637ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5647ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def fields_declaration(self):
5657ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result = 'extern const pb_field_t %s_fields[%d];' % (self.name, len(self.fields) + 1)
5667ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return result
5677ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5687ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def fields_definition(self):
5697ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result = 'const pb_field_t %s_fields[%d] = {\n' % (self.name, len(self.fields) + 1)
5707ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5717ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        prev = None
5727ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        for field in self.ordered_fields:
5737ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result += field.pb_field_t(prev)
5747ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result += ',\n'
5757ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            prev = field.name
5767ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5777ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        result += '    PB_LAST_FIELD\n};'
5787ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return result
5797ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5807ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    def encoded_size(self, allmsgs):
5817ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        '''Return the maximum size that this message can take when encoded.
5827ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        If the size cannot be determined, returns None.
5837ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        '''
5847ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        size = EncodedSize(0)
5857ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        for field in self.fields:
5867ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            fsize = field.encoded_size(allmsgs)
5877ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if fsize is None:
5887ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                return None
5897ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            size += fsize
5907ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5917ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return size
5927ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5937ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5947ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen# ---------------------------------------------------------------------------
5957ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen#                    Processing of entire .proto files
5967ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen# ---------------------------------------------------------------------------
5977ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5987ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
5997ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef iterate_messages(desc, names = Names()):
6007ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Recursively find all messages. For each, yield name, DescriptorProto.'''
6017ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if hasattr(desc, 'message_type'):
6027ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        submsgs = desc.message_type
6037ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    else:
6047ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        submsgs = desc.nested_type
6057ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
6067ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for submsg in submsgs:
6077ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        sub_names = names + submsg.name
6087ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield sub_names, submsg
6097ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
6107ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        for x in iterate_messages(submsg, sub_names):
6117ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            yield x
6127ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
6137ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef iterate_extensions(desc, names = Names()):
6147ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Recursively find all extensions.
6157ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    For each, yield name, FieldDescriptorProto.
6167ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''
6177ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for extension in desc.extension:
6187ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield names, extension
6197ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
6207ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for subname, subdesc in iterate_messages(desc, names):
6217ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        for extension in subdesc.extension:
6227ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            yield subname, extension
6237ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
6247ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef parse_file(fdesc, file_options):
6257ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Takes a FileDescriptorProto and returns tuple (enums, messages, extensions).'''
6267ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
6277ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    enums = []
6287ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    messages = []
6297ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    extensions = []
6307ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
6317ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if fdesc.package:
6327ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        base_name = Names(fdesc.package.split('.'))
6337ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    else:
6347ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        base_name = Names()
6357ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
6367ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for enum in fdesc.enum_type:
6377ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        enum_options = get_nanopb_suboptions(enum, file_options, base_name + enum.name)
6387ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        enums.append(Enum(base_name, enum, enum_options))
6397ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
6407ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for names, message in iterate_messages(fdesc, base_name):
6417ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        message_options = get_nanopb_suboptions(message, file_options, names)
6427ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        messages.append(Message(names, message, message_options))
6437ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        for enum in message.enum_type:
6447ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            enum_options = get_nanopb_suboptions(enum, message_options, names + enum.name)
6457ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            enums.append(Enum(names, enum, enum_options))
6467ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
6477ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for names, extension in iterate_extensions(fdesc, base_name):
6487ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        field_options = get_nanopb_suboptions(extension, file_options, names)
6497ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if field_options.type != nanopb_pb2.FT_IGNORE:
6507ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            extensions.append(ExtensionField(names, extension, field_options))
6517ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
6527ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    # Fix field default values where enum short names are used.
6537ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for enum in enums:
6547ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if not enum.options.long_names:
6557ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            for message in messages:
6567ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                for field in message.fields:
6577ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                    if field.default in enum.value_longnames:
6587ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                        idx = enum.value_longnames.index(field.default)
6597ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                        field.default = enum.values[idx][0]
6607ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
6617ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    return enums, messages, extensions
6627ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
6637ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef toposort2(data):
6647ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Topological sort.
6657ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    From http://code.activestate.com/recipes/577413-topological-sort/
6667ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    This function is under the MIT license.
6677ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''
6687ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for k, v in data.items():
6697ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        v.discard(k) # Ignore self dependencies
6707ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    extra_items_in_deps = reduce(set.union, data.values(), set()) - set(data.keys())
6717ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    data.update(dict([(item, set()) for item in extra_items_in_deps]))
6727ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    while True:
6737ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        ordered = set(item for item,dep in data.items() if not dep)
6747ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if not ordered:
6757ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            break
6767ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        for item in sorted(ordered):
6777ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            yield item
6787ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        data = dict([(item, (dep - ordered)) for item,dep in data.items()
6797ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                if item not in ordered])
6807ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    assert not data, "A cyclic dependency exists amongst %r" % data
6817ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
6827ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef sort_dependencies(messages):
6837ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Sort a list of Messages based on dependencies.'''
6847ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    dependencies = {}
6857ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    message_by_name = {}
6867ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for message in messages:
6877ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        dependencies[str(message.name)] = set(message.get_dependencies())
6887ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        message_by_name[str(message.name)] = message
6897ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
6907ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for msgname in toposort2(dependencies):
6917ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if msgname in message_by_name:
6927ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            yield message_by_name[msgname]
6937ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
6947ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef make_identifier(headername):
6957ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Make #ifndef identifier that contains uppercase A-Z and digits 0-9'''
6967ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    result = ""
6977ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for c in headername.upper():
6987ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if c.isalnum():
6997ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result += c
7007ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        else:
7017ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            result += '_'
7027ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    return result
7037ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7047ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef generate_header(dependencies, headername, enums, messages, extensions, options):
7057ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Generate content for a header file.
7067ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    Generates strings, which should be concatenated and stored to file.
7077ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''
7087ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7097ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '/* Automatically generated nanopb header */\n'
7107ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if options.notimestamp:
7117ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield '/* Generated by %s */\n\n' % (nanopb_version)
7127ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    else:
7137ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime())
7147ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7157ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    symbol = make_identifier(headername)
7167ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '#ifndef _PB_%s_\n' % symbol
7177ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '#define _PB_%s_\n' % symbol
7187ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    try:
7197ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield options.libformat % ('pb.h')
7207ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    except TypeError:
7217ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        # no %s specified - use whatever was passed in as options.libformat
7227ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield options.libformat
7237ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '\n'
7247ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7257ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for dependency in dependencies:
7267ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        noext = os.path.splitext(dependency)[0]
7277ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield options.genformat % (noext + '.' + options.extension + '.h')
7287ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield '\n'
7297ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7307ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '#ifdef __cplusplus\n'
7317ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield 'extern "C" {\n'
7327ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '#endif\n\n'
7337ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7347ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '/* Enum definitions */\n'
7357ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for enum in enums:
7367ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield str(enum) + '\n\n'
7377ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7387ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '/* Struct definitions */\n'
7397ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for msg in sort_dependencies(messages):
7407ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield msg.types()
7417ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield str(msg) + '\n\n'
7427ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7437ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if extensions:
7447ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield '/* Extensions */\n'
7457ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        for extension in extensions:
7467ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            yield extension.extension_decl()
7477ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield '\n'
7487ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7497ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '/* Default values for struct fields */\n'
7507ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for msg in messages:
7517ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield msg.default_decl(True)
7527ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '\n'
7537ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7547ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '/* Field tags (for use in manual encoding/decoding) */\n'
7557ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for msg in sort_dependencies(messages):
7567ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        for field in msg.fields:
7577ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            yield field.tags()
7587ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for extension in extensions:
7597ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield extension.tags()
7607ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '\n'
7617ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7627ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '/* Struct field encoding specification for nanopb */\n'
7637ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for msg in messages:
7647ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield msg.fields_declaration() + '\n'
7657ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '\n'
7667ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7677ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '/* Maximum encoded size of messages (where known) */\n'
7687ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for msg in messages:
7697ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        msize = msg.encoded_size(messages)
7707ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if msize is not None:
7717ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            identifier = '%s_size' % msg.name
7727ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            yield '#define %-40s %s\n' % (identifier, msize)
7737ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '\n'
7747ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7757ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '#ifdef __cplusplus\n'
7767ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '} /* extern "C" */\n'
7777ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '#endif\n'
7787ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7797ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    # End of header
7807ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '\n#endif\n'
7817ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7827ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef generate_source(headername, enums, messages, extensions, options):
7837ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Generate content for a source file.'''
7847ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7857ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '/* Automatically generated nanopb constant definitions */\n'
7867ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if options.notimestamp:
7877ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield '/* Generated by %s */\n\n' % (nanopb_version)
7887ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    else:
7897ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime())
7907ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield options.genformat % (headername)
7917ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '\n'
7927ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7937ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for msg in messages:
7947ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield msg.default_decl(False)
7957ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7967ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '\n\n'
7977ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
7987ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for msg in messages:
7997ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield msg.fields_definition() + '\n\n'
8007ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
8017ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for ext in extensions:
8027ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield ext.extension_def() + '\n'
8037ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
8047ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    # Add checks for numeric limits
8057ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if messages:
8067ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        count_required_fields = lambda m: len([f for f in msg.fields if f.rules == 'REQUIRED'])
8077ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        largest_msg = max(messages, key = count_required_fields)
8087ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        largest_count = count_required_fields(largest_msg)
8097ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if largest_count > 64:
8107ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            yield '\n/* Check that missing required fields will be properly detected */\n'
8117ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            yield '#if PB_MAX_REQUIRED_FIELDS < %d\n' % largest_count
8127ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            yield '#error Properly detecting missing required fields in %s requires \\\n' % largest_msg.name
8137ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            yield '       setting PB_MAX_REQUIRED_FIELDS to %d or more.\n' % largest_count
8147ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            yield '#endif\n'
8157ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
8167ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    worst = 0
8177ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    worst_field = ''
8187ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    checks = []
8197ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    checks_msgnames = []
8207ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for msg in messages:
8217ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        checks_msgnames.append(msg.name)
8227ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        for field in msg.fields:
8237ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            status = field.largest_field_value()
8247ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if isinstance(status, (str, unicode)):
8257ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                checks.append(status)
8267ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            elif status > worst:
8277ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                worst = status
8287ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                worst_field = str(field.struct_name) + '.' + str(field.name)
8297ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
8307ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if worst > 255 or checks:
8317ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield '\n/* Check that field information fits in pb_field_t */\n'
8327ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
8337ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if worst > 65535 or checks:
8347ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            yield '#if !defined(PB_FIELD_32BIT)\n'
8357ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if worst > 65535:
8367ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                yield '#error Field descriptor for %s is too large. Define PB_FIELD_32BIT to fix this.\n' % worst_field
8377ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            else:
8387ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                assertion = ' && '.join(str(c) + ' < 65536' for c in checks)
8397ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                msgs = '_'.join(str(n) for n in checks_msgnames)
8407ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                yield '/* If you get an error here, it means that you need to define PB_FIELD_32BIT\n'
8417ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                yield ' * compile-time option. You can do that in pb.h or on compiler command line.\n'
8427ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                yield ' * \n'
8437ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                yield ' * The reason you need to do this is that some of your messages contain tag\n'
8447ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                yield ' * numbers or field sizes that are larger than what can fit in 8 or 16 bit\n'
8457ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                yield ' * field descriptors.\n'
8467ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                yield ' */\n'
8477ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                yield 'STATIC_ASSERT((%s), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_%s)\n'%(assertion,msgs)
8487ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            yield '#endif\n\n'
8497ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
8507ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if worst < 65536:
8517ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            yield '#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)\n'
8527ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if worst > 255:
8537ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                yield '#error Field descriptor for %s is too large. Define PB_FIELD_16BIT to fix this.\n' % worst_field
8547ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            else:
8557ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                assertion = ' && '.join(str(c) + ' < 256' for c in checks)
8567ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                msgs = '_'.join(str(n) for n in checks_msgnames)
8577ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                yield '/* If you get an error here, it means that you need to define PB_FIELD_16BIT\n'
8587ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                yield ' * compile-time option. You can do that in pb.h or on compiler command line.\n'
8597ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                yield ' * \n'
8607ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                yield ' * The reason you need to do this is that some of your messages contain tag\n'
8617ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                yield ' * numbers or field sizes that are larger than what can fit in the default\n'
8627ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                yield ' * 8 bit descriptors.\n'
8637ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                yield ' */\n'
8647ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                yield 'STATIC_ASSERT((%s), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_%s)\n'%(assertion,msgs)
8657ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            yield '#endif\n\n'
8667ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
8677ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    # Add check for sizeof(double)
8687ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    has_double = False
8697ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for msg in messages:
8707ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        for field in msg.fields:
8717ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if field.ctype == 'double':
8727ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                has_double = True
8737ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
8747ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if has_double:
8757ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield '\n'
8767ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield '/* On some platforms (such as AVR), double is really float.\n'
8777ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield ' * These are not directly supported by nanopb, but see example_avr_double.\n'
8787ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield ' * To get rid of this error, remove any double fields from your .proto.\n'
8797ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield ' */\n'
8807ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        yield 'STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES)\n'
8817ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
8827ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    yield '\n'
8837ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
8847ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen# ---------------------------------------------------------------------------
8857ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen#                    Options parsing for the .proto files
8867ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen# ---------------------------------------------------------------------------
8877ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
8887ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenfrom fnmatch import fnmatch
8897ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
8907ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef read_options_file(infile):
8917ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Parse a separate options file to list:
8927ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        [(namemask, options), ...]
8937ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''
8947ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    results = []
8957ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for line in infile:
8967ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        line = line.strip()
8977ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if not line or line.startswith('//') or line.startswith('#'):
8987ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            continue
8997ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
9007ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        parts = line.split(None, 1)
9017ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        opts = nanopb_pb2.NanoPBOptions()
9027ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        text_format.Merge(parts[1], opts)
9037ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        results.append((parts[0], opts))
9047ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
9057ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    return results
9067ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
9077ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenclass Globals:
9087ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Ugly global variables, should find a good way to pass these.'''
9097ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    verbose_options = False
9107ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    separate_options = []
9117ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    matched_namemasks = set()
9127ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
9137ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef get_nanopb_suboptions(subdesc, options, name):
9147ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Get copy of options, and merge information from subdesc.'''
9157ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    new_options = nanopb_pb2.NanoPBOptions()
9167ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    new_options.CopyFrom(options)
9177ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
9187ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    # Handle options defined in a separate file
9197ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    dotname = '.'.join(name.parts)
9207ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for namemask, options in Globals.separate_options:
9217ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if fnmatch(dotname, namemask):
9227ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            Globals.matched_namemasks.add(namemask)
9237ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            new_options.MergeFrom(options)
9247ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
9257ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    # Handle options defined in .proto
9267ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if isinstance(subdesc.options, descriptor.FieldOptions):
9277ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        ext_type = nanopb_pb2.nanopb
9287ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    elif isinstance(subdesc.options, descriptor.FileOptions):
9297ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        ext_type = nanopb_pb2.nanopb_fileopt
9307ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    elif isinstance(subdesc.options, descriptor.MessageOptions):
9317ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        ext_type = nanopb_pb2.nanopb_msgopt
9327ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    elif isinstance(subdesc.options, descriptor.EnumOptions):
9337ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        ext_type = nanopb_pb2.nanopb_enumopt
9347ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    else:
9357ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        raise Exception("Unknown options type")
9367ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
9377ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if subdesc.options.HasExtension(ext_type):
9387ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        ext = subdesc.options.Extensions[ext_type]
9397ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        new_options.MergeFrom(ext)
9407ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
9417ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if Globals.verbose_options:
9427ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        sys.stderr.write("Options for " + dotname + ": ")
9437ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        sys.stderr.write(text_format.MessageToString(new_options) + "\n")
9447ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
9457ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    return new_options
9467ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
9477ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
9487ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen# ---------------------------------------------------------------------------
9497ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen#                         Command line interface
9507ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen# ---------------------------------------------------------------------------
9517ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
9527ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenimport sys
9537ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenimport os.path
9547ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenfrom optparse import OptionParser
9557ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
9567ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenoptparser = OptionParser(
9577ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    usage = "Usage: nanopb_generator.py [options] file.pb ...",
9587ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    epilog = "Compile file.pb from file.proto by: 'protoc -ofile.pb file.proto'. " +
9597ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen             "Output will be written to file.pb.h and file.pb.c.")
9607ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenoptparser.add_option("-x", dest="exclude", metavar="FILE", action="append", default=[],
9617ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    help="Exclude file from generated #include list.")
9627ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenoptparser.add_option("-e", "--extension", dest="extension", metavar="EXTENSION", default="pb",
9637ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    help="Set extension to use instead of 'pb' for generated files. [default: %default]")
9647ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenoptparser.add_option("-f", "--options-file", dest="options_file", metavar="FILE", default="%s.options",
9657ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    help="Set name of a separate generator options file.")
9667ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenoptparser.add_option("-Q", "--generated-include-format", dest="genformat",
9677ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    metavar="FORMAT", default='#include "%s"\n',
9687ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    help="Set format string to use for including other .pb.h files. [default: %default]")
9697ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenoptparser.add_option("-L", "--library-include-format", dest="libformat",
9707ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    metavar="FORMAT", default='#include <%s>\n',
9717ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    help="Set format string to use for including the nanopb pb.h header. [default: %default]")
9727ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenoptparser.add_option("-T", "--no-timestamp", dest="notimestamp", action="store_true", default=False,
9737ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    help="Don't add timestamp to .pb.h and .pb.c preambles")
9747ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenoptparser.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False,
9757ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    help="Don't print anything except errors.")
9767ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenoptparser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
9777ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    help="Print more information.")
9787ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenoptparser.add_option("-s", dest="settings", metavar="OPTION:VALUE", action="append", default=[],
9797ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    help="Set generator option (max_size, max_count etc.).")
9807ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
9817ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef process_file(filename, fdesc, options):
9827ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Process a single file.
9837ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    filename: The full path to the .proto or .pb source file, as string.
9847ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    fdesc: The loaded FileDescriptorSet, or None to read from the input file.
9857ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    options: Command line options as they come from OptionsParser.
9867ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
9877ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    Returns a dict:
9887ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        {'headername': Name of header file,
9897ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen         'headerdata': Data for the .h header file,
9907ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen         'sourcename': Name of the source code file,
9917ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen         'sourcedata': Data for the .c source code file
9927ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        }
9937ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''
9947ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    toplevel_options = nanopb_pb2.NanoPBOptions()
9957ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for s in options.settings:
9967ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        text_format.Merge(s, toplevel_options)
9977ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
9987ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if not fdesc:
9997ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        data = open(filename, 'rb').read()
10007ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        fdesc = descriptor.FileDescriptorSet.FromString(data).file[0]
10017ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10027ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    # Check if there is a separate .options file
10037ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    try:
10047ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        optfilename = options.options_file % os.path.splitext(filename)[0]
10057ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    except TypeError:
10067ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        # No %s specified, use the filename as-is
10077ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        optfilename = options.options_file
10087ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10097ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if os.path.isfile(optfilename):
10107ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if options.verbose:
10117ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            sys.stderr.write('Reading options from ' + optfilename + '\n')
10127ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10137ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        Globals.separate_options = read_options_file(open(optfilename, "rU"))
10147ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    else:
10157ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        Globals.separate_options = []
10167ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    Globals.matched_namemasks = set()
10177ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10187ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    # Parse the file
10197ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    file_options = get_nanopb_suboptions(fdesc, toplevel_options, Names([filename]))
10207ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    enums, messages, extensions = parse_file(fdesc, file_options)
10217ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10227ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    # Decide the file names
10237ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    noext = os.path.splitext(filename)[0]
10247ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    headername = noext + '.' + options.extension + '.h'
10257ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    sourcename = noext + '.' + options.extension + '.c'
10267ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    headerbasename = os.path.basename(headername)
10277ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10287ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    # List of .proto files that should not be included in the C header file
10297ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    # even if they are mentioned in the source .proto.
10307ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    excludes = ['nanopb.proto', 'google/protobuf/descriptor.proto'] + options.exclude
10317ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    dependencies = [d for d in fdesc.dependency if d not in excludes]
10327ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10337ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    headerdata = ''.join(generate_header(dependencies, headerbasename, enums,
10347ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                                         messages, extensions, options))
10357ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10367ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    sourcedata = ''.join(generate_source(headerbasename, enums,
10377ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                                         messages, extensions, options))
10387ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10397ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    # Check if there were any lines in .options that did not match a member
10407ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    unmatched = [n for n,o in Globals.separate_options if n not in Globals.matched_namemasks]
10417ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if unmatched and not options.quiet:
10427ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        sys.stderr.write("Following patterns in " + optfilename + " did not match any fields: "
10437ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                         + ', '.join(unmatched) + "\n")
10447ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if not Globals.verbose_options:
10457ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            sys.stderr.write("Use  protoc --nanopb-out=-v:.   to see a list of the field names.\n")
10467ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10477ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    return {'headername': headername, 'headerdata': headerdata,
10487ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            'sourcename': sourcename, 'sourcedata': sourcedata}
10497ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10507ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef main_cli():
10517ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Main function when invoked directly from the command line.'''
10527ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10537ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    options, filenames = optparser.parse_args()
10547ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10557ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if not filenames:
10567ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        optparser.print_help()
10577ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        sys.exit(1)
10587ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10597ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if options.quiet:
10607ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        options.verbose = False
10617ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10627ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    Globals.verbose_options = options.verbose
10637ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10647ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for filename in filenames:
10657ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        results = process_file(filename, None, options)
10667ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10677ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        if not options.quiet:
10687ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            sys.stderr.write("Writing to " + results['headername'] + " and "
10697ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                             + results['sourcename'] + "\n")
10707ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10717ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        open(results['headername'], 'w').write(results['headerdata'])
10727ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        open(results['sourcename'], 'w').write(results['sourcedata'])
10737ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10747ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef main_plugin():
10757ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Main function when invoked as a protoc plugin.'''
10767ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10777ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    import sys
10787ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if sys.platform == "win32":
10797ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        import os, msvcrt
10807ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        # Set stdin and stdout to binary mode
10817ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
10827ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
10837ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10847ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    data = sys.stdin.read()
10857ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    request = plugin_pb2.CodeGeneratorRequest.FromString(data)
10867ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10877ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    import shlex
10887ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    args = shlex.split(request.parameter)
10897ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    options, dummy = optparser.parse_args(args)
10907ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10917ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    Globals.verbose_options = options.verbose
10927ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10937ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    response = plugin_pb2.CodeGeneratorResponse()
10947ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
10957ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    for filename in request.file_to_generate:
10967ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        for fdesc in request.proto_file:
10977ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen            if fdesc.name == filename:
10987ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                results = process_file(filename, fdesc, options)
10997ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
11007ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                f = response.file.add()
11017ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                f.name = results['headername']
11027ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                f.content = results['headerdata']
11037ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
11047ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                f = response.file.add()
11057ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                f.name = results['sourcename']
11067ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen                f.content = results['sourcedata']
11077ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
11087ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    sys.stdout.write(response.SerializeToString())
11097ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
11107ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenif __name__ == '__main__':
11117ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    # Check if we are running as a plugin under protoc
11127ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if 'protoc-gen-' in sys.argv[0] or '--protoc-plugin' in sys.argv:
11137ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        main_plugin()
11147ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    else:
11157ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        main_cli()
11167ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1117