Browse Source

Split long string literals into shorter ones

This is necessary to circumvent "string is too big" errors in VS2008
Stefan Profanter 7 years ago
parent
commit
e5ef960ec5

+ 9 - 1
CMakeLists.txt

@@ -469,13 +469,21 @@ add_dependencies(open62541-amalgamation-header open62541-generator-types)
 add_dependencies(open62541-amalgamation-source open62541-generator-types
                  open62541-generator-transport open62541-generator-statuscode)
 
+
+set(NODESET_MAX_STR_LEN 0)
+if ( (MSVC) AND (MSVC_VERSION LESS 1600))
+    # Visual Studio 2008 has a maximum string length of 65535 bytes
+    set(NODESET_MAX_STR_LEN 65535)
+endif()
+
 # generated namespace 0
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_namespace0.c
                           ${PROJECT_BINARY_DIR}/src_generated/ua_namespace0.h
                    PRE_BUILD
                    COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
-                           --internal-headers
                            --generate-ns0
+                           --internal-headers
+                           --max-string-length=${NODESET_MAX_STR_LEN}
                            --ignore ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/NodeID_NS0_Base.txt
                            --xml ${UA_NAMESPACE0_XML}
                            ${PROJECT_BINARY_DIR}/src_generated/ua_namespace0

+ 2 - 2
tools/nodeset_compiler/backend_open62541.py

@@ -153,7 +153,7 @@ def reorderNodesMinDependencies(nodeset):
 # Generate C Code #
 ###################
 
-def generateOpen62541Code(nodeset, outfilename, supressGenerationOfAttribute=[], generate_ns0=False, internal_headers=False, typesArray=[]):
+def generateOpen62541Code(nodeset, outfilename, supressGenerationOfAttribute=[], generate_ns0=False, internal_headers=False, typesArray=[], max_string_length=0):
     outfilebase = basename(outfilename)
     # Printing functions
     outfileh = open(outfilename + ".h", r"w+")
@@ -228,7 +228,7 @@ UA_StatusCode retVal = UA_STATUSCODE_GOOD;
         # Print node
         if not node.hidden:
             writec("\n/* " + str(node.displayName) + " - " + str(node.id) + " */")
-            code = generateNodeCode(node, supressGenerationOfAttribute, generate_ns0, parentrefs, nodeset)
+            code = generateNodeCode(node, supressGenerationOfAttribute, generate_ns0, parentrefs, nodeset, max_string_length)
             if code is None:
                 writec("/* Ignored. No parent */")
                 nodeset.hide_node(node.id)

+ 43 - 18
tools/nodeset_compiler/backend_open62541_datatypes.py

@@ -2,27 +2,52 @@ from datatypes import *
 import datetime
 import re
 
+import logging
+
+logger = logging.getLogger(__name__)
+
 def generateBooleanCode(value):
     if value:
         return "true"
     return "false"
 
-def generateStringCode(value, alloc=False):
-    return "UA_STRING{}(\"{}\")".format("_ALLOC" if alloc else "", value.replace('"', r'\"'))
+def splitStringLiterals(value, splitLength=500, max_string_length=0):
+    """
+    Split a string literal longer than splitLength into smaller literals.
+    E.g. "Some very long text" will be split into "Some ver" "y long te" "xt"
+    On VS2008 there is a maximum allowed length of a single string literal.
+    If maxLength is set and the string is longer than maxLength, then an
+    empty string will be returned.
+    """
+    if max_string_length > 0 and len(value) > max_string_length:
+        logger.info("String is longer than {}. Returning empty string.".format(max_string_length))
+        return "\"\""
+    if len(value) < splitLength or splitLength == 0:
+        return "\"" + value.replace('"', r'\"') + "\""
+    ret = ""
+    tmp = value
+    while len(tmp) > splitLength:
+        ret += "\"" + tmp[:splitLength].replace('"', r'\"') + "\" "
+        tmp = tmp[splitLength:]
+    ret += "\"" + tmp.replace('"', r'\"') + "\" "
+    return ret
+
+def generateStringCode(value, alloc=False, max_string_length=0):
+    return "UA_STRING{}({})".format("_ALLOC" if alloc else "", splitStringLiterals(value, max_string_length=max_string_length))
 
