2 # QAPI introspection generator
4 # Copyright (C) 2015 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.
15 # Caveman's json.dumps() replacement (we're stuck at Python 2.4)
16 # TODO try to use json.dumps() once we get unstuck
17 def to_json(obj
, level
=0):
20 elif isinstance(obj
, str):
21 ret
= '"' + obj
.replace('"', r
'\"') + '"'
22 elif isinstance(obj
, list):
23 elts
= [to_json(elt
, level
+ 1)
25 ret
= '[' + ', '.join(elts
) + ']'
26 elif isinstance(obj
, dict):
27 elts
= ['"%s": %s' % (key
.replace('"', r
'\"'),
28 to_json(obj
[key
], level
+ 1))
29 for key
in sorted(obj
.keys())]
30 ret
= '{' + ', '.join(elts
) + '}'
32 assert False # not implemented
38 def to_c_string(string
):
39 return '"' + string
.replace('\\', r
'\\').replace('"', r
'\"') + '"'
42 class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor
):
43 def __init__(self
, unmask
):
49 self
._used
_types
= None
52 def visit_begin(self
, schema
):
59 # visit the types that are actually used
62 for typ
in self
._used
_types
:
65 # TODO can generate awfully long lines
66 jsons
.extend(self
._jsons
)
67 name
= prefix
+ 'qmp_schema_json'
69 extern const char %(c_name)s[];
72 lines
= to_json(jsons
).split('\n')
73 c_string
= '\n '.join([to_c_string(line
) for line
in lines
])
75 const char %(c_name)s[] = %(c_string)s;
81 self
._used
_types
= None
84 def visit_needed(self
, entity
):
85 # Ignore types on first pass; visit_end() will pick up used types
86 return not isinstance(entity
, QAPISchemaType
)
88 def _name(self
, name
):
91 if name
not in self
._name
_map
:
92 self
._name
_map
[name
] = '%d' % len(self
._name
_map
)
93 return self
._name
_map
[name
]
95 def _use_type(self
, typ
):
96 # Map the various integer types to plain int
97 if typ
.json_type() == 'int':
98 typ
= self
._schema
.lookup_type('int')
99 elif (isinstance(typ
, QAPISchemaArrayType
) and
100 typ
.element_type
.json_type() == 'int'):
101 typ
= self
._schema
.lookup_type('intList')
102 # Add type to work queue if new
103 if typ
not in self
._used
_types
:
104 self
._used
_types
.append(typ
)
105 # Clients should examine commands and events, not types. Hide
106 # type names to reduce the temptation. Also saves a few
108 if isinstance(typ
, QAPISchemaBuiltinType
):
110 return self
._name
(typ
.name
)
112 def _gen_json(self
, name
, mtype
, obj
):
113 if mtype
!= 'command' and mtype
!= 'event' and mtype
!= 'builtin':
114 name
= self
._name
(name
)
116 obj
['meta-type'] = mtype
117 self
._jsons
.append(obj
)
119 def _gen_member(self
, member
):
120 ret
= {'name': member
.name
, 'type': self
._use
_type
(member
.type)}
122 ret
['default'] = None
125 def _gen_variants(self
, tag_name
, variants
):
126 return {'tag': tag_name
,
127 'variants': [self
._gen
_variant
(v
) for v
in variants
]}
129 def _gen_variant(self
, variant
):
130 return {'case': variant
.name
, 'type': self
._use
_type
(variant
.type)}
132 def visit_builtin_type(self
, name
, info
, json_type
):
133 self
._gen
_json
(name
, 'builtin', {'json-type': json_type
})
135 def visit_enum_type(self
, name
, info
, values
, prefix
):
136 self
._gen
_json
(name
, 'enum', {'values': values
})
138 def visit_array_type(self
, name
, info
, element_type
):
139 self
._gen
_json
(name
, 'array',
140 {'element-type': self
._use
_type
(element_type
)})
142 def visit_object_type_flat(self
, name
, info
, members
, variants
):
143 obj
= {'members': [self
._gen
_member
(m
) for m
in members
]}
145 obj
.update(self
._gen
_variants
(variants
.tag_member
.name
,
147 self
._gen
_json
(name
, 'object', obj
)
149 def visit_alternate_type(self
, name
, info
, variants
):
150 self
._gen
_json
(name
, 'alternate',
151 {'members': [{'type': self
._use
_type
(m
.type)}
152 for m
in variants
.variants
]})
154 def visit_command(self
, name
, info
, arg_type
, ret_type
,
155 gen
, success_response
):
156 arg_type
= arg_type
or self
._schema
.the_empty_object_type
157 ret_type
= ret_type
or self
._schema
.the_empty_object_type
158 self
._gen
_json
(name
, 'command',
159 {'arg-type': self
._use
_type
(arg_type
),
160 'ret-type': self
._use
_type
(ret_type
)})
162 def visit_event(self
, name
, info
, arg_type
):
163 arg_type
= arg_type
or self
._schema
.the_empty_object_type
164 self
._gen
_json
(name
, 'event', {'arg-type': self
._use
_type
(arg_type
)})
166 # Debugging aid: unmask QAPI schema's type names
167 # We normally mask them, because they're not QMP wire ABI
170 (input_file
, output_dir
, do_c
, do_h
, prefix
, opts
) = \
171 parse_command_line("u", ["unmask-non-abi-names"])
174 if o
in ("-u", "--unmask-non-abi-names"):
179 * QAPI/QMP schema introspection
181 * Copyright (C) 2015 Red Hat, Inc.
183 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
184 * See the COPYING.LIB file in the top-level directory.
190 * QAPI/QMP schema introspection
192 * Copyright (C) 2015 Red Hat, Inc.
194 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
195 * See the COPYING.LIB file in the top-level directory.
200 (fdef
, fdecl
) = open_output(output_dir
, do_c
, do_h
, prefix
,
201 'qmp-introspect.c', 'qmp-introspect.h',
202 c_comment
, h_comment
)
205 #include "%(prefix)sqmp-introspect.h"
210 schema
= QAPISchema(input_file
)
211 gen
= QAPISchemaGenIntrospectVisitor(opt_unmask
)
214 fdecl
.write(gen
.decl
)
216 close_output(fdef
, fdecl
)