diff --git a/ConfigGenerator/config_compare.py b/ConfigGenerator/config_compare.py index 586227b..0624b5f 100644 --- a/ConfigGenerator/config_compare.py +++ b/ConfigGenerator/config_compare.py @@ -78,5 +78,5 @@ def ordered(obj): json2 = ordered(json.load(json_file_2)) if args.ignoreorder else json.load(json_file_2) if json1 != json2: - log.Error(f"JSON Input files are not identical") + log.Warn(f"JSON Input files are not identical") sys.exit(1) diff --git a/ConfigGenerator/params.config b/ConfigGenerator/params.config index 7b38901..52cd0fc 100644 --- a/ConfigGenerator/params.config +++ b/ConfigGenerator/params.config @@ -1,7 +1,13 @@ autostart +callsign +communicator +configuration +persistentpathpostfix precondition resumed +startmode +startuporder +systemrootpath terminations -callsign -persistentpathpostfix -configuration +volatilepathpostfix +webui \ No newline at end of file diff --git a/JsonGenerator/JsonGenerator.py b/JsonGenerator/JsonGenerator.py index 15e5e83..91cdcd4 100755 --- a/JsonGenerator/JsonGenerator.py +++ b/JsonGenerator/JsonGenerator.py @@ -60,6 +60,10 @@ files.append(p) for path in files: + + trackers.object_tracker.Reset() + trackers.enum_tracker.Reset() + try: log.Header(path) diff --git a/JsonGenerator/source/class_emitter.py b/JsonGenerator/source/class_emitter.py index a567719..5686870 100644 --- a/JsonGenerator/source/class_emitter.py +++ b/JsonGenerator/source/class_emitter.py @@ -51,9 +51,10 @@ def _EmitEnumRegistration(root, enum): count = 0 for obj in trackers.enum_tracker.objects: - if not obj.is_duplicate and not obj.included_from: + if not obj.is_duplicate and not obj.included_from and ("@register" not in obj.schema or obj.schema["@register"]): emit.Line() _EmitEnumRegistration(root, obj) + obj.schema["@register"] = False count += 1 emit.Line() diff --git a/JsonGenerator/source/code_generator.py b/JsonGenerator/source/code_generator.py index 9ec2409..e61f13a 100644 --- a/JsonGenerator/source/code_generator.py +++ b/JsonGenerator/source/code_generator.py @@ -43,9 +43,6 @@ def _ParseJsonRpcSchema(schema): else: return None - trackers.object_tracker.Reset() - trackers.enum_tracker.Reset() - directory = os.path.dirname(path) filename = (schema["info"]["namespace"]) if "info" in schema and "namespace" in schema["info"] else "" filename += (schema["info"]["class"]) if "info" in schema and "class" in schema["info"] else "" diff --git a/JsonGenerator/source/documentation_generator.py b/JsonGenerator/source/documentation_generator.py index 134b9b0..57dd262 100644 --- a/JsonGenerator/source/documentation_generator.py +++ b/JsonGenerator/source/documentation_generator.py @@ -31,7 +31,7 @@ def Create(log, schema, path, indent_size = 4): input_basename = os.path.basename(path) output_path = os.path.dirname(path) + os.sep + input_basename.replace(".json", "") + ".md" - with Emitter(output_path, config.INDENT_SIZE) as emit: + with Emitter(output_path, config.INDENT_SIZE, 10000) as emit: def bold(string): return "**%s**" % string diff --git a/JsonGenerator/source/emitter.py b/JsonGenerator/source/emitter.py index bfe0290..cafce1c 100644 --- a/JsonGenerator/source/emitter.py +++ b/JsonGenerator/source/emitter.py @@ -16,11 +16,11 @@ # limitations under the License. class Emitter(): - def __init__(self, file_name, indent_size): + def __init__(self, file_name, indent_size, max_line_length = 160): self.file = open(file_name, "w") if file_name else None self.indent_size = indent_size self.indent = 0 - self.threshold = 160 + self.threshold = max_line_length self.lines = [] def __del__(self): diff --git a/JsonGenerator/source/header_loader.py b/JsonGenerator/source/header_loader.py index 8e44bfd..f1166e3 100644 --- a/JsonGenerator/source/header_loader.py +++ b/JsonGenerator/source/header_loader.py @@ -41,7 +41,15 @@ def __init__(self, obj, msg): else: super(CppParseError, self).__init__(msg) + def LoadInterface(file, log, all = False, includePaths = []): + + def StripFrameworkNamespace(identifier): + return str(identifier).replace("::" + config.FRAMEWORK_NAMESPACE + "::", "") + + def StripInterfaceNamespace(identifier): + return str(identifier).replace(config.INTERFACE_NAMESPACE + "::", "") + try: tree = CppParser.ParseFiles([os.path.join(os.path.dirname(os.path.realpath(__file__)), posixpath.normpath(config.DEFAULT_DEFINITIONS_FILE)), file], includePaths, log) @@ -88,15 +96,12 @@ def _EvaluateRpcFormat(obj): info["format"] = rpc_format.value if not face.obj.parent.full_name.endswith(config.INTERFACE_NAMESPACE): - info["namespace"] = face.obj.parent.name + info["namespace"] = face.obj.parent.name[1:] if (face.obj.parent.name[0] == "I" and face.obj.parent.name[1].isupper()) else face.obj.parent.name info["class"] = face.obj.name[1:] if face.obj.name[0] == "I" else face.obj.name scoped_face = face.obj.full_name.split("::")[1:] - if scoped_face[0] == config.FRAMEWORK_NAMESPACE: - scoped_face = scoped_face[1:] - - info["interface"] = "::".join(scoped_face) + info["interface"] = StripInterfaceNamespace("::" + "::".join(scoped_face)) info["sourcefile"] = os.path.basename(file) if face.obj.sourcelocation: @@ -119,12 +124,6 @@ def _EvaluateRpcFormat(obj): def ResolveTypedef(type): return type.Resolve() - def StripFrameworkNamespace(identifier): - return str(identifier).replace("::" + config.FRAMEWORK_NAMESPACE + "::", "") - - def StripInterfaceNamespace(identifier): - return str(identifier).replace(config.INTERFACE_NAMESPACE + "::", "").replace("::" + config.FRAMEWORK_NAMESPACE + "::", "") - def ConvertType(var): var_type = ResolveTypedef(var.type) cppType = var_type.Type() @@ -195,13 +194,11 @@ def ConvertType(var): elif isinstance(cppType, CppParser.Enum): if len(cppType.items) > 1: - enum_values = [e.auto_value for e in cppType.items] - - for i, e in enumerate(cppType.items, 0): - if enum_values[i - 1] != enum_values[i]: - raise CppParseError(var, "enumerator values in an enum must all be explicit or all be implied") + autos = [e.auto_value for e in cppType.items].count(True) + if autos != 0 and (autos != len(cppType.items)): + raise CppParseError(var, "enumerator values in an enum must all be explicit or all be implied") - enum_spec = { "enum": [e.meta.text if e.meta.text else e.name.replace("_"," ").title().replace(" ","") for e in cppType.items], "scoped": var.type.Type().scoped } + enum_spec = { "enum": [e.meta.text if e.meta.text else e.name.replace("_"," ").title().replace(" ","") for e in cppType.items], "scoped": var_type.Type().scoped } enum_spec["ids"] = [e.name for e in cppType.items] enum_spec["hint"] = var.type.Type().name @@ -215,26 +212,36 @@ def ConvertType(var): result = ["array", { "items": enum_spec } ] else: result = ["string", enum_spec] + + + if isinstance(var.type.Type(), CppParser.Typedef): + result[1]["@register"] = False + # POD objects elif isinstance(cppType, CppParser.Class): - def GenerateObject(ctype): + def GenerateObject(ctype, was_typdef): properties = dict() for p in ctype.vars: name = p.name.lower() if isinstance(ResolveTypedef(p.type).Type(), CppParser.Class): - _, props = GenerateObject(ResolveTypedef(p.type).Type()) + _, props = GenerateObject(ResolveTypedef(p.type).Type(), isinstance(p.type.Type(), CppParser.Typedef)) properties[name] = props properties[name]["type"] = "object" properties[name]["original_type"] = StripFrameworkNamespace(p.type.Type().full_name) else: properties[name] = ConvertParameter(p) + properties[name]["@originalname"] = p.name + if was_typdef: + properties[name]["@register"] = False + return "object", { "properties": properties, "required": list(properties.keys()) } - result = GenerateObject(cppType) + result = GenerateObject(cppType, isinstance(var.type.Type(), CppParser.Typedef)) + # All other types are not supported else: raise CppParseError(var, "unable to convert C++ type to JSON type: %s" % cppType.type) @@ -405,14 +412,26 @@ def BuildResult(vars, is_property=False): if method.is_excluded: if event_params: - log.WarnLine(method, "'%s()': @json:omit is redundant for notification registration methods" % method.name) + log.WarnLine(method, "'%s': @json:omit is redundant for notification registration methods" % method.name) continue - prefix = (face.obj.parent.name.lower() + "_") if face.obj.parent.full_name != config.INTERFACE_NAMESPACE else "" + if face.obj.json_prefix: + prefix = (face.obj.json_prefix + "::") + elif face.obj.parent.full_name != config.INTERFACE_NAMESPACE: + prefix = (face.obj.parent.name.lower() + "_") + else: + prefix = "" + method_name = method.retval.meta.text if method.retval.meta.text else method.name method_name_lower = method_name.lower() + if method.retval.meta.alt == method_name_lower: + log.WarnLine(method, "%s': alternative name is same as original name" % method.name) + + if method.retval.meta.text == method_name_lower: + log.WarnLine(method, "%s': changed function name is same as original name" % method.name) + for e in event_params: exists = any(x.obj.type == e.type.type for x in event_interfaces) @@ -533,6 +552,11 @@ def BuildResult(vars, is_property=False): if obj["params"] == None or obj["params"]["type"] == "null": raise CppParseError(method.vars[value], "property setter method must have one input parameter") + + if method.retval.meta.alt: + properties[prefix + method.retval.meta.alt] = copy.deepcopy(obj) + properties[prefix + method.retval.meta.alt]["deprecated"] = True + else: raise CppParseError(method, "property method must have one parameter") diff --git a/JsonGenerator/source/json_loader.py b/JsonGenerator/source/json_loader.py index 906b4ad..9048a9a 100644 --- a/JsonGenerator/source/json_loader.py +++ b/JsonGenerator/source/json_loader.py @@ -110,7 +110,7 @@ def __init__(self, name, parent, schema, included=None): # Do some sanity check on the type name if parent and not isinstance(parent, JsonArray): - if not is_generated: + if not is_generated and not self.original_name: if not self.name.replace("_", "").isalnum(): raise JsonParseError("Invalid characters in identifier name: '%s'" % self.print_name) @@ -728,7 +728,7 @@ def function_name(self): return self.cpp_name def Headline(self): - return "%s%s%s" % (self.json_name, (" - " + self.summary.split(".", 1)[0]) if self.summary else "", + return "'%s'%s%s" % (self.json_name, (" - " + self.summary.split(".", 1)[0]) if self.summary else "", " (DEPRECATED)" if self.deprecated else " (OBSOLETE)" if self.obsolete else "") diff --git a/JsonGenerator/source/rpc_emitter.py b/JsonGenerator/source/rpc_emitter.py index e1ff2c2..cc9df5d 100644 --- a/JsonGenerator/source/rpc_emitter.py +++ b/JsonGenerator/source/rpc_emitter.py @@ -269,7 +269,7 @@ def _EmitRpcCode(root, emit, header_file, source_file, data_emitted): module_var = "_module_" impl_var = "_impl_" struct = "J" + root.json_name - face = "I" + root.json_name + face = root.info["interface"] if "interface" in root.info else ("I" + root.json_name) has_listeners = any((isinstance(m, JsonNotification) and m.is_status_listener) for m in root.properties) diff --git a/JsonGenerator/source/trackers.py b/JsonGenerator/source/trackers.py index 82bfe77..3647633 100644 --- a/JsonGenerator/source/trackers.py +++ b/JsonGenerator/source/trackers.py @@ -253,7 +253,7 @@ def __Compare(lhs, rhs): return obj - return None + return None def CommonObjects(self): return SortByDependency(filter(lambda obj: ((obj.RefCount() > 1) or self._IsTopmost(obj)), self.objects)) diff --git a/ProxyStubGenerator/CppParser.py b/ProxyStubGenerator/CppParser.py index 26c412f..2baca7f 100755 --- a/ProxyStubGenerator/CppParser.py +++ b/ProxyStubGenerator/CppParser.py @@ -25,6 +25,10 @@ from collections import OrderedDict from enum import IntEnum + +sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir + os.sep)) +import ProxyStubGenerator.Log as Log + class ParserError(RuntimeError): def __init__(self, msg): msg = "%s(%s): parse error: %s" % (CurrentFile(), CurrentLine(), msg) @@ -938,6 +942,7 @@ def __init__(self, parent_block, name): self.stub = False self.is_json = False self.json_version = "" + self.json_prefix = "" self.is_event = False self.is_extended = False self.is_collapsed = False @@ -1033,13 +1038,22 @@ def __init__(self, parent_block, name, ret_type, valid_specifiers=["static", "ex self.omit = False self.stub = False self.is_excluded = False - self.parent.methods.append(self) + for method in self.parent.methods: if method.name == self.name: - if method.retval.meta.is_property: - self.retval.meta.is_property = True + if self.parent.is_json: + if method.retval.meta.is_property: + if method.retval.meta.text: + self.retval.meta.text = method.retval.meta.text + if method.retval.meta.alt: + self.retval.meta.alt = method.retval.meta.alt + elif not method.omit and not method.is_excluded and not method.retval.meta.text: + raise ParserError("'%s': JSON-RPC name clash detected, resolve with @text tag" % method.name) + break + self.parent.methods.append(self) + def Proto(self): _str = "static " if self.IsStatic() else "" _str += TypeStr(self.retval.type) if self.retval.type else "" @@ -1279,8 +1293,9 @@ def Proto(self): def __str__(self): s = [] for i, _ in enumerate(self.params): - s.append(self.params[i].name + " = " + str(self.args[i])) - _str = "/* instantiated */ template class %s<%s>" % (self.baseName.full_name, ", ".join([str(p) for p in self.params])) + rhs = ("".join(self.resolvedArgs[i].type) if isinstance(self.resolvedArgs[i].type, list) else self.resolvedArgs[i].type.Proto()) + s.append(self.params[i].name + " = " + rhs) + _str = "class %s<%s>" % (self.baseName.full_name, ", ".join([str(p) for p in self.params])) _str += " [with %s]" % (", ".join(s)) return _str @@ -1337,6 +1352,7 @@ def _Substitute(identifier): instance.specifiers = self.specifiers instance.is_json = self.is_json instance.json_version = self.json_version + instance.json_prefix = self.json_prefix instance.is_extended = self.is_extended instance.is_collapsed = self.is_collapsed instance.is_compliant = self.is_compliant @@ -1560,6 +1576,8 @@ def _find(word, string): tagtokens.append(__ParseParameterValue(token, "@json", False)) if _find("@event", token): tagtokens.append("@EVENT") + if _find("@prefix", token): + tagtokens.append(__ParseParameterValue(token, "@prefix", False)) if _find("@extended", token): tagtokens.append("@EXTENDED") log.Warn("@extended keyword is deprecated, use @uncompliant:extended instead", ("%s(%i)" % (CurrentFile(), CurrentLine()))) @@ -1730,6 +1748,8 @@ def Parse(contents,log = None): stub_next = False json_next = False json_version = "" + prefix_next = False + prefix_string = "" exclude_next = False event_next = False extended_next = False @@ -1767,11 +1787,18 @@ def Parse(contents,log = None): json_next = True json_version = " ".join(tokens[i+1]) tokens[i] = ";" + tokens[i+1] = ";" + i += 2 + elif tokens[i] == "@PREFIX": + prefix_next = True + prefix_string = " ".join(tokens[i+1]) + tokens[i] = ";" + tokens[i+1] = ";" i += 2 elif tokens[i] == "@JSON_OMIT": exclude_next = True json_next = False - tokens[i] = ';' + tokens[i] = ";" i += 1 elif tokens[i] == "@EVENT": event_next = True @@ -1792,6 +1819,8 @@ def Parse(contents,log = None): i += 1 elif tokens[i] == "@SOURCELOCATION": sourcelocation_next = tokens[i + 1][0] + tokens[i] = ";" + tokens[i+1] = ";" i += 2 elif tokens[i] == "@ITERATOR": iterator_next = True @@ -1807,6 +1836,8 @@ def Parse(contents,log = None): stub_next = False json_next = False json_version = "" + prefix_next = False + prefix_string = "" event_next = False extended_next = False collapsed_next = False @@ -1935,6 +1966,8 @@ def Parse(contents,log = None): if json_next: new_class.is_json = True new_class.json_version = json_version + if prefix_next: + new_class.json_prefix = prefix_string if event_next: new_class.is_event = True if iterator_next: @@ -1956,6 +1989,9 @@ def Parse(contents,log = None): raise ParserError("@compliant and @uncompliant used together") json_next = False + json_version = "" + prefix_next = False + prefix_string = "" event_next = False iterator_next = False extended_next = False @@ -2372,7 +2408,7 @@ def DumpTree(tree, ind=0): # entry point if __name__ == "__main__": - tree = ParseFiles(["default.h", sys.argv[1]], sys.argv[2:]) + tree = ParseFiles([os.path.join(os.path.dirname(__file__), "default.h"), sys.argv[1]], sys.argv[2:], Log.Log("debug", True, True)) if isinstance(tree, Namespace): DumpTree(tree) else: diff --git a/cmake/FindConfigGenerator.cmake.in b/cmake/FindConfigGenerator.cmake.in index 646c1d6..3ab46a7 100644 --- a/cmake/FindConfigGenerator.cmake.in +++ b/cmake/FindConfigGenerator.cmake.in @@ -247,6 +247,41 @@ if(CMAKE_VERSION VERSION_LESS 3.20.0 AND LEGACY_CONFIG_GENERATOR) unset(persistentpathpostfix) endif() + if (NOT ${webui} STREQUAL "") + map_append(${plugin_config} webui ${webui}) + unset(webui) + endif() + + if (NOT ${systemrootpath} STREQUAL "") + map_append(${plugin_config} systemrootpath ${systemrootpath}) + unset(systemrootpath) + endif() + + if (NOT ${resumed} STREQUAL "") + map_append(${plugin_config} resumed ${resumed}) + unset(resumed) + endif() + + if (NOT ${volatilepathpostfix} STREQUAL "") + map_append(${plugin_config} volatilepathpostfix ${volatilepathpostfix}) + unset(volatilepathpostfix) + endif() + + if (NOT ${startuporder} STREQUAL "") + map_append(${plugin_config} startuporder ${startuporder}) + unset(startuporder) + endif() + + if (NOT ${startmode} STREQUAL "") + map_append(${plugin_config} startmode ${startmode}) + unset(startmode) + endif() + + if (NOT ${communicator} STREQUAL "") + map_append(${plugin_config} communicator ${communicator}) + unset(communicator) + endif() + if (NOT ${configuration} STREQUAL "") map_append(${plugin_config} configuration ${configuration}) endif()