]> git.proxmox.com Git - mirror_qemu.git/blame - scripts/qapi/gen.py
monitor: Add allow_hmp parameter to monitor_init()
[mirror_qemu.git] / scripts / qapi / gen.py
CommitLineData
e6c42b96
MA
1# -*- coding: utf-8 -*-
2#
3# QAPI code generation
4#
5# Copyright (c) 2018-2019 Red Hat Inc.
6#
7# Authors:
8# Markus Armbruster <armbru@redhat.com>
9# Marc-André Lureau <marcandre.lureau@redhat.com>
10#
11# This work is licensed under the terms of the GNU GPL, version 2.
12# See the COPYING file in the top-level directory.
13
14
15import errno
16import os
17import re
e6c42b96
MA
18from contextlib import contextmanager
19
20from qapi.common import *
21from qapi.schema import QAPISchemaVisitor
22
23
baa310f1 24class QAPIGen:
e6c42b96
MA
25
26 def __init__(self, fname):
27 self.fname = fname
28 self._preamble = ''
29 self._body = ''
30
31 def preamble_add(self, text):
32 self._preamble += text
33
34 def add(self, text):
35 self._body += text
36
37 def get_content(self):
38 return self._top() + self._preamble + self._body + self._bottom()
39
40 def _top(self):
41 return ''
42
43 def _bottom(self):
44 return ''
45
46 def write(self, output_dir):
47 pathname = os.path.join(output_dir, self.fname)
8ec0e1a4
MA
48 odir = os.path.dirname(pathname)
49 if odir:
e6c42b96 50 try:
8ec0e1a4 51 os.makedirs(odir)
e6c42b96
MA
52 except os.error as e:
53 if e.errno != errno.EEXIST:
54 raise
55 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
ed39c03e 56 f = open(fd, 'r+', encoding='utf-8')
e6c42b96
MA
57 text = self.get_content()
58 oldtext = f.read(len(text) + 1)
59 if text != oldtext:
60 f.seek(0)
61 f.truncate(0)
62 f.write(text)
63 f.close()
64
65
66def _wrap_ifcond(ifcond, before, after):
67 if before == after:
68 return after # suppress empty #if ... #endif
69
70 assert after.startswith(before)
71 out = before
72 added = after[len(before):]
73 if added[0] == '\n':
74 out += '\n'
75 added = added[1:]
76 out += gen_if(ifcond)
77 out += added
78 out += gen_endif(ifcond)
79 return out
80
81
82class QAPIGenCCode(QAPIGen):
83
84 def __init__(self, fname):
2cae67bc 85 super().__init__(fname)
e6c42b96
MA
86 self._start_if = None
87
88 def start_if(self, ifcond):
89 assert self._start_if is None
90 self._start_if = (ifcond, self._body, self._preamble)
91
92 def end_if(self):
93 assert self._start_if
94 self._wrap_ifcond()
95 self._start_if = None
96
97 def _wrap_ifcond(self):
98 self._body = _wrap_ifcond(self._start_if[0],
99 self._start_if[1], self._body)
100 self._preamble = _wrap_ifcond(self._start_if[0],
101 self._start_if[2], self._preamble)
102
103 def get_content(self):
104 assert self._start_if is None
2cae67bc 105 return super().get_content()
e6c42b96
MA
106
107
108class QAPIGenC(QAPIGenCCode):
109
110 def __init__(self, fname, blurb, pydoc):
2cae67bc 111 super().__init__(fname)
e6c42b96
MA
112 self._blurb = blurb
113 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
114 re.MULTILINE))
115
116 def _top(self):
117 return mcgen('''
118/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
119
120/*
121%(blurb)s
122 *
123 * %(copyright)s
124 *
125 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
126 * See the COPYING.LIB file in the top-level directory.
127 */
128
129''',
130 blurb=self._blurb, copyright=self._copyright)
131
132 def _bottom(self):
133 return mcgen('''
134
135/* Dummy declaration to prevent empty .o file */
136char qapi_dummy_%(name)s;
137''',
138 name=c_fname(self.fname))
139
140
141class QAPIGenH(QAPIGenC):
142
143 def _top(self):
2cae67bc 144 return super()._top() + guardstart(self.fname)
e6c42b96
MA
145
146 def _bottom(self):
147 return guardend(self.fname)
148
149
150@contextmanager
151def ifcontext(ifcond, *args):
152 """A 'with' statement context manager to wrap with start_if()/end_if()
153
154 *args: any number of QAPIGenCCode
155
156 Example::
157
158 with ifcontext(ifcond, self._genh, self._genc):
159 modify self._genh and self._genc ...
160
161 Is equivalent to calling::
162
163 self._genh.start_if(ifcond)
164 self._genc.start_if(ifcond)
165 modify self._genh and self._genc ...
166 self._genh.end_if()
167 self._genc.end_if()
168 """
169 for arg in args:
170 arg.start_if(ifcond)
171 yield
172 for arg in args:
173 arg.end_if()
174
175
176class QAPIGenDoc(QAPIGen):
177
178 def _top(self):
2cae67bc 179 return (super()._top()
e6c42b96
MA
180 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
181
182
183class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
184
185 def __init__(self, prefix, what, blurb, pydoc):
186 self._prefix = prefix
187 self._what = what
188 self._genc = QAPIGenC(self._prefix + self._what + '.c',
189 blurb, pydoc)
190 self._genh = QAPIGenH(self._prefix + self._what + '.h',
191 blurb, pydoc)
192
193 def write(self, output_dir):
194 self._genc.write(output_dir)
195 self._genh.write(output_dir)
196
197
198class QAPISchemaModularCVisitor(QAPISchemaVisitor):
199
3bef3aae 200 def __init__(self, prefix, what, user_blurb, builtin_blurb, pydoc):
e6c42b96
MA
201 self._prefix = prefix
202 self._what = what
3bef3aae
MA
203 self._user_blurb = user_blurb
204 self._builtin_blurb = builtin_blurb
e6c42b96
MA
205 self._pydoc = pydoc
206 self._genc = None
207 self._genh = None
208 self._module = {}
209 self._main_module = None
210
211 @staticmethod
212 def _is_user_module(name):
213 return name and not name.startswith('./')
214
215 @staticmethod
216 def _is_builtin_module(name):
217 return not name
218
219 def _module_dirname(self, what, name):
220 if self._is_user_module(name):
221 return os.path.dirname(name)
222 return ''
223
224 def _module_basename(self, what, name):
225 ret = '' if self._is_builtin_module(name) else self._prefix
226 if self._is_user_module(name):
227 basename = os.path.basename(name)
228 ret += what
229 if name != self._main_module:
230 ret += '-' + os.path.splitext(basename)[0]
231 else:
232 name = name[2:] if name else 'builtin'
233 ret += re.sub(r'-', '-' + name + '-', what)
234 return ret
235
236 def _module_filename(self, what, name):
237 return os.path.join(self._module_dirname(what, name),
238 self._module_basename(what, name))
239
240 def _add_module(self, name, blurb):
241 basename = self._module_filename(self._what, name)
242 genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
243 genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
244 self._module[name] = (genc, genh)
3bef3aae 245 self._genc, self._genh = self._module[name]
e6c42b96
MA
246
247 def _add_user_module(self, name, blurb):
248 assert self._is_user_module(name)
249 if self._main_module is None:
250 self._main_module = name
251 self._add_module(name, blurb)
252
253 def _add_system_module(self, name, blurb):
254 self._add_module(name and './' + name, blurb)
255
e6c42b96
MA
256 def write(self, output_dir, opt_builtins=False):
257 for name in self._module:
258 if self._is_builtin_module(name) and not opt_builtins:
259 continue
260 (genc, genh) = self._module[name]
261 genc.write(output_dir)
262 genh.write(output_dir)
263
8ec0e1a4
MA
264 def _begin_system_module(self, name):
265 pass
266
e6c42b96
MA
267 def _begin_user_module(self, name):
268 pass
269
270 def visit_module(self, name):
3bef3aae
MA
271 if name is None:
272 if self._builtin_blurb:
273 self._add_system_module(None, self._builtin_blurb)
274 self._begin_system_module(name)
275 else:
276 # The built-in module has not been created. No code may
277 # be generated.
278 self._genc = None
279 self._genh = None
e6c42b96 280 else:
3bef3aae 281 self._add_user_module(name, self._user_blurb)
e6c42b96
MA
282 self._begin_user_module(name)
283
284 def visit_include(self, name, info):
285 relname = os.path.relpath(self._module_filename(self._what, name),
286 os.path.dirname(self._genh.fname))
287 self._genh.preamble_add(mcgen('''
288#include "%(relname)s.h"
289''',
290 relname=relname))