Fixed issue 1 reported by Erik Rosen:
[apps/low-level-can-service.git] / generator / nanopb_generator.py
index a6d38c0..2ceafc7 100644 (file)
@@ -8,14 +8,14 @@ import os.path
 FieldD = descriptor.FieldDescriptorProto
 datatypes = {
     FieldD.TYPE_BOOL: ('bool', 'PB_LTYPE_VARINT'),
-    FieldD.TYPE_DOUBLE: ('double', 'PB_LTYPE_FIXED'),
-    FieldD.TYPE_FIXED32: ('uint32_t', 'PB_LTYPE_FIXED'),
-    FieldD.TYPE_FIXED64: ('uint64_t', 'PB_LTYPE_FIXED'),
-    FieldD.TYPE_FLOAT: ('float', 'PB_LTYPE_FIXED'),
+    FieldD.TYPE_DOUBLE: ('double', 'PB_LTYPE_FIXED64'),
+    FieldD.TYPE_FIXED32: ('uint32_t', 'PB_LTYPE_FIXED32'),
+    FieldD.TYPE_FIXED64: ('uint64_t', 'PB_LTYPE_FIXED64'),
+    FieldD.TYPE_FLOAT: ('float', 'PB_LTYPE_FIXED32'),
     FieldD.TYPE_INT32: ('int32_t', 'PB_LTYPE_VARINT'),
     FieldD.TYPE_INT64: ('int64_t', 'PB_LTYPE_VARINT'),
-    FieldD.TYPE_SFIXED32: ('int32_t', 'PB_LTYPE_FIXED'),
-    FieldD.TYPE_SFIXED64: ('int64_t', 'PB_LTYPE_FIXED'),
+    FieldD.TYPE_SFIXED32: ('int32_t', 'PB_LTYPE_FIXED32'),
+    FieldD.TYPE_SFIXED64: ('int64_t', 'PB_LTYPE_FIXED64'),
     FieldD.TYPE_SINT32: ('int32_t', 'PB_LTYPE_SVARINT'),
     FieldD.TYPE_SINT64: ('int64_t', 'PB_LTYPE_SVARINT'),
     FieldD.TYPE_UINT32: ('uint32_t', 'PB_LTYPE_VARINT'),
@@ -108,7 +108,8 @@ class Field:
         elif desc.type == FieldD.TYPE_ENUM:
             self.ltype = 'PB_LTYPE_VARINT'
             self.ctype = names_from_type_name(desc.type_name)
-            self.default = Names(self.ctype) + self.default
+            if self.default is not None:
+                self.default = self.ctype + self.default
         elif desc.type == FieldD.TYPE_STRING:
             self.ltype = 'PB_LTYPE_STRING'
             if self.max_size is None:
@@ -237,24 +238,10 @@ class Message:
         self.fields = [Field(self.name, f) for f in desc.field]
         self.ordered_fields = self.fields[:]
         self.ordered_fields.sort()
-    
-    def __cmp__(self, other):
-        '''Sort messages so that submessages are declared before the message
-        that uses them.
-        '''
-        if self.refers_to(other.name):
-            return 1
-        elif other.refers_to(self.name):
-            return -1
-        else:
-            return 0
-    
-    def refers_to(self, name):
-        '''Returns True if this message uses the specified type as field type.'''
-        for field in self.fields:
-            if str(field.ctype) == str(name):
-                return True
-        return False
+
+    def get_dependencies(self):
+        '''Get list of type names that this structure refers to.'''
+        return [str(field.ctype) for field in self.fields]
     
     def __str__(self):
         result = 'typedef struct {\n'
@@ -314,16 +301,52 @@ def parse_file(fdesc):
     enums = []
     messages = []
     
+    if fdesc.package:
+        base_name = Names(fdesc.package.split('.'))
+    else:
+        base_name = Names()
+    
     for enum in fdesc.enum_type:
-        enums.append(Enum(Names(), enum))
+        enums.append(Enum(base_name, enum))
     
-    for names, message in iterate_messages(fdesc):
+    for names, message in iterate_messages(fdesc, base_name):
         messages.append(Message(names, message))
         for enum in message.enum_type:
             enums.append(Enum(names, enum))
     
     return enums, messages
 
+def toposort2(data):
+    '''Topological sort.
+    From http://code.activestate.com/recipes/577413-topological-sort/
+    This function is under the MIT license.
+    '''
+    for k, v in data.items():
+        v.discard(k) # Ignore self dependencies
+    extra_items_in_deps = reduce(set.union, data.values()) - set(data.keys())
+    data.update(dict([(item, set()) for item in extra_items_in_deps]))
+    while True:
+        ordered = set(item for item,dep in data.items() if not dep)
+        if not ordered:
+            break
+        for item in sorted(ordered):
+            yield item
+        data = dict([(item, (dep - ordered)) for item,dep in data.items()
+                if item not in ordered])
+    assert not data, "A cyclic dependency exists amongst %r" % data
+
+def sort_dependencies(messages):
+    '''Sort a list of Messages based on dependencies.'''
+    dependencies = {}
+    message_by_name = {}
+    for message in messages:
+        dependencies[str(message.name)] = set(message.get_dependencies())
+        message_by_name[str(message.name)] = message
+    
+    for msgname in toposort2(dependencies):
+        if msgname in message_by_name:
+            yield message_by_name[msgname]
+
 def generate_header(headername, enums, messages):
     '''Generate content for a header file.
     Generates strings, which should be concatenated and stored to file.
@@ -341,8 +364,7 @@ def generate_header(headername, enums, messages):
         yield str(enum) + '\n\n'
     
     yield '/* Struct definitions */\n'
-    messages.sort()
-    for msg in messages:
+    for msg in sort_dependencies(messages):
         yield msg.types()
         yield str(msg) + '\n\n'
         
@@ -379,7 +401,7 @@ if __name__ == '__main__':
         print "Usage: " + sys.argv[0] + " file.pb"
         print "where file.pb has been compiled from .proto by:"
         print "protoc -ofile.pb file.proto"
-        print "Output fill be written to file.h and file.c"
+        print "Output fill be written to file.pb.h and file.pb.c"
         sys.exit(1)
     
     data = open(sys.argv[1]).read()
@@ -387,8 +409,8 @@ if __name__ == '__main__':
     enums, messages = parse_file(fdesc.file[0])
     
     noext = os.path.splitext(sys.argv[1])[0]
-    headername = noext + '.h'
-    sourcename = noext + '.c'
+    headername = noext + '.pb.h'
+    sourcename = noext + '.pb.c'
     headerbasename = os.path.basename(headername)
     
     print "Writing to " + headername + " and " + sourcename
@@ -401,4 +423,4 @@ if __name__ == '__main__':
     for part in generate_source(headerbasename, enums, messages):
         source.write(part)
 
-    
\ No newline at end of file
+