2 QAPI introspection generator
4 Copyright (C) 2015-2018 Red Hat, Inc.
7 Markus Armbruster <armbru@redhat.com>
9 This work is licensed under the terms of the GNU GPL, version 2.
10 See the COPYING file in the top-level directory.
13 from qapi
.common
import *
16 # Caveman's json.dumps() replacement (we're stuck at Python 2.4)
17 # TODO try to use json.dumps() once we get unstuck
18 def to_json(obj
, level
=0):
21 elif isinstance(obj
, str):
22 ret
= '"' + obj
.replace('"', r
'\"') + '"'
23 elif isinstance(obj
, list):
24 elts
= [to_json(elt
, level
+ 1)
26 ret
= '[' + ', '.join(elts
) + ']'
27 elif isinstance(obj
, dict):
28 elts
= ['"%s": %s' % (key
.replace('"', r
'\"'),
29 to_json(obj
[key
], level
+ 1))
30 for key
in sorted(obj
.keys())]
31 ret
= '{' + ', '.join(elts
) + '}'
33 assert False # not implemented
39 def to_c_string(string
):
40 return '"' + string
.replace('\\', r
'\\').replace('"', r
'\"') + '"'
43 class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor
):
45 def __init__(self
, prefix
, unmask
):
46 QAPISchemaMonolithicCVisitor
.__init
__(
47 self
, prefix
, 'qapi-introspect',
48 ' * QAPI/QMP schema introspection', __doc__
)
54 self
._genc
.add(mcgen('''
55 #include "qemu/osdep.h"
56 #include "%(prefix)sqapi-introspect.h"
61 def visit_begin(self
, schema
):
65 # visit the types that are actually used
68 for typ
in self
._used
_types
:
71 # TODO can generate awfully long lines
72 jsons
.extend(self
._jsons
)
73 name
= c_name(self
._prefix
, protect
=False) + 'qmp_schema_json'
74 self
._genh
.add(mcgen('''
75 extern const char %(c_name)s[];
78 lines
= to_json(jsons
).split('\n')
79 c_string
= '\n '.join([to_c_string(line
) for line
in lines
])
80 self
._genc
.add(mcgen('''
81 const char %(c_name)s[] = %(c_string)s;
90 def visit_needed(self
, entity
):
91 # Ignore types on first pass; visit_end() will pick up used types
92 return not isinstance(entity
, QAPISchemaType
)
94 def _name(self
, name
):
97 if name
not in self
._name
_map
:
98 self
._name
_map
[name
] = '%d' % len(self
._name
_map
)
99 return self
._name
_map
[name
]
101 def _use_type(self
, typ
):
102 # Map the various integer types to plain int
103 if typ
.json_type() == 'int':
104 typ
= self
._schema
.lookup_type('int')
105 elif (isinstance(typ
, QAPISchemaArrayType
) and
106 typ
.element_type
.json_type() == 'int'):
107 typ
= self
._schema
.lookup_type('intList')
108 # Add type to work queue if new
109 if typ
not in self
._used
_types
:
110 self
._used
_types
.append(typ
)
111 # Clients should examine commands and events, not types. Hide
112 # type names to reduce the temptation. Also saves a few
114 if isinstance(typ
, QAPISchemaBuiltinType
):
116 if isinstance(typ
, QAPISchemaArrayType
):
117 return '[' + self
._use
_type
(typ
.element_type
) + ']'
118 return self
._name
(typ
.name
)
120 def _gen_json(self
, name
, mtype
, obj
):
121 if mtype
not in ('command', 'event', 'builtin', 'array'):
122 name
= self
._name
(name
)
124 obj
['meta-type'] = mtype
125 self
._jsons
.append(obj
)
127 def _gen_member(self
, member
):
128 ret
= {'name': member
.name
, 'type': self
._use
_type
(member
.type)}
130 ret
['default'] = None
133 def _gen_variants(self
, tag_name
, variants
):
134 return {'tag': tag_name
,
135 'variants': [self
._gen
_variant
(v
) for v
in variants
]}
137 def _gen_variant(self
, variant
):
138 return {'case': variant
.name
, 'type': self
._use
_type
(variant
.type)}
140 def visit_builtin_type(self
, name
, info
, json_type
):
141 self
._gen
_json
(name
, 'builtin', {'json-type': json_type
})
143 def visit_enum_type(self
, name
, info
, values
, prefix
):
144 self
._gen
_json
(name
, 'enum', {'values': values
})
146 def visit_array_type(self
, name
, info
, element_type
):
147 element
= self
._use
_type
(element_type
)
148 self
._gen
_json
('[' + element
+ ']', 'array', {'element-type': element
})
150 def visit_object_type_flat(self
, name
, info
, members
, variants
):
151 obj
= {'members': [self
._gen
_member
(m
) for m
in members
]}
153 obj
.update(self
._gen
_variants
(variants
.tag_member
.name
,
155 self
._gen
_json
(name
, 'object', obj
)
157 def visit_alternate_type(self
, name
, info
, variants
):
158 self
._gen
_json
(name
, 'alternate',
159 {'members': [{'type': self
._use
_type
(m
.type)}
160 for m
in variants
.variants
]})
162 def visit_command(self
, name
, info
, arg_type
, ret_type
,
163 gen
, success_response
, boxed
):
164 arg_type
= arg_type
or self
._schema
.the_empty_object_type
165 ret_type
= ret_type
or self
._schema
.the_empty_object_type
166 self
._gen
_json
(name
, 'command',
167 {'arg-type': self
._use
_type
(arg_type
),
168 'ret-type': self
._use
_type
(ret_type
)})
170 def visit_event(self
, name
, info
, arg_type
, boxed
):
171 arg_type
= arg_type
or self
._schema
.the_empty_object_type
172 self
._gen
_json
(name
, 'event', {'arg-type': self
._use
_type
(arg_type
)})
175 def gen_introspect(schema
, output_dir
, prefix
, opt_unmask
):
176 vis
= QAPISchemaGenIntrospectVisitor(prefix
, opt_unmask
)
178 vis
.write(output_dir
)