| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | # -*- coding: utf-8 -*- | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # QAPI code generation | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:39 -04:00
										 |  |  | # Copyright (c) 2015-2019 Red Hat Inc. | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | # | 
					
						
							|  |  |  | # Authors: | 
					
						
							|  |  |  | #  Markus Armbruster <armbru@redhat.com> | 
					
						
							|  |  |  | #  Marc-André Lureau <marcandre.lureau@redhat.com> | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # This work is licensed under the terms of the GNU GPL, version 2. | 
					
						
							|  |  |  | # See the COPYING file in the top-level directory. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:28 -04:00
										 |  |  | from contextlib import contextmanager | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2023-05-26 17:53:54 +01:00
										 |  |  | import sys | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | import re | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  | from typing import ( | 
					
						
							|  |  |  |     Dict, | 
					
						
							|  |  |  |     Iterator, | 
					
						
							|  |  |  |     Optional, | 
					
						
							| 
									
										
										
										
											2021-10-28 12:25:15 +02:00
										 |  |  |     Sequence, | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     Tuple, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:28 -04:00
										 |  |  | from .common import ( | 
					
						
							|  |  |  |     c_fname, | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:39 -04:00
										 |  |  |     c_name, | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:28 -04:00
										 |  |  |     guardend, | 
					
						
							|  |  |  |     guardstart, | 
					
						
							|  |  |  |     mcgen, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:36 -05:00
										 |  |  | from .schema import ( | 
					
						
							| 
									
										
										
										
											2021-10-28 12:25:15 +02:00
										 |  |  |     QAPISchemaFeature, | 
					
						
							| 
									
										
										
										
											2021-08-04 12:30:57 +04:00
										 |  |  |     QAPISchemaIfCond, | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:36 -05:00
										 |  |  |     QAPISchemaModule, | 
					
						
							|  |  |  |     QAPISchemaObjectType, | 
					
						
							|  |  |  |     QAPISchemaVisitor, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  | from .source import QAPISourceInfo | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-28 12:25:15 +02:00
										 |  |  | def gen_special_features(features: Sequence[QAPISchemaFeature]) -> str: | 
					
						
							|  |  |  |     special_features = [f"1u << QAPI_{feat.name.upper()}" | 
					
						
							|  |  |  |                         for feat in features if feat.is_special()] | 
					
						
							|  |  |  |     return ' | '.join(special_features) or '0' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-04 16:59:29 +01:00
										 |  |  | class QAPIGen: | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:45 -05:00
										 |  |  |     def __init__(self, fname: str): | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         self.fname = fname | 
					
						
							|  |  |  |         self._preamble = '' | 
					
						
							|  |  |  |         self._body = '' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     def preamble_add(self, text: str) -> None: | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         self._preamble += text | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     def add(self, text: str) -> None: | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         self._body += text | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     def get_content(self) -> str: | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         return self._top() + self._preamble + self._body + self._bottom() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     def _top(self) -> str: | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:53 -04:00
										 |  |  |         # pylint: disable=no-self-use | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         return '' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     def _bottom(self) -> str: | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:53 -04:00
										 |  |  |         # pylint: disable=no-self-use | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         return '' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     def write(self, output_dir: str) -> None: | 
					
						
							| 
									
										
										
										
											2020-02-24 15:30:08 +01:00
										 |  |  |         # Include paths starting with ../ are used to reuse modules of the main | 
					
						
							|  |  |  |         # schema in specialised schemas. Don't overwrite the files that are | 
					
						
							|  |  |  |         # already generated for the main schema. | 
					
						
							|  |  |  |         if self.fname.startswith('../'): | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         pathname = os.path.join(output_dir, self.fname) | 
					
						
							| 
									
										
										
										
											2020-03-04 16:59:32 +01:00
										 |  |  |         odir = os.path.dirname(pathname) | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:52 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-04 16:59:32 +01:00
										 |  |  |         if odir: | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:52 -04:00
										 |  |  |             os.makedirs(odir, exist_ok=True) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # use os.open for O_CREAT to create and read a non-existant file | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666) | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:52 -04:00
										 |  |  |         with os.fdopen(fd, 'r+', encoding='utf-8') as fp: | 
					
						
							|  |  |  |             text = self.get_content() | 
					
						
							|  |  |  |             oldtext = fp.read(len(text) + 1) | 
					
						
							|  |  |  |             if text != oldtext: | 
					
						
							|  |  |  |                 fp.seek(0) | 
					
						
							|  |  |  |                 fp.truncate(0) | 
					
						
							|  |  |  |                 fp.write(text) | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-04 12:30:57 +04:00
										 |  |  | def _wrap_ifcond(ifcond: QAPISchemaIfCond, before: str, after: str) -> str: | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |     if before == after: | 
					
						
							|  |  |  |         return after   # suppress empty #if ... #endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert after.startswith(before) | 
					
						
							|  |  |  |     out = before | 
					
						
							|  |  |  |     added = after[len(before):] | 
					
						
							|  |  |  |     if added[0] == '\n': | 
					
						
							|  |  |  |         out += '\n' | 
					
						
							|  |  |  |         added = added[1:] | 
					
						
							| 
									
										
										
										
											2021-08-31 14:37:58 +02:00
										 |  |  |     out += ifcond.gen_if() | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |     out += added | 
					
						
							| 
									
										
										
										
											2021-08-31 14:37:58 +02:00
										 |  |  |     out += ifcond.gen_endif() | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |     return out | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:39 -04:00
										 |  |  | def build_params(arg_type: Optional[QAPISchemaObjectType], | 
					
						
							|  |  |  |                  boxed: bool, | 
					
						
							|  |  |  |                  extra: Optional[str] = None) -> str: | 
					
						
							|  |  |  |     ret = '' | 
					
						
							|  |  |  |     sep = '' | 
					
						
							|  |  |  |     if boxed: | 
					
						
							|  |  |  |         assert arg_type | 
					
						
							|  |  |  |         ret += '%s arg' % arg_type.c_param_type() | 
					
						
							|  |  |  |         sep = ', ' | 
					
						
							|  |  |  |     elif arg_type: | 
					
						
							|  |  |  |         assert not arg_type.variants | 
					
						
							|  |  |  |         for memb in arg_type.members: | 
					
						
							| 
									
										
										
										
											2023-03-16 08:13:25 +01:00
										 |  |  |             assert not memb.ifcond.is_present() | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:39 -04:00
										 |  |  |             ret += sep | 
					
						
							|  |  |  |             sep = ', ' | 
					
						
							| 
									
										
											  
											
												qapi: Start to elide redundant has_FOO in generated C
In QAPI, absent optional members are distinct from any present value.
We thus represent an optional schema member FOO as two C members: a
FOO with the member's type, and a bool has_FOO.  Likewise for function
arguments.
However, has_FOO is actually redundant for a pointer-valued FOO, which
can be null only when has_FOO is false, i.e. has_FOO == !!FOO.  Except
for arrays, where we a null FOO can also be a present empty array.
The redundant has_FOO are a nuisance to work with.  Improve the
generator to elide them.  Uses of has_FOO need to be replaced as
follows.
Tests of has_FOO become the equivalent comparison of FOO with null.
For brevity, this is commonly done by implicit conversion to bool.
Assignments to has_FOO get dropped.
Likewise for arguments to has_FOO parameters.
Beware: code may violate the invariant has_FOO == !!FOO before the
transformation, and get away with it.  The above transformation can
then break things.  Two cases:
* Absent: if code ignores FOO entirely when !has_FOO (except for
  freeing it if necessary), even non-null / uninitialized FOO works.
  Such code is known to exist.
* Present: if code ignores FOO entirely when has_FOO, even null FOO
  works.  Such code should not exist.
In both cases, replacing tests of has_FOO by FOO reverts their sense.
We have to fix the value of FOO then.
To facilitate review of the necessary updates to handwritten code, add
means to opt out of this change, and opt out for all QAPI schema
modules where the change requires updates to handwritten code.  The
next few commits will remove these opt-outs in reviewable chunks, then
drop the means to opt out.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Message-Id: <20221104160712.3005652-5-armbru@redhat.com>
											
										 
											2022-11-04 17:06:46 +01:00
										 |  |  |             if memb.need_has(): | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:39 -04:00
										 |  |  |                 ret += 'bool has_%s, ' % c_name(memb.name) | 
					
						
							|  |  |  |             ret += '%s %s' % (memb.type.c_param_type(), | 
					
						
							|  |  |  |                               c_name(memb.name)) | 
					
						
							|  |  |  |     if extra: | 
					
						
							|  |  |  |         ret += sep + extra | 
					
						
							|  |  |  |     return ret if ret else 'void' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | class QAPIGenCCode(QAPIGen): | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:45 -05:00
										 |  |  |     def __init__(self, fname: str): | 
					
						
							| 
									
										
										
										
											2020-03-04 16:59:31 +01:00
										 |  |  |         super().__init__(fname) | 
					
						
							| 
									
										
										
										
											2021-08-04 12:30:57 +04:00
										 |  |  |         self._start_if: Optional[Tuple[QAPISchemaIfCond, str, str]] = None | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-04 12:30:57 +04:00
										 |  |  |     def start_if(self, ifcond: QAPISchemaIfCond) -> None: | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         assert self._start_if is None | 
					
						
							|  |  |  |         self._start_if = (ifcond, self._body, self._preamble) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     def end_if(self) -> None: | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:35 -05:00
										 |  |  |         assert self._start_if is not None | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         self._body = _wrap_ifcond(self._start_if[0], | 
					
						
							|  |  |  |                                   self._start_if[1], self._body) | 
					
						
							|  |  |  |         self._preamble = _wrap_ifcond(self._start_if[0], | 
					
						
							|  |  |  |                                       self._start_if[2], self._preamble) | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:35 -05:00
										 |  |  |         self._start_if = None | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     def get_content(self) -> str: | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         assert self._start_if is None | 
					
						
							| 
									
										
										
										
											2020-03-04 16:59:31 +01:00
										 |  |  |         return super().get_content() | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class QAPIGenC(QAPIGenCCode): | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     def __init__(self, fname: str, blurb: str, pydoc: str): | 
					
						
							| 
									
										
										
										
											2020-03-04 16:59:31 +01:00
										 |  |  |         super().__init__(fname) | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         self._blurb = blurb | 
					
						
							|  |  |  |         self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc, | 
					
						
							|  |  |  |                                                   re.MULTILINE)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     def _top(self) -> str: | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         return mcgen('''
 | 
					
						
							| 
									
										
										
										
											2023-05-26 17:53:54 +01:00
										 |  |  | /* AUTOMATICALLY GENERATED by %(tool)s DO NOT MODIFY */ | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* | 
					
						
							|  |  |  | %(blurb)s | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * %(copyright)s | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | 
					
						
							|  |  |  |  * See the COPYING.LIB file in the top-level directory. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ''',
 | 
					
						
							| 
									
										
										
										
											2023-05-26 17:53:54 +01:00
										 |  |  |                      tool=os.path.basename(sys.argv[0]), | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |                      blurb=self._blurb, copyright=self._copyright) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     def _bottom(self) -> str: | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         return mcgen('''
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Dummy declaration to prevent empty .o file */ | 
					
						
							|  |  |  | char qapi_dummy_%(name)s; | 
					
						
							|  |  |  | ''',
 | 
					
						
							|  |  |  |                      name=c_fname(self.fname)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class QAPIGenH(QAPIGenC): | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     def _top(self) -> str: | 
					
						
							| 
									
										
										
										
											2020-03-04 16:59:31 +01:00
										 |  |  |         return super()._top() + guardstart(self.fname) | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     def _bottom(self) -> str: | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         return guardend(self.fname) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-26 17:11:24 +01:00
										 |  |  | class QAPIGenTrace(QAPIGen): | 
					
						
							|  |  |  |     def _top(self) -> str: | 
					
						
							| 
									
										
										
										
											2023-05-26 17:53:54 +01:00
										 |  |  |         return (super()._top() | 
					
						
							|  |  |  |                 + '# AUTOMATICALLY GENERATED by ' | 
					
						
							|  |  |  |                 + os.path.basename(sys.argv[0]) | 
					
						
							|  |  |  |                 + ', DO NOT MODIFY\n\n') | 
					
						
							| 
									
										
										
										
											2022-01-26 17:11:24 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | @contextmanager | 
					
						
							| 
									
										
										
										
											2021-08-04 12:30:57 +04:00
										 |  |  | def ifcontext(ifcond: QAPISchemaIfCond, *args: QAPIGenCCode) -> Iterator[None]: | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:24 -04:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     A with-statement context manager that wraps with `start_if()` / `end_if()`. | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-15 21:17:51 -05:00
										 |  |  |     :param ifcond: A sequence of conditionals, passed to `start_if()`. | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:24 -04:00
										 |  |  |     :param args: any number of `QAPIGenCCode`. | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Example:: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         with ifcontext(ifcond, self._genh, self._genc): | 
					
						
							|  |  |  |             modify self._genh and self._genc ... | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Is equivalent to calling:: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._genh.start_if(ifcond) | 
					
						
							|  |  |  |         self._genc.start_if(ifcond) | 
					
						
							|  |  |  |         modify self._genh and self._genc ... | 
					
						
							|  |  |  |         self._genh.end_if() | 
					
						
							|  |  |  |         self._genc.end_if() | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     for arg in args: | 
					
						
							|  |  |  |         arg.start_if(ifcond) | 
					
						
							|  |  |  |     yield | 
					
						
							|  |  |  |     for arg in args: | 
					
						
							|  |  |  |         arg.end_if() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor): | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     def __init__(self, | 
					
						
							|  |  |  |                  prefix: str, | 
					
						
							|  |  |  |                  what: str, | 
					
						
							|  |  |  |                  blurb: str, | 
					
						
							|  |  |  |                  pydoc: str): | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         self._prefix = prefix | 
					
						
							|  |  |  |         self._what = what | 
					
						
							|  |  |  |         self._genc = QAPIGenC(self._prefix + self._what + '.c', | 
					
						
							|  |  |  |                               blurb, pydoc) | 
					
						
							|  |  |  |         self._genh = QAPIGenH(self._prefix + self._what + '.h', | 
					
						
							|  |  |  |                               blurb, pydoc) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     def write(self, output_dir: str) -> None: | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         self._genc.write(output_dir) | 
					
						
							|  |  |  |         self._genh.write(output_dir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class QAPISchemaModularCVisitor(QAPISchemaVisitor): | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     def __init__(self, | 
					
						
							|  |  |  |                  prefix: str, | 
					
						
							|  |  |  |                  what: str, | 
					
						
							|  |  |  |                  user_blurb: str, | 
					
						
							|  |  |  |                  builtin_blurb: Optional[str], | 
					
						
							| 
									
										
										
										
											2022-01-26 17:11:24 +01:00
										 |  |  |                  pydoc: str, | 
					
						
							|  |  |  |                  gen_tracing: bool = False): | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         self._prefix = prefix | 
					
						
							|  |  |  |         self._what = what | 
					
						
							| 
									
										
										
										
											2019-11-20 19:25:51 +01:00
										 |  |  |         self._user_blurb = user_blurb | 
					
						
							|  |  |  |         self._builtin_blurb = builtin_blurb | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         self._pydoc = pydoc | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:42 -05:00
										 |  |  |         self._current_module: Optional[str] = None | 
					
						
							| 
									
										
										
										
											2022-01-26 17:11:24 +01:00
										 |  |  |         self._module: Dict[str, Tuple[QAPIGenC, QAPIGenH, | 
					
						
							|  |  |  |                                       Optional[QAPIGenTrace]]] = {} | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |         self._main_module: Optional[str] = None | 
					
						
							| 
									
										
										
										
											2022-01-26 17:11:24 +01:00
										 |  |  |         self._gen_tracing = gen_tracing | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:42 -05:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def _genc(self) -> QAPIGenC: | 
					
						
							|  |  |  |         assert self._current_module is not None | 
					
						
							|  |  |  |         return self._module[self._current_module][0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def _genh(self) -> QAPIGenH: | 
					
						
							|  |  |  |         assert self._current_module is not None | 
					
						
							|  |  |  |         return self._module[self._current_module][1] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-26 17:11:24 +01:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def _gen_trace_events(self) -> QAPIGenTrace: | 
					
						
							|  |  |  |         assert self._gen_tracing | 
					
						
							|  |  |  |         assert self._current_module is not None | 
					
						
							|  |  |  |         gent = self._module[self._current_module][2] | 
					
						
							|  |  |  |         assert gent is not None | 
					
						
							|  |  |  |         return gent | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:39 -05:00
										 |  |  |     def _module_dirname(name: str) -> str: | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:36 -05:00
										 |  |  |         if QAPISchemaModule.is_user_module(name): | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |             return os.path.dirname(name) | 
					
						
							|  |  |  |         return '' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:39 -05:00
										 |  |  |     def _module_basename(self, what: str, name: str) -> str: | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:36 -05:00
										 |  |  |         ret = '' if QAPISchemaModule.is_builtin_module(name) else self._prefix | 
					
						
							|  |  |  |         if QAPISchemaModule.is_user_module(name): | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |             basename = os.path.basename(name) | 
					
						
							|  |  |  |             ret += what | 
					
						
							|  |  |  |             if name != self._main_module: | 
					
						
							|  |  |  |                 ret += '-' + os.path.splitext(basename)[0] | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:39 -05:00
										 |  |  |             assert QAPISchemaModule.is_system_module(name) | 
					
						
							|  |  |  |             ret += re.sub(r'-', '-' + name[2:] + '-', what) | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         return ret | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:39 -05:00
										 |  |  |     def _module_filename(self, what: str, name: str) -> str: | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:51 -04:00
										 |  |  |         return os.path.join(self._module_dirname(name), | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |                             self._module_basename(what, name)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:39 -05:00
										 |  |  |     def _add_module(self, name: str, blurb: str) -> None: | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:40 -05:00
										 |  |  |         if QAPISchemaModule.is_user_module(name): | 
					
						
							|  |  |  |             if self._main_module is None: | 
					
						
							|  |  |  |                 self._main_module = name | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         basename = self._module_filename(self._what, name) | 
					
						
							|  |  |  |         genc = QAPIGenC(basename + '.c', blurb, self._pydoc) | 
					
						
							|  |  |  |         genh = QAPIGenH(basename + '.h', blurb, self._pydoc) | 
					
						
							| 
									
										
										
										
											2022-01-26 17:11:24 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         gent: Optional[QAPIGenTrace] = None | 
					
						
							|  |  |  |         if self._gen_tracing: | 
					
						
							|  |  |  |             gent = QAPIGenTrace(basename + '.trace-events') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._module[name] = (genc, genh, gent) | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:42 -05:00
										 |  |  |         self._current_module = name | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:43 -05:00
										 |  |  |     @contextmanager | 
					
						
							|  |  |  |     def _temp_module(self, name: str) -> Iterator[None]: | 
					
						
							|  |  |  |         old_module = self._current_module | 
					
						
							|  |  |  |         self._current_module = name | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |         self._current_module = old_module | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     def write(self, output_dir: str, opt_builtins: bool = False) -> None: | 
					
						
							| 
									
										
										
										
											2022-01-26 17:11:24 +01:00
										 |  |  |         for name, (genc, genh, gent) in self._module.items(): | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:36 -05:00
										 |  |  |             if QAPISchemaModule.is_builtin_module(name) and not opt_builtins: | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |                 continue | 
					
						
							|  |  |  |             genc.write(output_dir) | 
					
						
							|  |  |  |             genh.write(output_dir) | 
					
						
							| 
									
										
										
										
											2022-01-26 17:11:24 +01:00
										 |  |  |             if gent is not None: | 
					
						
							|  |  |  |                 gent.write(output_dir) | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:37 -05:00
										 |  |  |     def _begin_builtin_module(self) -> None: | 
					
						
							| 
									
										
										
										
											2020-03-04 16:59:32 +01:00
										 |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 12:15:49 -04:00
										 |  |  |     def _begin_user_module(self, name: str) -> None: | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:39 -05:00
										 |  |  |     def visit_module(self, name: str) -> None: | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:37 -05:00
										 |  |  |         if QAPISchemaModule.is_builtin_module(name): | 
					
						
							| 
									
										
										
										
											2019-11-20 19:25:51 +01:00
										 |  |  |             if self._builtin_blurb: | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:40 -05:00
										 |  |  |                 self._add_module(name, self._builtin_blurb) | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:37 -05:00
										 |  |  |                 self._begin_builtin_module() | 
					
						
							| 
									
										
										
										
											2019-11-20 19:25:51 +01:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 # The built-in module has not been created.  No code may | 
					
						
							|  |  |  |                 # be generated. | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:42 -05:00
										 |  |  |                 self._current_module = None | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:37 -05:00
										 |  |  |             assert QAPISchemaModule.is_user_module(name) | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:40 -05:00
										 |  |  |             self._add_module(name, self._user_blurb) | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |             self._begin_user_module(name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-01 14:37:46 -05:00
										 |  |  |     def visit_include(self, name: str, info: Optional[QAPISourceInfo]) -> None: | 
					
						
							| 
									
										
										
										
											2019-10-18 09:43:44 +02:00
										 |  |  |         relname = os.path.relpath(self._module_filename(self._what, name), | 
					
						
							|  |  |  |                                   os.path.dirname(self._genh.fname)) | 
					
						
							|  |  |  |         self._genh.preamble_add(mcgen('''
 | 
					
						
							|  |  |  | #include "%(relname)s.h" | 
					
						
							|  |  |  | ''',
 | 
					
						
							|  |  |  |                                       relname=relname)) |