#!/usr/bin/python
'''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
-nanopb_version = "nanopb-0.2.6-dev"
+nanopb_version = "nanopb-0.2.9-dev"
import sys
try:
import google.protobuf.text_format as text_format
+ import google.protobuf.descriptor_pb2 as descriptor
except:
sys.stderr.write('''
*************************************************************
try:
import proto.nanopb_pb2 as nanopb_pb2
- import proto.descriptor_pb2 as descriptor
+ import proto.plugin_pb2 as plugin_pb2
except:
sys.stderr.write('''
********************************************************************
''' + '\n')
raise
-
# ---------------------------------------------------------------------------
# Generation of single fields
# ---------------------------------------------------------------------------
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)
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)
result += '0)'
elif self.pbtype in ['BYTES', 'STRING'] and self.allocation != 'STATIC':
result += '0)' # Arbitrary size default values not implemented
+ elif self.rules == 'OPTEXT':
+ result += '0)' # Default value for extensions is not implemented
else:
result += '&%s_default)' % (self.struct_name + self.name)
enums.append(Enum(names, enum, enum_options))
for names, extension in iterate_extensions(fdesc, base_name):
- field_options = get_nanopb_suboptions(extension, file_options, names)
+ field_options = get_nanopb_suboptions(extension, file_options, names + extension.name)
if field_options.type != nanopb_pb2.FT_IGNORE:
extensions.append(ExtensionField(names, extension, field_options))
'''
yield '/* Automatically generated nanopb header */\n'
- yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime())
+ if options.notimestamp:
+ yield '/* Generated by %s */\n\n' % (nanopb_version)
+ else:
+ yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime())
symbol = make_identifier(headername)
yield '#ifndef _PB_%s_\n' % symbol
for dependency in dependencies:
noext = os.path.splitext(dependency)[0]
- yield options.genformat % (noext + '.' + options.extension + '.h')
+ yield options.genformat % (noext + options.extension + '.h')
yield '\n'
yield '#ifdef __cplusplus\n'
'''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())
+ if options.notimestamp:
+ yield '/* Generated by %s */\n\n' % (nanopb_version)
+ else:
+ yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime())
yield options.genformat % (headername)
yield '\n'
'''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.'''
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
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
"Output will be written to file.pb.h and file.pb.c.")
optparser.add_option("-x", dest="exclude", metavar="FILE", action="append", default=[],
help="Exclude file from generated #include list.")
-optparser.add_option("-e", "--extension", dest="extension", metavar="EXTENSION", default="pb",
- help="Set extension to use instead of 'pb' for generated files. [default: %default]")
+optparser.add_option("-e", "--extension", dest="extension", metavar="EXTENSION", default=".pb",
+ help="Set extension to use instead of '.pb' for generated files. [default: %default]")
optparser.add_option("-f", "--options-file", dest="options_file", metavar="FILE", default="%s.options",
help="Set name of a separate generator options file.")
optparser.add_option("-Q", "--generated-include-format", dest="genformat",
optparser.add_option("-L", "--library-include-format", dest="libformat",
metavar="FORMAT", default='#include <%s>\n',
help="Set format string to use for including the nanopb pb.h header. [default: %default]")
+optparser.add_option("-T", "--no-timestamp", dest="notimestamp", action="store_true", default=False,
+ help="Don't add timestamp to .pb.h and .pb.c preambles")
optparser.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False,
help="Don't print anything except errors.")
optparser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
fdesc = descriptor.FileDescriptorSet.FromString(data).file[0]
# Check if there is a separate .options file
+ had_abspath = False
try:
optfilename = options.options_file % os.path.splitext(filename)[0]
except TypeError:
# No %s specified, use the filename as-is
optfilename = options.options_file
-
- if options.verbose:
- print 'Reading options from ' + optfilename
-
+ had_abspath = True
+
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:
+ # If we are given a full filename and it does not exist, give an error.
+ # However, don't give error when we automatically look for .options file
+ # with the same name as .proto.
+ if options.verbose or had_abspath:
+ sys.stderr.write('Options file not found: ' + optfilename)
+
Globals.separate_options = []
+
+ Globals.matched_namemasks = set()
# Parse the file
file_options = get_nanopb_suboptions(fdesc, toplevel_options, Names([filename]))
enums, messages, extensions = parse_file(fdesc, file_options)
-
+
# Decide the file names
noext = os.path.splitext(filename)[0]
- headername = noext + '.' + options.extension + '.h'
- sourcename = noext + '.' + options.extension + '.c'
+ headername = noext + options.extension + '.h'
+ sourcename = noext + options.extension + '.c'
headerbasename = os.path.basename(headername)
# List of .proto files that should not be included in the C header file
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}
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'])
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
- import proto.plugin_pb2 as plugin_pb2
data = sys.stdin.read()
request = plugin_pb2.CodeGeneratorRequest.FromString(data)
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()