-def generateXmlElementCode(value, alloc=False):
-    return "UA_XMLELEMENT{}(\"{}\")".format("_ALLOC" if alloc else "", value.replace('"', r'\"'))
+def generateXmlElementCode(value, alloc=False, max_string_length=0):
+    return "UA_XMLELEMENT{}({})".format("_ALLOC" if alloc else "", splitStringLiterals(value, max_string_length=max_string_length))
 
-def generateByteStringCode(value, alloc=False):
-    return "UA_BYTESTRING{}(\"{}\")".format("_ALLOC" if alloc else "", value.replace('"', r'\"'))
+def generateByteStringCode(value, alloc=False, max_string_length=0):
+    return "UA_BYTESTRING{}({})".format("_ALLOC" if alloc else "", splitStringLiterals(value, max_string_length=max_string_length))
 
-def generateLocalizedTextCode(value, alloc=False):
-    return "UA_LOCALIZEDTEXT{}(\"{}\", \"{}\")".format("_ALLOC" if alloc else "",
-                                                       value.locale, value.text.replace('"', r'\"'))
+def generateLocalizedTextCode(value, alloc=False, max_string_length=0):
+    return "UA_LOCALIZEDTEXT{}(\"{}\", {})".format("_ALLOC" if alloc else "",
+                                                       value.locale, splitStringLiterals(value.text, max_string_length=max_string_length))
 
-def generateQualifiedNameCode(value, alloc=False):
-    return "UA_QUALIFIEDNAME{}(ns{}, \"{}\")".format("_ALLOC" if alloc else "",
-                                                     str(value.ns), value.name.replace('"', r'\"'))
+def generateQualifiedNameCode(value, alloc=False, max_string_length=0):
+    return "UA_QUALIFIEDNAME{}(ns{}, {})".format("_ALLOC" if alloc else "",
+                                                     str(value.ns), splitStringLiterals(value.name, max_string_length=max_string_length))
 
 def generateNodeIdCode(value):
     if not value:
@@ -45,17 +70,17 @@ def generateDateTimeCode(value):
     mSecsSinceEpoch = (value - epoch).total_seconds() * 1000.0
     return "( (" + str(mSecsSinceEpoch) + "f * UA_MSEC_TO_DATETIME) + UA_DATETIME_UNIX_EPOCH)"
 
-def generateNodeValueCode(node, instanceName, asIndirect=False):
+def generateNodeValueCode(node, instanceName, asIndirect=False, max_string_length=0):
     if type(node) in [Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Float, Double]:
         return "(UA_" + node.__class__.__name__ + ") " + str(node.value)
     elif type(node) == String:
-        return generateStringCode(node.value, asIndirect)
+        return generateStringCode(node.value, asIndirect, max_string_length)
     elif type(node) == XmlElement:
-        return generateXmlElementCode(node.value, asIndirect)
+        return generateXmlElementCode(node.value, asIndirect, max_string_length)
     elif type(node) == ByteString:
-        return generateByteStringCode(re.sub(r"[\r\n]+", "", node.value), asIndirect)
+        return generateByteStringCode(re.sub(r"[\r\n]+", "", node.value), asIndirect, max_string_length)
     elif type(node) == LocalizedText:
-        return generateLocalizedTextCode(node, asIndirect)
+        return generateLocalizedTextCode(node, asIndirect, max_string_length)
     elif type(node) == NodeId:
         return generateNodeIdCode(node)
     elif type(node) == ExpandedNodeId:
@@ -63,7 +88,7 @@ def generateNodeValueCode(node, instanceName, asIndirect=False):
     elif type(node) == DateTime:
         return generateDateTimeCode(node.value)
     elif type(node) == QualifiedName:
