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
34 from .schema
import QAPISchemaObjectType
, QAPISchemaVisitor
35 from .source
import QAPISourceInfo
39 def __init__(self
, fname
: Optional
[str]):
44 def preamble_add(self
, text
: str) -> None:
45 self
._preamble
+= text
47 def add(self
, text
: str) -> None:
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
57 def _bottom(self
) -> str:
58 # pylint: disable=no-self-use
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('../'):
67 pathname
= os
.path
.join(output_dir
, self
.fname
)
68 odir
= os
.path
.dirname(pathname
)
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)
84 def _wrap_ifcond(ifcond
: List
[str], before
: str, after
: str) -> str:
86 return after
# suppress empty #if ... #endif
88 assert after
.startswith(before
)
90 added
= after
[len(before
):]
96 out
+= gen_endif(ifcond
)
100 def build_params(arg_type
: Optional
[QAPISchemaObjectType
],
102 extra
: Optional
[str] = None) -> str:
107 ret
+= '%s arg' % arg_type
.c_param_type()
110 assert not arg_type
.variants
111 for memb
in arg_type
.members
:
115 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
116 ret
+= '%s %s' % (memb
.type.c_param_type(),
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
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
)
152 self
._copyright
= '\n * '.join(re
.findall(r
'^Copyright .*', pydoc
,
155 def _top(self
) -> str:
157 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
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.
169 blurb
=self
._blurb
, copyright
=self
._copyright
)
171 def _bottom(self
) -> str:
174 /* Dummy declaration to prevent empty .o file */
175 char qapi_dummy_%(name)s;
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
)
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`.
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 ...
216 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor
):
222 self
._prefix
= prefix
224 self
._genc
= QAPIGenC(self
._prefix
+ self
._what
+ '.c',
226 self
._genh
= QAPIGenH(self
._prefix
+ self
._what
+ '.h',
229 def write(self
, output_dir
: str) -> None:
230 self
._genc
.write(output_dir
)
231 self
._genh
.write(output_dir
)
234 class QAPISchemaModularCVisitor(QAPISchemaVisitor
):
239 builtin_blurb
: Optional
[str],
241 self
._prefix
= prefix
243 self
._user
_blurb
= user_blurb
244 self
._builtin
_blurb
= builtin_blurb
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
252 def _is_user_module(name
: Optional
[str]) -> bool:
253 return bool(name
and not name
.startswith('./'))
256 def _is_builtin_module(name
: Optional
[str]) -> bool:
259 def _module_dirname(self
, name
: Optional
[str]) -> str:
260 if self
._is
_user
_module
(name
):
261 return os
.path
.dirname(name
)
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
)
269 if name
!= self
._main
_module
:
270 ret
+= '-' + os
.path
.splitext(basename
)[0]
272 name
= name
[2:] if name
else 'builtin'
273 ret
+= re
.sub(r
'-', '-' + name
+ '-', what
)
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
:
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:
307 def _begin_user_module(self
, name
: str) -> None:
310 def visit_module(self
, name
: Optional
[str]) -> None:
312 if self
._builtin
_blurb
:
313 self
._add
_system
_module
(None, self
._builtin
_blurb
)
314 self
._begin
_system
_module
(name
)
316 # The built-in module has not been created. No code may
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"