Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nanopb/nanopb.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPetteri Aimonen <jpa@git.mail.kapsi.fi>2023-10-19 12:14:45 +0300
committerPetteri Aimonen <jpa@git.mail.kapsi.fi>2023-10-19 12:14:45 +0300
commit499c696de469f2f5aa3f209af8fdc42f96525e0b (patch)
tree2e7b7ba86631552e8a65c4e0b2caeb6756089285
parent31eb134c3fe886d9cc721e40594899eac78e8d2b (diff)
Automatically break circular dependencies (#881)
It is better to define FT_CALLBACK manually at the point where it makes sense, but the automatic logic avoids build failures if you don't care about the details.
-rwxr-xr-xgenerator/nanopb_generator.py29
-rw-r--r--tests/recursive_proto/SConscript6
-rw-r--r--tests/recursive_proto/recursive.proto12
3 files changed, 46 insertions, 1 deletions
diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py
index 6ea8e84..7b3d7ee 100755
--- a/generator/nanopb_generator.py
+++ b/generator/nanopb_generator.py
@@ -1697,6 +1697,24 @@ def iterate_extensions(desc, flatten = False, names = Names()):
for extension in subdesc.extension:
yield subname, extension
+def check_recursive_dependencies(message, all_messages, root = None):
+ '''Returns True if message has a recursive dependency on root (or itself if root is None).'''
+
+ if not isinstance(all_messages, dict):
+ all_messages = dict((str(m.name), m) for m in all_messages)
+
+ if not root:
+ root = message
+
+ for dep in message.get_dependencies():
+ if dep == str(root.name):
+ return True
+ elif dep in all_messages:
+ if check_recursive_dependencies(all_messages[dep], all_messages, root):
+ return True
+
+ return False
+
def sort_dependencies(messages):
'''Sort a list of Messages based on dependencies.'''
@@ -1861,12 +1879,21 @@ class ProtoFile:
if message_options.skip_message:
continue
+ # Apply any configured typename mangling options
message = copy.deepcopy(message)
for field in message.field:
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, comment_path, self.comment_locations))
+ # Check for circular dependencies
+ msgobject = Message(name, message, message_options, comment_path, self.comment_locations)
+ if check_recursive_dependencies(msgobject, self.messages):
+ sys.stderr.write('Breaking circular dependency at message %s by converting to callback\n' % msgobject.name)
+ message_options.type = nanopb_pb2.FT_CALLBACK
+ msgobject = Message(name, message, message_options, comment_path, self.comment_locations)
+ self.messages.append(msgobject)
+
+ # Process any nested enums
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)
diff --git a/tests/recursive_proto/SConscript b/tests/recursive_proto/SConscript
new file mode 100644
index 0000000..8205851
--- /dev/null
+++ b/tests/recursive_proto/SConscript
@@ -0,0 +1,6 @@
+# Test building of a recursive protobuf definition
+
+Import('env')
+
+env.NanopbProto("recursive.proto")
+env.Object("recursive.pb.c")
diff --git a/tests/recursive_proto/recursive.proto b/tests/recursive_proto/recursive.proto
new file mode 100644
index 0000000..b4eee22
--- /dev/null
+++ b/tests/recursive_proto/recursive.proto
@@ -0,0 +1,12 @@
+message SingleRecursion {
+ optional SingleRecursion msg = 1;
+}
+
+message Recurse1 {
+ optional Recurse2 msg = 1;
+}
+
+message Recurse2 {
+ optional Recurse1 msg = 1;
+ optional SingleRecursion msg2 = 2;
+}