Better error messages for syntax errors in .options file
[apps/agl-service-can-low-level.git] / generator / nanopb_generator.py
index 3f309a4..8260290 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/python
 
 '''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
-nanopb_version = "nanopb-0.3.2-dev"
+nanopb_version = "nanopb-0.3.3-dev"
 
 import sys
 
@@ -587,11 +587,14 @@ class OneOf(Field):
         self.name = oneof_desc.name
         self.ctype = 'union'
         self.fields = []
+        self.allocation = 'ONEOF'
+        self.default = None
+        self.rules = 'ONEOF'
 
     def add_field(self, field):
         if field.allocation == 'CALLBACK':
             raise Exception("Callback fields inside of oneof are not supported"
-                            + " (field %s)" % field.fullname)
+                            + " (field %s)" % field.name)
 
         field.union_name = self.name
         field.rules = 'ONEOF'
@@ -633,7 +636,6 @@ class OneOf(Field):
         return '\n'.join([f.tags() for f in self.fields])
 
     def pb_field_t(self, prev_field_name):
-        prev_field_name = prev_field_name or self.name
         result = ',\n'.join([f.pb_field_t(prev_field_name) for f in self.fields])
         return result
 
@@ -641,7 +643,17 @@ class OneOf(Field):
         return max([f.largest_field_value() for f in self.fields])
 
     def encoded_size(self, allmsgs):
-        return max([f.encoded_size(allmsgs) for f in self.fields])
+        largest = EncodedSize(0)
+        for f in self.fields:
+            size = f.encoded_size(allmsgs)
+            if size is None:
+                return None
+            elif size.symbols:
+                return None # Cannot resolve maximum of symbols
+            elif size.value > largest.value:
+                largest = size
+
+        return largest
 
 # ---------------------------------------------------------------------------
 #                   Generation of messages (structures)
@@ -652,13 +664,20 @@ class Message:
     def __init__(self, names, desc, message_options):
         self.name = names
         self.fields = []
-        self.oneofs = []
+        self.oneofs = {}
+        no_unions = []
 
         if hasattr(desc, 'oneof_decl'):
-            for f in desc.oneof_decl:
-                oneof = OneOf(self.name, f)
-                self.oneofs.append(oneof)
-                self.fields.append(oneof)
+            for i, f in enumerate(desc.oneof_decl):
+                oneof_options = get_nanopb_suboptions(desc, message_options, self.name + f.name)
+                if oneof_options.no_unions:
+                    no_unions.append(i) # No union, but add fields normally
+                elif oneof_options.type == nanopb_pb2.FT_IGNORE:
+                    pass # No union and skip fields also
+                else:
+                    oneof = OneOf(self.name, f)
+                    self.oneofs[i] = oneof
+                    self.fields.append(oneof)
 
         for f in desc.field:
             field_options = get_nanopb_suboptions(f, message_options, self.name + f.name)
@@ -666,8 +685,11 @@ class Message:
                 continue
 
             field = Field(self.name, f, field_options)
-            if hasattr(f, 'oneof_index') and f.HasField('oneof_index'):
-                self.oneofs[f.oneof_index].add_field(field)
+            if (hasattr(f, 'oneof_index') and
+                f.HasField('oneof_index') and
+                f.oneof_index not in no_unions):
+                if f.oneof_index in self.oneofs:
+                    self.oneofs[f.oneof_index].add_field(field)
             else:
                 self.fields.append(field)
         
@@ -734,7 +756,7 @@ class Message:
         '''Returns number of required fields inside this message'''
         count = 0
         for f in self.fields:
-            if f not in self.oneofs:
+            if not isinstance(f, OneOf):
                 if f.rules == 'REQUIRED':
                     count += 1
         return count
@@ -742,7 +764,7 @@ class Message:
     def count_all_fields(self):
         count = 0
         for f in self.fields:
-            if f in self.oneofs:
+            if isinstance(f, OneOf):
                 count += len(f.fields)
             else:
                 count += 1
@@ -1104,14 +1126,28 @@ def read_options_file(infile):
         [(namemask, options), ...]
     '''
     results = []
-    for line in infile:
+    for i, line in enumerate(infile):
         line = line.strip()
         if not line or line.startswith('//') or line.startswith('#'):
             continue
         
         parts = line.split(None, 1)
+        
+        if len(parts) < 2:
+            sys.stderr.write("%s:%d: " % (infile.name, i + 1) +
+                             "Option lines should have space between field name and options. " +
+                             "Skipping line: '%s'\n" % line)
+            continue
+        
         opts = nanopb_pb2.NanoPBOptions()
-        text_format.Merge(parts[1], opts)
+        
+        try:
+            text_format.Merge(parts[1], opts)
+        except Exception, e:
+            sys.stderr.write("%s:%d: " % (infile.name, i + 1) +
+                             "Unparseable option line: '%s'. " % line +
+                             "Error: %s\n" % str(e))
+            continue
         results.append((parts[0], opts))
 
     return results