]>
git.proxmox.com Git - mirror_qemu.git/blob - scripts/qapi/gen.py
6a8abe004154b87dce3abde6c853d128efa78de2
1 # -*- coding: utf-8 -*-
5 # Copyright (c) 2015-2019 Red Hat Inc.
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
40 from .source
import QAPISourceInfo
43 def gen_special_features(features
: Sequence
[QAPISchemaFeature
]) -> str:
44 special_features
= [f
"1u << QAPI_{feat.name.upper()}"
45 for feat
in features
if feat
.is_special()]
46 return ' | '.join(special_features
) or '0'
50 def __init__(self
, fname
: str):
55 def preamble_add(self
, text
: str) -> None:
56 self
._preamble
+= text
58 def add(self
, text
: str) -> None:
61 def get_content(self
) -> str:
62 return self
._top
() + self
._preamble
+ self
._body
+ self
._bottom
()
64 def _top(self
) -> str:
65 # pylint: disable=no-self-use
68 def _bottom(self
) -> str:
69 # pylint: disable=no-self-use
72 def write(self
, output_dir
: str) -> None:
73 # Include paths starting with ../ are used to reuse modules of the main
74 # schema in specialised schemas. Don't overwrite the files that are
75 # already generated for the main schema.
76 if self
.fname
.startswith('../'):
78 pathname
= os
.path
.join(output_dir
, self
.fname
)
79 odir
= os
.path
.dirname(pathname
)
82 os
.makedirs(odir
, exist_ok
=True)
84 # use os.open for O_CREAT to create and read a non-existent file
85 fd
= os
.open(pathname
, os
.O_RDWR | os
.O_CREAT
, 0o666)
86 with os
.fdopen(fd
, 'r+', encoding
='utf-8') as fp
:
87 text
= self
.get_content()
88 oldtext
= fp
.read(len(text
) + 1)
95 def _wrap_ifcond(ifcond
: QAPISchemaIfCond
, before
: str, after
: str) -> str:
97 return after
# suppress empty #if ... #endif
99 assert after
.startswith(before
)
101 added
= after
[len(before
):]
105 out
+= ifcond
.gen_if()
107 out
+= ifcond
.gen_endif()
111 def build_params(arg_type
: Optional
[QAPISchemaObjectType
],
113 extra
: Optional
[str] = None) -> str:
118 ret
+= '%s arg' % arg_type
.c_param_type()
121 assert not arg_type
.branches
122 for memb
in arg_type
.members
:
123 assert not memb
.ifcond
.is_present()
127 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
128 ret
+= '%s %s' % (memb
.type.c_param_type(),
132 return ret
if ret
else 'void'
135 class QAPIGenCCode(QAPIGen
):
136 def __init__(self
, fname
: str):
137 super().__init
__(fname
)
138 self
._start
_if
: Optional
[Tuple
[QAPISchemaIfCond
, str, str]] = None
140 def start_if(self
, ifcond
: QAPISchemaIfCond
) -> None:
141 assert self
._start
_if
is None
142 self
._start
_if
= (ifcond
, self
._body
, self
._preamble
)
144 def end_if(self
) -> None:
145 assert self
._start
_if
is not None
146 self
._body
= _wrap_ifcond(self
._start
_if
[0],
147 self
._start
_if
[1], self
._body
)
148 self
._preamble
= _wrap_ifcond(self
._start
_if
[0],
149 self
._start
_if
[2], self
._preamble
)
150 self
._start
_if
= None
152 def get_content(self
) -> str:
153 assert self
._start
_if
is None
154 return super().get_content()
157 class QAPIGenC(QAPIGenCCode
):
158 def __init__(self
, fname
: str, blurb
: str, pydoc
: str):
159 super().__init
__(fname
)
161 self
._copyright
= '\n * '.join(re
.findall(r
'^Copyright .*', pydoc
,
164 def _top(self
) -> str:
166 /* AUTOMATICALLY GENERATED by %(tool)s DO NOT MODIFY */
173 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
174 * See the COPYING.LIB file in the top-level directory.
178 tool
=os
.path
.basename(sys
.argv
[0]),
179 blurb
=self
._blurb
, copyright
=self
._copyright
)
181 def _bottom(self
) -> str:
184 /* Dummy declaration to prevent empty .o file */
185 char qapi_dummy_%(name)s;
187 name
=c_fname(self
.fname
))
190 class QAPIGenH(QAPIGenC
):
191 def _top(self
) -> str:
192 return super()._top
() + guardstart(self
.fname
)
194 def _bottom(self
) -> str:
195 return guardend(self
.fname
)
198 class QAPIGenTrace(QAPIGen
):
199 def _top(self
) -> str:
200 return (super()._top
()
201 + '# AUTOMATICALLY GENERATED by '
202 + os
.path
.basename(sys
.argv
[0])
203 + ', DO NOT MODIFY\n\n')
207 def ifcontext(ifcond
: QAPISchemaIfCond
, *args
: QAPIGenCCode
) -> Iterator
[None]:
209 A with-statement context manager that wraps with `start_if()` / `end_if()`.
211 :param ifcond: A sequence of conditionals, passed to `start_if()`.
212 :param args: any number of `QAPIGenCCode`.
216 with ifcontext(ifcond, self._genh, self._genc):
217 modify self._genh and self._genc ...
219 Is equivalent to calling::
221 self._genh.start_if(ifcond)
222 self._genc.start_if(ifcond)
223 modify self._genh and self._genc ...
234 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor
):
240 self
._prefix
= prefix
242 self
._genc
= QAPIGenC(self
._prefix
+ self
._what
+ '.c',
244 self
._genh
= QAPIGenH(self
._prefix
+ self
._what
+ '.h',
247 def write(self
, output_dir
: str) -> None:
248 self
._genc
.write(output_dir
)
249 self
._genh
.write(output_dir
)
252 class QAPISchemaModularCVisitor(QAPISchemaVisitor
):
257 builtin_blurb
: Optional
[str],
259 gen_tracing
: bool = False):
260 self
._prefix
= prefix
262 self
._user
_blurb
= user_blurb
263 self
._builtin
_blurb
= builtin_blurb
265 self
._current
_module
: Optional
[str] = None
266 self
._module
: Dict
[str, Tuple
[QAPIGenC
, QAPIGenH
,
267 Optional
[QAPIGenTrace
]]] = {}
268 self
._main
_module
: Optional
[str] = None
269 self
._gen
_tracing
= gen_tracing
272 def _genc(self
) -> QAPIGenC
:
273 assert self
._current
_module
is not None
274 return self
._module
[self
._current
_module
][0]
277 def _genh(self
) -> QAPIGenH
:
278 assert self
._current
_module
is not None
279 return self
._module
[self
._current
_module
][1]
282 def _gen_trace_events(self
) -> QAPIGenTrace
:
283 assert self
._gen
_tracing
284 assert self
._current
_module
is not None
285 gent
= self
._module
[self
._current
_module
][2]
286 assert gent
is not None
290 def _module_dirname(name
: str) -> str:
291 if QAPISchemaModule
.is_user_module(name
):
292 return os
.path
.dirname(name
)
295 def _module_basename(self
, what
: str, name
: str) -> str:
296 ret
= '' if QAPISchemaModule
.is_builtin_module(name
) else self
._prefix
297 if QAPISchemaModule
.is_user_module(name
):
298 basename
= os
.path
.basename(name
)
300 if name
!= self
._main
_module
:
301 ret
+= '-' + os
.path
.splitext(basename
)[0]
303 assert QAPISchemaModule
.is_system_module(name
)
304 ret
+= re
.sub(r
'-', '-' + name
[2:] + '-', what
)
307 def _module_filename(self
, what
: str, name
: str) -> str:
308 return os
.path
.join(self
._module
_dirname
(name
),
309 self
._module
_basename
(what
, name
))
311 def _add_module(self
, name
: str, blurb
: str) -> None:
312 if QAPISchemaModule
.is_user_module(name
):
313 if self
._main
_module
is None:
314 self
._main
_module
= name
315 basename
= self
._module
_filename
(self
._what
, name
)
316 genc
= QAPIGenC(basename
+ '.c', blurb
, self
._pydoc
)
317 genh
= QAPIGenH(basename
+ '.h', blurb
, self
._pydoc
)
319 gent
: Optional
[QAPIGenTrace
] = None
320 if self
._gen
_tracing
:
321 gent
= QAPIGenTrace(basename
+ '.trace-events')
323 self
._module
[name
] = (genc
, genh
, gent
)
324 self
._current
_module
= name
327 def _temp_module(self
, name
: str) -> Iterator
[None]:
328 old_module
= self
._current
_module
329 self
._current
_module
= name
331 self
._current
_module
= old_module
333 def write(self
, output_dir
: str, opt_builtins
: bool = False) -> None:
334 for name
, (genc
, genh
, gent
) in self
._module
.items():
335 if QAPISchemaModule
.is_builtin_module(name
) and not opt_builtins
:
337 genc
.write(output_dir
)
338 genh
.write(output_dir
)
340 gent
.write(output_dir
)
342 def _begin_builtin_module(self
) -> None:
345 def _begin_user_module(self
, name
: str) -> None:
348 def visit_module(self
, name
: str) -> None:
349 if QAPISchemaModule
.is_builtin_module(name
):
350 if self
._builtin
_blurb
:
351 self
._add
_module
(name
, self
._builtin
_blurb
)
352 self
._begin
_builtin
_module
()
354 # The built-in module has not been created. No code may
356 self
._current
_module
= None
358 assert QAPISchemaModule
.is_user_module(name
)
359 self
._add
_module
(name
, self
._user
_blurb
)
360 self
._begin
_user
_module
(name
)
362 def visit_include(self
, name
: str, info
: Optional
[QAPISourceInfo
]) -> None:
363 relname
= os
.path
.relpath(self
._module
_filename
(self
._what
, name
),
364 os
.path
.dirname(self
._genh
.fname
))
365 self
._genh
.preamble_add(mcgen('''
366 #include "%(relname)s.h"