1 # -*- coding: utf-8 -*-
5 # Copyright (c) 2015-2019 Red Hat Inc.
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
39 from .source import QAPISourceInfo
43 def __init__(self, fname: str):
48 def preamble_add(self, text: str) -> None:
49 self._preamble += text
51 def add(self, text: str) -> None:
54 def get_content(self) -> str:
55 return self._top() + self._preamble + self._body + self._bottom()
57 def _top(self) -> str:
58 # pylint: disable=no-self-use
61 def _bottom(self) -> str:
62 # pylint: disable=no-self-use
65 def write(self, output_dir: str) -> None:
66 # Include paths starting with ../ are used to reuse modules of the main
67 # schema in specialised schemas. Don't overwrite the files that are
68 # already generated for the main schema.
69 if self.fname.startswith('../'):
71 pathname = os.path.join(output_dir, self.fname)
72 odir = os.path.dirname(pathname)
75 os.makedirs(odir, exist_ok=True)
77 # use os.open for O_CREAT to create and read a non-existant file
78 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
79 with os.fdopen(fd, 'r+', encoding='utf-8') as fp:
80 text = self.get_content()
81 oldtext = fp.read(len(text) + 1)
88 def _wrap_ifcond(ifcond: List[str], before: str, after: str) -> str:
90 return after # suppress empty #if ... #endif
92 assert after.startswith(before)
94 added = after[len(before):]
100 out += gen_endif(ifcond)
104 def build_params(arg_type: Optional[QAPISchemaObjectType],
106 extra: Optional[str] = None) -> str:
111 ret += '%s arg' % arg_type.c_param_type()
114 assert not arg_type.variants
115 for memb in arg_type.members:
119 ret += 'bool has_%s, ' % c_name(memb.name)
120 ret += '%s %s' % (memb.type.c_param_type(),
124 return ret if ret else 'void'
127 class QAPIGenCCode(QAPIGen):
128 def __init__(self, fname: str):
129 super().__init__(fname)
130 self._start_if: Optional[Tuple[List[str], str, str]] = None
132 def start_if(self, ifcond: List[str]) -> None:
133 assert self._start_if is None
134 self._start_if = (ifcond, self._body, self._preamble)
136 def end_if(self) -> None:
137 assert self._start_if is not 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)
142 self._start_if = None
144 def get_content(self) -> str:
145 assert self._start_if is None
146 return super().get_content()
149 class QAPIGenC(QAPIGenCCode):
150 def __init__(self, fname: str, blurb: str, pydoc: str):
151 super().__init__(fname)
153 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
156 def _top(self) -> str:
158 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
165 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
166 * See the COPYING.LIB file in the top-level directory.
170 blurb=self._blurb, copyright=self._copyright)
172 def _bottom(self) -> str:
175 /* Dummy declaration to prevent empty .o file */
176 char qapi_dummy_%(name)s;
178 name=c_fname(self.fname))
181 class QAPIGenH(QAPIGenC):
182 def _top(self) -> str:
183 return super()._top() + guardstart(self.fname)
185 def _bottom(self) -> str:
186 return guardend(self.fname)
190 def ifcontext(ifcond: List[str], *args: QAPIGenCCode) -> Iterator[None]:
192 A with-statement context manager that wraps with `start_if()` / `end_if()`.
194 :param ifcond: A list of conditionals, passed to `start_if()`.
195 :param args: any number of `QAPIGenCCode`.
199 with ifcontext(ifcond, self._genh, self._genc):
200 modify self._genh and self._genc ...
202 Is equivalent to calling::
204 self._genh.start_if(ifcond)
205 self._genc.start_if(ifcond)
206 modify self._genh and self._genc ...
217 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
223 self._prefix = prefix
225 self._genc = QAPIGenC(self._prefix + self._what + '.c',
227 self._genh = QAPIGenH(self._prefix + self._what + '.h',
230 def write(self, output_dir: str) -> None:
231 self._genc.write(output_dir)
232 self._genh.write(output_dir)
235 class QAPISchemaModularCVisitor(QAPISchemaVisitor):
240 builtin_blurb: Optional[str],
242 self._prefix = prefix
244 self._user_blurb = user_blurb
245 self._builtin_blurb = builtin_blurb
247 self._current_module: Optional[str] = None
248 self._module: Dict[str, Tuple[QAPIGenC, QAPIGenH]] = {}
249 self._main_module: Optional[str] = None
252 def _genc(self) -> QAPIGenC:
253 assert self._current_module is not None
254 return self._module[self._current_module][0]
257 def _genh(self) -> QAPIGenH:
258 assert self._current_module is not None
259 return self._module[self._current_module][1]
262 def _module_dirname(name: str) -> str:
263 if QAPISchemaModule.is_user_module(name):
264 return os.path.dirname(name)
267 def _module_basename(self, what: str, name: str) -> str:
268 ret = '' if QAPISchemaModule.is_builtin_module(name) else self._prefix
269 if QAPISchemaModule.is_user_module(name):
270 basename = os.path.basename(name)
272 if name != self._main_module:
273 ret += '-' + os.path.splitext(basename)[0]
275 assert QAPISchemaModule.is_system_module(name)
276 ret += re.sub(r'-', '-' + name[2:] + '-', what)
279 def _module_filename(self, what: str, name: str) -> str:
280 return os.path.join(self._module_dirname(name),
281 self._module_basename(what, name))
283 def _add_module(self, name: str, blurb: str) -> None:
284 if QAPISchemaModule.is_user_module(name):
285 if self._main_module is None:
286 self._main_module = name
287 basename = self._module_filename(self._what, name)
288 genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
289 genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
290 self._module[name] = (genc, genh)
291 self._current_module = name
294 def _temp_module(self, name: str) -> Iterator[None]:
295 old_module = self._current_module
296 self._current_module = name
298 self._current_module = old_module
300 def write(self, output_dir: str, opt_builtins: bool = False) -> None:
301 for name in self._module:
302 if QAPISchemaModule.is_builtin_module(name) and not opt_builtins:
304 (genc, genh) = self._module[name]
305 genc.write(output_dir)
306 genh.write(output_dir)
308 def _begin_builtin_module(self) -> None:
311 def _begin_user_module(self, name: str) -> None:
314 def visit_module(self, name: str) -> None:
315 if QAPISchemaModule.is_builtin_module(name):
316 if self._builtin_blurb:
317 self._add_module(name, self._builtin_blurb)
318 self._begin_builtin_module()
320 # The built-in module has not been created. No code may
322 self._current_module = None
324 assert QAPISchemaModule.is_user_module(name)
325 self._add_module(name, self._user_blurb)
326 self._begin_user_module(name)
328 def visit_include(self, name: str, info: Optional[QAPISourceInfo]) -> None:
329 relname = os.path.relpath(self._module_filename(self._what, name),
330 os.path.dirname(self._genh.fname))
331 self._genh.preamble_add(mcgen('''
332 #include "%(relname)s.h"