-        return generateQualifiedNameCode(node.value, asIndirect)
+        return generateQualifiedNameCode(node.value, asIndirect, max_string_length)
     elif type(node) == StatusCode:
         raise Exception("generateNodeValueCode for type " + node.__class__.name + " not implemented")
     elif type(node) == DiagnosticInfo:

+ 20 - 20
tools/nodeset_compiler/backend_open62541_nodes.py

@@ -106,7 +106,7 @@ def generateObjectNodeCode(node):
         code.append("attr.eventNotifier = true;")
     return code
 
-def generateVariableNodeCode(node, nodeset):
+def generateVariableNodeCode(node, nodeset, max_string_length):
     code = []
     codeCleanup = []
     code.append("UA_VariableAttributes attr = UA_VariableAttributes_default;")
@@ -135,14 +135,14 @@ def generateVariableNodeCode(node, nodeset):
 
             if dataTypeNode.isEncodable():
                 if node.value is not None:
-                    [code1, codeCleanup1] = generateValueCode(node.value, nodeset.nodes[node.id], nodeset)
+                    [code1, codeCleanup1] = generateValueCode(node.value, nodeset.nodes[node.id], nodeset, max_string_length=max_string_length)
                     code += code1
                     codeCleanup += codeCleanup1
                 else:
                     code += generateValueCodeDummy(dataTypeNode, nodeset.nodes[node.id], nodeset)
     return [code, codeCleanup]
 
-def generateVariableTypeNodeCode(node, nodeset):
+def generateVariableTypeNodeCode(node, nodeset, max_string_length):
     code = []
     codeCleanup = []
     code.append("UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default;")
@@ -159,14 +159,14 @@ def generateVariableTypeNodeCode(node, nodeset):
             code.append("attr.dataType = %s;" % generateNodeIdCode(dataTypeNode.id))
             if dataTypeNode.isEncodable():
                 if node.value is not None:
-                    [code1, codeCleanup1] = generateValueCode(node.value, nodeset.nodes[node.id], nodeset)
+                    [code1, codeCleanup1] = generateValueCode(node.value, nodeset.nodes[node.id], nodeset, max_string_length)
                     code += code1
                     codeCleanup += codeCleanup1
                 else:
                     code += generateValueCodeDummy(dataTypeNode, nodeset.nodes[node.id], nodeset)
     return [code, codeCleanup]
 
-def generateExtensionObjectSubtypeCode(node, parent, nodeset, recursionDepth=0, arrayIndex=0):
+def generateExtensionObjectSubtypeCode(node, parent, nodeset, recursionDepth=0, arrayIndex=0, max_string_length=0):
     code = [""]
     codeCleanup = [""]
 
@@ -205,7 +205,7 @@ def generateExtensionObjectSubtypeCode(node, parent, nodeset, recursionDepth=0,
         # Check if this is an array
         if encField[2] == 0:
             code.append(instanceName + "_struct." + subv.alias + " = " +
-                        generateNodeValueCode(subv, instanceName, asIndirect=False) + ";")
+                        generateNodeValueCode(subv, instanceName, asIndirect=False, max_string_length=max_string_length) + ";")
         else:
             if isinstance(subv, list):
                 # this is an array
@@ -219,7 +219,7 @@ def generateExtensionObjectSubtypeCode(node, parent, nodeset, recursionDepth=0,
                     subvv = subv[subvidx]
                     logger.debug("  " + str(subvidx) + " " + str(subvv))
                     code.append(instanceName + "_struct." + subv.alias + "[" + str(
-                        subvidx) + "] = " + generateNodeValueCode(subvv, instanceName) + ";")
+                        subvidx) + "] = " + generateNodeValueCode(subvv, instanceName, max_string_length=max_string_length) + ";")
                 code.append("}")
             else:
                 code.append(instanceName + "_struct." + subv.alias + "Size = 1;")
