Get rid of pb_bytes_ptr_t, just allocate pb_bytes_array_t dynamically.
[apps/agl-service-can-low-level.git] / generator / nanopb_generator.py
index d67fe94..c32b26a 100755 (executable)
@@ -1,36 +1,42 @@
 #!/usr/bin/python
 
 '''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
-nanopb_version = "nanopb-0.2.5-dev"
+nanopb_version = "nanopb-0.2.7-dev"
+
+import sys
+
+try:
+    # Add some dummy imports to keep packaging tools happy.
+    import google, distutils.util # bbfreeze seems to need these
+    import pkg_resources # pyinstaller / protobuf 2.5 seem to need these
+except:
+    # Don't care, we will error out later if it is actually important.
+    pass
 
 try:
-    import google.protobuf.descriptor_pb2 as descriptor
     import google.protobuf.text_format as text_format
+    import google.protobuf.descriptor_pb2 as descriptor
 except:
-    print
-    print "*************************************************************"
-    print "*** Could not import the Google protobuf Python libraries ***"
-    print "*** Try installing package 'python-protobuf' or similar.  ***"
-    print "*************************************************************"
-    print
+    sys.stderr.write('''
+         *************************************************************
+         *** Could not import the Google protobuf Python libraries ***
+         *** Try installing package 'python-protobuf' or similar.  ***
+         *************************************************************
+    ''' + '\n')
     raise
 
 try:
-    import nanopb_pb2
+    import proto.nanopb_pb2 as nanopb_pb2
+    import proto.plugin_pb2 as plugin_pb2
 except:
-    print
-    print "***************************************************************"
-    print "*** Could not import the precompiled nanopb_pb2.py.         ***"
-    print "*** Run 'make' in the 'generator' folder to update the file.***"
-    print "***************************************************************"
-    print
+    sys.stderr.write('''
+         ********************************************************************
+         *** Failed to import the protocol definitions for generator.     ***
+         *** You have to run 'make' in the nanopb/generator/proto folder. ***
+         ********************************************************************
+    ''' + '\n')
     raise
 
-
-
-
-
-
 # ---------------------------------------------------------------------------
 #                     Generation of single fields
 # ---------------------------------------------------------------------------
@@ -163,6 +169,7 @@ class Field:
         self.max_count = None
         self.array_decl = ""
         self.enc_size = None
+        self.ctype = None
         
         # Parse field options
         if field_options.HasField("max_size"):
@@ -239,7 +246,7 @@ class Field:
                 self.ctype = self.struct_name + self.name + 't'
                 self.enc_size = varint_max_size(self.max_size) + self.max_size
             elif self.allocation == 'POINTER':
-                self.ctype = 'pb_bytes_ptr_t'
+                self.ctype = 'pb_bytes_array_t'
         elif desc.type == FieldD.TYPE_MESSAGE:
             self.pbtype = 'MESSAGE'
             self.ctype = self.submsgname = names_from_type_name(desc.type_name)
@@ -259,8 +266,8 @@ class Field:
             if self.pbtype == 'MESSAGE':
                 # Use struct definition, so recursive submessages are possible
                 result += '    struct _%s *%s;' % (self.ctype, self.name)
-            elif self.rules == 'REPEATED' and self.pbtype == 'STRING':
-                # String arrays need to be defined as pointers to pointers
+            elif self.rules == 'REPEATED' and self.pbtype in ['STRING', 'BYTES']:
+                # String/bytes arrays need to be defined as pointers to pointers
                 result += '    %s **%s;' % (self.ctype, self.name)
             else:
                 result += '    %s *%s;' % (self.ctype, self.name)
@@ -308,6 +315,12 @@ class Field:
             data = self.default.decode('string_escape')
             data = ['0x%02x' % ord(c) for c in data]
             default = '{%d, {%s}}' % (len(data), ','.join(data))
+        elif self.pbtype in ['FIXED32', 'UINT32']:
+            default += 'u'
+        elif self.pbtype in ['FIXED64', 'UINT64']:
+            default += 'ull'
+        elif self.pbtype in ['SFIXED64', 'INT64']:
+            default += 'll'
         
         if declaration_only:
             return 'extern const %s %s_default%s;' % (ctype, self.struct_name + self.name, array_decl)
@@ -809,6 +822,23 @@ def generate_source(headername, enums, messages, extensions, options):
     if worst > 255 or checks:
         yield '\n/* Check that field information fits in pb_field_t */\n'
         
+        if worst > 65535 or checks:
+            yield '#if !defined(PB_FIELD_32BIT)\n'
+            if worst > 65535:
+                yield '#error Field descriptor for %s is too large. Define PB_FIELD_32BIT to fix this.\n' % worst_field
+            else:
+                assertion = ' && '.join(str(c) + ' < 65536' for c in checks)
+                msgs = '_'.join(str(n) for n in checks_msgnames)
+                yield '/* If you get an error here, it means that you need to define PB_FIELD_32BIT\n'
+                yield ' * compile-time option. You can do that in pb.h or on compiler command line.\n'
+                yield ' * \n'
+                yield ' * The reason you need to do this is that some of your messages contain tag\n'
+                yield ' * numbers or field sizes that are larger than what can fit in 8 or 16 bit\n'
+                yield ' * field descriptors.\n'
+                yield ' */\n'
+                yield 'STATIC_ASSERT((%s), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_%s)\n'%(assertion,msgs)
+            yield '#endif\n\n'
+        
         if worst < 65536:
             yield '#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)\n'
             if worst > 255:
@@ -816,18 +846,15 @@ def generate_source(headername, enums, messages, extensions, options):
             else:
                 assertion = ' && '.join(str(c) + ' < 256' for c in checks)
                 msgs = '_'.join(str(n) for n in checks_msgnames)
+                yield '/* If you get an error here, it means that you need to define PB_FIELD_16BIT\n'
+                yield ' * compile-time option. You can do that in pb.h or on compiler command line.\n'
+                yield ' * \n'
+                yield ' * The reason you need to do this is that some of your messages contain tag\n'
+                yield ' * numbers or field sizes that are larger than what can fit in the default\n'
+                yield ' * 8 bit descriptors.\n'
+                yield ' */\n'
                 yield 'STATIC_ASSERT((%s), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_%s)\n'%(assertion,msgs)
             yield '#endif\n\n'
-        
-        if worst > 65535 or checks:
-            yield '#if !defined(PB_FIELD_32BIT)\n'
-            if worst > 65535:
-                yield '#error Field descriptor for %s is too large. Define PB_FIELD_32BIT to fix this.\n' % worst_field
-            else:
-                assertion = ' && '.join(str(c) + ' < 65536' for c in checks)
-                msgs = '_'.join(str(n) for n in checks_msgnames)
-                yield 'STATIC_ASSERT((%s), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_%s)\n'%(assertion,msgs)
-            yield '#endif\n'
     
     # Add check for sizeof(double)
     has_double = False
@@ -873,6 +900,7 @@ class Globals:
     '''Ugly global variables, should find a good way to pass these.'''
     verbose_options = False
     separate_options = []
+    matched_namemasks = set()
 
 def get_nanopb_suboptions(subdesc, options, name):
     '''Get copy of options, and merge information from subdesc.'''
@@ -883,6 +911,7 @@ def get_nanopb_suboptions(subdesc, options, name):
     dotname = '.'.join(name.parts)
     for namemask, options in Globals.separate_options:
         if fnmatch(dotname, namemask):
+            Globals.matched_namemasks.add(namemask)
             new_options.MergeFrom(options)
     
     # Handle options defined in .proto
@@ -902,8 +931,8 @@ def get_nanopb_suboptions(subdesc, options, name):
         new_options.MergeFrom(ext)
     
     if Globals.verbose_options:
-        print "Options for " + dotname + ":"
-        print text_format.MessageToString(new_options)
+        sys.stderr.write("Options for " + dotname + ": ")
+        sys.stderr.write(text_format.MessageToString(new_options) + "\n")
     
     return new_options
 
@@ -967,13 +996,14 @@ def process_file(filename, fdesc, options):
         # No %s specified, use the filename as-is
         optfilename = options.options_file
     
-    if options.verbose:
-        print 'Reading options from ' + optfilename
-    
     if os.path.isfile(optfilename):
+        if options.verbose:
+            sys.stderr.write('Reading options from ' + optfilename + '\n')
+
         Globals.separate_options = read_options_file(open(optfilename, "rU"))
     else:
         Globals.separate_options = []
+    Globals.matched_namemasks = set()
     
     # Parse the file
     file_options = get_nanopb_suboptions(fdesc, toplevel_options, Names([filename]))
@@ -996,6 +1026,14 @@ def process_file(filename, fdesc, options):
     sourcedata = ''.join(generate_source(headerbasename, enums,
                                          messages, extensions, options))
 
+    # Check if there were any lines in .options that did not match a member
+    unmatched = [n for n,o in Globals.separate_options if n not in Globals.matched_namemasks]
+    if unmatched and not options.quiet:
+        sys.stderr.write("Following patterns in " + optfilename + " did not match any fields: "
+                         + ', '.join(unmatched) + "\n")
+        if not Globals.verbose_options:
+            sys.stderr.write("Use  protoc --nanopb-out=-v:.   to see a list of the field names.\n")
+
     return {'headername': headername, 'headerdata': headerdata,
             'sourcename': sourcename, 'sourcedata': sourcedata}
     
@@ -1017,7 +1055,8 @@ def main_cli():
         results = process_file(filename, None, options)
         
         if not options.quiet:
-            print "Writing to " + results['headername'] + " and " + results['sourcename']
+            sys.stderr.write("Writing to " + results['headername'] + " and "
+                             + results['sourcename'] + "\n")
     
         open(results['headername'], 'w').write(results['headerdata'])
         open(results['sourcename'], 'w').write(results['sourcedata'])        
@@ -1025,7 +1064,13 @@ def main_cli():
 def main_plugin():
     '''Main function when invoked as a protoc plugin.'''
 
-    import plugin_pb2
+    import sys
+    if sys.platform == "win32":
+        import os, msvcrt
+        # Set stdin and stdout to binary mode
+        msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
+        msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
+    
     data = sys.stdin.read()
     request = plugin_pb2.CodeGeneratorRequest.FromString(data)
     
@@ -1033,10 +1078,7 @@ def main_plugin():
     args = shlex.split(request.parameter)
     options, dummy = optparser.parse_args(args)
     
-    # We can't go printing stuff to stdout
-    Globals.verbose_options = False
-    options.verbose = False
-    options.quiet = True
+    Globals.verbose_options = options.verbose
     
     response = plugin_pb2.CodeGeneratorResponse()
     
@@ -1057,7 +1099,7 @@ def main_plugin():
 
 if __name__ == '__main__':
     # Check if we are running as a plugin under protoc
-    if 'protoc-gen-' in sys.argv[0]:
+    if 'protoc-gen-' in sys.argv[0] or '--protoc-plugin' in sys.argv:
         main_plugin()
     else:
         main_cli()