mirror of https://github.com/xemu-project/xemu.git
QAPI patches patches for 2020-10-10
-----BEGIN PGP SIGNATURE----- iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAl+BgjISHGFybWJydUBy ZWRoYXQuY29tAAoJEDhwtADrkYZT3zEP/iud3fGwcKlt+et2ZnKwsD2xpiT+L8CL r4qpKDDoqWFufNpCzLhoGNZjNJq8JlgOdjp/RufDFl+QN5aVJmogX/EKW3dan0dK qm67Mz7EItm3ssBUitHeipZgHTsLwtt7Bu2VWUcJ/z6UD3QJBNoJ4Bypv4stXtuG 5OQ3Afwu1ctkmQDiRoG+pXtDVk53SGp8aRrouH4ORGasujS6vAz/gXvBbg+yuD3t 7dXpOMtbJL3haY/P5fB3pMSwn7Kql09YPe2REcWyvz6zl+SvFcypd5j7Ag5RXj7U WK6vkgOgIO4YlTfrr9sHv3jQnYLT9OkcDxDiSi4Pql4EptQydyFfQMGsR3TI7yuM LOTLLnDTX/uxCPocVwGT9bSrrbCgUg4adc95r0RrncurJdtldtdJ6idtuB/xZQV2 O4qTeyCZdbuMjN4GiaGmhrJy9ySWQkJTDjpeJWPrZYIAQyfUz4BBtNmfuGG+gsEK A5WIfPVmuI+FM3T/Ck51DXcEGgf+ezcFtzYzWDLsWGOB9m9QB4H0yAwwmqHqZBMP mezLuzs6A7T/38xdNGkwj4dXcUKiKz06C00Z9F6jdXS6LnonNh+PZ01Buqkhpg7F JQofKIsLcNLzQDyflUluBfC4wDw51BrCRs2ynHNbYLdOgBNNylCQLxxfjOAO5iDE LjUcXT5Dg0BU =/96J -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2020-10-10' into staging QAPI patches patches for 2020-10-10 # gpg: Signature made Sat 10 Oct 2020 10:43:14 BST # gpg: using RSA key 354BC8B3D7EB2A6B68674E5F3870B400EB918653 # gpg: issuer "armbru@redhat.com" # gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" [full] # gpg: aka "Markus Armbruster <armbru@pond.sub.org>" [full] # Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867 4E5F 3870 B400 EB91 8653 * remotes/armbru/tags/pull-qapi-2020-10-10: (34 commits) qapi/visit.py: add type hint annotations qapi/visit.py: remove unused parameters from gen_visit_object qapi/visit.py: assert tag_member contains a QAPISchemaEnumType qapi/types.py: remove one-letter variables qapi/types.py: add type hint annotations qapi/gen.py: delint with pylint qapi/gen.py: update write() to be more idiomatic qapi/gen.py: Remove unused parameter qapi/gen.py: add type hint annotations qapi/gen: Make _is_user_module() return bool qapi/source.py: delint with pylint qapi/source.py: add type hint annotations qapi/commands.py: add type hint annotations qapi/commands.py: Don't re-bind to variable of different type qapi/events.py: Move comments into docstrings qapi/events.py: add type hint annotations qapi: establish mypy type-checking baseline qapi/common.py: move build_params into gen.py qapi/common.py: Convert comments into docstrings, and elaborate qapi/common.py: add type hint annotations ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
2387df497b
|
@ -267,7 +267,7 @@ of view of external observers (e.g. another processor core). They can
|
||||||
apply to any memory operations as well as just loads or stores.
|
apply to any memory operations as well as just loads or stores.
|
||||||
|
|
||||||
The Linux kernel has an excellent `write-up
|
The Linux kernel has an excellent `write-up
|
||||||
<https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/plain/Documentation/memory-barriers.txt>`
|
<https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/plain/Documentation/memory-barriers.txt>`_
|
||||||
on the various forms of memory barrier and the guarantees they can
|
on the various forms of memory barrier and the guarantees they can
|
||||||
provide.
|
provide.
|
||||||
|
|
||||||
|
|
|
@ -953,7 +953,7 @@ compiler flags are needed to build for a given target.
|
||||||
If you have the ability to run containers as the user you can also
|
If you have the ability to run containers as the user you can also
|
||||||
take advantage of the build systems "Docker" support. It will then use
|
take advantage of the build systems "Docker" support. It will then use
|
||||||
containers to build any test case for an enabled guest where there is
|
containers to build any test case for an enabled guest where there is
|
||||||
no system compiler available. See :ref: `_docker-ref` for details.
|
no system compiler available. See :ref:`docker-ref` for details.
|
||||||
|
|
||||||
Running subset of tests
|
Running subset of tests
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
|
@ -1,56 +1,19 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# QAPI generator
|
|
||||||
#
|
|
||||||
# This work is licensed under the terms of the GNU GPL, version 2 or later.
|
# This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
# See the COPYING file in the top-level directory.
|
# See the COPYING file in the top-level directory.
|
||||||
|
|
||||||
|
"""
|
||||||
|
QAPI code generation execution shim.
|
||||||
|
|
||||||
|
This standalone script exists primarily to facilitate the running of the QAPI
|
||||||
|
code generator without needing to install the python module to the current
|
||||||
|
execution environment.
|
||||||
|
"""
|
||||||
|
|
||||||
import argparse
|
|
||||||
import re
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from qapi.commands import gen_commands
|
from qapi import main
|
||||||
from qapi.events import gen_events
|
|
||||||
from qapi.introspect import gen_introspect
|
|
||||||
from qapi.schema import QAPIError, QAPISchema
|
|
||||||
from qapi.types import gen_types
|
|
||||||
from qapi.visit import gen_visit
|
|
||||||
|
|
||||||
|
|
||||||
def main(argv):
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description='Generate code from a QAPI schema')
|
|
||||||
parser.add_argument('-b', '--builtins', action='store_true',
|
|
||||||
help="generate code for built-in types")
|
|
||||||
parser.add_argument('-o', '--output-dir', action='store', default='',
|
|
||||||
help="write output to directory OUTPUT_DIR")
|
|
||||||
parser.add_argument('-p', '--prefix', action='store', default='',
|
|
||||||
help="prefix for symbols")
|
|
||||||
parser.add_argument('-u', '--unmask-non-abi-names', action='store_true',
|
|
||||||
dest='unmask',
|
|
||||||
help="expose non-ABI names in introspection")
|
|
||||||
parser.add_argument('schema', action='store')
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', args.prefix)
|
|
||||||
if match.end() != len(args.prefix):
|
|
||||||
print("%s: 'funny character '%s' in argument of --prefix"
|
|
||||||
% (sys.argv[0], args.prefix[match.end()]),
|
|
||||||
file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
try:
|
|
||||||
schema = QAPISchema(args.schema)
|
|
||||||
except QAPIError as err:
|
|
||||||
print(err, file=sys.stderr)
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
gen_types(schema, args.output_dir, args.prefix, args.builtins)
|
|
||||||
gen_visit(schema, args.output_dir, args.prefix, args.builtins)
|
|
||||||
gen_commands(schema, args.output_dir, args.prefix)
|
|
||||||
gen_events(schema, args.output_dir, args.prefix)
|
|
||||||
gen_introspect(schema, args.output_dir, args.prefix, args.unmask)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main(sys.argv)
|
sys.exit(main.main())
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[flake8]
|
||||||
|
extend-ignore = E722 # Prefer pylint's bare-except checks to flake8's
|
|
@ -0,0 +1,7 @@
|
||||||
|
[settings]
|
||||||
|
force_grid_wrap=4
|
||||||
|
force_sort_within_sections=True
|
||||||
|
include_trailing_comma=True
|
||||||
|
line_length=72
|
||||||
|
lines_after_imports=2
|
||||||
|
multi_line_output=3
|
|
@ -13,11 +13,34 @@ This work is licensed under the terms of the GNU GPL, version 2.
|
||||||
See the COPYING file in the top-level directory.
|
See the COPYING file in the top-level directory.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from qapi.common import *
|
from typing import (
|
||||||
from qapi.gen import QAPIGenCCode, QAPISchemaModularCVisitor, ifcontext
|
Dict,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
Set,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .common import c_name, mcgen
|
||||||
|
from .gen import (
|
||||||
|
QAPIGenC,
|
||||||
|
QAPIGenCCode,
|
||||||
|
QAPISchemaModularCVisitor,
|
||||||
|
build_params,
|
||||||
|
ifcontext,
|
||||||
|
)
|
||||||
|
from .schema import (
|
||||||
|
QAPISchema,
|
||||||
|
QAPISchemaFeature,
|
||||||
|
QAPISchemaObjectType,
|
||||||
|
QAPISchemaType,
|
||||||
|
)
|
||||||
|
from .source import QAPISourceInfo
|
||||||
|
|
||||||
|
|
||||||
def gen_command_decl(name, arg_type, boxed, ret_type):
|
def gen_command_decl(name: str,
|
||||||
|
arg_type: Optional[QAPISchemaObjectType],
|
||||||
|
boxed: bool,
|
||||||
|
ret_type: Optional[QAPISchemaType]) -> str:
|
||||||
return mcgen('''
|
return mcgen('''
|
||||||
%(c_type)s qmp_%(c_name)s(%(params)s);
|
%(c_type)s qmp_%(c_name)s(%(params)s);
|
||||||
''',
|
''',
|
||||||
|
@ -26,7 +49,10 @@ def gen_command_decl(name, arg_type, boxed, ret_type):
|
||||||
params=build_params(arg_type, boxed, 'Error **errp'))
|
params=build_params(arg_type, boxed, 'Error **errp'))
|
||||||
|
|
||||||
|
|
||||||
def gen_call(name, arg_type, boxed, ret_type):
|
def gen_call(name: str,
|
||||||
|
arg_type: Optional[QAPISchemaObjectType],
|
||||||
|
boxed: bool,
|
||||||
|
ret_type: Optional[QAPISchemaType]) -> str:
|
||||||
ret = ''
|
ret = ''
|
||||||
|
|
||||||
argstr = ''
|
argstr = ''
|
||||||
|
@ -62,10 +88,11 @@ def gen_call(name, arg_type, boxed, ret_type):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def gen_marshal_output(ret_type):
|
def gen_marshal_output(ret_type: QAPISchemaType) -> str:
|
||||||
return mcgen('''
|
return mcgen('''
|
||||||
|
|
||||||
static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp)
|
static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in,
|
||||||
|
QObject **ret_out, Error **errp)
|
||||||
{
|
{
|
||||||
Visitor *v;
|
Visitor *v;
|
||||||
|
|
||||||
|
@ -82,19 +109,22 @@ static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out,
|
||||||
c_type=ret_type.c_type(), c_name=ret_type.c_name())
|
c_type=ret_type.c_type(), c_name=ret_type.c_name())
|
||||||
|
|
||||||
|
|
||||||
def build_marshal_proto(name):
|
def build_marshal_proto(name: str) -> str:
|
||||||
return ('void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)'
|
return ('void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)'
|
||||||
% c_name(name))
|
% c_name(name))
|
||||||
|
|
||||||
|
|
||||||
def gen_marshal_decl(name):
|
def gen_marshal_decl(name: str) -> str:
|
||||||
return mcgen('''
|
return mcgen('''
|
||||||
%(proto)s;
|
%(proto)s;
|
||||||
''',
|
''',
|
||||||
proto=build_marshal_proto(name))
|
proto=build_marshal_proto(name))
|
||||||
|
|
||||||
|
|
||||||
def gen_marshal(name, arg_type, boxed, ret_type):
|
def gen_marshal(name: str,
|
||||||
|
arg_type: Optional[QAPISchemaObjectType],
|
||||||
|
boxed: bool,
|
||||||
|
ret_type: Optional[QAPISchemaType]) -> str:
|
||||||
have_args = boxed or (arg_type and not arg_type.is_empty())
|
have_args = boxed or (arg_type and not arg_type.is_empty())
|
||||||
|
|
||||||
ret = mcgen('''
|
ret = mcgen('''
|
||||||
|
@ -176,8 +206,11 @@ out:
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def gen_register_command(name, success_response, allow_oob, allow_preconfig,
|
def gen_register_command(name: str,
|
||||||
coroutine):
|
success_response: bool,
|
||||||
|
allow_oob: bool,
|
||||||
|
allow_preconfig: bool,
|
||||||
|
coroutine: bool) -> str:
|
||||||
options = []
|
options = []
|
||||||
|
|
||||||
if not success_response:
|
if not success_response:
|
||||||
|
@ -192,18 +225,16 @@ def gen_register_command(name, success_response, allow_oob, allow_preconfig,
|
||||||
if not options:
|
if not options:
|
||||||
options = ['QCO_NO_OPTIONS']
|
options = ['QCO_NO_OPTIONS']
|
||||||
|
|
||||||
options = " | ".join(options)
|
|
||||||
|
|
||||||
ret = mcgen('''
|
ret = mcgen('''
|
||||||
qmp_register_command(cmds, "%(name)s",
|
qmp_register_command(cmds, "%(name)s",
|
||||||
qmp_marshal_%(c_name)s, %(opts)s);
|
qmp_marshal_%(c_name)s, %(opts)s);
|
||||||
''',
|
''',
|
||||||
name=name, c_name=c_name(name),
|
name=name, c_name=c_name(name),
|
||||||
opts=options)
|
opts=" | ".join(options))
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def gen_registry(registry, prefix):
|
def gen_registry(registry: str, prefix: str) -> str:
|
||||||
ret = mcgen('''
|
ret = mcgen('''
|
||||||
|
|
||||||
void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds)
|
void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds)
|
||||||
|
@ -220,15 +251,14 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds)
|
||||||
|
|
||||||
|
|
||||||
class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
|
class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
|
||||||
|
def __init__(self, prefix: str):
|
||||||
def __init__(self, prefix):
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
prefix, 'qapi-commands',
|
prefix, 'qapi-commands',
|
||||||
' * Schema-defined QAPI/QMP commands', None, __doc__)
|
' * Schema-defined QAPI/QMP commands', None, __doc__)
|
||||||
self._regy = QAPIGenCCode(None)
|
self._regy = QAPIGenCCode(None)
|
||||||
self._visited_ret_types = {}
|
self._visited_ret_types: Dict[QAPIGenC, Set[QAPISchemaType]] = {}
|
||||||
|
|
||||||
def _begin_user_module(self, name):
|
def _begin_user_module(self, name: str) -> None:
|
||||||
self._visited_ret_types[self._genc] = set()
|
self._visited_ret_types[self._genc] = set()
|
||||||
commands = self._module_basename('qapi-commands', name)
|
commands = self._module_basename('qapi-commands', name)
|
||||||
types = self._module_basename('qapi-types', name)
|
types = self._module_basename('qapi-types', name)
|
||||||
|
@ -252,7 +282,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
|
||||||
''',
|
''',
|
||||||
types=types))
|
types=types))
|
||||||
|
|
||||||
def visit_end(self):
|
def visit_end(self) -> None:
|
||||||
self._add_system_module('init', ' * QAPI Commands initialization')
|
self._add_system_module('init', ' * QAPI Commands initialization')
|
||||||
self._genh.add(mcgen('''
|
self._genh.add(mcgen('''
|
||||||
#include "qapi/qmp/dispatch.h"
|
#include "qapi/qmp/dispatch.h"
|
||||||
|
@ -268,9 +298,19 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
|
||||||
prefix=self._prefix))
|
prefix=self._prefix))
|
||||||
self._genc.add(gen_registry(self._regy.get_content(), self._prefix))
|
self._genc.add(gen_registry(self._regy.get_content(), self._prefix))
|
||||||
|
|
||||||
def visit_command(self, name, info, ifcond, features,
|
def visit_command(self,
|
||||||
arg_type, ret_type, gen, success_response, boxed,
|
name: str,
|
||||||
allow_oob, allow_preconfig, coroutine):
|
info: QAPISourceInfo,
|
||||||
|
ifcond: List[str],
|
||||||
|
features: List[QAPISchemaFeature],
|
||||||
|
arg_type: Optional[QAPISchemaObjectType],
|
||||||
|
ret_type: Optional[QAPISchemaType],
|
||||||
|
gen: bool,
|
||||||
|
success_response: bool,
|
||||||
|
boxed: bool,
|
||||||
|
allow_oob: bool,
|
||||||
|
allow_preconfig: bool,
|
||||||
|
coroutine: bool) -> None:
|
||||||
if not gen:
|
if not gen:
|
||||||
return
|
return
|
||||||
# FIXME: If T is a user-defined type, the user is responsible
|
# FIXME: If T is a user-defined type, the user is responsible
|
||||||
|
@ -292,7 +332,9 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
|
||||||
coroutine))
|
coroutine))
|
||||||
|
|
||||||
|
|
||||||
def gen_commands(schema, output_dir, prefix):
|
def gen_commands(schema: QAPISchema,
|
||||||
|
output_dir: str,
|
||||||
|
prefix: str) -> None:
|
||||||
vis = QAPISchemaGenCommandVisitor(prefix)
|
vis = QAPISchemaGenCommandVisitor(prefix)
|
||||||
schema.visit(vis)
|
schema.visit(vis)
|
||||||
vis.write(output_dir)
|
vis.write(output_dir)
|
||||||
|
|
|
@ -12,12 +12,28 @@
|
||||||
# See the COPYING file in the top-level directory.
|
# See the COPYING file in the top-level directory.
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
from typing import Optional, Sequence
|
||||||
|
|
||||||
|
|
||||||
# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
|
#: Magic string that gets removed along with all space to its right.
|
||||||
# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
|
EATSPACE = '\033EATSPACE.'
|
||||||
# ENUM24_Name -> ENUM24_NAME
|
POINTER_SUFFIX = ' *' + EATSPACE
|
||||||
def camel_to_upper(value):
|
_C_NAME_TRANS = str.maketrans('.-', '__')
|
||||||
|
|
||||||
|
|
||||||
|
def camel_to_upper(value: str) -> str:
|
||||||
|
"""
|
||||||
|
Converts CamelCase to CAMEL_CASE.
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
ENUMName -> ENUM_NAME
|
||||||
|
EnumName1 -> ENUM_NAME1
|
||||||
|
ENUM_NAME -> ENUM_NAME
|
||||||
|
ENUM_NAME1 -> ENUM_NAME1
|
||||||
|
ENUM_Name2 -> ENUM_NAME2
|
||||||
|
ENUM24_Name -> ENUM24_NAME
|
||||||
|
"""
|
||||||
c_fun_str = c_name(value, False)
|
c_fun_str = c_name(value, False)
|
||||||
if value.isupper():
|
if value.isupper():
|
||||||
return c_fun_str
|
return c_fun_str
|
||||||
|
@ -25,36 +41,47 @@ def camel_to_upper(value):
|
||||||
new_name = ''
|
new_name = ''
|
||||||
length = len(c_fun_str)
|
length = len(c_fun_str)
|
||||||
for i in range(length):
|
for i in range(length):
|
||||||
c = c_fun_str[i]
|
char = c_fun_str[i]
|
||||||
# When c is upper and no '_' appears before, do more checks
|
# When char is upper case and no '_' appears before, do more checks
|
||||||
if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
|
if char.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
|
||||||
if i < length - 1 and c_fun_str[i + 1].islower():
|
if i < length - 1 and c_fun_str[i + 1].islower():
|
||||||
new_name += '_'
|
new_name += '_'
|
||||||
elif c_fun_str[i - 1].isdigit():
|
elif c_fun_str[i - 1].isdigit():
|
||||||
new_name += '_'
|
new_name += '_'
|
||||||
new_name += c
|
new_name += char
|
||||||
return new_name.lstrip('_').upper()
|
return new_name.lstrip('_').upper()
|
||||||
|
|
||||||
|
|
||||||
def c_enum_const(type_name, const_name, prefix=None):
|
def c_enum_const(type_name: str,
|
||||||
|
const_name: str,
|
||||||
|
prefix: Optional[str] = None) -> str:
|
||||||
|
"""
|
||||||
|
Generate a C enumeration constant name.
|
||||||
|
|
||||||
|
:param type_name: The name of the enumeration.
|
||||||
|
:param const_name: The name of this constant.
|
||||||
|
:param prefix: Optional, prefix that overrides the type_name.
|
||||||
|
"""
|
||||||
if prefix is not None:
|
if prefix is not None:
|
||||||
type_name = prefix
|
type_name = prefix
|
||||||
return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
|
return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
|
||||||
|
|
||||||
|
|
||||||
c_name_trans = str.maketrans('.-', '__')
|
def c_name(name: str, protect: bool = True) -> str:
|
||||||
|
"""
|
||||||
|
Map ``name`` to a valid C identifier.
|
||||||
|
|
||||||
|
Used for converting 'name' from a 'name':'type' qapi definition
|
||||||
|
into a generated struct member, as well as converting type names
|
||||||
|
into substrings of a generated C function name.
|
||||||
|
|
||||||
# Map @name to a valid C identifier.
|
'__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
|
||||||
# If @protect, avoid returning certain ticklish identifiers (like
|
protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
|
||||||
# C keywords) by prepending 'q_'.
|
|
||||||
#
|
:param name: The name to map.
|
||||||
# Used for converting 'name' from a 'name':'type' qapi definition
|
:param protect: If true, avoid returning certain ticklish identifiers
|
||||||
# into a generated struct member, as well as converting type names
|
(like C keywords) by prepending ``q_``.
|
||||||
# into substrings of a generated C function name.
|
"""
|
||||||
# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
|
|
||||||
# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
|
|
||||||
def c_name(name, protect=True):
|
|
||||||
# ANSI X3J11/88-090, 3.1.1
|
# ANSI X3J11/88-090, 3.1.1
|
||||||
c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
|
c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
|
||||||
'default', 'do', 'double', 'else', 'enum', 'extern',
|
'default', 'do', 'double', 'else', 'enum', 'extern',
|
||||||
|
@ -82,61 +109,75 @@ def c_name(name, protect=True):
|
||||||
'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
|
'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
|
||||||
# namespace pollution:
|
# namespace pollution:
|
||||||
polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
|
polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
|
||||||
name = name.translate(c_name_trans)
|
name = name.translate(_C_NAME_TRANS)
|
||||||
if protect and (name in c89_words | c99_words | c11_words | gcc_words
|
if protect and (name in c89_words | c99_words | c11_words | gcc_words
|
||||||
| cpp_words | polluted_words):
|
| cpp_words | polluted_words):
|
||||||
return 'q_' + name
|
return 'q_' + name
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
eatspace = '\033EATSPACE.'
|
class Indentation:
|
||||||
pointer_suffix = ' *' + eatspace
|
"""
|
||||||
|
Indentation level management.
|
||||||
|
|
||||||
|
:param initial: Initial number of spaces, default 0.
|
||||||
|
"""
|
||||||
|
def __init__(self, initial: int = 0) -> None:
|
||||||
|
self._level = initial
|
||||||
|
|
||||||
|
def __int__(self) -> int:
|
||||||
|
return self._level
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return "{}({:d})".format(type(self).__name__, self._level)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
"""Return the current indentation as a string of spaces."""
|
||||||
|
return ' ' * self._level
|
||||||
|
|
||||||
|
def __bool__(self) -> bool:
|
||||||
|
"""True when there is a non-zero indentation."""
|
||||||
|
return bool(self._level)
|
||||||
|
|
||||||
|
def increase(self, amount: int = 4) -> None:
|
||||||
|
"""Increase the indentation level by ``amount``, default 4."""
|
||||||
|
self._level += amount
|
||||||
|
|
||||||
|
def decrease(self, amount: int = 4) -> None:
|
||||||
|
"""Decrease the indentation level by ``amount``, default 4."""
|
||||||
|
if self._level < amount:
|
||||||
|
raise ArithmeticError(
|
||||||
|
f"Can't remove {amount:d} spaces from {self!r}")
|
||||||
|
self._level -= amount
|
||||||
|
|
||||||
|
|
||||||
def genindent(count):
|
#: Global, current indent level for code generation.
|
||||||
ret = ''
|
indent = Indentation()
|
||||||
for _ in range(count):
|
|
||||||
ret += ' '
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
indent_level = 0
|
def cgen(code: str, **kwds: object) -> str:
|
||||||
|
"""
|
||||||
|
Generate ``code`` with ``kwds`` interpolated.
|
||||||
|
|
||||||
|
Obey `indent`, and strip `EATSPACE`.
|
||||||
def push_indent(indent_amount=4):
|
"""
|
||||||
global indent_level
|
|
||||||
indent_level += indent_amount
|
|
||||||
|
|
||||||
|
|
||||||
def pop_indent(indent_amount=4):
|
|
||||||
global indent_level
|
|
||||||
indent_level -= indent_amount
|
|
||||||
|
|
||||||
|
|
||||||
# Generate @code with @kwds interpolated.
|
|
||||||
# Obey indent_level, and strip eatspace.
|
|
||||||
def cgen(code, **kwds):
|
|
||||||
raw = code % kwds
|
raw = code % kwds
|
||||||
if indent_level:
|
if indent:
|
||||||
indent = genindent(indent_level)
|
raw = re.sub(r'^(?!(#|$))', str(indent), raw, flags=re.MULTILINE)
|
||||||
# re.subn() lacks flags support before Python 2.7, use re.compile()
|
return re.sub(re.escape(EATSPACE) + r' *', '', raw)
|
||||||
raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
|
|
||||||
indent, raw)
|
|
||||||
raw = raw[0]
|
|
||||||
return re.sub(re.escape(eatspace) + r' *', '', raw)
|
|
||||||
|
|
||||||
|
|
||||||
def mcgen(code, **kwds):
|
def mcgen(code: str, **kwds: object) -> str:
|
||||||
if code[0] == '\n':
|
if code[0] == '\n':
|
||||||
code = code[1:]
|
code = code[1:]
|
||||||
return cgen(code, **kwds)
|
return cgen(code, **kwds)
|
||||||
|
|
||||||
|
|
||||||
def c_fname(filename):
|
def c_fname(filename: str) -> str:
|
||||||
return re.sub(r'[^A-Za-z0-9_]', '_', filename)
|
return re.sub(r'[^A-Za-z0-9_]', '_', filename)
|
||||||
|
|
||||||
|
|
||||||
def guardstart(name):
|
def guardstart(name: str) -> str:
|
||||||
return mcgen('''
|
return mcgen('''
|
||||||
#ifndef %(name)s
|
#ifndef %(name)s
|
||||||
#define %(name)s
|
#define %(name)s
|
||||||
|
@ -145,7 +186,7 @@ def guardstart(name):
|
||||||
name=c_fname(name).upper())
|
name=c_fname(name).upper())
|
||||||
|
|
||||||
|
|
||||||
def guardend(name):
|
def guardend(name: str) -> str:
|
||||||
return mcgen('''
|
return mcgen('''
|
||||||
|
|
||||||
#endif /* %(name)s */
|
#endif /* %(name)s */
|
||||||
|
@ -153,7 +194,7 @@ def guardend(name):
|
||||||
name=c_fname(name).upper())
|
name=c_fname(name).upper())
|
||||||
|
|
||||||
|
|
||||||
def gen_if(ifcond):
|
def gen_if(ifcond: Sequence[str]) -> str:
|
||||||
ret = ''
|
ret = ''
|
||||||
for ifc in ifcond:
|
for ifc in ifcond:
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
|
@ -162,31 +203,10 @@ def gen_if(ifcond):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def gen_endif(ifcond):
|
def gen_endif(ifcond: Sequence[str]) -> str:
|
||||||
ret = ''
|
ret = ''
|
||||||
for ifc in reversed(ifcond):
|
for ifc in reversed(ifcond):
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
#endif /* %(cond)s */
|
#endif /* %(cond)s */
|
||||||
''', cond=ifc)
|
''', cond=ifc)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def build_params(arg_type, boxed, extra=None):
|
|
||||||
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:
|
|
||||||
ret += sep
|
|
||||||
sep = ', '
|
|
||||||
if memb.optional:
|
|
||||||
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'
|
|
||||||
|
|
|
@ -12,19 +12,31 @@ This work is licensed under the terms of the GNU GPL, version 2.
|
||||||
See the COPYING file in the top-level directory.
|
See the COPYING file in the top-level directory.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from qapi.common import *
|
from typing import List
|
||||||
from qapi.gen import QAPISchemaModularCVisitor, ifcontext
|
|
||||||
from qapi.schema import QAPISchemaEnumMember
|
from .common import c_enum_const, c_name, mcgen
|
||||||
from qapi.types import gen_enum, gen_enum_lookup
|
from .gen import QAPISchemaModularCVisitor, build_params, ifcontext
|
||||||
|
from .schema import (
|
||||||
|
QAPISchema,
|
||||||
|
QAPISchemaEnumMember,
|
||||||
|
QAPISchemaFeature,
|
||||||
|
QAPISchemaObjectType,
|
||||||
|
)
|
||||||
|
from .source import QAPISourceInfo
|
||||||
|
from .types import gen_enum, gen_enum_lookup
|
||||||
|
|
||||||
|
|
||||||
def build_event_send_proto(name, arg_type, boxed):
|
def build_event_send_proto(name: str,
|
||||||
|
arg_type: QAPISchemaObjectType,
|
||||||
|
boxed: bool) -> str:
|
||||||
return 'void qapi_event_send_%(c_name)s(%(param)s)' % {
|
return 'void qapi_event_send_%(c_name)s(%(param)s)' % {
|
||||||
'c_name': c_name(name.lower()),
|
'c_name': c_name(name.lower()),
|
||||||
'param': build_params(arg_type, boxed)}
|
'param': build_params(arg_type, boxed)}
|
||||||
|
|
||||||
|
|
||||||
def gen_event_send_decl(name, arg_type, boxed):
|
def gen_event_send_decl(name: str,
|
||||||
|
arg_type: QAPISchemaObjectType,
|
||||||
|
boxed: bool) -> str:
|
||||||
return mcgen('''
|
return mcgen('''
|
||||||
|
|
||||||
%(proto)s;
|
%(proto)s;
|
||||||
|
@ -32,8 +44,12 @@ def gen_event_send_decl(name, arg_type, boxed):
|
||||||
proto=build_event_send_proto(name, arg_type, boxed))
|
proto=build_event_send_proto(name, arg_type, boxed))
|
||||||
|
|
||||||
|
|
||||||
# Declare and initialize an object 'qapi' using parameters from build_params()
|
def gen_param_var(typ: QAPISchemaObjectType) -> str:
|
||||||
def gen_param_var(typ):
|
"""
|
||||||
|
Generate a struct variable holding the event parameters.
|
||||||
|
|
||||||
|
Initialize it with the function arguments defined in `gen_event_send`.
|
||||||
|
"""
|
||||||
assert not typ.variants
|
assert not typ.variants
|
||||||
ret = mcgen('''
|
ret = mcgen('''
|
||||||
%(c_name)s param = {
|
%(c_name)s param = {
|
||||||
|
@ -61,7 +77,11 @@ def gen_param_var(typ):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def gen_event_send(name, arg_type, boxed, event_enum_name, event_emit):
|
def gen_event_send(name: str,
|
||||||
|
arg_type: QAPISchemaObjectType,
|
||||||
|
boxed: bool,
|
||||||
|
event_enum_name: str,
|
||||||
|
event_emit: str) -> str:
|
||||||
# FIXME: Our declaration of local variables (and of 'errp' in the
|
# FIXME: Our declaration of local variables (and of 'errp' in the
|
||||||
# parameter list) can collide with exploded members of the event's
|
# parameter list) can collide with exploded members of the event's
|
||||||
# data type passed in as parameters. If this collision ever hits in
|
# data type passed in as parameters. If this collision ever hits in
|
||||||
|
@ -137,15 +157,15 @@ def gen_event_send(name, arg_type, boxed, event_enum_name, event_emit):
|
||||||
|
|
||||||
class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
|
class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
|
||||||
|
|
||||||
def __init__(self, prefix):
|
def __init__(self, prefix: str):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
prefix, 'qapi-events',
|
prefix, 'qapi-events',
|
||||||
' * Schema-defined QAPI/QMP events', None, __doc__)
|
' * Schema-defined QAPI/QMP events', None, __doc__)
|
||||||
self._event_enum_name = c_name(prefix + 'QAPIEvent', protect=False)
|
self._event_enum_name = c_name(prefix + 'QAPIEvent', protect=False)
|
||||||
self._event_enum_members = []
|
self._event_enum_members: List[QAPISchemaEnumMember] = []
|
||||||
self._event_emit_name = c_name(prefix + 'qapi_event_emit')
|
self._event_emit_name = c_name(prefix + 'qapi_event_emit')
|
||||||
|
|
||||||
def _begin_user_module(self, name):
|
def _begin_user_module(self, name: str) -> None:
|
||||||
events = self._module_basename('qapi-events', name)
|
events = self._module_basename('qapi-events', name)
|
||||||
types = self._module_basename('qapi-types', name)
|
types = self._module_basename('qapi-types', name)
|
||||||
visit = self._module_basename('qapi-visit', name)
|
visit = self._module_basename('qapi-visit', name)
|
||||||
|
@ -168,7 +188,7 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
|
||||||
''',
|
''',
|
||||||
types=types))
|
types=types))
|
||||||
|
|
||||||
def visit_end(self):
|
def visit_end(self) -> None:
|
||||||
self._add_system_module('emit', ' * QAPI Events emission')
|
self._add_system_module('emit', ' * QAPI Events emission')
|
||||||
self._genc.preamble_add(mcgen('''
|
self._genc.preamble_add(mcgen('''
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
@ -189,7 +209,13 @@ void %(event_emit)s(%(event_enum)s event, QDict *qdict);
|
||||||
event_emit=self._event_emit_name,
|
event_emit=self._event_emit_name,
|
||||||
event_enum=self._event_enum_name))
|
event_enum=self._event_enum_name))
|
||||||
|
|
||||||
def visit_event(self, name, info, ifcond, features, arg_type, boxed):
|
def visit_event(self,
|
||||||
|
name: str,
|
||||||
|
info: QAPISourceInfo,
|
||||||
|
ifcond: List[str],
|
||||||
|
features: List[QAPISchemaFeature],
|
||||||
|
arg_type: QAPISchemaObjectType,
|
||||||
|
boxed: bool) -> None:
|
||||||
with ifcontext(ifcond, self._genh, self._genc):
|
with ifcontext(ifcond, self._genh, self._genc):
|
||||||
self._genh.add(gen_event_send_decl(name, arg_type, boxed))
|
self._genh.add(gen_event_send_decl(name, arg_type, boxed))
|
||||||
self._genc.add(gen_event_send(name, arg_type, boxed,
|
self._genc.add(gen_event_send(name, arg_type, boxed,
|
||||||
|
@ -200,7 +226,9 @@ void %(event_emit)s(%(event_enum)s event, QDict *qdict);
|
||||||
self._event_enum_members.append(QAPISchemaEnumMember(name, None))
|
self._event_enum_members.append(QAPISchemaEnumMember(name, None))
|
||||||
|
|
||||||
|
|
||||||
def gen_events(schema, output_dir, prefix):
|
def gen_events(schema: QAPISchema,
|
||||||
|
output_dir: str,
|
||||||
|
prefix: str) -> None:
|
||||||
vis = QAPISchemaGenEventVisitor(prefix)
|
vis = QAPISchemaGenEventVisitor(prefix)
|
||||||
schema.visit(vis)
|
schema.visit(vis)
|
||||||
vis.write(output_dir)
|
vis.write(output_dir)
|
||||||
|
|
|
@ -14,10 +14,11 @@
|
||||||
# This work is licensed under the terms of the GNU GPL, version 2.
|
# This work is licensed under the terms of the GNU GPL, version 2.
|
||||||
# See the COPYING file in the top-level directory.
|
# See the COPYING file in the top-level directory.
|
||||||
|
|
||||||
import re
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from qapi.common import c_name
|
import re
|
||||||
from qapi.error import QAPISemError
|
|
||||||
|
from .common import c_name
|
||||||
|
from .error import QAPISemError
|
||||||
|
|
||||||
|
|
||||||
# Names must be letters, numbers, -, and _. They must start with letter,
|
# Names must be letters, numbers, -, and _. They must start with letter,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#
|
#
|
||||||
# QAPI code generation
|
# QAPI code generation
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2019 Red Hat Inc.
|
# Copyright (c) 2015-2019 Red Hat Inc.
|
||||||
#
|
#
|
||||||
# Authors:
|
# Authors:
|
||||||
# Markus Armbruster <armbru@redhat.com>
|
# Markus Armbruster <armbru@redhat.com>
|
||||||
|
@ -11,39 +11,54 @@
|
||||||
# This work is licensed under the terms of the GNU GPL, version 2.
|
# This work is licensed under the terms of the GNU GPL, version 2.
|
||||||
# See the COPYING file in the top-level directory.
|
# See the COPYING file in the top-level directory.
|
||||||
|
|
||||||
|
from contextlib import contextmanager
|
||||||
import errno
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from contextlib import contextmanager
|
from typing import (
|
||||||
|
Dict,
|
||||||
|
Iterator,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
Tuple,
|
||||||
|
)
|
||||||
|
|
||||||
from qapi.common import *
|
from .common import (
|
||||||
from qapi.schema import QAPISchemaVisitor
|
c_fname,
|
||||||
|
c_name,
|
||||||
|
gen_endif,
|
||||||
|
gen_if,
|
||||||
|
guardend,
|
||||||
|
guardstart,
|
||||||
|
mcgen,
|
||||||
|
)
|
||||||
|
from .schema import QAPISchemaObjectType, QAPISchemaVisitor
|
||||||
|
from .source import QAPISourceInfo
|
||||||
|
|
||||||
|
|
||||||
class QAPIGen:
|
class QAPIGen:
|
||||||
|
def __init__(self, fname: Optional[str]):
|
||||||
def __init__(self, fname):
|
|
||||||
self.fname = fname
|
self.fname = fname
|
||||||
self._preamble = ''
|
self._preamble = ''
|
||||||
self._body = ''
|
self._body = ''
|
||||||
|
|
||||||
def preamble_add(self, text):
|
def preamble_add(self, text: str) -> None:
|
||||||
self._preamble += text
|
self._preamble += text
|
||||||
|
|
||||||
def add(self, text):
|
def add(self, text: str) -> None:
|
||||||
self._body += text
|
self._body += text
|
||||||
|
|
||||||
def get_content(self):
|
def get_content(self) -> str:
|
||||||
return self._top() + self._preamble + self._body + self._bottom()
|
return self._top() + self._preamble + self._body + self._bottom()
|
||||||
|
|
||||||
def _top(self):
|
def _top(self) -> str:
|
||||||
|
# pylint: disable=no-self-use
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def _bottom(self):
|
def _bottom(self) -> str:
|
||||||
|
# pylint: disable=no-self-use
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def write(self, output_dir):
|
def write(self, output_dir: str) -> None:
|
||||||
# Include paths starting with ../ are used to reuse modules of the main
|
# Include paths starting with ../ are used to reuse modules of the main
|
||||||
# schema in specialised schemas. Don't overwrite the files that are
|
# schema in specialised schemas. Don't overwrite the files that are
|
||||||
# already generated for the main schema.
|
# already generated for the main schema.
|
||||||
|
@ -51,24 +66,22 @@ class QAPIGen:
|
||||||
return
|
return
|
||||||
pathname = os.path.join(output_dir, self.fname)
|
pathname = os.path.join(output_dir, self.fname)
|
||||||
odir = os.path.dirname(pathname)
|
odir = os.path.dirname(pathname)
|
||||||
|
|
||||||
if odir:
|
if odir:
|
||||||
try:
|
os.makedirs(odir, exist_ok=True)
|
||||||
os.makedirs(odir)
|
|
||||||
except os.error as e:
|
# use os.open for O_CREAT to create and read a non-existant file
|
||||||
if e.errno != errno.EEXIST:
|
|
||||||
raise
|
|
||||||
fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
|
fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
|
||||||
f = open(fd, 'r+', encoding='utf-8')
|
with os.fdopen(fd, 'r+', encoding='utf-8') as fp:
|
||||||
text = self.get_content()
|
text = self.get_content()
|
||||||
oldtext = f.read(len(text) + 1)
|
oldtext = fp.read(len(text) + 1)
|
||||||
if text != oldtext:
|
if text != oldtext:
|
||||||
f.seek(0)
|
fp.seek(0)
|
||||||
f.truncate(0)
|
fp.truncate(0)
|
||||||
f.write(text)
|
fp.write(text)
|
||||||
f.close()
|
|
||||||
|
|
||||||
|
|
||||||
def _wrap_ifcond(ifcond, before, after):
|
def _wrap_ifcond(ifcond: List[str], before: str, after: str) -> str:
|
||||||
if before == after:
|
if before == after:
|
||||||
return after # suppress empty #if ... #endif
|
return after # suppress empty #if ... #endif
|
||||||
|
|
||||||
|
@ -84,41 +97,62 @@ def _wrap_ifcond(ifcond, before, after):
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
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:
|
||||||
|
ret += sep
|
||||||
|
sep = ', '
|
||||||
|
if memb.optional:
|
||||||
|
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'
|
||||||
|
|
||||||
|
|
||||||
class QAPIGenCCode(QAPIGen):
|
class QAPIGenCCode(QAPIGen):
|
||||||
|
def __init__(self, fname: Optional[str]):
|
||||||
def __init__(self, fname):
|
|
||||||
super().__init__(fname)
|
super().__init__(fname)
|
||||||
self._start_if = None
|
self._start_if: Optional[Tuple[List[str], str, str]] = None
|
||||||
|
|
||||||
def start_if(self, ifcond):
|
def start_if(self, ifcond: List[str]) -> None:
|
||||||
assert self._start_if is None
|
assert self._start_if is None
|
||||||
self._start_if = (ifcond, self._body, self._preamble)
|
self._start_if = (ifcond, self._body, self._preamble)
|
||||||
|
|
||||||
def end_if(self):
|
def end_if(self) -> None:
|
||||||
assert self._start_if
|
assert self._start_if
|
||||||
self._wrap_ifcond()
|
self._wrap_ifcond()
|
||||||
self._start_if = None
|
self._start_if = None
|
||||||
|
|
||||||
def _wrap_ifcond(self):
|
def _wrap_ifcond(self) -> None:
|
||||||
self._body = _wrap_ifcond(self._start_if[0],
|
self._body = _wrap_ifcond(self._start_if[0],
|
||||||
self._start_if[1], self._body)
|
self._start_if[1], self._body)
|
||||||
self._preamble = _wrap_ifcond(self._start_if[0],
|
self._preamble = _wrap_ifcond(self._start_if[0],
|
||||||
self._start_if[2], self._preamble)
|
self._start_if[2], self._preamble)
|
||||||
|
|
||||||
def get_content(self):
|
def get_content(self) -> str:
|
||||||
assert self._start_if is None
|
assert self._start_if is None
|
||||||
return super().get_content()
|
return super().get_content()
|
||||||
|
|
||||||
|
|
||||||
class QAPIGenC(QAPIGenCCode):
|
class QAPIGenC(QAPIGenCCode):
|
||||||
|
def __init__(self, fname: str, blurb: str, pydoc: str):
|
||||||
def __init__(self, fname, blurb, pydoc):
|
|
||||||
super().__init__(fname)
|
super().__init__(fname)
|
||||||
self._blurb = blurb
|
self._blurb = blurb
|
||||||
self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
|
self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
|
||||||
re.MULTILINE))
|
re.MULTILINE))
|
||||||
|
|
||||||
def _top(self):
|
def _top(self) -> str:
|
||||||
return mcgen('''
|
return mcgen('''
|
||||||
/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
|
/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
|
||||||
|
|
||||||
|
@ -134,7 +168,7 @@ class QAPIGenC(QAPIGenCCode):
|
||||||
''',
|
''',
|
||||||
blurb=self._blurb, copyright=self._copyright)
|
blurb=self._blurb, copyright=self._copyright)
|
||||||
|
|
||||||
def _bottom(self):
|
def _bottom(self) -> str:
|
||||||
return mcgen('''
|
return mcgen('''
|
||||||
|
|
||||||
/* Dummy declaration to prevent empty .o file */
|
/* Dummy declaration to prevent empty .o file */
|
||||||
|
@ -144,19 +178,20 @@ char qapi_dummy_%(name)s;
|
||||||
|
|
||||||
|
|
||||||
class QAPIGenH(QAPIGenC):
|
class QAPIGenH(QAPIGenC):
|
||||||
|
def _top(self) -> str:
|
||||||
def _top(self):
|
|
||||||
return super()._top() + guardstart(self.fname)
|
return super()._top() + guardstart(self.fname)
|
||||||
|
|
||||||
def _bottom(self):
|
def _bottom(self) -> str:
|
||||||
return guardend(self.fname)
|
return guardend(self.fname)
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def ifcontext(ifcond, *args):
|
def ifcontext(ifcond: List[str], *args: QAPIGenCCode) -> Iterator[None]:
|
||||||
"""A 'with' statement context manager to wrap with start_if()/end_if()
|
"""
|
||||||
|
A with-statement context manager that wraps with `start_if()` / `end_if()`.
|
||||||
|
|
||||||
*args: any number of QAPIGenCCode
|
:param ifcond: A list of conditionals, passed to `start_if()`.
|
||||||
|
:param args: any number of `QAPIGenCCode`.
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
|
@ -179,8 +214,11 @@ def ifcontext(ifcond, *args):
|
||||||
|
|
||||||
|
|
||||||
class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
|
class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
|
||||||
|
def __init__(self,
|
||||||
def __init__(self, prefix, what, blurb, pydoc):
|
prefix: str,
|
||||||
|
what: str,
|
||||||
|
blurb: str,
|
||||||
|
pydoc: str):
|
||||||
self._prefix = prefix
|
self._prefix = prefix
|
||||||
self._what = what
|
self._what = what
|
||||||
self._genc = QAPIGenC(self._prefix + self._what + '.c',
|
self._genc = QAPIGenC(self._prefix + self._what + '.c',
|
||||||
|
@ -188,38 +226,42 @@ class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
|
||||||
self._genh = QAPIGenH(self._prefix + self._what + '.h',
|
self._genh = QAPIGenH(self._prefix + self._what + '.h',
|
||||||
blurb, pydoc)
|
blurb, pydoc)
|
||||||
|
|
||||||
def write(self, output_dir):
|
def write(self, output_dir: str) -> None:
|
||||||
self._genc.write(output_dir)
|
self._genc.write(output_dir)
|
||||||
self._genh.write(output_dir)
|
self._genh.write(output_dir)
|
||||||
|
|
||||||
|
|
||||||
class QAPISchemaModularCVisitor(QAPISchemaVisitor):
|
class QAPISchemaModularCVisitor(QAPISchemaVisitor):
|
||||||
|
def __init__(self,
|
||||||
def __init__(self, prefix, what, user_blurb, builtin_blurb, pydoc):
|
prefix: str,
|
||||||
|
what: str,
|
||||||
|
user_blurb: str,
|
||||||
|
builtin_blurb: Optional[str],
|
||||||
|
pydoc: str):
|
||||||
self._prefix = prefix
|
self._prefix = prefix
|
||||||
self._what = what
|
self._what = what
|
||||||
self._user_blurb = user_blurb
|
self._user_blurb = user_blurb
|
||||||
self._builtin_blurb = builtin_blurb
|
self._builtin_blurb = builtin_blurb
|
||||||
self._pydoc = pydoc
|
self._pydoc = pydoc
|
||||||
self._genc = None
|
self._genc: Optional[QAPIGenC] = None
|
||||||
self._genh = None
|
self._genh: Optional[QAPIGenH] = None
|
||||||
self._module = {}
|
self._module: Dict[Optional[str], Tuple[QAPIGenC, QAPIGenH]] = {}
|
||||||
self._main_module = None
|
self._main_module: Optional[str] = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _is_user_module(name):
|
def _is_user_module(name: Optional[str]) -> bool:
|
||||||
return name and not name.startswith('./')
|
return bool(name and not name.startswith('./'))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _is_builtin_module(name):
|
def _is_builtin_module(name: Optional[str]) -> bool:
|
||||||
return not name
|
return not name
|
||||||
|
|
||||||
def _module_dirname(self, what, name):
|
def _module_dirname(self, name: Optional[str]) -> str:
|
||||||
if self._is_user_module(name):
|
if self._is_user_module(name):
|
||||||
return os.path.dirname(name)
|
return os.path.dirname(name)
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def _module_basename(self, what, name):
|
def _module_basename(self, what: str, name: Optional[str]) -> str:
|
||||||
ret = '' if self._is_builtin_module(name) else self._prefix
|
ret = '' if self._is_builtin_module(name) else self._prefix
|
||||||
if self._is_user_module(name):
|
if self._is_user_module(name):
|
||||||
basename = os.path.basename(name)
|
basename = os.path.basename(name)
|
||||||
|
@ -231,27 +273,27 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor):
|
||||||
ret += re.sub(r'-', '-' + name + '-', what)
|
ret += re.sub(r'-', '-' + name + '-', what)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def _module_filename(self, what, name):
|
def _module_filename(self, what: str, name: Optional[str]) -> str:
|
||||||
return os.path.join(self._module_dirname(what, name),
|
return os.path.join(self._module_dirname(name),
|
||||||
self._module_basename(what, name))
|
self._module_basename(what, name))
|
||||||
|
|
||||||
def _add_module(self, name, blurb):
|
def _add_module(self, name: Optional[str], blurb: str) -> None:
|
||||||
basename = self._module_filename(self._what, name)
|
basename = self._module_filename(self._what, name)
|
||||||
genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
|
genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
|
||||||
genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
|
genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
|
||||||
self._module[name] = (genc, genh)
|
self._module[name] = (genc, genh)
|
||||||
self._genc, self._genh = self._module[name]
|
self._genc, self._genh = self._module[name]
|
||||||
|
|
||||||
def _add_user_module(self, name, blurb):
|
def _add_user_module(self, name: str, blurb: str) -> None:
|
||||||
assert self._is_user_module(name)
|
assert self._is_user_module(name)
|
||||||
if self._main_module is None:
|
if self._main_module is None:
|
||||||
self._main_module = name
|
self._main_module = name
|
||||||
self._add_module(name, blurb)
|
self._add_module(name, blurb)
|
||||||
|
|
||||||
def _add_system_module(self, name, blurb):
|
def _add_system_module(self, name: Optional[str], blurb: str) -> None:
|
||||||
self._add_module(name and './' + name, blurb)
|
self._add_module(name and './' + name, blurb)
|
||||||
|
|
||||||
def write(self, output_dir, opt_builtins=False):
|
def write(self, output_dir: str, opt_builtins: bool = False) -> None:
|
||||||
for name in self._module:
|
for name in self._module:
|
||||||
if self._is_builtin_module(name) and not opt_builtins:
|
if self._is_builtin_module(name) and not opt_builtins:
|
||||||
continue
|
continue
|
||||||
|
@ -259,13 +301,13 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor):
|
||||||
genc.write(output_dir)
|
genc.write(output_dir)
|
||||||
genh.write(output_dir)
|
genh.write(output_dir)
|
||||||
|
|
||||||
def _begin_system_module(self, name):
|
def _begin_system_module(self, name: None) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _begin_user_module(self, name):
|
def _begin_user_module(self, name: str) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def visit_module(self, name):
|
def visit_module(self, name: Optional[str]) -> None:
|
||||||
if name is None:
|
if name is None:
|
||||||
if self._builtin_blurb:
|
if self._builtin_blurb:
|
||||||
self._add_system_module(None, self._builtin_blurb)
|
self._add_system_module(None, self._builtin_blurb)
|
||||||
|
@ -279,7 +321,7 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor):
|
||||||
self._add_user_module(name, self._user_blurb)
|
self._add_user_module(name, self._user_blurb)
|
||||||
self._begin_user_module(name)
|
self._begin_user_module(name)
|
||||||
|
|
||||||
def visit_include(self, name, info):
|
def visit_include(self, name: str, info: QAPISourceInfo) -> None:
|
||||||
relname = os.path.relpath(self._module_filename(self._what, name),
|
relname = os.path.relpath(self._module_filename(self._what, name),
|
||||||
os.path.dirname(self._genh.fname))
|
os.path.dirname(self._genh.fname))
|
||||||
self._genh.preamble_add(mcgen('''
|
self._genh.preamble_add(mcgen('''
|
||||||
|
|
|
@ -10,10 +10,18 @@ This work is licensed under the terms of the GNU GPL, version 2.
|
||||||
See the COPYING file in the top-level directory.
|
See the COPYING file in the top-level directory.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from qapi.common import *
|
from .common import (
|
||||||
from qapi.gen import QAPISchemaMonolithicCVisitor
|
c_name,
|
||||||
from qapi.schema import (QAPISchemaArrayType, QAPISchemaBuiltinType,
|
gen_endif,
|
||||||
QAPISchemaType)
|
gen_if,
|
||||||
|
mcgen,
|
||||||
|
)
|
||||||
|
from .gen import QAPISchemaMonolithicCVisitor
|
||||||
|
from .schema import (
|
||||||
|
QAPISchemaArrayType,
|
||||||
|
QAPISchemaBuiltinType,
|
||||||
|
QAPISchemaType,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _make_tree(obj, ifcond, features, extra=None):
|
def _make_tree(obj, ifcond, features, extra=None):
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
# This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
# See the COPYING file in the top-level directory.
|
||||||
|
|
||||||
|
"""
|
||||||
|
QAPI Generator
|
||||||
|
|
||||||
|
This is the main entry point for generating C code from the QAPI schema.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from .commands import gen_commands
|
||||||
|
from .error import QAPIError
|
||||||
|
from .events import gen_events
|
||||||
|
from .introspect import gen_introspect
|
||||||
|
from .schema import QAPISchema
|
||||||
|
from .types import gen_types
|
||||||
|
from .visit import gen_visit
|
||||||
|
|
||||||
|
|
||||||
|
def invalid_prefix_char(prefix: str) -> Optional[str]:
|
||||||
|
match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', prefix)
|
||||||
|
if match.end() != len(prefix):
|
||||||
|
return prefix[match.end()]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def generate(schema_file: str,
|
||||||
|
output_dir: str,
|
||||||
|
prefix: str,
|
||||||
|
unmask: bool = False,
|
||||||
|
builtins: bool = False) -> None:
|
||||||
|
"""
|
||||||
|
Generate C code for the given schema into the target directory.
|
||||||
|
|
||||||
|
:param schema_file: The primary QAPI schema file.
|
||||||
|
:param output_dir: The output directory to store generated code.
|
||||||
|
:param prefix: Optional C-code prefix for symbol names.
|
||||||
|
:param unmask: Expose non-ABI names through introspection?
|
||||||
|
:param builtins: Generate code for built-in types?
|
||||||
|
|
||||||
|
:raise QAPIError: On failures.
|
||||||
|
"""
|
||||||
|
assert invalid_prefix_char(prefix) is None
|
||||||
|
|
||||||
|
schema = QAPISchema(schema_file)
|
||||||
|
gen_types(schema, output_dir, prefix, builtins)
|
||||||
|
gen_visit(schema, output_dir, prefix, builtins)
|
||||||
|
gen_commands(schema, output_dir, prefix)
|
||||||
|
gen_events(schema, output_dir, prefix)
|
||||||
|
gen_introspect(schema, output_dir, prefix, unmask)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
"""
|
||||||
|
gapi-gen executable entry point.
|
||||||
|
Expects arguments via sys.argv, see --help for details.
|
||||||
|
|
||||||
|
:return: int, 0 on success, 1 on failure.
|
||||||
|
"""
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Generate code from a QAPI schema')
|
||||||
|
parser.add_argument('-b', '--builtins', action='store_true',
|
||||||
|
help="generate code for built-in types")
|
||||||
|
parser.add_argument('-o', '--output-dir', action='store',
|
||||||
|
default='',
|
||||||
|
help="write output to directory OUTPUT_DIR")
|
||||||
|
parser.add_argument('-p', '--prefix', action='store',
|
||||||
|
default='',
|
||||||
|
help="prefix for symbols")
|
||||||
|
parser.add_argument('-u', '--unmask-non-abi-names', action='store_true',
|
||||||
|
dest='unmask',
|
||||||
|
help="expose non-ABI names in introspection")
|
||||||
|
parser.add_argument('schema', action='store')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
funny_char = invalid_prefix_char(args.prefix)
|
||||||
|
if funny_char:
|
||||||
|
msg = f"funny character '{funny_char}' in argument of --prefix"
|
||||||
|
print(f"{sys.argv[0]}: {msg}", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
generate(args.schema,
|
||||||
|
output_dir=args.output_dir,
|
||||||
|
prefix=args.prefix,
|
||||||
|
unmask=args.unmask,
|
||||||
|
builtins=args.builtins)
|
||||||
|
except QAPIError as err:
|
||||||
|
print(f"{sys.argv[0]}: {str(err)}", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
return 0
|
|
@ -0,0 +1,30 @@
|
||||||
|
[mypy]
|
||||||
|
strict = True
|
||||||
|
strict_optional = False
|
||||||
|
disallow_untyped_calls = False
|
||||||
|
python_version = 3.6
|
||||||
|
|
||||||
|
[mypy-qapi.error]
|
||||||
|
disallow_untyped_defs = False
|
||||||
|
disallow_incomplete_defs = False
|
||||||
|
check_untyped_defs = False
|
||||||
|
|
||||||
|
[mypy-qapi.expr]
|
||||||
|
disallow_untyped_defs = False
|
||||||
|
disallow_incomplete_defs = False
|
||||||
|
check_untyped_defs = False
|
||||||
|
|
||||||
|
[mypy-qapi.introspect]
|
||||||
|
disallow_untyped_defs = False
|
||||||
|
disallow_incomplete_defs = False
|
||||||
|
check_untyped_defs = False
|
||||||
|
|
||||||
|
[mypy-qapi.parser]
|
||||||
|
disallow_untyped_defs = False
|
||||||
|
disallow_incomplete_defs = False
|
||||||
|
check_untyped_defs = False
|
||||||
|
|
||||||
|
[mypy-qapi.schema]
|
||||||
|
disallow_untyped_defs = False
|
||||||
|
disallow_incomplete_defs = False
|
||||||
|
check_untyped_defs = False
|
|
@ -14,12 +14,12 @@
|
||||||
# This work is licensed under the terms of the GNU GPL, version 2.
|
# This work is licensed under the terms of the GNU GPL, version 2.
|
||||||
# See the COPYING file in the top-level directory.
|
# See the COPYING file in the top-level directory.
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from collections import OrderedDict
|
|
||||||
|
|
||||||
from qapi.error import QAPIParseError, QAPISemError
|
from .error import QAPIParseError, QAPISemError
|
||||||
from qapi.source import QAPISourceInfo
|
from .source import QAPISourceInfo
|
||||||
|
|
||||||
|
|
||||||
class QAPISchemaParser:
|
class QAPISchemaParser:
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
[MASTER]
|
||||||
|
|
||||||
|
# Add files or directories matching the regex patterns to the ignore list.
|
||||||
|
# The regex matches against base names, not paths.
|
||||||
|
ignore-patterns=error.py,
|
||||||
|
expr.py,
|
||||||
|
parser.py,
|
||||||
|
schema.py,
|
||||||
|
|
||||||
|
|
||||||
|
[MESSAGES CONTROL]
|
||||||
|
|
||||||
|
# Disable the message, report, category or checker with the given id(s). You
|
||||||
|
# can either give multiple identifiers separated by comma (,) or put this
|
||||||
|
# option multiple times (only on the command line, not in the configuration
|
||||||
|
# file where it should appear only once). You can also use "--disable=all" to
|
||||||
|
# disable everything first and then reenable specific checks. For example, if
|
||||||
|
# you want to run only the similarities checker, you can use "--disable=all
|
||||||
|
# --enable=similarities". If you want to run only the classes checker, but have
|
||||||
|
# no Warning level messages displayed, use "--disable=all --enable=classes
|
||||||
|
# --disable=W".
|
||||||
|
disable=fixme,
|
||||||
|
missing-docstring,
|
||||||
|
too-many-arguments,
|
||||||
|
too-many-branches,
|
||||||
|
too-many-statements,
|
||||||
|
too-many-instance-attributes,
|
||||||
|
|
||||||
|
[REPORTS]
|
||||||
|
|
||||||
|
[REFACTORING]
|
||||||
|
|
||||||
|
[MISCELLANEOUS]
|
||||||
|
|
||||||
|
[LOGGING]
|
||||||
|
|
||||||
|
[BASIC]
|
||||||
|
|
||||||
|
# Good variable names which should always be accepted, separated by a comma.
|
||||||
|
good-names=i,
|
||||||
|
j,
|
||||||
|
k,
|
||||||
|
ex,
|
||||||
|
Run,
|
||||||
|
_,
|
||||||
|
fp, # fp = open(...)
|
||||||
|
fd, # fd = os.open(...)
|
||||||
|
|
||||||
|
[VARIABLES]
|
||||||
|
|
||||||
|
[STRING]
|
||||||
|
|
||||||
|
[SPELLING]
|
||||||
|
|
||||||
|
[FORMAT]
|
||||||
|
|
||||||
|
[SIMILARITIES]
|
||||||
|
|
||||||
|
# Ignore import statements themselves when computing similarities.
|
||||||
|
ignore-imports=yes
|
||||||
|
|
||||||
|
[TYPECHECK]
|
||||||
|
|
||||||
|
[CLASSES]
|
||||||
|
|
||||||
|
[IMPORTS]
|
||||||
|
|
||||||
|
[DESIGN]
|
||||||
|
|
||||||
|
[EXCEPTIONS]
|
|
@ -14,18 +14,19 @@
|
||||||
|
|
||||||
# TODO catching name collisions in generated code would be nice
|
# TODO catching name collisions in generated code would be nice
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from collections import OrderedDict
|
from typing import Optional
|
||||||
|
|
||||||
from qapi.common import c_name, pointer_suffix
|
from .common import POINTER_SUFFIX, c_name
|
||||||
from qapi.error import QAPIError, QAPISemError
|
from .error import QAPIError, QAPISemError
|
||||||
from qapi.expr import check_exprs
|
from .expr import check_exprs
|
||||||
from qapi.parser import QAPISchemaParser
|
from .parser import QAPISchemaParser
|
||||||
|
|
||||||
|
|
||||||
class QAPISchemaEntity:
|
class QAPISchemaEntity:
|
||||||
meta = None
|
meta: Optional[str] = None
|
||||||
|
|
||||||
def __init__(self, name, info, doc, ifcond=None, features=None):
|
def __init__(self, name, info, doc, ifcond=None, features=None):
|
||||||
assert name is None or isinstance(name, str)
|
assert name is None or isinstance(name, str)
|
||||||
|
@ -309,7 +310,7 @@ class QAPISchemaArrayType(QAPISchemaType):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def c_type(self):
|
def c_type(self):
|
||||||
return c_name(self.name) + pointer_suffix
|
return c_name(self.name) + POINTER_SUFFIX
|
||||||
|
|
||||||
def json_type(self):
|
def json_type(self):
|
||||||
return 'array'
|
return 'array'
|
||||||
|
@ -430,7 +431,7 @@ class QAPISchemaObjectType(QAPISchemaType):
|
||||||
|
|
||||||
def c_type(self):
|
def c_type(self):
|
||||||
assert not self.is_implicit()
|
assert not self.is_implicit()
|
||||||
return c_name(self.name) + pointer_suffix
|
return c_name(self.name) + POINTER_SUFFIX
|
||||||
|
|
||||||
def c_unboxed_type(self):
|
def c_unboxed_type(self):
|
||||||
return c_name(self.name)
|
return c_name(self.name)
|
||||||
|
@ -504,7 +505,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
|
||||||
v.connect_doc(doc)
|
v.connect_doc(doc)
|
||||||
|
|
||||||
def c_type(self):
|
def c_type(self):
|
||||||
return c_name(self.name) + pointer_suffix
|
return c_name(self.name) + POINTER_SUFFIX
|
||||||
|
|
||||||
def json_type(self):
|
def json_type(self):
|
||||||
return 'value'
|
return 'value'
|
||||||
|
@ -536,7 +537,7 @@ class QAPISchemaVariants:
|
||||||
v.set_defined_in(name)
|
v.set_defined_in(name)
|
||||||
|
|
||||||
def check(self, schema, seen):
|
def check(self, schema, seen):
|
||||||
if not self.tag_member: # flat union
|
if not self.tag_member: # flat union
|
||||||
self.tag_member = seen.get(c_name(self._tag_name))
|
self.tag_member = seen.get(c_name(self._tag_name))
|
||||||
base = "'base'"
|
base = "'base'"
|
||||||
# Pointing to the base type when not implicit would be
|
# Pointing to the base type when not implicit would be
|
||||||
|
@ -824,7 +825,7 @@ class QAPISchema:
|
||||||
self._entity_dict = {}
|
self._entity_dict = {}
|
||||||
self._module_dict = OrderedDict()
|
self._module_dict = OrderedDict()
|
||||||
self._schema_dir = os.path.dirname(fname)
|
self._schema_dir = os.path.dirname(fname)
|
||||||
self._make_module(None) # built-ins
|
self._make_module(None) # built-ins
|
||||||
self._make_module(fname)
|
self._make_module(fname)
|
||||||
self._predefining = True
|
self._predefining = True
|
||||||
self._def_predefineds()
|
self._def_predefineds()
|
||||||
|
@ -899,7 +900,7 @@ class QAPISchema:
|
||||||
self._make_array_type(name, None)
|
self._make_array_type(name, None)
|
||||||
|
|
||||||
def _def_predefineds(self):
|
def _def_predefineds(self):
|
||||||
for t in [('str', 'string', 'char' + pointer_suffix),
|
for t in [('str', 'string', 'char' + POINTER_SUFFIX),
|
||||||
('number', 'number', 'double'),
|
('number', 'number', 'double'),
|
||||||
('int', 'int', 'int64_t'),
|
('int', 'int', 'int64_t'),
|
||||||
('int8', 'int', 'int8_t'),
|
('int8', 'int', 'int8_t'),
|
||||||
|
@ -912,8 +913,8 @@ class QAPISchema:
|
||||||
('uint64', 'int', 'uint64_t'),
|
('uint64', 'int', 'uint64_t'),
|
||||||
('size', 'int', 'uint64_t'),
|
('size', 'int', 'uint64_t'),
|
||||||
('bool', 'boolean', 'bool'),
|
('bool', 'boolean', 'bool'),
|
||||||
('any', 'value', 'QObject' + pointer_suffix),
|
('any', 'value', 'QObject' + POINTER_SUFFIX),
|
||||||
('null', 'null', 'QNull' + pointer_suffix)]:
|
('null', 'null', 'QNull' + POINTER_SUFFIX)]:
|
||||||
self._def_builtin_type(*t)
|
self._def_builtin_type(*t)
|
||||||
self.the_empty_object_type = QAPISchemaObjectType(
|
self.the_empty_object_type = QAPISchemaObjectType(
|
||||||
'q_empty', None, None, None, None, None, [], None)
|
'q_empty', None, None, None, None, None, [], None)
|
||||||
|
@ -968,7 +969,9 @@ class QAPISchema:
|
||||||
# But it's not tight: the disjunction need not imply it. We
|
# But it's not tight: the disjunction need not imply it. We
|
||||||
# may end up compiling useless wrapper types.
|
# may end up compiling useless wrapper types.
|
||||||
# TODO kill simple unions or implement the disjunction
|
# TODO kill simple unions or implement the disjunction
|
||||||
assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access
|
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
assert (ifcond or []) == typ._ifcond
|
||||||
else:
|
else:
|
||||||
self._def_entity(QAPISchemaObjectType(
|
self._def_entity(QAPISchemaObjectType(
|
||||||
name, info, None, ifcond, None, None, members, None))
|
name, info, None, ifcond, None, None, members, None))
|
||||||
|
|
|
@ -11,37 +11,46 @@
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import sys
|
import sys
|
||||||
|
from typing import List, Optional, TypeVar
|
||||||
|
|
||||||
|
|
||||||
class QAPISchemaPragma:
|
class QAPISchemaPragma:
|
||||||
def __init__(self):
|
# Replace with @dataclass in Python 3.7+
|
||||||
|
# pylint: disable=too-few-public-methods
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
# Are documentation comments required?
|
# Are documentation comments required?
|
||||||
self.doc_required = False
|
self.doc_required = False
|
||||||
# Whitelist of commands allowed to return a non-dictionary
|
# Whitelist of commands allowed to return a non-dictionary
|
||||||
self.returns_whitelist = []
|
self.returns_whitelist: List[str] = []
|
||||||
# Whitelist of entities allowed to violate case conventions
|
# Whitelist of entities allowed to violate case conventions
|
||||||
self.name_case_whitelist = []
|
self.name_case_whitelist: List[str] = []
|
||||||
|
|
||||||
|
|
||||||
class QAPISourceInfo:
|
class QAPISourceInfo:
|
||||||
def __init__(self, fname, line, parent):
|
T = TypeVar('T', bound='QAPISourceInfo')
|
||||||
|
|
||||||
|
def __init__(self, fname: str, line: int,
|
||||||
|
parent: Optional['QAPISourceInfo']):
|
||||||
self.fname = fname
|
self.fname = fname
|
||||||
self.line = line
|
self.line = line
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.pragma = parent.pragma if parent else QAPISchemaPragma()
|
self.pragma: QAPISchemaPragma = (
|
||||||
self.defn_meta = None
|
parent.pragma if parent else QAPISchemaPragma()
|
||||||
self.defn_name = None
|
)
|
||||||
|
self.defn_meta: Optional[str] = None
|
||||||
|
self.defn_name: Optional[str] = None
|
||||||
|
|
||||||
def set_defn(self, meta, name):
|
def set_defn(self, meta: str, name: str) -> None:
|
||||||
self.defn_meta = meta
|
self.defn_meta = meta
|
||||||
self.defn_name = name
|
self.defn_name = name
|
||||||
|
|
||||||
def next_line(self):
|
def next_line(self: T) -> T:
|
||||||
info = copy.copy(self)
|
info = copy.copy(self)
|
||||||
info.line += 1
|
info.line += 1
|
||||||
return info
|
return info
|
||||||
|
|
||||||
def loc(self):
|
def loc(self) -> str:
|
||||||
if self.fname is None:
|
if self.fname is None:
|
||||||
return sys.argv[0]
|
return sys.argv[0]
|
||||||
ret = self.fname
|
ret = self.fname
|
||||||
|
@ -49,13 +58,13 @@ class QAPISourceInfo:
|
||||||
ret += ':%d' % self.line
|
ret += ':%d' % self.line
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def in_defn(self):
|
def in_defn(self) -> str:
|
||||||
if self.defn_name:
|
if self.defn_name:
|
||||||
return "%s: In %s '%s':\n" % (self.fname,
|
return "%s: In %s '%s':\n" % (self.fname,
|
||||||
self.defn_meta, self.defn_name)
|
self.defn_meta, self.defn_name)
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def include_path(self):
|
def include_path(self) -> str:
|
||||||
ret = ''
|
ret = ''
|
||||||
parent = self.parent
|
parent = self.parent
|
||||||
while parent:
|
while parent:
|
||||||
|
@ -63,5 +72,5 @@ class QAPISourceInfo:
|
||||||
parent = parent.parent
|
parent = parent.parent
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return self.include_path() + self.in_defn() + self.loc()
|
return self.include_path() + self.in_defn() + self.loc()
|
||||||
|
|
|
@ -13,9 +13,26 @@ This work is licensed under the terms of the GNU GPL, version 2.
|
||||||
# See the COPYING file in the top-level directory.
|
# See the COPYING file in the top-level directory.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from qapi.common import *
|
from typing import List, Optional
|
||||||
from qapi.gen import QAPISchemaModularCVisitor, ifcontext
|
|
||||||
from qapi.schema import QAPISchemaEnumMember, QAPISchemaObjectType
|
from .common import (
|
||||||
|
c_enum_const,
|
||||||
|
c_name,
|
||||||
|
gen_endif,
|
||||||
|
gen_if,
|
||||||
|
mcgen,
|
||||||
|
)
|
||||||
|
from .gen import QAPISchemaModularCVisitor, ifcontext
|
||||||
|
from .schema import (
|
||||||
|
QAPISchema,
|
||||||
|
QAPISchemaEnumMember,
|
||||||
|
QAPISchemaFeature,
|
||||||
|
QAPISchemaObjectType,
|
||||||
|
QAPISchemaObjectTypeMember,
|
||||||
|
QAPISchemaType,
|
||||||
|
QAPISchemaVariants,
|
||||||
|
)
|
||||||
|
from .source import QAPISourceInfo
|
||||||
|
|
||||||
|
|
||||||
# variants must be emitted before their container; track what has already
|
# variants must be emitted before their container; track what has already
|
||||||
|
@ -23,21 +40,23 @@ from qapi.schema import QAPISchemaEnumMember, QAPISchemaObjectType
|
||||||
objects_seen = set()
|
objects_seen = set()
|
||||||
|
|
||||||
|
|
||||||
def gen_enum_lookup(name, members, prefix=None):
|
def gen_enum_lookup(name: str,
|
||||||
|
members: List[QAPISchemaEnumMember],
|
||||||
|
prefix: Optional[str] = None) -> str:
|
||||||
ret = mcgen('''
|
ret = mcgen('''
|
||||||
|
|
||||||
const QEnumLookup %(c_name)s_lookup = {
|
const QEnumLookup %(c_name)s_lookup = {
|
||||||
.array = (const char *const[]) {
|
.array = (const char *const[]) {
|
||||||
''',
|
''',
|
||||||
c_name=c_name(name))
|
c_name=c_name(name))
|
||||||
for m in members:
|
for memb in members:
|
||||||
ret += gen_if(m.ifcond)
|
ret += gen_if(memb.ifcond)
|
||||||
index = c_enum_const(name, m.name, prefix)
|
index = c_enum_const(name, memb.name, prefix)
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
[%(index)s] = "%(name)s",
|
[%(index)s] = "%(name)s",
|
||||||
''',
|
''',
|
||||||
index=index, name=m.name)
|
index=index, name=memb.name)
|
||||||
ret += gen_endif(m.ifcond)
|
ret += gen_endif(memb.ifcond)
|
||||||
|
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
},
|
},
|
||||||
|
@ -48,7 +67,9 @@ const QEnumLookup %(c_name)s_lookup = {
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def gen_enum(name, members, prefix=None):
|
def gen_enum(name: str,
|
||||||
|
members: List[QAPISchemaEnumMember],
|
||||||
|
prefix: Optional[str] = None) -> str:
|
||||||
# append automatically generated _MAX value
|
# append automatically generated _MAX value
|
||||||
enum_members = members + [QAPISchemaEnumMember('_MAX', None)]
|
enum_members = members + [QAPISchemaEnumMember('_MAX', None)]
|
||||||
|
|
||||||
|
@ -58,13 +79,13 @@ typedef enum %(c_name)s {
|
||||||
''',
|
''',
|
||||||
c_name=c_name(name))
|
c_name=c_name(name))
|
||||||
|
|
||||||
for m in enum_members:
|
for memb in enum_members:
|
||||||
ret += gen_if(m.ifcond)
|
ret += gen_if(memb.ifcond)
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
%(c_enum)s,
|
%(c_enum)s,
|
||||||
''',
|
''',
|
||||||
c_enum=c_enum_const(name, m.name, prefix))
|
c_enum=c_enum_const(name, memb.name, prefix))
|
||||||
ret += gen_endif(m.ifcond)
|
ret += gen_endif(memb.ifcond)
|
||||||
|
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
} %(c_name)s;
|
} %(c_name)s;
|
||||||
|
@ -82,7 +103,7 @@ extern const QEnumLookup %(c_name)s_lookup;
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def gen_fwd_object_or_array(name):
|
def gen_fwd_object_or_array(name: str) -> str:
|
||||||
return mcgen('''
|
return mcgen('''
|
||||||
|
|
||||||
typedef struct %(c_name)s %(c_name)s;
|
typedef struct %(c_name)s %(c_name)s;
|
||||||
|
@ -90,7 +111,7 @@ typedef struct %(c_name)s %(c_name)s;
|
||||||
c_name=c_name(name))
|
c_name=c_name(name))
|
||||||
|
|
||||||
|
|
||||||
def gen_array(name, element_type):
|
def gen_array(name: str, element_type: QAPISchemaType) -> str:
|
||||||
return mcgen('''
|
return mcgen('''
|
||||||
|
|
||||||
struct %(c_name)s {
|
struct %(c_name)s {
|
||||||
|
@ -101,7 +122,7 @@ struct %(c_name)s {
|
||||||
c_name=c_name(name), c_type=element_type.c_type())
|
c_name=c_name(name), c_type=element_type.c_type())
|
||||||
|
|
||||||
|
|
||||||
def gen_struct_members(members):
|
def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str:
|
||||||
ret = ''
|
ret = ''
|
||||||
for memb in members:
|
for memb in members:
|
||||||
ret += gen_if(memb.ifcond)
|
ret += gen_if(memb.ifcond)
|
||||||
|
@ -118,17 +139,21 @@ def gen_struct_members(members):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def gen_object(name, ifcond, base, members, variants):
|
def gen_object(name: str, ifcond: List[str],
|
||||||
|
base: Optional[QAPISchemaObjectType],
|
||||||
|
members: List[QAPISchemaObjectTypeMember],
|
||||||
|
variants: Optional[QAPISchemaVariants]) -> str:
|
||||||
if name in objects_seen:
|
if name in objects_seen:
|
||||||
return ''
|
return ''
|
||||||
objects_seen.add(name)
|
objects_seen.add(name)
|
||||||
|
|
||||||
ret = ''
|
ret = ''
|
||||||
if variants:
|
for var in variants.variants if variants else ():
|
||||||
for v in variants.variants:
|
obj = var.type
|
||||||
if isinstance(v.type, QAPISchemaObjectType):
|
if not isinstance(obj, QAPISchemaObjectType):
|
||||||
ret += gen_object(v.type.name, v.type.ifcond, v.type.base,
|
continue
|
||||||
v.type.local_members, v.type.variants)
|
ret += gen_object(obj.name, obj.ifcond, obj.base,
|
||||||
|
obj.local_members, obj.variants)
|
||||||
|
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
|
|
||||||
|
@ -172,7 +197,7 @@ struct %(c_name)s {
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def gen_upcast(name, base):
|
def gen_upcast(name: str, base: QAPISchemaObjectType) -> str:
|
||||||
# C makes const-correctness ugly. We have to cast away const to let
|
# C makes const-correctness ugly. We have to cast away const to let
|
||||||
# this function work for both const and non-const obj.
|
# this function work for both const and non-const obj.
|
||||||
return mcgen('''
|
return mcgen('''
|
||||||
|
@ -185,7 +210,7 @@ static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj)
|
||||||
c_name=c_name(name), base=base.c_name())
|
c_name=c_name(name), base=base.c_name())
|
||||||
|
|
||||||
|
|
||||||
def gen_variants(variants):
|
def gen_variants(variants: QAPISchemaVariants) -> str:
|
||||||
ret = mcgen('''
|
ret = mcgen('''
|
||||||
union { /* union tag is @%(c_name)s */
|
union { /* union tag is @%(c_name)s */
|
||||||
''',
|
''',
|
||||||
|
@ -209,7 +234,7 @@ def gen_variants(variants):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def gen_type_cleanup_decl(name):
|
def gen_type_cleanup_decl(name: str) -> str:
|
||||||
ret = mcgen('''
|
ret = mcgen('''
|
||||||
|
|
||||||
void qapi_free_%(c_name)s(%(c_name)s *obj);
|
void qapi_free_%(c_name)s(%(c_name)s *obj);
|
||||||
|
@ -219,7 +244,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(%(c_name)s, qapi_free_%(c_name)s)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def gen_type_cleanup(name):
|
def gen_type_cleanup(name: str) -> str:
|
||||||
ret = mcgen('''
|
ret = mcgen('''
|
||||||
|
|
||||||
void qapi_free_%(c_name)s(%(c_name)s *obj)
|
void qapi_free_%(c_name)s(%(c_name)s *obj)
|
||||||
|
@ -241,12 +266,12 @@ void qapi_free_%(c_name)s(%(c_name)s *obj)
|
||||||
|
|
||||||
class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
|
class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
|
||||||
|
|
||||||
def __init__(self, prefix):
|
def __init__(self, prefix: str):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
prefix, 'qapi-types', ' * Schema-defined QAPI types',
|
prefix, 'qapi-types', ' * Schema-defined QAPI types',
|
||||||
' * Built-in QAPI types', __doc__)
|
' * Built-in QAPI types', __doc__)
|
||||||
|
|
||||||
def _begin_system_module(self, name):
|
def _begin_system_module(self, name: None) -> None:
|
||||||
self._genc.preamble_add(mcgen('''
|
self._genc.preamble_add(mcgen('''
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/dealloc-visitor.h"
|
#include "qapi/dealloc-visitor.h"
|
||||||
|
@ -257,7 +282,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
|
||||||
#include "qapi/util.h"
|
#include "qapi/util.h"
|
||||||
'''))
|
'''))
|
||||||
|
|
||||||
def _begin_user_module(self, name):
|
def _begin_user_module(self, name: str) -> None:
|
||||||
types = self._module_basename('qapi-types', name)
|
types = self._module_basename('qapi-types', name)
|
||||||
visit = self._module_basename('qapi-visit', name)
|
visit = self._module_basename('qapi-visit', name)
|
||||||
self._genc.preamble_add(mcgen('''
|
self._genc.preamble_add(mcgen('''
|
||||||
|
@ -271,27 +296,43 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
|
||||||
#include "qapi/qapi-builtin-types.h"
|
#include "qapi/qapi-builtin-types.h"
|
||||||
'''))
|
'''))
|
||||||
|
|
||||||
def visit_begin(self, schema):
|
def visit_begin(self, schema: QAPISchema) -> None:
|
||||||
# gen_object() is recursive, ensure it doesn't visit the empty type
|
# gen_object() is recursive, ensure it doesn't visit the empty type
|
||||||
objects_seen.add(schema.the_empty_object_type.name)
|
objects_seen.add(schema.the_empty_object_type.name)
|
||||||
|
|
||||||
def _gen_type_cleanup(self, name):
|
def _gen_type_cleanup(self, name: str) -> None:
|
||||||
self._genh.add(gen_type_cleanup_decl(name))
|
self._genh.add(gen_type_cleanup_decl(name))
|
||||||
self._genc.add(gen_type_cleanup(name))
|
self._genc.add(gen_type_cleanup(name))
|
||||||
|
|
||||||
def visit_enum_type(self, name, info, ifcond, features, members, prefix):
|
def visit_enum_type(self,
|
||||||
|
name: str,
|
||||||
|
info: Optional[QAPISourceInfo],
|
||||||
|
ifcond: List[str],
|
||||||
|
features: List[QAPISchemaFeature],
|
||||||
|
members: List[QAPISchemaEnumMember],
|
||||||
|
prefix: Optional[str]) -> None:
|
||||||
with ifcontext(ifcond, self._genh, self._genc):
|
with ifcontext(ifcond, self._genh, self._genc):
|
||||||
self._genh.preamble_add(gen_enum(name, members, prefix))
|
self._genh.preamble_add(gen_enum(name, members, prefix))
|
||||||
self._genc.add(gen_enum_lookup(name, members, prefix))
|
self._genc.add(gen_enum_lookup(name, members, prefix))
|
||||||
|
|
||||||
def visit_array_type(self, name, info, ifcond, element_type):
|
def visit_array_type(self,
|
||||||
|
name: str,
|
||||||
|
info: Optional[QAPISourceInfo],
|
||||||
|
ifcond: List[str],
|
||||||
|
element_type: QAPISchemaType) -> None:
|
||||||
with ifcontext(ifcond, self._genh, self._genc):
|
with ifcontext(ifcond, self._genh, self._genc):
|
||||||
self._genh.preamble_add(gen_fwd_object_or_array(name))
|
self._genh.preamble_add(gen_fwd_object_or_array(name))
|
||||||
self._genh.add(gen_array(name, element_type))
|
self._genh.add(gen_array(name, element_type))
|
||||||
self._gen_type_cleanup(name)
|
self._gen_type_cleanup(name)
|
||||||
|
|
||||||
def visit_object_type(self, name, info, ifcond, features,
|
def visit_object_type(self,
|
||||||
base, members, variants):
|
name: str,
|
||||||
|
info: Optional[QAPISourceInfo],
|
||||||
|
ifcond: List[str],
|
||||||
|
features: List[QAPISchemaFeature],
|
||||||
|
base: Optional[QAPISchemaObjectType],
|
||||||
|
members: List[QAPISchemaObjectTypeMember],
|
||||||
|
variants: Optional[QAPISchemaVariants]) -> None:
|
||||||
# Nothing to do for the special empty builtin
|
# Nothing to do for the special empty builtin
|
||||||
if name == 'q_empty':
|
if name == 'q_empty':
|
||||||
return
|
return
|
||||||
|
@ -307,7 +348,12 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
|
||||||
# implicit types won't be directly allocated/freed
|
# implicit types won't be directly allocated/freed
|
||||||
self._gen_type_cleanup(name)
|
self._gen_type_cleanup(name)
|
||||||
|
|
||||||
def visit_alternate_type(self, name, info, ifcond, features, variants):
|
def visit_alternate_type(self,
|
||||||
|
name: str,
|
||||||
|
info: QAPISourceInfo,
|
||||||
|
ifcond: List[str],
|
||||||
|
features: List[QAPISchemaFeature],
|
||||||
|
variants: QAPISchemaVariants) -> None:
|
||||||
with ifcontext(ifcond, self._genh):
|
with ifcontext(ifcond, self._genh):
|
||||||
self._genh.preamble_add(gen_fwd_object_or_array(name))
|
self._genh.preamble_add(gen_fwd_object_or_array(name))
|
||||||
self._genh.add(gen_object(name, ifcond, None,
|
self._genh.add(gen_object(name, ifcond, None,
|
||||||
|
@ -316,7 +362,10 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
|
||||||
self._gen_type_cleanup(name)
|
self._gen_type_cleanup(name)
|
||||||
|
|
||||||
|
|
||||||
def gen_types(schema, output_dir, prefix, opt_builtins):
|
def gen_types(schema: QAPISchema,
|
||||||
|
output_dir: str,
|
||||||
|
prefix: str,
|
||||||
|
opt_builtins: bool) -> None:
|
||||||
vis = QAPISchemaGenTypeVisitor(prefix)
|
vis = QAPISchemaGenTypeVisitor(prefix)
|
||||||
schema.visit(vis)
|
schema.visit(vis)
|
||||||
vis.write(output_dir, opt_builtins)
|
vis.write(output_dir, opt_builtins)
|
||||||
|
|
|
@ -13,22 +13,43 @@ This work is licensed under the terms of the GNU GPL, version 2.
|
||||||
See the COPYING file in the top-level directory.
|
See the COPYING file in the top-level directory.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from qapi.common import *
|
from typing import List, Optional
|
||||||
from qapi.gen import QAPISchemaModularCVisitor, ifcontext
|
|
||||||
from qapi.schema import QAPISchemaObjectType
|
from .common import (
|
||||||
|
c_enum_const,
|
||||||
|
c_name,
|
||||||
|
gen_endif,
|
||||||
|
gen_if,
|
||||||
|
indent,
|
||||||
|
mcgen,
|
||||||
|
)
|
||||||
|
from .gen import QAPISchemaModularCVisitor, ifcontext
|
||||||
|
from .schema import (
|
||||||
|
QAPISchema,
|
||||||
|
QAPISchemaEnumMember,
|
||||||
|
QAPISchemaEnumType,
|
||||||
|
QAPISchemaFeature,
|
||||||
|
QAPISchemaObjectType,
|
||||||
|
QAPISchemaObjectTypeMember,
|
||||||
|
QAPISchemaType,
|
||||||
|
QAPISchemaVariants,
|
||||||
|
)
|
||||||
|
from .source import QAPISourceInfo
|
||||||
|
|
||||||
|
|
||||||
def gen_visit_decl(name, scalar=False):
|
def gen_visit_decl(name: str, scalar: bool = False) -> str:
|
||||||
c_type = c_name(name) + ' *'
|
c_type = c_name(name) + ' *'
|
||||||
if not scalar:
|
if not scalar:
|
||||||
c_type += '*'
|
c_type += '*'
|
||||||
return mcgen('''
|
return mcgen('''
|
||||||
bool visit_type_%(c_name)s(Visitor *v, const char *name, %(c_type)sobj, Error **errp);
|
|
||||||
|
bool visit_type_%(c_name)s(Visitor *v, const char *name,
|
||||||
|
%(c_type)sobj, Error **errp);
|
||||||
''',
|
''',
|
||||||
c_name=c_name(name), c_type=c_type)
|
c_name=c_name(name), c_type=c_type)
|
||||||
|
|
||||||
|
|
||||||
def gen_visit_members_decl(name):
|
def gen_visit_members_decl(name: str) -> str:
|
||||||
return mcgen('''
|
return mcgen('''
|
||||||
|
|
||||||
bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
|
bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
|
||||||
|
@ -36,7 +57,10 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
|
||||||
c_name=c_name(name))
|
c_name=c_name(name))
|
||||||
|
|
||||||
|
|
||||||
def gen_visit_object_members(name, base, members, variants):
|
def gen_visit_object_members(name: str,
|
||||||
|
base: Optional[QAPISchemaObjectType],
|
||||||
|
members: List[QAPISchemaObjectTypeMember],
|
||||||
|
variants: Optional[QAPISchemaVariants]) -> str:
|
||||||
ret = mcgen('''
|
ret = mcgen('''
|
||||||
|
|
||||||
bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
|
bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
|
||||||
|
@ -59,7 +83,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
|
||||||
if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
|
if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
|
||||||
''',
|
''',
|
||||||
name=memb.name, c_name=c_name(memb.name))
|
name=memb.name, c_name=c_name(memb.name))
|
||||||
push_indent()
|
indent.increase()
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
if (!visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, errp)) {
|
if (!visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, errp)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -68,22 +92,24 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
|
||||||
c_type=memb.type.c_name(), name=memb.name,
|
c_type=memb.type.c_name(), name=memb.name,
|
||||||
c_name=c_name(memb.name))
|
c_name=c_name(memb.name))
|
||||||
if memb.optional:
|
if memb.optional:
|
||||||
pop_indent()
|
indent.decrease()
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
}
|
}
|
||||||
''')
|
''')
|
||||||
ret += gen_endif(memb.ifcond)
|
ret += gen_endif(memb.ifcond)
|
||||||
|
|
||||||
if variants:
|
if variants:
|
||||||
|
tag_member = variants.tag_member
|
||||||
|
assert isinstance(tag_member.type, QAPISchemaEnumType)
|
||||||
|
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
switch (obj->%(c_name)s) {
|
switch (obj->%(c_name)s) {
|
||||||
''',
|
''',
|
||||||
c_name=c_name(variants.tag_member.name))
|
c_name=c_name(tag_member.name))
|
||||||
|
|
||||||
for var in variants.variants:
|
for var in variants.variants:
|
||||||
case_str = c_enum_const(variants.tag_member.type.name,
|
case_str = c_enum_const(tag_member.type.name, var.name,
|
||||||
var.name,
|
tag_member.type.prefix)
|
||||||
variants.tag_member.type.prefix)
|
|
||||||
ret += gen_if(var.ifcond)
|
ret += gen_if(var.ifcond)
|
||||||
if var.type.name == 'q_empty':
|
if var.type.name == 'q_empty':
|
||||||
# valid variant and nothing to do
|
# valid variant and nothing to do
|
||||||
|
@ -114,10 +140,11 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def gen_visit_list(name, element_type):
|
def gen_visit_list(name: str, element_type: QAPISchemaType) -> str:
|
||||||
return mcgen('''
|
return mcgen('''
|
||||||
|
|
||||||
bool visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
|
bool visit_type_%(c_name)s(Visitor *v, const char *name,
|
||||||
|
%(c_name)s **obj, Error **errp)
|
||||||
{
|
{
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
%(c_name)s *tail;
|
%(c_name)s *tail;
|
||||||
|
@ -147,10 +174,11 @@ out_obj:
|
||||||
c_name=c_name(name), c_elt_type=element_type.c_name())
|
c_name=c_name(name), c_elt_type=element_type.c_name())
|
||||||
|
|
||||||
|
|
||||||
def gen_visit_enum(name):
|
def gen_visit_enum(name: str) -> str:
|
||||||
return mcgen('''
|
return mcgen('''
|
||||||
|
|
||||||
bool visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error **errp)
|
bool visit_type_%(c_name)s(Visitor *v, const char *name,
|
||||||
|
%(c_name)s *obj, Error **errp)
|
||||||
{
|
{
|
||||||
int value = *obj;
|
int value = *obj;
|
||||||
bool ok = visit_type_enum(v, name, &value, &%(c_name)s_lookup, errp);
|
bool ok = visit_type_enum(v, name, &value, &%(c_name)s_lookup, errp);
|
||||||
|
@ -161,10 +189,11 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error
|
||||||
c_name=c_name(name))
|
c_name=c_name(name))
|
||||||
|
|
||||||
|
|
||||||
def gen_visit_alternate(name, variants):
|
def gen_visit_alternate(name: str, variants: QAPISchemaVariants) -> str:
|
||||||
ret = mcgen('''
|
ret = mcgen('''
|
||||||
|
|
||||||
bool visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
|
bool visit_type_%(c_name)s(Visitor *v, const char *name,
|
||||||
|
%(c_name)s **obj, Error **errp)
|
||||||
{
|
{
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
|
||||||
|
@ -236,10 +265,11 @@ out_obj:
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def gen_visit_object(name, base, members, variants):
|
def gen_visit_object(name: str) -> str:
|
||||||
return mcgen('''
|
return mcgen('''
|
||||||
|
|
||||||
bool visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
|
bool visit_type_%(c_name)s(Visitor *v, const char *name,
|
||||||
|
%(c_name)s **obj, Error **errp)
|
||||||
{
|
{
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
|
||||||
|
@ -270,12 +300,12 @@ out_obj:
|
||||||
|
|
||||||
class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
|
class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
|
||||||
|
|
||||||
def __init__(self, prefix):
|
def __init__(self, prefix: str):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
prefix, 'qapi-visit', ' * Schema-defined QAPI visitors',
|
prefix, 'qapi-visit', ' * Schema-defined QAPI visitors',
|
||||||
' * Built-in QAPI visitors', __doc__)
|
' * Built-in QAPI visitors', __doc__)
|
||||||
|
|
||||||
def _begin_system_module(self, name):
|
def _begin_system_module(self, name: None) -> None:
|
||||||
self._genc.preamble_add(mcgen('''
|
self._genc.preamble_add(mcgen('''
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
|
@ -287,7 +317,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
|
||||||
|
|
||||||
'''))
|
'''))
|
||||||
|
|
||||||
def _begin_user_module(self, name):
|
def _begin_user_module(self, name: str) -> None:
|
||||||
types = self._module_basename('qapi-types', name)
|
types = self._module_basename('qapi-types', name)
|
||||||
visit = self._module_basename('qapi-visit', name)
|
visit = self._module_basename('qapi-visit', name)
|
||||||
self._genc.preamble_add(mcgen('''
|
self._genc.preamble_add(mcgen('''
|
||||||
|
@ -304,18 +334,34 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
|
||||||
''',
|
''',
|
||||||
types=types))
|
types=types))
|
||||||
|
|
||||||
def visit_enum_type(self, name, info, ifcond, features, members, prefix):
|
def visit_enum_type(self,
|
||||||
|
name: str,
|
||||||
|
info: QAPISourceInfo,
|
||||||
|
ifcond: List[str],
|
||||||
|
features: List[QAPISchemaFeature],
|
||||||
|
members: List[QAPISchemaEnumMember],
|
||||||
|
prefix: Optional[str]) -> None:
|
||||||
with ifcontext(ifcond, self._genh, self._genc):
|
with ifcontext(ifcond, self._genh, self._genc):
|
||||||
self._genh.add(gen_visit_decl(name, scalar=True))
|
self._genh.add(gen_visit_decl(name, scalar=True))
|
||||||
self._genc.add(gen_visit_enum(name))
|
self._genc.add(gen_visit_enum(name))
|
||||||
|
|
||||||
def visit_array_type(self, name, info, ifcond, element_type):
|
def visit_array_type(self,
|
||||||
|
name: str,
|
||||||
|
info: Optional[QAPISourceInfo],
|
||||||
|
ifcond: List[str],
|
||||||
|
element_type: QAPISchemaType) -> None:
|
||||||
with ifcontext(ifcond, self._genh, self._genc):
|
with ifcontext(ifcond, self._genh, self._genc):
|
||||||
self._genh.add(gen_visit_decl(name))
|
self._genh.add(gen_visit_decl(name))
|
||||||
self._genc.add(gen_visit_list(name, element_type))
|
self._genc.add(gen_visit_list(name, element_type))
|
||||||
|
|
||||||
def visit_object_type(self, name, info, ifcond, features,
|
def visit_object_type(self,
|
||||||
base, members, variants):
|
name: str,
|
||||||
|
info: Optional[QAPISourceInfo],
|
||||||
|
ifcond: List[str],
|
||||||
|
features: List[QAPISchemaFeature],
|
||||||
|
base: Optional[QAPISchemaObjectType],
|
||||||
|
members: List[QAPISchemaObjectTypeMember],
|
||||||
|
variants: Optional[QAPISchemaVariants]) -> None:
|
||||||
# Nothing to do for the special empty builtin
|
# Nothing to do for the special empty builtin
|
||||||
if name == 'q_empty':
|
if name == 'q_empty':
|
||||||
return
|
return
|
||||||
|
@ -328,15 +374,23 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
|
||||||
if not name.startswith('q_'):
|
if not name.startswith('q_'):
|
||||||
# only explicit types need an allocating visit
|
# only explicit types need an allocating visit
|
||||||
self._genh.add(gen_visit_decl(name))
|
self._genh.add(gen_visit_decl(name))
|
||||||
self._genc.add(gen_visit_object(name, base, members, variants))
|
self._genc.add(gen_visit_object(name))
|
||||||
|
|
||||||
def visit_alternate_type(self, name, info, ifcond, features, variants):
|
def visit_alternate_type(self,
|
||||||
|
name: str,
|
||||||
|
info: QAPISourceInfo,
|
||||||
|
ifcond: List[str],
|
||||||
|
features: List[QAPISchemaFeature],
|
||||||
|
variants: QAPISchemaVariants) -> None:
|
||||||
with ifcontext(ifcond, self._genh, self._genc):
|
with ifcontext(ifcond, self._genh, self._genc):
|
||||||
self._genh.add(gen_visit_decl(name))
|
self._genh.add(gen_visit_decl(name))
|
||||||
self._genc.add(gen_visit_alternate(name, variants))
|
self._genc.add(gen_visit_alternate(name, variants))
|
||||||
|
|
||||||
|
|
||||||
def gen_visit(schema, output_dir, prefix, opt_builtins):
|
def gen_visit(schema: QAPISchema,
|
||||||
|
output_dir: str,
|
||||||
|
prefix: str,
|
||||||
|
opt_builtins: bool) -> None:
|
||||||
vis = QAPISchemaGenVisitVisitor(prefix)
|
vis = QAPISchemaGenVisitVisitor(prefix)
|
||||||
schema.visit(vis)
|
schema.visit(vis)
|
||||||
vis.write(output_dir, opt_builtins)
|
vis.write(output_dir, opt_builtins)
|
||||||
|
|
Loading…
Reference in New Issue