Merge pull request #500 from silvergasp/master
Add bazel rules for nanopb generation
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b7f43c9..bf42cd0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -123,7 +123,7 @@
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
target_include_directories(protobuf-nanopb-static INTERFACE
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
- $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
)
endif()
diff --git a/docs/reference.md b/docs/reference.md
index 2d591ae..7f0d85d 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -2,12 +2,13 @@
## Compilation options
-The following options can be specified in one of two ways:
+Compilation options affect the functionality included in the nanopb core C code.
+The options can be specified in one of two ways:
1. Using the -D switch on the C compiler command line.
2. Using a `#define` at the top of pb.h.
-> **NOTE:** You must have the same settings for the nanopb library and all code that
+> **NOTE:** You must have the same compilation options for the nanopb library and all code that
includes nanopb headers.
* `PB_ENABLE_MALLOC`: Enable dynamic allocation support in the decoder.
@@ -29,10 +30,12 @@
generated `.pb.c` files. The default setting is to use the smallest
datatypes (least resources used).
-## Proto file options
+## Generator options
-The generator behaviour can be adjusted using several options, defined
-in the [nanopb.proto](https://github.com/nanopb/nanopb/blob/master/generator/proto/nanopb.proto) file in the generator folder. Here is a list of the most common options, but see the file for a full list:
+Generator options affect how the `.proto` files get converted to `.pb.c` and `.pb.h.` files.
+
+Most options are related to specific message or field in `.proto` file.
+The full set of available options is defined in [nanopb.proto](https://github.com/nanopb/nanopb/blob/master/generator/proto/nanopb.proto). Here is a list of the most common options, but see the file for a full list:
* `max_size`: Allocated maximum size for `bytes` and `string` fields. For strings, this includes the terminating zero.
* `max_length`: Maximum length for `string` fields. Setting this is equivalent to setting `max_size` to a value of length + 1.
@@ -50,7 +53,7 @@
* `int_size`: Override the integer type of a field. For example, specify `int_size = IS_8` to convert `int32` from protocol definition into `int8_t` in the structure.
These options can be defined for the .proto files before they are
-converted using the nanopb-generatory.py. There are three ways to define
+converted using the nanopb-generator.py. There are three ways to define
the options:
1. Using a separate .options file. This allows using wildcards for
@@ -122,11 +125,6 @@
If preferred, the name of the options file can be set using generator
argument `-f`.
-### Defining the options on command line
-
-The nanopb_generator.py has a simple command line option `-s OPTION:VALUE`.
-The setting applies to the whole file that is being processed.
-
### Defining the options in the .proto file
The .proto file format allows defining custom options for the fields.
@@ -159,6 +157,19 @@
}
~~~~
+### Defining the options on command line
+
+The nanopb_generator.py has a simple command line option `-s OPTION:VALUE`.
+The setting applies to the whole file that is being processed.
+
+There are also a few command line options that cannot be applied using the
+other mechanisms, as they affect the whole generation:
+
+* `--c-style`: Modify symbol names to better match C naming conventions.
+* `--no-timestamp`: Do not add timestamp to generated files.
+* `--strip-path`: Remove relative path from generated `#include` directives.
+* `--cpp-descriptors`: Generate extra convenience definitions for use from C++
+
## pb.h
### pb_byte_t
diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py
index b7cddde..0bec974 100755
--- a/generator/nanopb_generator.py
+++ b/generator/nanopb_generator.py
@@ -143,12 +143,70 @@
(FieldD.TYPE_UINT64, nanopb_pb2.IS_64): ('uint64_t','UINT64', 10, 8),
}
+class NamingStyle:
+ def enum_name(self, name):
+ return "_%s" % (name)
+
+ def struct_name(self, name):
+ return "_%s" % (name)
+
+ def type_name(self, name):
+ return "%s" % (name)
+
+ def define_name(self, name):
+ return "%s" % (name)
+
+ def var_name(self, name):
+ return "%s" % (name)
+
+ def enum_entry(self, name):
+ return "%s" % (name)
+
+ def func_name(self, name):
+ return "%s" % (name)
+
+ def bytes_type(self, struct_name, name):
+ return "%s_%s_t" % (struct_name, name)
+
+class NamingStyleC(NamingStyle):
+ def enum_name(self, name):
+ return self.underscore(name)
+
+ def struct_name(self, name):
+ return self.underscore(name)
+
+ def type_name(self, name):
+ return "%s_t" % self.underscore(name)
+
+ def define_name(self, name):
+ return self.underscore(name).upper()
+
+ def var_name(self, name):
+ return self.underscore(name)
+
+ def enum_entry(self, name):
+ return self.underscore(name).upper()
+
+ def func_name(self, name):
+ return self.underscore(name)
+
+ def bytes_type(self, struct_name, name):
+ return "%s_%s_t" % (self.underscore(struct_name), self.underscore(name))
+
+ def underscore(self, word):
+ word = str(word)
+ word = re.sub(r"([A-Z]+)([A-Z][a-z])", r'\1_\2', word)
+ word = re.sub(r"([a-z\d])([A-Z])", r'\1_\2', word)
+ word = word.replace("-", "_")
+ return word.lower()
+
class Globals:
'''Ugly global variables, should find a good way to pass these.'''
verbose_options = False
separate_options = []
matched_namemasks = set()
protoc_insertion_points = False
+ naming_style = NamingStyle()
# String types and file encoding for Python2 UTF-8 support
if sys.version_info.major == 2:
@@ -286,17 +344,17 @@
FIELD = 2
MESSAGE = 4
ENUM = 5
+ NESTED_TYPE = 3
+ NESTED_ENUM = 4
- def __init__(self, element_type, index, comments = None, parent = ()):
+ def __init__(self, path, comments = None):
'''
- element_type is a predefined value for each element type in proto file.
- For example, message == 4, enum == 5, service == 6
- index is the N-th occurrence of the `path` in the proto file.
- For example, 4-th message in the proto file or 2-nd enum etc ...
+ path is a tuple containing integers (type, index, ...)
comments is a dictionary mapping between element path & SourceCodeInfo.Location
(contains information about source comments).
'''
- self.element_path = parent + (element_type, index)
+ assert(isinstance(path, tuple))
+ self.element_path = path
self.comments = comments or {}
def get_member_comments(self, index):
@@ -332,14 +390,14 @@
class Enum(ProtoElement):
- def __init__(self, names, desc, enum_options, index, comments):
+ def __init__(self, names, desc, enum_options, element_path, comments):
'''
desc is EnumDescriptorProto
index is the index of this enum element inside the file
comments is a dictionary mapping between element path & SourceCodeInfo.Location
(contains information about source comments)
'''
- super(Enum, self).__init__(ProtoElement.ENUM, index, comments)
+ super(Enum, self).__init__(element_path, comments)
self.options = enum_options
self.names = names
@@ -371,7 +429,11 @@
if leading_comment:
result = '%s\n' % leading_comment
- result += 'typedef enum _%s { %s\n' % (self.names, trailing_comment)
+ result += 'typedef enum %s {' % Globals.naming_style.enum_name(self.names)
+ if trailing_comment:
+ result += " " + trailing_comment
+
+ result += "\n"
enum_length = len(self.values)
enum_values = []
@@ -386,7 +448,11 @@
# last enum member should not end with a comma
comma = ""
- enum_values.append(" %s = %d%s %s" % (name, value, comma, trailing_comment))
+ enum_value = " %s = %d%s" % (Globals.naming_style.enum_entry(name), value, comma)
+ if trailing_comment:
+ enum_value += " " + trailing_comment
+
+ enum_values.append(enum_value)
result += '\n'.join(enum_values)
result += '\n}'
@@ -394,15 +460,22 @@
if self.packed:
result += ' pb_packed'
- result += ' %s;' % self.names
+ result += ' %s;' % Globals.naming_style.type_name(self.names)
return result
def auxiliary_defines(self):
# sort the enum by value
sorted_values = sorted(self.values, key = lambda x: (x[1], x[0]))
- result = '#define _%s_MIN %s\n' % (self.names, sorted_values[0][0])
- result += '#define _%s_MAX %s\n' % (self.names, sorted_values[-1][0])
- result += '#define _%s_ARRAYSIZE ((%s)(%s+1))\n' % (self.names, self.names, sorted_values[-1][0])
+ result = '#define %s %s\n' % (
+ Globals.naming_style.define_name('_%s_MIN' % self.names),
+ Globals.naming_style.enum_entry(sorted_values[0][0]))
+ result += '#define %s %s\n' % (
+ Globals.naming_style.define_name('_%s_MAX' % self.names),
+ Globals.naming_style.enum_entry(sorted_values[-1][0]))
+ result += '#define %s ((%s)(%s+1))\n' % (
+ Globals.naming_style.define_name('_%s_ARRAYSIZE' % self.names),
+ Globals.naming_style.type_name(self.names),
+ Globals.naming_style.enum_entry(sorted_values[-1][0]))
if not self.options.long_names:
# Define the long names always so that enum value references
@@ -411,7 +484,9 @@
result += '#define %s %s\n' % (self.value_longnames[i], x[0])
if self.options.enum_to_string:
- result += 'const char *%s_name(%s v);\n' % (self.names, self.names)
+ result += 'const char *%s(%s v);\n' % (
+ Globals.naming_style.func_name('%s_name' % self.names),
+ Globals.naming_style.type_name(self.names))
return result
@@ -419,13 +494,18 @@
if not self.options.enum_to_string:
return ""
- result = 'const char *%s_name(%s v) {\n' % (self.names, self.names)
+ result = 'const char *%s(%s v) {\n' % (
+ Globals.naming_style.func_name('%s_name' % self.names),
+ Globals.naming_style.type_name(self.names))
+
result += ' switch (v) {\n'
for ((enumname, _), strname) in zip(self.values, self.value_longnames):
# Strip off the leading type name from the string value.
strval = str(strname)[len(str(self.names)) + 1:]
- result += ' case %s: return "%s";\n' % (enumname, strval)
+ result += ' case %s: return "%s";\n' % (
+ Globals.naming_style.enum_entry(enumname),
+ Globals.naming_style.enum_entry(strval))
result += ' }\n'
result += ' return "unknown";\n'
@@ -455,9 +535,9 @@
macro_x_param = 'X'
macro_a_param = 'a'
- def __init__(self, struct_name, desc, field_options, parent_path = (), index = None, comments = None):
+ def __init__(self, struct_name, desc, field_options, element_path = (), comments = None):
'''desc is FieldDescriptorProto'''
- ProtoElement.__init__(self, ProtoElement.FIELD, index, comments, parent_path)
+ ProtoElement.__init__(self, element_path, comments)
self.tag = desc.number
self.struct_name = struct_name
self.union_name = None
@@ -470,7 +550,6 @@
self.data_item_size = None
self.ctype = None
self.fixed_count = False
- self.index = index
self.callback_datatype = field_options.callback_datatype
self.math_include_required = False
self.sort_by_tag = field_options.sort_by_tag
@@ -600,7 +679,7 @@
self.pbtype = 'BYTES'
self.ctype = 'pb_bytes_array_t'
if self.allocation == 'STATIC':
- self.ctype = self.struct_name + self.name + 't'
+ self.ctype = Globals.naming_style.bytes_type(self.struct_name, self.name)
self.enc_size = varint_max_size(self.max_size) + self.max_size
elif desc.type == FieldD.TYPE_MESSAGE:
self.pbtype = 'MESSAGE'
@@ -620,34 +699,39 @@
def __str__(self):
result = ''
+
+ var_name = Globals.naming_style.var_name(self.name)
+ type_name = Globals.naming_style.type_name(self.ctype) if isinstance(self.ctype, Names) else self.ctype
+
if self.allocation == 'POINTER':
if self.rules == 'REPEATED':
if self.pbtype == 'MSG_W_CB':
- result += ' pb_callback_t cb_' + self.name + ';\n'
- result += ' pb_size_t ' + self.name + '_count;\n'
+ result += ' pb_callback_t cb_' + var_name + ';\n'
+ result += ' pb_size_t ' + var_name + '_count;\n'
if self.pbtype in ['MESSAGE', 'MSG_W_CB']:
# Use struct definition, so recursive submessages are possible
- result += ' struct _%s *%s;' % (self.ctype, self.name)
+ result += ' struct %s *%s;' % (Globals.naming_style.struct_name(self.ctype), var_name)
elif self.pbtype == 'FIXED_LENGTH_BYTES' or self.rules == 'FIXARRAY':
# Pointer to fixed size array
- result += ' %s (*%s)%s;' % (self.ctype, self.name, self.array_decl)
+ result += ' %s (*%s)%s;' % (type_name, var_name, self.array_decl)
elif self.rules in ['REPEATED', 'FIXARRAY'] and self.pbtype in ['STRING', 'BYTES']:
# String/bytes arrays need to be defined as pointers to pointers
- result += ' %s **%s;' % (self.ctype, self.name)
+ result += ' %s **%s;' % (type_name, var_name)
else:
- result += ' %s *%s;' % (self.ctype, self.name)
+ result += ' %s *%s;' % (type_name, var_name)
elif self.allocation == 'CALLBACK':
- result += ' %s %s;' % (self.callback_datatype, self.name)
+ result += ' %s %s;' % (self.callback_datatype, var_name)
else:
if self.pbtype == 'MSG_W_CB' and self.rules in ['OPTIONAL', 'REPEATED']:
- result += ' pb_callback_t cb_' + self.name + ';\n'
+ result += ' pb_callback_t cb_' + var_name + ';\n'
if self.rules == 'OPTIONAL':
- result += ' bool has_' + self.name + ';\n'
+ result += ' bool has_' + var_name + ';\n'
elif self.rules == 'REPEATED':
- result += ' pb_size_t ' + self.name + '_count;\n'
- result += ' %s %s%s;' % (self.ctype, self.name, self.array_decl)
+ result += ' pb_size_t ' + var_name + '_count;\n'
+
+ result += ' %s %s%s;' % (type_name, var_name, self.array_decl)
leading_comment, trailing_comment = self.get_comments(leading_indent = True)
if leading_comment: result = leading_comment + "\n" + result
@@ -658,7 +742,7 @@
def types(self):
'''Return definitions for any special types this field might need.'''
if self.pbtype == 'BYTES' and self.allocation == 'STATIC':
- result = 'typedef PB_BYTES_ARRAY_T(%d) %s;\n' % (self.max_size, self.ctype)
+ result = 'typedef PB_BYTES_ARRAY_T(%d) %s;\n' % (self.max_size, Globals.naming_style.var_name(self.ctype))
else:
result = ''
return result
@@ -679,9 +763,9 @@
inner_init = None
if self.pbtype in ['MESSAGE', 'MSG_W_CB']:
if null_init:
- inner_init = '%s_init_zero' % self.ctype
+ inner_init = Globals.naming_style.define_name('%s_init_zero' % self.ctype)
else:
- inner_init = '%s_init_default' % self.ctype
+ inner_init = Globals.naming_style.define_name('%s_init_default' % self.ctype)
elif self.default is None or null_init:
if self.pbtype == 'STRING':
inner_init = '""'
@@ -690,7 +774,7 @@
elif self.pbtype == 'FIXED_LENGTH_BYTES':
inner_init = '{0}'
elif self.pbtype in ('ENUM', 'UENUM'):
- inner_init = '_%s_MIN' % self.ctype
+ inner_init = '_%s_MIN' % Globals.naming_style.define_name(self.ctype)
else:
inner_init = '0'
else:
@@ -764,22 +848,29 @@
def tags(self):
'''Return the #define for the tag number of this field.'''
- identifier = '%s_%s_tag' % (self.struct_name, self.name)
+ identifier = Globals.naming_style.define_name('%s_%s_tag' % (self.struct_name, self.name))
return '#define %-40s %d\n' % (identifier, self.tag)
def fieldlist(self):
'''Return the FIELDLIST macro entry for this field.
Format is: X(a, ATYPE, HTYPE, LTYPE, field_name, tag)
'''
- name = self.name
+ name = Globals.naming_style.var_name(self.name)
if self.rules == "ONEOF":
# For oneofs, make a tuple of the union name, union member name,
# and the name inside the parent struct.
if not self.anonymous:
- name = '(%s,%s,%s)' % (self.union_name, self.name, self.union_name + '.' + self.name)
+ name = '(%s,%s,%s)' % (
+ Globals.naming_style.var_name(self.union_name),
+ Globals.naming_style.var_name(self.name),
+ Globals.naming_style.var_name(self.union_name) + '.' +
+ Globals.naming_style.var_name(self.name))
else:
- name = '(%s,%s,%s)' % (self.union_name, self.name, self.name)
+ name = '(%s,%s,%s)' % (
+ Globals.naming_style.var_name(self.union_name),
+ Globals.naming_style.var_name(self.name),
+ Globals.naming_style.var_name(self.name))
return '%s(%s, %-9s %-9s %-9s %-16s %3d)' % (self.macro_x_param,
self.macro_a_param,
@@ -968,13 +1059,13 @@
else:
self.skip = False
self.rules = 'REQUIRED' # We don't really want the has_field for extensions
- # currently no support for comments for extension fields => provide 0, {}
- self.msg = Message(self.fullname + "extmsg", None, field_options, 0, {})
+ # currently no support for comments for extension fields => provide (), {}
+ self.msg = Message(self.fullname + "extmsg", None, field_options, (), {})
self.msg.fields.append(self)
def tags(self):
'''Return the #define for the tag number of this field.'''
- identifier = '%s_tag' % self.fullname
+ identifier = Globals.naming_style.define_name('%s_tag' % (self.fullname))
return '#define %-40s %d\n' % (identifier, self.tag)
def extension_decl(self):
@@ -985,7 +1076,7 @@
return msg
return ('extern const pb_extension_type_t %s; /* field type: %s */\n' %
- (self.fullname, str(self).strip()))
+ (Globals.naming_style.var_name(self.fullname), str(self).strip()))
def extension_def(self, dependencies):
'''Definition of the extension type in the .pb.c file'''
@@ -998,10 +1089,10 @@
result += self.msg.fields_declaration(dependencies)
result += 'pb_byte_t %s_default[] = {0x00};\n' % self.msg.name
result += self.msg.fields_definition(dependencies)
- result += 'const pb_extension_type_t %s = {\n' % self.fullname
+ result += 'const pb_extension_type_t %s = {\n' % Globals.naming_style.var_name(self.fullname)
result += ' NULL,\n'
result += ' NULL,\n'
- result += ' &%s_msg\n' % self.msg.name
+ result += ' &%s_msg\n' % Globals.naming_style.type_name(self.msg.name)
result += '};\n'
return result
@@ -1020,7 +1111,6 @@
self.allocation = 'ONEOF'
self.default = None
self.rules = 'ONEOF'
- self.index = None
self.anonymous = oneof_options.anonymous_oneof
self.sort_by_tag = oneof_options.sort_by_tag
self.has_msg_cb = False
@@ -1044,16 +1134,16 @@
result = ''
if self.fields:
if self.has_msg_cb:
- result += ' pb_callback_t cb_' + self.name + ';\n'
+ result += ' pb_callback_t cb_' + Globals.naming_style.var_name(self.name) + ';\n'
- result += ' pb_size_t which_' + self.name + ";\n"
+ result += ' pb_size_t which_' + Globals.naming_style.var_name(self.name) + ";\n"
result += ' union {\n'
for f in self.fields:
result += ' ' + str(f).replace('\n', '\n ') + '\n'
if self.anonymous:
result += ' };'
else:
- result += ' } ' + self.name + ';'
+ result += ' } ' + Globals.naming_style.var_name(self.name) + ';'
return result
def types(self):
@@ -1123,8 +1213,8 @@
class Message(ProtoElement):
- def __init__(self, names, desc, message_options, index, comments):
- super(Message, self).__init__(ProtoElement.MESSAGE, index, comments)
+ def __init__(self, names, desc, message_options, element_path, comments):
+ super(Message, self).__init__(element_path, comments)
self.name = names
self.fields = []
self.oneofs = {}
@@ -1174,7 +1264,7 @@
if field_options.descriptorsize > self.descriptorsize:
self.descriptorsize = field_options.descriptorsize
- field = Field(self.name, f, field_options, self.element_path, index, self.comments)
+ field = Field(self.name, f, field_options, self.element_path + (ProtoElement.FIELD, index), self.comments)
if hasattr(f, 'oneof_index') and f.HasField('oneof_index'):
if hasattr(f, 'proto3_optional') and f.proto3_optional:
no_unions.append(f.oneof_index)
@@ -1215,7 +1305,11 @@
if leading_comment:
result = '%s\n' % leading_comment
- result += 'typedef struct _%s { %s\n' % (self.name, trailing_comment)
+ result += 'typedef struct %s {' % Globals.naming_style.struct_name(self.name)
+ if trailing_comment:
+ result += " " + trailing_comment
+
+ result += '\n'
if not self.fields:
# Empty structs are not allowed in C standard.
@@ -1232,7 +1326,7 @@
if self.packed:
result += ' pb_packed'
- result += ' %s;' % self.name
+ result += ' %s;' % Globals.naming_style.type_name(self.name)
if self.packed:
result = 'PB_PACKED_STRUCT_START\n' + result
@@ -1301,9 +1395,10 @@
sorted_fields = list(self.all_fields())
sorted_fields.sort(key = lambda x: x.tag)
- result = '#define %s_FIELDLIST(%s, %s) \\\n' % (self.name,
- Field.macro_x_param,
- Field.macro_a_param)
+ result = '#define %s_FIELDLIST(%s, %s) \\\n' % (
+ Globals.naming_style.define_name(self.name),
+ Field.macro_x_param,
+ Field.macro_a_param)
result += ' \\\n'.join(x.fieldlist() for x in sorted_fields)
result += '\n'
@@ -1311,23 +1406,36 @@
if has_callbacks:
if self.callback_function != 'pb_default_field_callback':
result += "extern bool %s(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field);\n" % self.callback_function
- result += "#define %s_CALLBACK %s\n" % (self.name, self.callback_function)
+ result += "#define %s_CALLBACK %s\n" % (
+ Globals.naming_style.define_name(self.name),
+ self.callback_function)
else:
- result += "#define %s_CALLBACK NULL\n" % self.name
+ result += "#define %s_CALLBACK NULL\n" % Globals.naming_style.define_name(self.name)
defval = self.default_value(dependencies)
if defval:
hexcoded = ''.join("\\x%02x" % ord(defval[i:i+1]) for i in range(len(defval)))
- result += '#define %s_DEFAULT (const pb_byte_t*)"%s\\x00"\n' % (self.name, hexcoded)
+ result += '#define %s_DEFAULT (const pb_byte_t*)"%s\\x00"\n' % (
+ Globals.naming_style.define_name(self.name),
+ hexcoded)
else:
- result += '#define %s_DEFAULT NULL\n' % self.name
+ result += '#define %s_DEFAULT NULL\n' % Globals.naming_style.define_name(self.name)
for field in sorted_fields:
if field.pbtype in ['MESSAGE', 'MSG_W_CB']:
if field.rules == 'ONEOF':
- result += "#define %s_%s_%s_MSGTYPE %s\n" % (self.name, field.union_name, field.name, field.ctype)
+ result += "#define %s_%s_%s_MSGTYPE %s\n" % (
+ Globals.naming_style.type_name(self.name),
+ Globals.naming_style.var_name(field.union_name),
+ Globals.naming_style.var_name(field.name),
+ Globals.naming_style.type_name(field.ctype)
+ )
else:
- result += "#define %s_%s_MSGTYPE %s\n" % (self.name, field.name, field.ctype)
+ result += "#define %s_%s_MSGTYPE %s\n" % (
+ Globals.naming_style.type_name(self.name),
+ Globals.naming_style.var_name(field.name),
+ Globals.naming_style.type_name(field.ctype)
+ )
return result
@@ -1347,7 +1455,10 @@
if width == 1:
width = 'AUTO'
- result = 'PB_BIND(%s, %s, %s)\n' % (self.name, self.name, width)
+ result = 'PB_BIND(%s, %s, %s)\n' % (
+ Globals.naming_style.define_name(self.name),
+ Globals.naming_style.type_name(self.name),
+ width)
return result
def required_descriptor_width(self, dependencies):
@@ -1476,21 +1587,24 @@
# Processing of entire .proto files
# ---------------------------------------------------------------------------
-def iterate_messages(desc, flatten = False, names = Names()):
- '''Recursively find all messages. For each, yield name, DescriptorProto.'''
+def iterate_messages(desc, flatten = False, names = Names(), comment_path = ()):
+ '''Recursively find all messages. For each, yield name, DescriptorProto, comment_path.'''
if hasattr(desc, 'message_type'):
submsgs = desc.message_type
+ comment_path += (ProtoElement.MESSAGE,)
else:
submsgs = desc.nested_type
+ comment_path += (ProtoElement.NESTED_TYPE,)
- for submsg in submsgs:
+ for idx, submsg in enumerate(submsgs):
sub_names = names + submsg.name
+ sub_path = comment_path + (idx,)
if flatten:
- yield Names(submsg.name), submsg
+ yield Names(submsg.name), submsg, sub_path
else:
- yield sub_names, submsg
+ yield sub_names, submsg, sub_path
- for x in iterate_messages(submsg, flatten, sub_names):
+ for x in iterate_messages(submsg, flatten, sub_names, sub_path):
yield x
def iterate_extensions(desc, flatten = False, names = Names()):
@@ -1500,7 +1614,7 @@
for extension in desc.extension:
yield names, extension
- for subname, subdesc in iterate_messages(desc, flatten, names):
+ for subname, subdesc, comment_path in iterate_messages(desc, flatten, names):
for extension in subdesc.extension:
yield subname, extension
@@ -1654,9 +1768,10 @@
for index, enum in enumerate(self.fdesc.enum_type):
name = self.manglenames.create_name(enum.name)
enum_options = get_nanopb_suboptions(enum, self.file_options, name)
- self.enums.append(Enum(name, enum, enum_options, index, self.comment_locations))
+ enum_path = (ProtoElement.ENUM, index)
+ self.enums.append(Enum(name, enum, enum_options, enum_path, self.comment_locations))
- for index, (names, message) in enumerate(iterate_messages(self.fdesc, self.manglenames.flatten)):
+ for names, message, comment_path in iterate_messages(self.fdesc, self.manglenames.flatten):
name = self.manglenames.create_name(names)
message_options = get_nanopb_suboptions(message, self.file_options, name)
@@ -1668,11 +1783,12 @@
if field.type in (FieldD.TYPE_MESSAGE, FieldD.TYPE_ENUM):
field.type_name = self.manglenames.mangle_field_typename(field.type_name)
- self.messages.append(Message(name, message, message_options, index, self.comment_locations))
+ self.messages.append(Message(name, message, message_options, comment_path, self.comment_locations))
for index, enum in enumerate(message.enum_type):
name = self.manglenames.create_name(names + enum.name)
enum_options = get_nanopb_suboptions(enum, message_options, name)
- self.enums.append(Enum(name, enum, enum_options, index, self.comment_locations))
+ enum_path = comment_path + (ProtoElement.NESTED_ENUM, index)
+ self.enums.append(Enum(name, enum, enum_options, enum_path, self.comment_locations))
for names, extension in iterate_extensions(self.fdesc, self.manglenames.flatten):
name = self.manglenames.create_name(names + extension.name)
@@ -1791,10 +1907,10 @@
if self.messages:
yield '/* Initializer values for message structs */\n'
for msg in self.messages:
- identifier = '%s_init_default' % msg.name
+ identifier = Globals.naming_style.define_name('%s_init_default' % msg.name)
yield '#define %-40s %s\n' % (identifier, msg.get_initializer(False))
for msg in self.messages:
- identifier = '%s_init_zero' % msg.name
+ identifier = Globals.naming_style.define_name('%s_init_zero' % msg.name)
yield '#define %-40s %s\n' % (identifier, msg.get_initializer(True))
yield '\n'
@@ -1810,12 +1926,14 @@
for msg in self.messages:
yield msg.fields_declaration(self.dependencies) + '\n'
for msg in self.messages:
- yield 'extern const pb_msgdesc_t %s_msg;\n' % msg.name
+ yield 'extern const pb_msgdesc_t %s_msg;\n' % Globals.naming_style.type_name(msg.name)
yield '\n'
yield '/* Defines for backwards compatibility with code written before nanopb-0.4.0 */\n'
for msg in self.messages:
- yield '#define %s_fields &%s_msg\n' % (msg.name, msg.name)
+ yield '#define %s &%s_msg\n' % (
+ Globals.naming_style.define_name('%s_fields' % msg.name),
+ Globals.naming_style.type_name(msg.name))
yield '\n'
yield '/* Maximum encoded size of messages (where known) */\n'
@@ -1850,7 +1968,8 @@
cpp_guard = msize.get_cpp_guard(local_defines)
if cpp_guard not in guards:
guards[cpp_guard] = set()
- guards[cpp_guard].add('#define %-40s %s' % (identifier, msize))
+ guards[cpp_guard].add('#define %-40s %s' % (
+ Globals.naming_style.define_name(identifier), msize))
else:
yield '/* %s depends on runtime parameters */\n' % identifier
for guard, values in guards.items():
@@ -2106,7 +2225,9 @@
optparser.add_option("-s", dest="settings", metavar="OPTION:VALUE", action="append", default=[],
help="Set generator option (max_size, max_count etc.).")
optparser.add_option("--protoc-insertion-points", dest="protoc_insertion_points", action="store_true", default=False,
- help="Include insertion point comments in output for use by custom protoc plugins")
+ help="Include insertion point comments in output for use by custom protoc plugins")
+optparser.add_option("-C", "--c-style", dest="c_style", action="store_true", default=False,
+ help="Use C naming convention.")
def parse_file(filename, fdesc, options):
'''Parse a single file. Returns a ProtoFile instance.'''
@@ -2238,6 +2359,9 @@
sys.stderr.write('Google Python protobuf library imported from %s, version %s\n'
% (google.protobuf.__file__, google.protobuf.__version__))
+ if options.c_style:
+ Globals.naming_style = NamingStyleC()
+
# Load .pb files into memory and compile any .proto files.
include_path = ['-I%s' % p for p in options.options_path]
all_fdescs = {}
@@ -2339,6 +2463,9 @@
Globals.verbose_options = options.verbose
+ if options.c_style:
+ Globals.naming_style = NamingStyleC()
+
if options.verbose:
sys.stderr.write("Nanopb version %s\n" % nanopb_version)
sys.stderr.write('Google Python protobuf library imported from %s, version %s\n'
diff --git a/tests/comments/comments.expected b/tests/comments/comments.expected
index 0d96dc2..ae22a92 100644
--- a/tests/comments/comments.expected
+++ b/tests/comments/comments.expected
@@ -8,3 +8,7 @@
m2member4.*m2comment4
m2oneof10.*m2oneof10_comment
m2oneof5.*m2oneof5_comment
+A.*A_comment
+B.*B_comment
+C.*C_comment
+subfield.*subfield_comment
diff --git a/tests/comments/comments.proto b/tests/comments/comments.proto
index 0dba90f..0f952ce 100644
--- a/tests/comments/comments.proto
+++ b/tests/comments/comments.proto
@@ -27,3 +27,17 @@
int32 m2oneof5 = 5; // m2oneof5_comment
}
}
+
+message Message3
+{
+ message SubMessage {
+ required int32 subfield = 1; // subfield_comment
+
+ enum SubEnum
+ {
+ A = 0; /// A_comment
+ B = 1; /// B_comment
+ C = 2; /// C_comment
+ }
+ }
+}
diff --git a/tests/namingstyle/SConscript b/tests/namingstyle/SConscript
new file mode 100644
index 0000000..3922340
--- /dev/null
+++ b/tests/namingstyle/SConscript
@@ -0,0 +1,11 @@
+# Test namingstyle option
+
+Import('env')
+
+env = env.Clone()
+env.Replace(NANOPBFLAGS = "-C")
+
+env.NanopbProto(["naming_style", "naming_style.options"])
+
+test = env.Program(["test_naming_style_c.c", "naming_style.pb.c", "$COMMON/pb_decode.o", "$COMMON/pb_encode.o", '$COMMON/pb_common.o'])
+env.RunTest(test)
diff --git a/tests/namingstyle/naming_style.options b/tests/namingstyle/naming_style.options
new file mode 100644
index 0000000..0026b0b
--- /dev/null
+++ b/tests/namingstyle/naming_style.options
@@ -0,0 +1,15 @@
+* long_names:true
+* enum_to_string:true
+
+MainMessage.repeatedNumber max_count:4, fixed_count:true
+MainMessage.string_Values1 type:FT_POINTER
+MainMessage.stringValues2 max_length:40, max_count:5
+MainMessage.requiredString max_length:10
+MainMessage.repeatedFixed32 max_count:10
+MainMessage.requiredBytes1 max_size:10, fixed_length:true
+MainMessage.requiredBytes2 max_size:10
+MainMessage.repeatedBytes1 type:FT_POINTER
+MainMessage.repeatedBytes2 type:FT_POINTER, fixed_count:true, max_count:5
+MainMessage.repeatedInts type:FT_POINTER
+MainMessage.SUB_MESSAGE2 type:FT_CALLBACK
+MainMessage.oneOfName2 anonymous_oneof:true
diff --git a/tests/namingstyle/naming_style.proto b/tests/namingstyle/naming_style.proto
new file mode 100644
index 0000000..7e626e8
--- /dev/null
+++ b/tests/namingstyle/naming_style.proto
@@ -0,0 +1,62 @@
+syntax = "proto2";
+
+enum MyEnum1 {
+ ENTRY_FIRST = 0;
+ ENTRY_Second = 1;
+ EnumThird = 2;
+}
+
+enum MY_ENUM2 {
+ ENUM2_ENTRY = 0;
+}
+
+message SubMessage {
+ optional int32 test_value = 1;
+}
+
+message MainMessage {
+ optional int32 LUCKY_number = 1;
+ required int32 REQUIRED_NUMBER = 2;
+ repeated int32 repeatedNumber = 3;
+ repeated int32 repeatedInts = 4;
+
+ optional MyEnum1 MyEnum1 = 5;
+ optional MY_ENUM2 My_Enum2 = 6;
+ required MY_ENUM2 MY_ENUM3 = 7;
+ repeated MY_ENUM2 MY_ENUM4 = 8;
+
+ repeated string string_Values1 = 9;
+ repeated string stringValues2 = 10;
+ optional string OPTIONAL_String = 11;
+ required string requiredString = 12;
+
+ repeated fixed32 repeatedFixed32 = 13;
+
+ required bytes requiredBytes1 = 14;
+ required bytes requiredBytes2 = 15;
+ repeated bytes repeatedBytes1 = 16;
+ repeated bytes repeatedBytes2 = 17;
+
+ optional SubMessage subMessage1 = 18;
+ repeated SubMessage SUB_MESSAGE2 = 19;
+ required SubMessage sub_message3 = 20;
+
+ oneof oneOfName {
+ SubMessage testMessage1 = 21;
+ SubMessage testMessage2 = 22;
+ }
+
+ oneof oneOfName2 {
+ SubMessage testMessage4 = 23;
+ SubMessage testMessage5 = 24;
+ }
+
+ extensions 200 to 255;
+}
+
+message TestExtension {
+ extend MainMessage {
+ optional TestExtension testExtension = 250;
+ }
+ optional string stringValue = 1;
+}
diff --git a/tests/namingstyle/test_naming_style_c.c b/tests/namingstyle/test_naming_style_c.c
new file mode 100644
index 0000000..f28bdce
--- /dev/null
+++ b/tests/namingstyle/test_naming_style_c.c
@@ -0,0 +1,77 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <pb_encode.h>
+#include <pb_decode.h>
+#include "unittests.h"
+#include "naming_style.pb.h"
+
+int main()
+{
+ int status = 0;
+ main_message_t message = MAIN_MESSAGE_INIT_ZERO;
+
+ /* Verify that all members have the expected names */
+ message.lucky_number = 13;
+ message.required_number = 1;
+ message.repeated_number[0] = 1;
+ message.repeated_ints = NULL;
+
+ message.my_enum1 = MY_ENUM1_ENUM_THIRD;
+ message.my_enum2 = MY_ENUM2_ENUM2_ENTRY;
+ message.my_enum3 = MY_ENUM2_ENUM2_ENTRY;
+ message.my_enum4.arg = NULL;
+
+ message.string_values1 = NULL;
+ message.string_values2[0][0] = 'a';
+ message.optional_string.arg = NULL;
+ message.required_string[0] = 'a';
+
+ message.repeated_fixed32[0] = 1;
+
+ message.required_bytes1[0] = 0;
+ message.required_bytes2.size = 0;
+ message.repeated_bytes1_count = 0;
+ message.repeated_bytes2 = NULL;
+
+ message.has_sub_message1 = true;
+ message.sub_message1.has_test_value = true;
+ message.sub_message1.test_value = 0;
+ message.sub_message2.arg = NULL;
+ message.sub_message3.test_value = 0;
+
+ message.which_one_of_name = MAIN_MESSAGE_TEST_MESSAGE2_TAG;
+ message.one_of_name.test_message2.has_test_value = true;
+ message.one_of_name.test_message2.test_value = 5;
+
+ message.which_one_of_name2 = MAIN_MESSAGE_TEST_MESSAGE5_TAG;
+ message.test_message5.test_value = 5;
+
+ TEST(strcmp("ENTRY_FIRST", my_enum1_name(MY_ENUM1_ENTRY_FIRST)) == 0);
+
+ /* Verify that the descriptor structure is at least mostly correct
+ * by doing a round-trip encoding test.
+ */
+ {
+ uint8_t buffer1[256];
+ uint8_t buffer2[256];
+ pb_ostream_t ostream1 = pb_ostream_from_buffer(buffer1, sizeof(buffer1));
+ pb_ostream_t ostream2 = pb_ostream_from_buffer(buffer2, sizeof(buffer2));
+ pb_istream_t istream;
+ main_message_t message2 = MAIN_MESSAGE_INIT_ZERO;
+
+ TEST(pb_encode(&ostream1, &main_message_t_msg, &message));
+
+ istream = pb_istream_from_buffer(buffer1, ostream1.bytes_written);
+ TEST(pb_decode(&istream, &main_message_t_msg, &message2));
+
+ /* Encoding a second time should produce same output */
+ TEST(pb_encode(&ostream2, &main_message_t_msg, &message2));
+
+ TEST(ostream2.bytes_written == ostream1.bytes_written);
+ TEST(memcmp(buffer1, buffer2, ostream1.bytes_written) == 0);
+ }
+
+ return status;
+}
diff --git a/tests/site_scons/site_tools/nanopb.py b/tests/site_scons/site_tools/nanopb.py
index 37b8e20..4abf02a 100644
--- a/tests/site_scons/site_tools/nanopb.py
+++ b/tests/site_scons/site_tools/nanopb.py
@@ -34,6 +34,7 @@
from SCons.Script import Dir, File
import os.path
import platform
+import sys
try:
warningbase = SCons.Warnings.SConsWarning