qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

qapi: allow override of default enum prefix naming

The camel_to_upper() method applies some heuristics to turn
a mixed case type name into an all-uppercase name. This is
used for example, to generate enum constant name prefixes.

The heuristics don't also generate a satisfactory name
though. eg

{ 'enum': 'QCryptoTLSCredsEndpoint',
'data': ['client', 'server']}

Results in Q_CRYPTOTLS_CREDS_ENDPOINT_CLIENT. This has
an undesirable _ after the initial Q and is missing an
_ between the CRYPTO & TLS strings.

Rather than try to add more and more heuristics to try
to cope with this, simply allow the QAPI schema to
specify the desired enum constant prefix explicitly.

eg

{ 'enum': 'QCryptoTLSCredsEndpoint',
'prefix': 'QCRYPTO_TLS_CREDS_ENDPOINT',
'data': ['client', 'server']}

Now gives the QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT name.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>

+38 -10
+8
docs/qapi-code-gen.txt
··· 236 236 === Enumeration types === 237 237 238 238 Usage: { 'enum': STRING, 'data': ARRAY-OF-STRING } 239 + { 'enum': STRING, '*prefix': STRING, 'data': ARRAY-OF-STRING } 239 240 240 241 An enumeration type is a dictionary containing a single 'data' key 241 242 whose value is a list of strings. An example enumeration is: ··· 246 247 useful. The list of strings should be lower case; if an enum name 247 248 represents multiple words, use '-' between words. The string 'max' is 248 249 not allowed as an enum value, and values should not be repeated. 250 + 251 + The enum constants will be named by using a heuristic to turn the 252 + type name into a set of underscore separated words. For the example 253 + above, 'MyEnum' will turn into 'MY_ENUM' giving a constant name 254 + of 'MY_ENUM_VALUE1' for the first value. If the default heuristic 255 + does not result in a desirable name, the optional 'prefix' field 256 + can be used when defining the enum. 249 257 250 258 The enumeration values are passed as strings over the Client JSON 251 259 Protocol, but are encoded as C enum integral values in generated code.
+9 -7
scripts/qapi-types.py
··· 101 101 102 102 return ret 103 103 104 - def generate_enum_lookup(name, values): 104 + def generate_enum_lookup(name, values, prefix=None): 105 105 ret = mcgen(''' 106 106 107 107 const char *const %(name)s_lookup[] = { 108 108 ''', 109 109 name=c_name(name)) 110 110 for value in values: 111 - index = c_enum_const(name, value) 111 + index = c_enum_const(name, value, prefix) 112 112 ret += mcgen(''' 113 113 [%(index)s] = "%(value)s", 114 114 ''', 115 115 index = index, value = value) 116 116 117 - max_index = c_enum_const(name, 'MAX') 117 + max_index = c_enum_const(name, 'MAX', prefix) 118 118 ret += mcgen(''' 119 119 [%(max_index)s] = NULL, 120 120 }; ··· 122 122 max_index=max_index) 123 123 return ret 124 124 125 - def generate_enum(name, values): 125 + def generate_enum(name, values, prefix=None): 126 126 name = c_name(name) 127 127 lookup_decl = mcgen(''' 128 128 ··· 141 141 142 142 i = 0 143 143 for value in enum_values: 144 - enum_full_value = c_enum_const(name, value) 144 + enum_full_value = c_enum_const(name, value, prefix) 145 145 enum_decl += mcgen(''' 146 146 %(enum_full_value)s = %(i)d, 147 147 ''', ··· 348 348 if expr.has_key('struct'): 349 349 ret += generate_fwd_struct(expr['struct']) 350 350 elif expr.has_key('enum'): 351 - ret += generate_enum(expr['enum'], expr['data']) 351 + ret += generate_enum(expr['enum'], expr['data'], 352 + expr.get('prefix')) 352 353 ret += generate_fwd_enum_struct(expr['enum']) 353 - fdef.write(generate_enum_lookup(expr['enum'], expr['data'])) 354 + fdef.write(generate_enum_lookup(expr['enum'], expr['data'], 355 + expr.get('prefix'))) 354 356 elif expr.has_key('union'): 355 357 ret += generate_fwd_struct(expr['union']) 356 358 enum_define = discriminator_find_enum_define(expr)
+8 -2
scripts/qapi.py
··· 634 634 def check_enum(expr, expr_info): 635 635 name = expr['enum'] 636 636 members = expr.get('data') 637 + prefix = expr.get('prefix') 637 638 values = { 'MAX': '(automatic)' } 638 639 639 640 if not isinstance(members, list): 640 641 raise QAPIExprError(expr_info, 641 642 "Enum '%s' requires an array for 'data'" % name) 643 + if prefix is not None and not isinstance(prefix, str): 644 + raise QAPIExprError(expr_info, 645 + "Enum '%s' requires a string for 'prefix'" % name) 642 646 for member in members: 643 647 check_name(expr_info, "Member of enum '%s'" %name, member, 644 648 enum_member=True) ··· 693 697 expr = expr_elem['expr'] 694 698 info = expr_elem['info'] 695 699 if expr.has_key('enum'): 696 - check_keys(expr_elem, 'enum', ['data']) 700 + check_keys(expr_elem, 'enum', ['data'], ['prefix']) 697 701 add_enum(expr['enum'], info, expr['data']) 698 702 elif expr.has_key('union'): 699 703 check_keys(expr_elem, 'union', ['data'], ··· 813 817 new_name += c 814 818 return new_name.lstrip('_').upper() 815 819 816 - def c_enum_const(type_name, const_name): 820 + def c_enum_const(type_name, const_name, prefix=None): 821 + if prefix is not None: 822 + type_name = prefix 817 823 return camel_to_upper(type_name + '_' + const_name) 818 824 819 825 c_name_trans = string.maketrans('.-', '__')
+2 -1
tests/Makefile
··· 221 221 comments.json empty.json enum-empty.json enum-missing-data.json \ 222 222 enum-wrong-data.json enum-int-member.json enum-dict-member.json \ 223 223 enum-clash-member.json enum-max-member.json enum-union-clash.json \ 224 - enum-bad-name.json funny-char.json indented-expr.json \ 224 + enum-bad-name.json enum-bad-prefix.json \ 225 + funny-char.json indented-expr.json \ 225 226 missing-type.json bad-ident.json ident-with-escape.json \ 226 227 escape-outside-string.json unknown-escape.json \ 227 228 escape-too-short.json escape-too-big.json unicode-str.json \
+1
tests/qapi-schema/enum-bad-prefix.err
··· 1 + tests/qapi-schema/enum-bad-prefix.json:2: Enum 'MyEnum' requires a string for 'prefix'
+1
tests/qapi-schema/enum-bad-prefix.exit
··· 1 + 1
+2
tests/qapi-schema/enum-bad-prefix.json
··· 1 + # The prefix must be a string type 2 + { 'enum': 'MyEnum', 'data': [ 'one' ], 'prefix': [ 'fish' ] }
tests/qapi-schema/enum-bad-prefix.out

This is a binary file and will not be displayed.

+5
tests/qapi-schema/qapi-schema-test.json
··· 6 6 { 'struct': 'NestedEnumsOne', 7 7 'data': { 'enum1': 'EnumOne', '*enum2': 'EnumOne', 'enum3': 'EnumOne', '*enum4': 'EnumOne' } } 8 8 9 + # for testing override of default naming heuristic 10 + { 'enum': 'QEnumTwo', 11 + 'prefix': 'QENUM_TWO', 12 + 'data': [ 'value1', 'value2' ] } 13 + 9 14 # for testing nested structs 10 15 { 'struct': 'UserDefOne', 11 16 'base': 'UserDefZero', # intentional forward reference
+2
tests/qapi-schema/qapi-schema-test.out
··· 1 1 [OrderedDict([('enum', 'EnumOne'), ('data', ['value1', 'value2', 'value3'])]), 2 2 OrderedDict([('struct', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]), 3 + OrderedDict([('enum', 'QEnumTwo'), ('prefix', 'QENUM_TWO'), ('data', ['value1', 'value2'])]), 3 4 OrderedDict([('struct', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]), 4 5 OrderedDict([('struct', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]), 5 6 OrderedDict([('struct', 'UserDefTwoDictDict'), ('data', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]), ··· 33 34 OrderedDict([('event', '__ORG.QEMU_X-EVENT'), ('data', '__org.qemu_x-Struct')]), 34 35 OrderedDict([('command', '__org.qemu_x-command'), ('data', OrderedDict([('a', ['__org.qemu_x-Enum']), ('b', ['__org.qemu_x-Struct']), ('c', '__org.qemu_x-Union2'), ('d', '__org.qemu_x-Alt')])), ('returns', '__org.qemu_x-Union1')])] 35 36 [{'enum_name': 'EnumOne', 'enum_values': ['value1', 'value2', 'value3']}, 37 + {'enum_name': 'QEnumTwo', 'enum_values': ['value1', 'value2']}, 36 38 {'enum_name': '__org.qemu_x-Enum', 'enum_values': ['__org.qemu_x-value']}, 37 39 {'enum_name': 'UserDefAlternateKind', 'enum_values': None}, 38 40 {'enum_name': 'UserDefNativeListUnionKind', 'enum_values': None},