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.
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
no system compiler available. See :ref: `_docker-ref` for details.
|
||||
no system compiler available. See :ref:`docker-ref` for details.
|
||||
|
||||
Running subset of tests
|
||||
-----------------------
|
||||
|
|
|
@ -1,56 +1,19 @@
|
|||
#!/usr/bin/env python3
|
||||
# QAPI generator
|
||||
#
|
||||
|
||||
# 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 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
|
||||
|
||||
from qapi.commands import gen_commands
|
||||
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)
|
||||
|
||||
from qapi import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
||||
sys.exit(main.main())
|
||||
|
|
2
scripts/qapi/.flake8
Normal file
2
scripts/qapi/.flake8
Normal file
|
@ -0,0 +1,2 @@
|
|||
[flake8]
|
||||
extend-ignore = E722 # Prefer pylint's bare-except checks to flake8's
|
7
scripts/qapi/.isort.cfg
Normal file
7
scripts/qapi/.isort.cfg
Normal file
|
@ -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.
|
||||
"""
|
||||
|
||||
from qapi.common import *
|
||||
from qapi.gen import QAPIGenCCode, QAPISchemaModularCVisitor, ifcontext
|
||||
from typing import (
|
||||
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('''
|
||||
%(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'))
|
||||
|
||||
|
||||
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 = ''
|
||||
|
||||
argstr = ''
|
||||
|
@ -62,10 +88,11 @@ def gen_call(name, arg_type, boxed, ret_type):
|
|||
return ret
|
||||
|
||||
|
||||
def gen_marshal_output(ret_type):
|
||||
def gen_marshal_output(ret_type: QAPISchemaType) -> str:
|
||||
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;
|
||||
|
||||
|
@ -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())
|
||||
|
||||
|
||||
def build_marshal_proto(name):
|
||||
def build_marshal_proto(name: str) -> str:
|
||||
return ('void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)'
|
||||
% c_name(name))
|
||||
|
||||
|
||||
def gen_marshal_decl(name):
|
||||
def gen_marshal_decl(name: str) -> str:
|
||||
return mcgen('''
|
||||
%(proto)s;
|
||||
''',
|
||||
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())
|
||||
|
||||
ret = mcgen('''
|
||||
|
@ -176,8 +206,11 @@ out:
|
|||
return ret
|
||||
|
||||
|
||||
def gen_register_command(name, success_response, allow_oob, allow_preconfig,
|
||||
coroutine):
|
||||
def gen_register_command(name: str,
|
||||
success_response: bool,
|
||||
allow_oob: bool,
|
||||
allow_preconfig: bool,
|
||||
coroutine: bool) -> str:
|
||||
options = []
|
||||
|
||||
if not success_response:
|
||||
|
@ -192,18 +225,16 @@ def gen_register_command(name, success_response, allow_oob, allow_preconfig,
|
|||
if not options:
|
||||
options = ['QCO_NO_OPTIONS']
|
||||
|
||||
options = " | ".join(options)
|
||||
|
||||
ret = mcgen('''
|
||||
qmp_register_command(cmds, "%(name)s",
|
||||
qmp_marshal_%(c_name)s, %(opts)s);
|
||||
''',
|
||||
name=name, c_name=c_name(name),
|
||||
opts=options)
|
||||
opts=" | ".join(options))
|
||||
return ret
|
||||
|
||||
|
||||
def gen_registry(registry, prefix):
|
||||
def gen_registry(registry: str, prefix: str) -> str:
|
||||
ret = mcgen('''
|
||||
|
||||
void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds)
|
||||
|
@ -220,15 +251,14 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds)
|
|||
|
||||
|
||||
class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
|
||||
|
||||
def __init__(self, prefix):
|
||||
def __init__(self, prefix: str):
|
||||
super().__init__(
|
||||
prefix, 'qapi-commands',
|
||||
' * Schema-defined QAPI/QMP commands', None, __doc__)
|
||||
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()
|
||||
commands = self._module_basename('qapi-commands', name)
|
||||
types = self._module_basename('qapi-types', name)
|
||||
|
@ -252,7 +282,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
|
|||
''',
|
||||
types=types))
|
||||
|
||||
def visit_end(self):
|
||||
def visit_end(self) -> None:
|
||||
self._add_system_module('init', ' * QAPI Commands initialization')
|
||||
self._genh.add(mcgen('''
|
||||
#include "qapi/qmp/dispatch.h"
|
||||
|
@ -268,9 +298,19 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
|
|||
prefix=self._prefix))
|
||||
self._genc.add(gen_registry(self._regy.get_content(), self._prefix))
|
||||
|
||||
def visit_command(self, name, info, ifcond, features,
|
||||
arg_type, ret_type, gen, success_response, boxed,
|
||||
allow_oob, allow_preconfig, coroutine):
|
||||
def visit_command(self,
|
||||
name: str,
|
||||
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:
|
||||
return
|
||||
# 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))
|
||||
|
||||
|
||||
def gen_commands(schema, output_dir, prefix):
|
||||
def gen_commands(schema: QAPISchema,
|
||||
output_dir: str,
|
||||
prefix: str) -> None:
|
||||
vis = QAPISchemaGenCommandVisitor(prefix)
|
||||
schema.visit(vis)
|
||||
vis.write(output_dir)
|
||||
|
|
|
@ -12,12 +12,28 @@
|
|||
# See the COPYING file in the top-level directory.
|
||||
|
||||
import re
|
||||
from typing import Optional, Sequence
|
||||
|
||||
|
||||
# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
|
||||
# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
|
||||
# ENUM24_Name -> ENUM24_NAME
|
||||
def camel_to_upper(value):
|
||||
#: Magic string that gets removed along with all space to its right.
|
||||
EATSPACE = '\033EATSPACE.'
|
||||
POINTER_SUFFIX = ' *' + EATSPACE
|
||||
_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)
|
||||
if value.isupper():
|
||||
return c_fun_str
|
||||
|
@ -25,36 +41,47 @@ def camel_to_upper(value):
|
|||
new_name = ''
|
||||
length = len(c_fun_str)
|
||||
for i in range(length):
|
||||
c = c_fun_str[i]
|
||||
# When c is upper and no '_' appears before, do more checks
|
||||
if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
|
||||
char = c_fun_str[i]
|
||||
# When char is upper case and no '_' appears before, do more checks
|
||||
if char.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
|
||||
if i < length - 1 and c_fun_str[i + 1].islower():
|
||||
new_name += '_'
|
||||
elif c_fun_str[i - 1].isdigit():
|
||||
new_name += '_'
|
||||
new_name += c
|
||||
new_name += char
|
||||
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:
|
||||
type_name = prefix
|
||||
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.
|
||||
# If @protect, avoid returning certain ticklish identifiers (like
|
||||
# C keywords) by prepending 'q_'.
|
||||
#
|
||||
# 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.
|
||||
# '__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):
|
||||
'__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
|
||||
protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
|
||||
|
||||
:param name: The name to map.
|
||||
:param protect: If true, avoid returning certain ticklish identifiers
|
||||
(like C keywords) by prepending ``q_``.
|
||||
"""
|
||||
# ANSI X3J11/88-090, 3.1.1
|
||||
c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
|
||||
'default', 'do', 'double', 'else', 'enum', 'extern',
|
||||
|
@ -82,61 +109,75 @@ def c_name(name, protect=True):
|
|||
'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
|
||||
# namespace pollution:
|
||||
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
|
||||
| cpp_words | polluted_words):
|
||||
return 'q_' + name
|
||||
return name
|
||||
|
||||
|
||||
eatspace = '\033EATSPACE.'
|
||||
pointer_suffix = ' *' + eatspace
|
||||
class Indentation:
|
||||
"""
|
||||
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):
|
||||
ret = ''
|
||||
for _ in range(count):
|
||||
ret += ' '
|
||||
return ret
|
||||
#: Global, current indent level for code generation.
|
||||
indent = Indentation()
|
||||
|
||||
|
||||
indent_level = 0
|
||||
def cgen(code: str, **kwds: object) -> str:
|
||||
"""
|
||||
Generate ``code`` with ``kwds`` interpolated.
|
||||
|
||||
|
||||
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):
|
||||
Obey `indent`, and strip `EATSPACE`.
|
||||
"""
|
||||
raw = code % kwds
|
||||
if indent_level:
|
||||
indent = genindent(indent_level)
|
||||
# re.subn() lacks flags support before Python 2.7, use re.compile()
|
||||
raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
|
||||
indent, raw)
|
||||
raw = raw[0]
|
||||
return re.sub(re.escape(eatspace) + r' *', '', raw)
|
||||
if indent:
|
||||
raw = re.sub(r'^(?!(#|$))', str(indent), raw, flags=re.MULTILINE)
|
||||
return re.sub(re.escape(EATSPACE) + r' *', '', raw)
|
||||
|
||||
|
||||
def mcgen(code, **kwds):
|
||||
def mcgen(code: str, **kwds: object) -> str:
|
||||
if code[0] == '\n':
|
||||
code = code[1:]
|
||||
return cgen(code, **kwds)
|
||||
|
||||
|
||||
def c_fname(filename):
|
||||
def c_fname(filename: str) -> str:
|
||||
return re.sub(r'[^A-Za-z0-9_]', '_', filename)
|
||||
|
||||
|
||||
def guardstart(name):
|
||||
def guardstart(name: str) -> str:
|
||||
return mcgen('''
|
||||
#ifndef %(name)s
|
||||
#define %(name)s
|
||||
|
@ -145,7 +186,7 @@ def guardstart(name):
|
|||
name=c_fname(name).upper())
|
||||
|
||||
|
||||
def guardend(name):
|
||||
def guardend(name: str) -> str:
|
||||
return mcgen('''
|
||||
|
||||
#endif /* %(name)s */
|
||||
|
@ -153,7 +194,7 @@ def guardend(name):
|
|||
name=c_fname(name).upper())
|
||||
|
||||
|
||||
def gen_if(ifcond):
|
||||
def gen_if(ifcond: Sequence[str]) -> str:
|
||||
ret = ''
|
||||
for ifc in ifcond:
|
||||
ret += mcgen('''
|
||||
|
@ -162,31 +203,10 @@ def gen_if(ifcond):
|
|||
return ret
|
||||
|
||||
|
||||
def gen_endif(ifcond):
|
||||
def gen_endif(ifcond: Sequence[str]) -> str:
|
||||
ret = ''
|
||||
for ifc in reversed(ifcond):
|
||||
ret += mcgen('''
|
||||
#endif /* %(cond)s */
|
||||
''', cond=ifc)
|
||||
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.
|
||||
"""
|
||||
|
||||
from qapi.common import *
|
||||
from qapi.gen import QAPISchemaModularCVisitor, ifcontext
|
||||
from qapi.schema import QAPISchemaEnumMember
|
||||
from qapi.types import gen_enum, gen_enum_lookup
|
||||
from typing import List
|
||||
|
||||
from .common import c_enum_const, c_name, mcgen
|
||||
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)' % {
|
||||
'c_name': c_name(name.lower()),
|
||||
'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('''
|
||||
|
||||
%(proto)s;
|
||||
|
@ -32,8 +44,12 @@ def gen_event_send_decl(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):
|
||||
def gen_param_var(typ: QAPISchemaObjectType) -> str:
|
||||
"""
|
||||
Generate a struct variable holding the event parameters.
|
||||
|
||||
Initialize it with the function arguments defined in `gen_event_send`.
|
||||
"""
|
||||
assert not typ.variants
|
||||
ret = mcgen('''
|
||||
%(c_name)s param = {
|
||||
|
@ -61,7 +77,11 @@ def gen_param_var(typ):
|
|||
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
|
||||
# parameter list) can collide with exploded members of the event's
|
||||
# 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):
|
||||
|
||||
def __init__(self, prefix):
|
||||
def __init__(self, prefix: str):
|
||||
super().__init__(
|
||||
prefix, 'qapi-events',
|
||||
' * Schema-defined QAPI/QMP events', None, __doc__)
|
||||
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')
|
||||
|
||||
def _begin_user_module(self, name):
|
||||
def _begin_user_module(self, name: str) -> None:
|
||||
events = self._module_basename('qapi-events', name)
|
||||
types = self._module_basename('qapi-types', name)
|
||||
visit = self._module_basename('qapi-visit', name)
|
||||
|
@ -168,7 +188,7 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
|
|||
''',
|
||||
types=types))
|
||||
|
||||
def visit_end(self):
|
||||
def visit_end(self) -> None:
|
||||
self._add_system_module('emit', ' * QAPI Events emission')
|
||||
self._genc.preamble_add(mcgen('''
|
||||
#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_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):
|
||||
self._genh.add(gen_event_send_decl(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))
|
||||
|
||||
|
||||
def gen_events(schema, output_dir, prefix):
|
||||
def gen_events(schema: QAPISchema,
|
||||
output_dir: str,
|
||||
prefix: str) -> None:
|
||||
vis = QAPISchemaGenEventVisitor(prefix)
|
||||
schema.visit(vis)
|
||||
vis.write(output_dir)
|
||||
|
|
|
@ -14,10 +14,11 @@
|
|||
# This work is licensed under the terms of the GNU GPL, version 2.
|
||||
# See the COPYING file in the top-level directory.
|
||||
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
from qapi.common import c_name
|
||||
from qapi.error import QAPISemError
|
||||
import re
|
||||
|
||||
from .common import c_name
|
||||
from .error import QAPISemError
|
||||
|
||||
|
||||
# Names must be letters, numbers, -, and _. They must start with letter,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#
|
||||
# QAPI code generation
|
||||
#
|
||||
# Copyright (c) 2018-2019 Red Hat Inc.
|
||||
# Copyright (c) 2015-2019 Red Hat Inc.
|
||||
#
|
||||
# Authors:
|
||||
# Markus Armbruster <armbru@redhat.com>
|
||||
|
@ -11,39 +11,54 @@
|
|||
# This work is licensed under the terms of the GNU GPL, version 2.
|
||||
# See the COPYING file in the top-level directory.
|
||||
|
||||
|
||||
import errno
|
||||
from contextlib import contextmanager
|
||||
import os
|
||||
import re
|
||||
from contextlib import contextmanager
|
||||
from typing import (
|
||||
Dict,
|
||||
Iterator,
|
||||
List,
|
||||
Optional,
|
||||
Tuple,
|
||||
)
|
||||
|
||||
from qapi.common import *
|
||||
from qapi.schema import QAPISchemaVisitor
|
||||
from .common import (
|
||||
c_fname,
|
||||
c_name,
|
||||
gen_endif,
|
||||
gen_if,
|
||||
guardend,
|
||||
guardstart,
|
||||
mcgen,
|
||||
)
|
||||
from .schema import QAPISchemaObjectType, QAPISchemaVisitor
|
||||
from .source import QAPISourceInfo
|
||||
|
||||
|
||||
class QAPIGen:
|
||||
|
||||
def __init__(self, fname):
|
||||
def __init__(self, fname: Optional[str]):
|
||||
self.fname = fname
|
||||
self._preamble = ''
|
||||
self._body = ''
|
||||
|
||||
def preamble_add(self, text):
|
||||
def preamble_add(self, text: str) -> None:
|
||||
self._preamble += text
|
||||
|
||||
def add(self, text):
|
||||
def add(self, text: str) -> None:
|
||||
self._body += text
|
||||
|
||||
def get_content(self):
|
||||
def get_content(self) -> str:
|
||||
return self._top() + self._preamble + self._body + self._bottom()
|
||||
|
||||
def _top(self):
|
||||
def _top(self) -> str:
|
||||
# pylint: disable=no-self-use
|
||||
return ''
|
||||
|
||||
def _bottom(self):
|
||||
def _bottom(self) -> str:
|
||||
# pylint: disable=no-self-use
|
||||
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
|
||||
# schema in specialised schemas. Don't overwrite the files that are
|
||||
# already generated for the main schema.
|
||||
|
@ -51,24 +66,22 @@ class QAPIGen:
|
|||
return
|
||||
pathname = os.path.join(output_dir, self.fname)
|
||||
odir = os.path.dirname(pathname)
|
||||
|
||||
if odir:
|
||||
try:
|
||||
os.makedirs(odir)
|
||||
except os.error as e:
|
||||
if e.errno != errno.EEXIST:
|
||||
raise
|
||||
os.makedirs(odir, exist_ok=True)
|
||||
|
||||
# use os.open for O_CREAT to create and read a non-existant file
|
||||
fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
|
||||
f = open(fd, 'r+', encoding='utf-8')
|
||||
text = self.get_content()
|
||||
oldtext = f.read(len(text) + 1)
|
||||
if text != oldtext:
|
||||
f.seek(0)
|
||||
f.truncate(0)
|
||||
f.write(text)
|
||||
f.close()
|
||||
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)
|
||||
|
||||
|
||||
def _wrap_ifcond(ifcond, before, after):
|
||||
def _wrap_ifcond(ifcond: List[str], before: str, after: str) -> str:
|
||||
if before == after:
|
||||
return after # suppress empty #if ... #endif
|
||||
|
||||
|
@ -84,41 +97,62 @@ def _wrap_ifcond(ifcond, before, after):
|
|||
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):
|
||||
|
||||
def __init__(self, fname):
|
||||
def __init__(self, fname: Optional[str]):
|
||||
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
|
||||
self._start_if = (ifcond, self._body, self._preamble)
|
||||
|
||||
def end_if(self):
|
||||
def end_if(self) -> None:
|
||||
assert self._start_if
|
||||
self._wrap_ifcond()
|
||||
self._start_if = None
|
||||
|
||||
def _wrap_ifcond(self):
|
||||
def _wrap_ifcond(self) -> None:
|
||||
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)
|
||||
|
||||
def get_content(self):
|
||||
def get_content(self) -> str:
|
||||
assert self._start_if is None
|
||||
return super().get_content()
|
||||
|
||||
|
||||
class QAPIGenC(QAPIGenCCode):
|
||||
|
||||
def __init__(self, fname, blurb, pydoc):
|
||||
def __init__(self, fname: str, blurb: str, pydoc: str):
|
||||
super().__init__(fname)
|
||||
self._blurb = blurb
|
||||
self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
|
||||
re.MULTILINE))
|
||||
|
||||
def _top(self):
|
||||
def _top(self) -> str:
|
||||
return mcgen('''
|
||||
/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
|
||||
|
||||
|
@ -134,7 +168,7 @@ class QAPIGenC(QAPIGenCCode):
|
|||
''',
|
||||
blurb=self._blurb, copyright=self._copyright)
|
||||
|
||||
def _bottom(self):
|
||||
def _bottom(self) -> str:
|
||||
return mcgen('''
|
||||
|
||||
/* Dummy declaration to prevent empty .o file */
|
||||
|
@ -144,19 +178,20 @@ char qapi_dummy_%(name)s;
|
|||
|
||||
|
||||
class QAPIGenH(QAPIGenC):
|
||||
|
||||
def _top(self):
|
||||
def _top(self) -> str:
|
||||
return super()._top() + guardstart(self.fname)
|
||||
|
||||
def _bottom(self):
|
||||
def _bottom(self) -> str:
|
||||
return guardend(self.fname)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def ifcontext(ifcond, *args):
|
||||
"""A 'with' statement context manager to wrap with start_if()/end_if()
|
||||
def ifcontext(ifcond: List[str], *args: QAPIGenCCode) -> Iterator[None]:
|
||||
"""
|
||||
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::
|
||||
|
||||
|
@ -179,8 +214,11 @@ def ifcontext(ifcond, *args):
|
|||
|
||||
|
||||
class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
|
||||
|
||||
def __init__(self, prefix, what, blurb, pydoc):
|
||||
def __init__(self,
|
||||
prefix: str,
|
||||
what: str,
|
||||
blurb: str,
|
||||
pydoc: str):
|
||||
self._prefix = prefix
|
||||
self._what = what
|
||||
self._genc = QAPIGenC(self._prefix + self._what + '.c',
|
||||
|
@ -188,38 +226,42 @@ class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
|
|||
self._genh = QAPIGenH(self._prefix + self._what + '.h',
|
||||
blurb, pydoc)
|
||||
|
||||
def write(self, output_dir):
|
||||
def write(self, output_dir: str) -> None:
|
||||
self._genc.write(output_dir)
|
||||
self._genh.write(output_dir)
|
||||
|
||||
|
||||
class QAPISchemaModularCVisitor(QAPISchemaVisitor):
|
||||
|
||||
def __init__(self, prefix, what, user_blurb, builtin_blurb, pydoc):
|
||||
def __init__(self,
|
||||
prefix: str,
|
||||
what: str,
|
||||
user_blurb: str,
|
||||
builtin_blurb: Optional[str],
|
||||
pydoc: str):
|
||||
self._prefix = prefix
|
||||
self._what = what
|
||||
self._user_blurb = user_blurb
|
||||
self._builtin_blurb = builtin_blurb
|
||||
self._pydoc = pydoc
|
||||
self._genc = None
|
||||
self._genh = None
|
||||
self._module = {}
|
||||
self._main_module = None
|
||||
self._genc: Optional[QAPIGenC] = None
|
||||
self._genh: Optional[QAPIGenH] = None
|
||||
self._module: Dict[Optional[str], Tuple[QAPIGenC, QAPIGenH]] = {}
|
||||
self._main_module: Optional[str] = None
|
||||
|
||||
@staticmethod
|
||||
def _is_user_module(name):
|
||||
return name and not name.startswith('./')
|
||||
def _is_user_module(name: Optional[str]) -> bool:
|
||||
return bool(name and not name.startswith('./'))
|
||||
|
||||
@staticmethod
|
||||
def _is_builtin_module(name):
|
||||
def _is_builtin_module(name: Optional[str]) -> bool:
|
||||
return not name
|
||||
|
||||
def _module_dirname(self, what, name):
|
||||
def _module_dirname(self, name: Optional[str]) -> str:
|
||||
if self._is_user_module(name):
|
||||
return os.path.dirname(name)
|
||||
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
|
||||
if self._is_user_module(name):
|
||||
basename = os.path.basename(name)
|
||||
|
@ -231,27 +273,27 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor):
|
|||
ret += re.sub(r'-', '-' + name + '-', what)
|
||||
return ret
|
||||
|
||||
def _module_filename(self, what, name):
|
||||
return os.path.join(self._module_dirname(what, name),
|
||||
def _module_filename(self, what: str, name: Optional[str]) -> str:
|
||||
return os.path.join(self._module_dirname(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)
|
||||
genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
|
||||
genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
|
||||
self._module[name] = (genc, genh)
|
||||
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)
|
||||
if self._main_module is None:
|
||||
self._main_module = name
|
||||
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)
|
||||
|
||||
def write(self, output_dir, opt_builtins=False):
|
||||
def write(self, output_dir: str, opt_builtins: bool = False) -> None:
|
||||
for name in self._module:
|
||||
if self._is_builtin_module(name) and not opt_builtins:
|
||||
continue
|
||||
|
@ -259,13 +301,13 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor):
|
|||
genc.write(output_dir)
|
||||
genh.write(output_dir)
|
||||
|
||||
def _begin_system_module(self, name):
|
||||
def _begin_system_module(self, name: None) -> None:
|
||||
pass
|
||||
|
||||
def _begin_user_module(self, name):
|
||||
def _begin_user_module(self, name: str) -> None:
|
||||
pass
|
||||
|
||||
def visit_module(self, name):
|
||||
def visit_module(self, name: Optional[str]) -> None:
|
||||
if name is None:
|
||||
if 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._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),
|
||||
os.path.dirname(self._genh.fname))
|
||||
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.
|
||||
"""
|
||||
|
||||
from qapi.common import *
|
||||
from qapi.gen import QAPISchemaMonolithicCVisitor
|
||||
from qapi.schema import (QAPISchemaArrayType, QAPISchemaBuiltinType,
|
||||
QAPISchemaType)
|
||||
from .common import (
|
||||
c_name,
|
||||
gen_endif,
|
||||
gen_if,
|
||||
mcgen,
|
||||
)
|
||||
from .gen import QAPISchemaMonolithicCVisitor
|
||||
from .schema import (
|
||||
QAPISchemaArrayType,
|
||||
QAPISchemaBuiltinType,
|
||||
QAPISchemaType,
|
||||
)
|
||||
|
||||
|
||||
def _make_tree(obj, ifcond, features, extra=None):
|
||||
|
|
95
scripts/qapi/main.py
Normal file
95
scripts/qapi/main.py
Normal file
|
@ -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
|
30
scripts/qapi/mypy.ini
Normal file
30
scripts/qapi/mypy.ini
Normal file
|
@ -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.
|
||||
# See the COPYING file in the top-level directory.
|
||||
|
||||
from collections import OrderedDict
|
||||
import os
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
|
||||
from qapi.error import QAPIParseError, QAPISemError
|
||||
from qapi.source import QAPISourceInfo
|
||||
from .error import QAPIParseError, QAPISemError
|
||||
from .source import QAPISourceInfo
|
||||
|
||||
|
||||
class QAPISchemaParser:
|
||||
|
|
70
scripts/qapi/pylintrc
Normal file
70
scripts/qapi/pylintrc
Normal file
|
@ -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
|
||||
|
||||
from collections import OrderedDict
|
||||
import os
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
from typing import Optional
|
||||
|
||||
from qapi.common import c_name, pointer_suffix
|
||||
from qapi.error import QAPIError, QAPISemError
|
||||
from qapi.expr import check_exprs
|
||||
from qapi.parser import QAPISchemaParser
|
||||
from .common import POINTER_SUFFIX, c_name
|
||||
from .error import QAPIError, QAPISemError
|
||||
from .expr import check_exprs
|
||||
from .parser import QAPISchemaParser
|
||||
|
||||
|
||||
class QAPISchemaEntity:
|
||||
meta = None
|
||||
meta: Optional[str] = None
|
||||
|
||||
def __init__(self, name, info, doc, ifcond=None, features=None):
|
||||
assert name is None or isinstance(name, str)
|
||||
|
@ -309,7 +310,7 @@ class QAPISchemaArrayType(QAPISchemaType):
|
|||
return True
|
||||
|
||||
def c_type(self):
|
||||
return c_name(self.name) + pointer_suffix
|
||||
return c_name(self.name) + POINTER_SUFFIX
|
||||
|
||||
def json_type(self):
|
||||
return 'array'
|
||||
|
@ -430,7 +431,7 @@ class QAPISchemaObjectType(QAPISchemaType):
|
|||
|
||||
def c_type(self):
|
||||
assert not self.is_implicit()
|
||||
return c_name(self.name) + pointer_suffix
|
||||
return c_name(self.name) + POINTER_SUFFIX
|
||||
|
||||
def c_unboxed_type(self):
|
||||
return c_name(self.name)
|
||||
|
@ -504,7 +505,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
|
|||
v.connect_doc(doc)
|
||||
|
||||
def c_type(self):
|
||||
return c_name(self.name) + pointer_suffix
|
||||
return c_name(self.name) + POINTER_SUFFIX
|
||||
|
||||
def json_type(self):
|
||||
return 'value'
|
||||
|
@ -536,7 +537,7 @@ class QAPISchemaVariants:
|
|||
v.set_defined_in(name)
|
||||
|
||||
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))
|
||||
base = "'base'"
|
||||
# Pointing to the base type when not implicit would be
|
||||
|
@ -824,7 +825,7 @@ class QAPISchema:
|
|||
self._entity_dict = {}
|
||||
self._module_dict = OrderedDict()
|
||||
self._schema_dir = os.path.dirname(fname)
|
||||
self._make_module(None) # built-ins
|
||||
self._make_module(None) # built-ins
|
||||
self._make_module(fname)
|
||||
self._predefining = True
|
||||
self._def_predefineds()
|
||||
|
@ -899,7 +900,7 @@ class QAPISchema:
|
|||
self._make_array_type(name, None)
|
||||
|
||||
def _def_predefineds(self):
|
||||
for t in [('str', 'string', 'char' + pointer_suffix),
|
||||
for t in [('str', 'string', 'char' + POINTER_SUFFIX),
|
||||
('number', 'number', 'double'),
|
||||
('int', 'int', 'int64_t'),
|
||||
('int8', 'int', 'int8_t'),
|
||||
|
@ -912,8 +913,8 @@ class QAPISchema:
|
|||
('uint64', 'int', 'uint64_t'),
|
||||
('size', 'int', 'uint64_t'),
|
||||
('bool', 'boolean', 'bool'),
|
||||
('any', 'value', 'QObject' + pointer_suffix),
|
||||
('null', 'null', 'QNull' + pointer_suffix)]:
|
||||
('any', 'value', 'QObject' + POINTER_SUFFIX),
|
||||
('null', 'null', 'QNull' + POINTER_SUFFIX)]:
|
||||
self._def_builtin_type(*t)
|
||||
self.the_empty_object_type = QAPISchemaObjectType(
|
||||
'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
|
||||
# may end up compiling useless wrapper types.
|
||||
# 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:
|
||||
self._def_entity(QAPISchemaObjectType(
|
||||
name, info, None, ifcond, None, None, members, None))
|
||||
|
|
|
@ -11,37 +11,46 @@
|
|||
|
||||
import copy
|
||||
import sys
|
||||
from typing import List, Optional, TypeVar
|
||||
|
||||
|
||||
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?
|
||||
self.doc_required = False
|
||||
# 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
|
||||
self.name_case_whitelist = []
|
||||
self.name_case_whitelist: List[str] = []
|
||||
|
||||
|
||||
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.line = line
|
||||
self.parent = parent
|
||||
self.pragma = parent.pragma if parent else QAPISchemaPragma()
|
||||
self.defn_meta = None
|
||||
self.defn_name = None
|
||||
self.pragma: QAPISchemaPragma = (
|
||||
parent.pragma if parent else QAPISchemaPragma()
|
||||
)
|
||||
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_name = name
|
||||
|
||||
def next_line(self):
|
||||
def next_line(self: T) -> T:
|
||||
info = copy.copy(self)
|
||||
info.line += 1
|
||||
return info
|
||||
|
||||
def loc(self):
|
||||
def loc(self) -> str:
|
||||
if self.fname is None:
|
||||
return sys.argv[0]
|
||||
ret = self.fname
|
||||
|
@ -49,13 +58,13 @@ class QAPISourceInfo:
|
|||
ret += ':%d' % self.line
|
||||
return ret
|
||||
|
||||
def in_defn(self):
|
||||
def in_defn(self) -> str:
|
||||
if self.defn_name:
|
||||
return "%s: In %s '%s':\n" % (self.fname,
|
||||
self.defn_meta, self.defn_name)
|
||||
return ''
|
||||
|
||||
def include_path(self):
|
||||
def include_path(self) -> str:
|
||||
ret = ''
|
||||
parent = self.parent
|
||||
while parent:
|
||||
|
@ -63,5 +72,5 @@ class QAPISourceInfo:
|
|||
parent = parent.parent
|
||||
return ret
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
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.
|
||||
"""
|
||||
|
||||
from qapi.common import *
|
||||
from qapi.gen import QAPISchemaModularCVisitor, ifcontext
|
||||
from qapi.schema import QAPISchemaEnumMember, QAPISchemaObjectType
|
||||
from typing import List, Optional
|
||||
|
||||
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
|
||||
|
@ -23,21 +40,23 @@ from qapi.schema import QAPISchemaEnumMember, QAPISchemaObjectType
|
|||
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('''
|
||||
|
||||
const QEnumLookup %(c_name)s_lookup = {
|
||||
.array = (const char *const[]) {
|
||||
''',
|
||||
c_name=c_name(name))
|
||||
for m in members:
|
||||
ret += gen_if(m.ifcond)
|
||||
index = c_enum_const(name, m.name, prefix)
|
||||
for memb in members:
|
||||
ret += gen_if(memb.ifcond)
|
||||
index = c_enum_const(name, memb.name, prefix)
|
||||
ret += mcgen('''
|
||||
[%(index)s] = "%(name)s",
|
||||
''',
|
||||
index=index, name=m.name)
|
||||
ret += gen_endif(m.ifcond)
|
||||
index=index, name=memb.name)
|
||||
ret += gen_endif(memb.ifcond)
|
||||
|
||||
ret += mcgen('''
|
||||
},
|
||||
|
@ -48,7 +67,9 @@ const QEnumLookup %(c_name)s_lookup = {
|
|||
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
|
||||
enum_members = members + [QAPISchemaEnumMember('_MAX', None)]
|
||||
|
||||
|
@ -58,13 +79,13 @@ typedef enum %(c_name)s {
|
|||
''',
|
||||
c_name=c_name(name))
|
||||
|
||||
for m in enum_members:
|
||||
ret += gen_if(m.ifcond)
|
||||
for memb in enum_members:
|
||||
ret += gen_if(memb.ifcond)
|
||||
ret += mcgen('''
|
||||
%(c_enum)s,
|
||||
''',
|
||||
c_enum=c_enum_const(name, m.name, prefix))
|
||||
ret += gen_endif(m.ifcond)
|
||||
c_enum=c_enum_const(name, memb.name, prefix))
|
||||
ret += gen_endif(memb.ifcond)
|
||||
|
||||
ret += mcgen('''
|
||||
} %(c_name)s;
|
||||
|
@ -82,7 +103,7 @@ extern const QEnumLookup %(c_name)s_lookup;
|
|||
return ret
|
||||
|
||||
|
||||
def gen_fwd_object_or_array(name):
|
||||
def gen_fwd_object_or_array(name: str) -> str:
|
||||
return mcgen('''
|
||||
|
||||
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))
|
||||
|
||||
|
||||
def gen_array(name, element_type):
|
||||
def gen_array(name: str, element_type: QAPISchemaType) -> str:
|
||||
return mcgen('''
|
||||
|
||||
struct %(c_name)s {
|
||||
|
@ -101,7 +122,7 @@ struct %(c_name)s {
|
|||
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 = ''
|
||||
for memb in members:
|
||||
ret += gen_if(memb.ifcond)
|
||||
|
@ -118,17 +139,21 @@ def gen_struct_members(members):
|
|||
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:
|
||||
return ''
|
||||
objects_seen.add(name)
|
||||
|
||||
ret = ''
|
||||
if variants:
|
||||
for v in variants.variants:
|
||||
if isinstance(v.type, QAPISchemaObjectType):
|
||||
ret += gen_object(v.type.name, v.type.ifcond, v.type.base,
|
||||
v.type.local_members, v.type.variants)
|
||||
for var in variants.variants if variants else ():
|
||||
obj = var.type
|
||||
if not isinstance(obj, QAPISchemaObjectType):
|
||||
continue
|
||||
ret += gen_object(obj.name, obj.ifcond, obj.base,
|
||||
obj.local_members, obj.variants)
|
||||
|
||||
ret += mcgen('''
|
||||
|
||||
|
@ -172,7 +197,7 @@ struct %(c_name)s {
|
|||
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
|
||||
# this function work for both const and non-const obj.
|
||||
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())
|
||||
|
||||
|
||||
def gen_variants(variants):
|
||||
def gen_variants(variants: QAPISchemaVariants) -> str:
|
||||
ret = mcgen('''
|
||||
union { /* union tag is @%(c_name)s */
|
||||
''',
|
||||
|
@ -209,7 +234,7 @@ def gen_variants(variants):
|
|||
return ret
|
||||
|
||||
|
||||
def gen_type_cleanup_decl(name):
|
||||
def gen_type_cleanup_decl(name: str) -> str:
|
||||
ret = mcgen('''
|
||||
|
||||
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
|
||||
|
||||
|
||||
def gen_type_cleanup(name):
|
||||
def gen_type_cleanup(name: str) -> str:
|
||||
ret = mcgen('''
|
||||
|
||||
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):
|
||||
|
||||
def __init__(self, prefix):
|
||||
def __init__(self, prefix: str):
|
||||
super().__init__(
|
||||
prefix, 'qapi-types', ' * Schema-defined QAPI types',
|
||||
' * Built-in QAPI types', __doc__)
|
||||
|
||||
def _begin_system_module(self, name):
|
||||
def _begin_system_module(self, name: None) -> None:
|
||||
self._genc.preamble_add(mcgen('''
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/dealloc-visitor.h"
|
||||
|
@ -257,7 +282,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
|
|||
#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)
|
||||
visit = self._module_basename('qapi-visit', name)
|
||||
self._genc.preamble_add(mcgen('''
|
||||
|
@ -271,27 +296,43 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
|
|||
#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
|
||||
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._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):
|
||||
self._genh.preamble_add(gen_enum(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):
|
||||
self._genh.preamble_add(gen_fwd_object_or_array(name))
|
||||
self._genh.add(gen_array(name, element_type))
|
||||
self._gen_type_cleanup(name)
|
||||
|
||||
def visit_object_type(self, name, info, ifcond, features,
|
||||
base, members, variants):
|
||||
def visit_object_type(self,
|
||||
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
|
||||
if name == 'q_empty':
|
||||
return
|
||||
|
@ -307,7 +348,12 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
|
|||
# implicit types won't be directly allocated/freed
|
||||
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):
|
||||
self._genh.preamble_add(gen_fwd_object_or_array(name))
|
||||
self._genh.add(gen_object(name, ifcond, None,
|
||||
|
@ -316,7 +362,10 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
|
|||
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)
|
||||
schema.visit(vis)
|
||||
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.
|
||||
"""
|
||||
|
||||
from qapi.common import *
|
||||
from qapi.gen import QAPISchemaModularCVisitor, ifcontext
|
||||
from qapi.schema import QAPISchemaObjectType
|
||||
from typing import List, Optional
|
||||
|
||||
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) + ' *'
|
||||
if not scalar:
|
||||
c_type += '*'
|
||||
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)
|
||||
|
||||
|
||||
def gen_visit_members_decl(name):
|
||||
def gen_visit_members_decl(name: str) -> str:
|
||||
return mcgen('''
|
||||
|
||||
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))
|
||||
|
||||
|
||||
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('''
|
||||
|
||||
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)) {
|
||||
''',
|
||||
name=memb.name, c_name=c_name(memb.name))
|
||||
push_indent()
|
||||
indent.increase()
|
||||
ret += mcgen('''
|
||||
if (!visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, errp)) {
|
||||
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_name=c_name(memb.name))
|
||||
if memb.optional:
|
||||
pop_indent()
|
||||
indent.decrease()
|
||||
ret += mcgen('''
|
||||
}
|
||||
''')
|
||||
ret += gen_endif(memb.ifcond)
|
||||
|
||||
if variants:
|
||||
tag_member = variants.tag_member
|
||||
assert isinstance(tag_member.type, QAPISchemaEnumType)
|
||||
|
||||
ret += mcgen('''
|
||||
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:
|
||||
case_str = c_enum_const(variants.tag_member.type.name,
|
||||
var.name,
|
||||
variants.tag_member.type.prefix)
|
||||
case_str = c_enum_const(tag_member.type.name, var.name,
|
||||
tag_member.type.prefix)
|
||||
ret += gen_if(var.ifcond)
|
||||
if var.type.name == 'q_empty':
|
||||
# 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
|
||||
|
||||
|
||||
def gen_visit_list(name, element_type):
|
||||
def gen_visit_list(name: str, element_type: QAPISchemaType) -> str:
|
||||
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;
|
||||
%(c_name)s *tail;
|
||||
|
@ -147,10 +174,11 @@ out_obj:
|
|||
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('''
|
||||
|
||||
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;
|
||||
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))
|
||||
|
||||
|
||||
def gen_visit_alternate(name, variants):
|
||||
def gen_visit_alternate(name: str, variants: QAPISchemaVariants) -> str:
|
||||
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;
|
||||
|
||||
|
@ -236,10 +265,11 @@ out_obj:
|
|||
return ret
|
||||
|
||||
|
||||
def gen_visit_object(name, base, members, variants):
|
||||
def gen_visit_object(name: str) -> str:
|
||||
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;
|
||||
|
||||
|
@ -270,12 +300,12 @@ out_obj:
|
|||
|
||||
class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
|
||||
|
||||
def __init__(self, prefix):
|
||||
def __init__(self, prefix: str):
|
||||
super().__init__(
|
||||
prefix, 'qapi-visit', ' * Schema-defined QAPI visitors',
|
||||
' * Built-in QAPI visitors', __doc__)
|
||||
|
||||
def _begin_system_module(self, name):
|
||||
def _begin_system_module(self, name: None) -> None:
|
||||
self._genc.preamble_add(mcgen('''
|
||||
#include "qemu/osdep.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)
|
||||
visit = self._module_basename('qapi-visit', name)
|
||||
self._genc.preamble_add(mcgen('''
|
||||
|
@ -304,18 +334,34 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
|
|||
''',
|
||||
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):
|
||||
self._genh.add(gen_visit_decl(name, scalar=True))
|
||||
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):
|
||||
self._genh.add(gen_visit_decl(name))
|
||||
self._genc.add(gen_visit_list(name, element_type))
|
||||
|
||||
def visit_object_type(self, name, info, ifcond, features,
|
||||
base, members, variants):
|
||||
def visit_object_type(self,
|
||||
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
|
||||
if name == 'q_empty':
|
||||
return
|
||||
|
@ -328,15 +374,23 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
|
|||
if not name.startswith('q_'):
|
||||
# only explicit types need an allocating visit
|
||||
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):
|
||||
self._genh.add(gen_visit_decl(name))
|
||||
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)
|
||||
schema.visit(vis)
|
||||
vis.write(output_dir, opt_builtins)
|
||||
|
|
Loading…
Reference in a new issue