@@ -229,7 +229,7 @@ def generateExtensionObjectSubtypeCode(node, parent, nodeset, recursionDepth=0,
                 codeCleanup.append("UA_free({0}_struct.{1});".format(instanceName, subv.alias))
 
                 code.append(instanceName + "_struct." + subv.alias + "[0]  = " +
-                            generateNodeValueCode(subv, instanceName, asIndirect=True) + ";")
+                            generateNodeValueCode(subv, instanceName, asIndirect=True, max_string_length=max_string_length) + ";")
 
     # Allocate some memory
     code.append("UA_ExtensionObject *" + instanceName + " =  UA_ExtensionObject_new();")
@@ -311,7 +311,7 @@ def getTypesArrayForValue(nodeset, value):
     return "&" + typesArray + "[" + typesArray + "_" + \
                     value.__class__.__name__.upper() + "]"
 
-def generateValueCode(node, parentNode, nodeset, bootstrapping=True):
+def generateValueCode(node, parentNode, nodeset, bootstrapping=True, max_string_length=0):
     code = []
     codeCleanup = []
     valueName = generateNodeIdPrintable(parentNode) + "_variant_DataContents"
@@ -348,7 +348,7 @@ def generateValueCode(node, parentNode, nodeset, bootstrapping=True):
             if node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
                 for idx, v in enumerate(node.value):
                     logger.debug("Building extObj array index " + str(idx))
-                    [code1, codeCleanup1] = generateExtensionObjectSubtypeCode(v, parent=parentNode, nodeset=nodeset, arrayIndex=idx)
+                    [code1, codeCleanup1] = generateExtensionObjectSubtypeCode(v, parent=parentNode, nodeset=nodeset, arrayIndex=idx, max_string_length=max_string_length)
                     code = code + code1
                     codeCleanup = codeCleanup + codeCleanup1
             code.append("UA_" + node.value[0].__class__.__name__ + " " + valueName + "[" + str(len(node.value)) + "];")
@@ -358,13 +358,13 @@ def generateValueCode(node, parentNode, nodeset, bootstrapping=True):
                     instanceName = generateNodeValueInstanceName(v, parentNode, 0, idx)
                     code.append(
                         valueName + "[" + str(idx) + "] = " +
-                        generateNodeValueCode(v, instanceName) + ";")
+                        generateNodeValueCode(v, instanceName, max_string_length=max_string_length) + ";")
                     # code.append("UA_free(&" +valueName + "[" + str(idx) + "]);")
             else:
                 for idx, v in enumerate(node.value):
                     instanceName = generateNodeValueInstanceName(v, parentNode, 0, idx)
                     code.append(
-                        valueName + "[" + str(idx) + "] = " + generateNodeValueCode(v, instanceName) + ";")
+                        valueName + "[" + str(idx) + "] = " + generateNodeValueCode(v, instanceName, max_string_length=max_string_length) + ";")
             code.append("UA_Variant_setArray( &attr.value, &" + valueName +
                         ", (UA_Int32) " + str(len(node.value)) + ", " +
                         getTypesArrayForValue(nodeset, node.value[0]) + ");")
@@ -381,13 +381,13 @@ def generateValueCode(node, parentNode, nodeset, bootstrapping=True):
         else:
             # The following strategy applies to all other types, in particular strings and numerics.
             if node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
-                [code1, codeCleanup1] = generateExtensionObjectSubtypeCode(node.value[0], parent=parentNode, nodeset=nodeset)
+                [code1, codeCleanup1] = generateExtensionObjectSubtypeCode(node.value[0], parent=parentNode, nodeset=nodeset, max_string_length=max_string_length)
                 code = code + code1
                 codeCleanup = codeCleanup + codeCleanup1
             instanceName = generateNodeValueInstanceName(node.value[0], parentNode, 0, 0)
             if node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
                 code.append("UA_" + node.value[0].__class__.__name__ + " *" + valueName + " = " +
-                            generateNodeValueCode(node.value[0], instanceName) + ";")
+                            generateNodeValueCode(node.value[0], instanceName, max_string_length=max_string_length) + ";")
                 code.append(
                     "UA_Variant_setScalar( &attr.value, " + valueName + ", " +
                     getTypesArrayForValue(nodeset, node.value[0]) + ");")
