Add nanopb version number to generated files.
[apps/agl-service-can-low-level.git] / generator / nanopb_generator.py
index 69a9eab..dadad64 100644 (file)
@@ -1,4 +1,5 @@
 '''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
+nanopb_version = "nanopb-0.1.7-dev"
 
 try:
     import google.protobuf.descriptor_pb2 as descriptor
@@ -31,7 +32,7 @@ except:
 #                     Generation of single fields
 # ---------------------------------------------------------------------------
 
-
+import time
 import os.path
 
 # Values are tuple (c type, pb ltype)
@@ -81,7 +82,12 @@ def names_from_type_name(type_name):
 class Enum:
     def __init__(self, names, desc, enum_options):
         '''desc is EnumDescriptorProto'''
-        self.names = names + desc.name
+        
+        if enum_options.long_names:
+            self.names = names + desc.name
+        else:
+            self.names = names
+        
         self.values = [(self.names + x.name, x.number) for x in desc.value]
     
     def __str__(self):
@@ -110,18 +116,18 @@ class Field:
         
         if desc.HasField('default_value'):
             self.default = desc.default_value
-        
+           
         # Decide HTYPE
         # HTYPE is the high-order nibble of nanopb field description,
         # defining whether value is required/optional/repeated.
-        is_callback = False
+        can_be_static = True
         if desc.label == FieldD.LABEL_REQUIRED:
             self.htype = 'PB_HTYPE_REQUIRED'
         elif desc.label == FieldD.LABEL_OPTIONAL:
             self.htype = 'PB_HTYPE_OPTIONAL'
         elif desc.label == FieldD.LABEL_REPEATED:
             if self.max_count is None:
-                is_callback = True
+                can_be_static = False
             else:
                 self.htype = 'PB_HTYPE_ARRAY'
                 self.array_decl = '[%d]' % self.max_count
@@ -142,14 +148,14 @@ class Field:
         elif desc.type == FieldD.TYPE_STRING:
             self.ltype = 'PB_LTYPE_STRING'
             if self.max_size is None:
-                is_callback = True
+                can_be_static = False
             else:
                 self.ctype = 'char'
                 self.array_decl += '[%d]' % self.max_size
         elif desc.type == FieldD.TYPE_BYTES:
             self.ltype = 'PB_LTYPE_BYTES'
             if self.max_size is None:
-                is_callback = True
+                can_be_static = False
             else:
                 self.ctype = self.struct_name + self.name + 't'
         elif desc.type == FieldD.TYPE_MESSAGE:
@@ -158,7 +164,16 @@ class Field:
         else:
             raise NotImplementedError(desc.type)
         
-        if is_callback:
+        if field_options.type == nanopb_pb2.FT_DEFAULT:
+            if can_be_static:
+                field_options.type = nanopb_pb2.FT_STATIC
+            else:
+                field_options.type = nanopb_pb2.FT_CALLBACK
+        
+        if field_options.type == nanopb_pb2.FT_STATIC and not can_be_static:
+            raise Exception("Field %s is defined as static, but max_size or max_count is not given." % self.name)
+        
+        if field_options.type == nanopb_pb2.FT_CALLBACK:
             self.htype = 'PB_HTYPE_CALLBACK'
             self.ctype = 'pb_callback_t'
             self.array_decl = ''
@@ -367,7 +382,8 @@ def parse_file(fdesc, file_options):
         base_name = Names()
     
     for enum in fdesc.enum_type:
-        enums.append(Enum(base_name, enum, file_options))
+        enum_options = get_nanopb_suboptions(enum, file_options)
+        enums.append(Enum(base_name, enum, enum_options))
     
     for names, message in iterate_messages(fdesc, base_name):
         message_options = get_nanopb_suboptions(message, file_options)
@@ -414,6 +430,7 @@ def generate_header(dependencies, headername, enums, messages):
     '''
     
     yield '/* Automatically generated nanopb header */\n'
+    yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime())
     
     symbol = headername.replace('.', '_').upper()
     yield '#ifndef _PB_%s_\n' % symbol
@@ -423,7 +440,10 @@ def generate_header(dependencies, headername, enums, messages):
     for dependency in dependencies:
         noext = os.path.splitext(dependency)[0]
         yield '#include "%s.pb.h"\n' % noext
-    yield '\n'
+    
+    yield '#ifdef __cplusplus\n'
+    yield 'extern "C" {\n'
+    yield '#endif\n\n'
     
     yield '/* Enum definitions */\n'
     for enum in enums:
@@ -487,6 +507,10 @@ def generate_header(dependencies, headername, enums, messages):
                 yield 'STATIC_ASSERT((%s), YOU_MUST_DEFINE_PB_FIELD_32BIT)\n' % assertion
             yield '#endif\n'
     
+    yield '\n#ifdef __cplusplus\n'
+    yield '} /* extern "C" */\n'
+    yield '#endif\n'
+    
     # End of header
     yield '\n#endif\n'
 
@@ -494,6 +518,7 @@ def generate_source(headername, enums, messages):
     '''Generate content for a source file.'''
     
     yield '/* Automatically generated nanopb constant definitions */\n'
+    yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime())
     yield '#include "%s"\n\n' % headername
     
     for msg in messages:
@@ -539,6 +564,8 @@ def get_nanopb_suboptions(subdesc, options):
         ext_type = nanopb_pb2.nanopb_fileopt
     elif isinstance(subdesc.options, descriptor.MessageOptions):
         ext_type = nanopb_pb2.nanopb_msgopt
+    elif isinstance(subdesc.options, descriptor.EnumOptions):
+        ext_type = nanopb_pb2.nanopb_enumopt
     else:
         raise Exception("Unknown options type")