hw/i386/acpi-build: Fix maybe-uninitialized error when ACPI hotplug off
[qemu/ar7.git] / scripts / qapi / gen.py
blobb40f18eee3cdc7f199a67e91ca4718e3bec93579
1 # -*- coding: utf-8 -*-
3 # QAPI code generation
5 # Copyright (c) 2015-2019 Red Hat Inc.
7 # Authors:
8 # Markus Armbruster <armbru@redhat.com>
9 # Marc-André Lureau <marcandre.lureau@redhat.com>
11 # This work is licensed under the terms of the GNU GPL, version 2.
12 # See the COPYING file in the top-level directory.
14 from contextlib import contextmanager
15 import os
16 import re
17 from typing import (
18 Dict,
19 Iterator,
20 List,
21 Optional,
22 Tuple,
25 from .common import (
26 c_fname,
27 c_name,
28 gen_endif,
29 gen_if,
30 guardend,
31 guardstart,
32 mcgen,
34 from .schema import QAPISchemaObjectType, QAPISchemaVisitor
35 from .source import QAPISourceInfo
38 class QAPIGen:
39 def __init__(self, fname: Optional[str]):
40 self.fname = fname
41 self._preamble = ''
42 self._body = ''
44 def preamble_add(self, text: str) -> None:
45 self._preamble += text
47 def add(self, text: str) -> None:
48 self._body += text
50 def get_content(self) -> str:
51 return self._top() + self._preamble + self._body + self._bottom()
53 def _top(self) -> str:
54 # pylint: disable=no-self-use
55 return ''
57 def _bottom(self) -> str:
58 # pylint: disable=no-self-use
59 return ''
61 def write(self, output_dir: str) -> None:
62 # Include paths starting with ../ are used to reuse modules of the main
63 # schema in specialised schemas. Don't overwrite the files that are
64 # already generated for the main schema.
65 if self.fname.startswith('../'):
66 return
67 pathname = os.path.join(output_dir, self.fname)
68 odir = os.path.dirname(pathname)
70 if odir:
71 os.makedirs(odir, exist_ok=True)
73 # use os.open for O_CREAT to create and read a non-existant file
74 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
75 with os.fdopen(fd, 'r+', encoding='utf-8') as fp:
76 text = self.get_content()
77 oldtext = fp.read(len(text) + 1)
78 if text != oldtext:
79 fp.seek(0)
80 fp.truncate(0)
81 fp.write(text)
84 def _wrap_ifcond(ifcond: List[str], before: str, after: str) -> str:
85 if before == after:
86 return after # suppress empty #if ... #endif
88 assert after.startswith(before)
89 out = before
90 added = after[len(before):]
91 if added[0] == '\n':
92 out += '\n'
93 added = added[1:]
94 out += gen_if(ifcond)
95 out += added
96 out += gen_endif(ifcond)
97 return out
100 def build_params(arg_type: Optional[QAPISchemaObjectType],
101 boxed: bool,
102 extra: Optional[str] = None) -> str:
103 ret = ''
104 sep = ''
105 if boxed:
106 assert arg_type
107 ret += '%s arg' % arg_type.c_param_type()
108 sep = ', '
109 elif arg_type:
110 assert not arg_type.variants
111 for memb in arg_type.members:
112 ret += sep
113 sep = ', '
114 if memb.optional:
115 ret += 'bool has_%s, ' % c_name(memb.name)
116 ret += '%s %s' % (memb.type.c_param_type(),
117 c_name(memb.name))
118 if extra:
119 ret += sep + extra
120 return ret if ret else 'void'
123 class QAPIGenCCode(QAPIGen):
124 def __init__(self, fname: Optional[str]):
125 super().__init__(fname)
126 self._start_if: Optional[Tuple[List[str], str, str]] = None
128 def start_if(self, ifcond: List[str]) -> None:
129 assert self._start_if is None
130 self._start_if = (ifcond, self._body, self._preamble)
132 def end_if(self) -> None:
133 assert self._start_if
134 self._wrap_ifcond()
135 self._start_if = None
137 def _wrap_ifcond(self) -> None:
138 self._body = _wrap_ifcond(self._start_if[0],
139 self._start_if[1], self._body)
140 self._preamble = _wrap_ifcond(self._start_if[0],
141 self._start_if[2], self._preamble)
143 def get_content(self) -> str:
144 assert self._start_if is None
145 return super().get_content()
148 class QAPIGenC(QAPIGenCCode):
149 def __init__(self, fname: str, blurb: str, pydoc: str):
150 super().__init__(fname)
151 self._blurb = blurb
152 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
153 re.MULTILINE))
155 def _top(self) -> str:
156 return mcgen('''
157 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
160 %(blurb)s
162 * %(copyright)s
164 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
165 * See the COPYING.LIB file in the top-level directory.
168 ''',
169 blurb=self._blurb, copyright=self._copyright)
171 def _bottom(self) -> str:
172 return mcgen('''
174 /* Dummy declaration to prevent empty .o file */
175 char qapi_dummy_%(name)s;
176 ''',
177 name=c_fname(self.fname))
180 class QAPIGenH(QAPIGenC):
181 def _top(self) -> str:
182 return super()._top() + guardstart(self.fname)
184 def _bottom(self) -> str:
185 return guardend(self.fname)
188 @contextmanager
189 def ifcontext(ifcond: List[str], *args: QAPIGenCCode) -> Iterator[None]:
191 A with-statement context manager that wraps with `start_if()` / `end_if()`.
193 :param ifcond: A list of conditionals, passed to `start_if()`.
194 :param args: any number of `QAPIGenCCode`.
196 Example::
198 with ifcontext(ifcond, self._genh, self._genc):
199 modify self._genh and self._genc ...
201 Is equivalent to calling::
203 self._genh.start_if(ifcond)
204 self._genc.start_if(ifcond)
205 modify self._genh and self._genc ...
206 self._genh.end_if()
207 self._genc.end_if()
209 for arg in args:
210 arg.start_if(ifcond)
211 yield
212 for arg in args:
213 arg.end_if()
216 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
217 def __init__(self,
218 prefix: str,
219 what: str,
220 blurb: str,
221 pydoc: str):
222 self._prefix = prefix
223 self._what = what
224 self._genc = QAPIGenC(self._prefix + self._what + '.c',
225 blurb, pydoc)
226 self._genh = QAPIGenH(self._prefix + self._what + '.h',
227 blurb, pydoc)
229 def write(self, output_dir: str) -> None:
230 self._genc.write(output_dir)
231 self._genh.write(output_dir)
234 class QAPISchemaModularCVisitor(QAPISchemaVisitor):
235 def __init__(self,
236 prefix: str,
237 what: str,
238 user_blurb: str,
239 builtin_blurb: Optional[str],
240 pydoc: str):
241 self._prefix = prefix
242 self._what = what
243 self._user_blurb = user_blurb
244 self._builtin_blurb = builtin_blurb
245 self._pydoc = pydoc
246 self._genc: Optional[QAPIGenC] = None
247 self._genh: Optional[QAPIGenH] = None
248 self._module: Dict[Optional[str], Tuple[QAPIGenC, QAPIGenH]] = {}
249 self._main_module: Optional[str] = None
251 @staticmethod
252 def _is_user_module(name: Optional[str]) -> bool:
253 return bool(name and not name.startswith('./'))
255 @staticmethod
256 def _is_builtin_module(name: Optional[str]) -> bool:
257 return not name
259 def _module_dirname(self, name: Optional[str]) -> str:
260 if self._is_user_module(name):
261 return os.path.dirname(name)
262 return ''
264 def _module_basename(self, what: str, name: Optional[str]) -> str:
265 ret = '' if self._is_builtin_module(name) else self._prefix
266 if self._is_user_module(name):
267 basename = os.path.basename(name)
268 ret += what
269 if name != self._main_module:
270 ret += '-' + os.path.splitext(basename)[0]
271 else:
272 name = name[2:] if name else 'builtin'
273 ret += re.sub(r'-', '-' + name + '-', what)
274 return ret
276 def _module_filename(self, what: str, name: Optional[str]) -> str:
277 return os.path.join(self._module_dirname(name),
278 self._module_basename(what, name))
280 def _add_module(self, name: Optional[str], blurb: str) -> None:
281 basename = self._module_filename(self._what, name)
282 genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
283 genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
284 self._module[name] = (genc, genh)
285 self._genc, self._genh = self._module[name]
287 def _add_user_module(self, name: str, blurb: str) -> None:
288 assert self._is_user_module(name)
289 if self._main_module is None:
290 self._main_module = name
291 self._add_module(name, blurb)
293 def _add_system_module(self, name: Optional[str], blurb: str) -> None:
294 self._add_module(name and './' + name, blurb)
296 def write(self, output_dir: str, opt_builtins: bool = False) -> None:
297 for name in self._module:
298 if self._is_builtin_module(name) and not opt_builtins:
299 continue
300 (genc, genh) = self._module[name]
301 genc.write(output_dir)
302 genh.write(output_dir)
304 def _begin_system_module(self, name: None) -> None:
305 pass
307 def _begin_user_module(self, name: str) -> None:
308 pass
310 def visit_module(self, name: Optional[str]) -> None:
311 if name is None:
312 if self._builtin_blurb:
313 self._add_system_module(None, self._builtin_blurb)
314 self._begin_system_module(name)
315 else:
316 # The built-in module has not been created. No code may
317 # be generated.
318 self._genc = None
319 self._genh = None
320 else:
321 self._add_user_module(name, self._user_blurb)
322 self._begin_user_module(name)
324 def visit_include(self, name: str, info: QAPISourceInfo) -> None:
325 relname = os.path.relpath(self._module_filename(self._what, name),
326 os.path.dirname(self._genh.fname))
327 self._genh.preamble_add(mcgen('''
328 #include "%(relname)s.h"
329 ''',
330 relname=relname))