@@ -397,7 +397,7 @@ def generateValueCode(node, parentNode, nodeset, bootstrapping=True):
             else:
                 code.append("UA_" + node.value[0].__class__.__name__ + " *" + valueName + " =  UA_" + node.value[
                     0].__class__.__name__ + "_new();")
-                code.append("*" + valueName + " = " + generateNodeValueCode(node.value[0], instanceName, asIndirect=True) + ";")
+                code.append("*" + valueName + " = " + generateNodeValueCode(node.value[0], instanceName, asIndirect=True, max_string_length=max_string_length) + ";")
                 code.append(
                         "UA_Variant_setScalar( &attr.value, " + valueName + ", " +
                         getTypesArrayForValue(nodeset, node.value[0]) + ");")
@@ -450,7 +450,7 @@ def generateSubtypeOfDefinitionCode(node):
             return generateNodeIdCode(ref.target)
     return "UA_NODEID_NULL"
 
-def generateNodeCode(node, supressGenerationOfAttribute, generate_ns0, parentrefs, nodeset):
+def generateNodeCode(node, supressGenerationOfAttribute, generate_ns0, parentrefs, nodeset, max_string_length):
     code = []
     code.append("{")
 
@@ -461,11 +461,11 @@ def generateNodeCode(node, supressGenerationOfAttribute, generate_ns0, parentref
     elif isinstance(node, ObjectNode):
         code.extend(generateObjectNodeCode(node))
     elif isinstance(node, VariableNode) and not isinstance(node, VariableTypeNode):
-        [code1, codeCleanup1] = generateVariableNodeCode(node, nodeset)
+        [code1, codeCleanup1] = generateVariableNodeCode(node, nodeset, max_string_length)
         code.extend(code1)
         codeCleanup.extend(codeCleanup1)
     elif isinstance(node, VariableTypeNode):
-        [code1, codeCleanup1] = generateVariableTypeNodeCode(node, nodeset)
+        [code1, codeCleanup1] = generateVariableTypeNodeCode(node, nodeset, max_string_length)
         code.extend(code1)
         codeCleanup.extend(codeCleanup1)
     elif isinstance(node, MethodNode):
@@ -477,8 +477,8 @@ def generateNodeCode(node, supressGenerationOfAttribute, generate_ns0, parentref
     elif isinstance(node, ViewNode):
         code.extend(generateViewNodeCode(node))
 
-    code.append("attr.displayName = " + generateLocalizedTextCode(node.displayName) + ";")
-    code.append("attr.description = " + generateLocalizedTextCode(node.description) + ";")
+    code.append("attr.displayName = " + generateLocalizedTextCode(node.displayName, max_string_length) + ";")
+    code.append("attr.description = " + generateLocalizedTextCode(node.description, max_string_length) + ";")
     code.append("attr.writeMask = %d;" % node.writeMask)
     code.append("attr.userWriteMask = %d;" % node.userWriteMask)
 

+ 7 - 1
tools/nodeset_compiler/nodeset_compiler.py

@@ -88,6 +88,12 @@ parser.add_argument('-t', '--types-array',
                     default=[],
                     help='Types array for the given namespace. Can be used mutliple times to define (in the same order as the .xml files, first for --existing, then --xml) the type arrays')
 
+parser.add_argument('--max-string-length',
+                    type=int,
+                    dest="max_string_length",
+                    default=0,
+                    help='Maximum allowed length of a string literal. If longer, it will be set to an empty string')
+
 parser.add_argument('-v', '--verbose', action='count',
                     help='Make the script more verbose. Can be applied up to 4 times')
 
@@ -181,5 +187,5 @@ ns.allocateVariables()
 
 # Create the C code with the open62541 backend of the compiler
 logger.info("Generating Code")
-generateOpen62541Code(ns, args.outputFile, args.suppressedAttributes, args.generate_ns0, args.internal_headers, args.typesArray)
+generateOpen62541Code(ns, args.outputFile, args.suppressedAttributes, args.generate_ns0, args.internal_headers, args.typesArray, args.max_string_length)
 logger.info("NodeSet generation code successfully printed")