]> git.ipfire.org Git - thirdparty/qemu.git/blob - scripts/qapi/introspect.py
qapi: Move qapi-schema.json to qapi/, rename generated files
[thirdparty/qemu.git] / scripts / qapi / introspect.py
1 """
2 QAPI introspection generator
3
4 Copyright (C) 2015-2018 Red Hat, Inc.
5
6 Authors:
7 Markus Armbruster <armbru@redhat.com>
8
9 This work is licensed under the terms of the GNU GPL, version 2.
10 See the COPYING file in the top-level directory.
11 """
12
13 from qapi.common import *
14
15
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):
19 if obj is None:
20 ret = 'null'
21 elif isinstance(obj, str):
22 ret = '"' + obj.replace('"', r'\"') + '"'
23 elif isinstance(obj, list):
24 elts = [to_json(elt, level + 1)
25 for elt in obj]
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) + '}'
32 else:
33 assert False # not implemented
34 if level == 1:
35 ret = '\n' + ret
36 return ret
37
38
39 def to_c_string(string):
40 return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"'
41
42
43 class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor):
44
45 def __init__(self, prefix, unmask):
46 QAPISchemaMonolithicCVisitor.__init__(
47 self, prefix, 'qapi-introspect',
48 ' * QAPI/QMP schema introspection', __doc__)
49 self._unmask = unmask
50 self._schema = None
51 self._jsons = []
52 self._used_types = []
53 self._name_map = {}
54 self._genc.add(mcgen('''
55 #include "qemu/osdep.h"
56 #include "%(prefix)sqapi-introspect.h"
57
58 ''',
59 prefix=prefix))
60
61 def visit_begin(self, schema):
62 self._schema = schema
63
64 def visit_end(self):
65 # visit the types that are actually used
66 jsons = self._jsons
67 self._jsons = []
68 for typ in self._used_types:
69 typ.visit(self)
70 # generate C
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[];
76 ''',
77 c_name=c_name(name)))
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;
82 ''',
83 c_name=c_name(name),
84 c_string=c_string))
85 self._schema = None
86 self._jsons = []
87 self._used_types = []
88 self._name_map = {}
89
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)
93
94 def _name(self, name):
95 if self._unmask:
96 return 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]
100
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
113 # characters.
114 if isinstance(typ, QAPISchemaBuiltinType):
115 return typ.name
116 if isinstance(typ, QAPISchemaArrayType):
117 return '[' + self._use_type(typ.element_type) + ']'
118 return self._name(typ.name)
119
120 def _gen_json(self, name, mtype, obj):
121 if mtype not in ('command', 'event', 'builtin', 'array'):
122 name = self._name(name)
123 obj['name'] = name
124 obj['meta-type'] = mtype
125 self._jsons.append(obj)
126
127 def _gen_member(self, member):
128 ret = {'name': member.name, 'type': self._use_type(member.type)}
129 if member.optional:
130 ret['default'] = None
131 return ret
132
133 def _gen_variants(self, tag_name, variants):
134 return {'tag': tag_name,
135 'variants': [self._gen_variant(v) for v in variants]}
136
137 def _gen_variant(self, variant):
138 return {'case': variant.name, 'type': self._use_type(variant.type)}
139
140 def visit_builtin_type(self, name, info, json_type):
141 self._gen_json(name, 'builtin', {'json-type': json_type})
142
143 def visit_enum_type(self, name, info, values, prefix):
144 self._gen_json(name, 'enum', {'values': values})
145
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})
149
150 def visit_object_type_flat(self, name, info, members, variants):
151 obj = {'members': [self._gen_member(m) for m in members]}
152 if variants:
153 obj.update(self._gen_variants(variants.tag_member.name,
154 variants.variants))
155 self._gen_json(name, 'object', obj)
156
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]})
161
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)})
169
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)})
173
174
175 def gen_introspect(schema, output_dir, prefix, opt_unmask):
176 vis = QAPISchemaGenIntrospectVisitor(prefix, opt_unmask)
177 schema.visit(vis)
178 vis.write(output_dir)