+ # Handle options defined in a separate file
+ 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
+ if isinstance(subdesc.options, descriptor.FieldOptions):
+ ext_type = nanopb_pb2.nanopb
+ elif isinstance(subdesc.options, descriptor.FileOptions):
+ 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")
+
+ if subdesc.options.HasExtension(ext_type):
+ ext = subdesc.options.Extensions[ext_type]
+ new_options.MergeFrom(ext)
+
+ if Globals.verbose_options:
+ sys.stderr.write("Options for " + dotname + ": ")
+ sys.stderr.write(text_format.MessageToString(new_options) + "\n")
+
+ return new_options
+
+
+# ---------------------------------------------------------------------------
+# Command line interface
+# ---------------------------------------------------------------------------
+
+import sys
+import os.path
+from optparse import OptionParser
+
+optparser = OptionParser(
+ usage = "Usage: nanopb_generator.py [options] file.pb ...",
+ epilog = "Compile file.pb from file.proto by: 'protoc -ofile.pb file.proto'. " +
+ "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("-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",
+ metavar="FORMAT", default='#include "%s"\n',
+ help="Set format string to use for including other .pb.h files. [default: %default]")
+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("-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,
+ help="Print more information.")
+optparser.add_option("-s", dest="settings", metavar="OPTION:VALUE", action="append", default=[],
+ help="Set generator option (max_size, max_count etc.).")
+
+def process_file(filename, fdesc, options):
+ '''Process a single file.
+ filename: The full path to the .proto or .pb source file, as string.
+ fdesc: The loaded FileDescriptorSet, or None to read from the input file.
+ options: Command line options as they come from OptionsParser.
+
+ Returns a dict:
+ {'headername': Name of header file,
+ 'headerdata': Data for the .h header file,
+ 'sourcename': Name of the source code file,
+ 'sourcedata': Data for the .c source code file
+ }
+ '''
+ toplevel_options = nanopb_pb2.NanoPBOptions()
+ for s in options.settings:
+ text_format.Merge(s, toplevel_options)
+
+ if not fdesc:
+ data = open(filename, 'rb').read()
+ fdesc = descriptor.FileDescriptorSet.FromString(data).file[0]
+
+ # Check if there is a separate .options file
+ 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 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]))
+ 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'
+ headerbasename = os.path.basename(headername)