Fix handling of unsigned 8- or 16-bit enums.
[apps/agl-service-can-low-level.git] / generator / nanopb_generator.py
index 4673ac6..2b1d63e 100755 (executable)
@@ -1,9 +1,10 @@
 #!/usr/bin/python
 
 '''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
-nanopb_version = "nanopb-0.3.3-dev"
+nanopb_version = "nanopb-0.3.4-dev"
 
 import sys
+import re
 
 try:
     # Add some dummy imports to keep packaging tools happy.
@@ -159,11 +160,30 @@ class Enum:
             self.values = [(names + x.name, x.number) for x in desc.value] 
         
         self.value_longnames = [self.names + x.name for x in desc.value]
+        self.packed = enum_options.packed_enum
+    
+    def has_negative(self):
+        for n, v in self.values:
+            if v < 0:
+                return True
+        return False
     
     def __str__(self):
         result = 'typedef enum _%s {\n' % self.names
         result += ',\n'.join(["    %s = %d" % x for x in self.values])
-        result += '\n} %s;' % self.names
+        result += '\n}'
+        
+        if self.packed:
+            result += ' pb_packed'
+        
+        result += ' %s;' % self.names
+        
+        if not self.options.long_names:
+            # Define the long names always so that enum value references
+            # from other files work properly.
+            for i, x in enumerate(self.values):
+                result += '\n#define %s %s' % (self.value_longnames[i], x[0])
+        
         return result
 
 class Field:
@@ -328,7 +348,7 @@ class Field:
                 inner_init = '""'
             elif self.pbtype == 'BYTES':
                 inner_init = '{0, {0}}'
-            elif self.pbtype == 'ENUM':
+            elif self.pbtype in ('ENUM', 'UENUM'):
                 inner_init = '(%s)0' % self.ctype
             else:
                 inner_init = '0'
@@ -586,6 +606,7 @@ class OneOf(Field):
         self.struct_name = struct_name
         self.name = oneof_desc.name
         self.ctype = 'union'
+        self.pbtype = 'oneof'
         self.fields = []
         self.allocation = 'ONEOF'
         self.default = None
@@ -877,6 +898,14 @@ def parse_file(fdesc, file_options):
                         idx = enum.value_longnames.index(field.default)
                         field.default = enum.values[idx][0]
     
+    # Fix field data types where enums have negative values.
+    for enum in enums:
+        if not enum.has_negative():
+            for message in messages:
+                for field in message.fields:
+                    if field.pbtype == 'ENUM' and field.ctype == enum.names:
+                        field.pbtype = 'UENUM'
+    
     return enums, messages, extensions
 
 def toposort2(data):
@@ -1005,9 +1034,8 @@ def generate_header(dependencies, headername, enums, messages, extensions, optio
             yield '#define %-40s %s\n' % (identifier, msize)
     yield '\n'
 
-    yield '/* helper macros for message type ids if set with */\n'
-    yield '/* option (nanopb_msgopt).msgid = <id>; */\n\n'
-
+    yield '/* Message IDs (where set with "msgid" option) */\n'
+    
     yield '#ifdef PB_MSGID\n'
     for msg in messages:
         if hasattr(msg,'msgid'):
@@ -1026,6 +1054,11 @@ def generate_header(dependencies, headername, enums, messages, extensions, optio
             yield '\tPB_MSG(%d,%s,%s) \\\n' % (msg.msgid, m, msg.name)
     yield '\n'
 
+    for msg in messages:
+        if hasattr(msg,'msgid'):
+            yield '#define %s_msgid %d\n' % (msg.name, msg.msgid)
+    yield '\n'
+
     yield '#endif\n\n'
 
 
@@ -1153,9 +1186,13 @@ def read_options_file(infile):
         [(namemask, options), ...]
     '''
     results = []
-    for i, line in enumerate(infile):
+    data = infile.read()
+    data = re.sub('/\*.*?\*/', '', data, flags = re.MULTILINE)
+    data = re.sub('//.*?$', '', data, flags = re.MULTILINE)
+    data = re.sub('#.*?$', '', data, flags = re.MULTILINE)
+    for i, line in enumerate(data.split('\n')):
         line = line.strip()
-        if not line or line.startswith('//') or line.startswith('#'):
+        if not line:
             continue
         
         parts = line.split(None, 1)
@@ -1368,8 +1405,15 @@ def main_plugin():
     data = sys.stdin.read()
     request = plugin_pb2.CodeGeneratorRequest.FromString(data)
     
+    try:
+        # Versions of Python prior to 2.7.3 do not support unicode
+        # input to shlex.split(). Try to convert to str if possible.
+        params = str(request.parameter)
+    except UnicodeEncodeError:
+        params = request.parameter
+    
     import shlex
-    args = shlex.split(request.parameter)
+    args = shlex.split(params)
     options, dummy = optparser.parse_args(args)
     
     Globals.verbose_options = options.verbose