mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2015-09-21' into staging
qapi: QMP introspection # gpg: Signature made Mon 21 Sep 2015 08:59:17 BST using RSA key ID EB918653 # gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" # gpg: aka "Markus Armbruster <armbru@pond.sub.org>" * remotes/armbru/tags/pull-qapi-2015-09-21: (26 commits) qapi-introspect: Hide type names qapi: New QMP command query-qmp-schema for QMP introspection qapi: Pseudo-type '**' is now unused, drop it qapi-schema: Fix up misleading specification of netdev_add qom: Don't use 'gen': false for qom-get, qom-set, object-add qapi: Introduce a first class 'any' type qapi: Make output visitor return qnull() instead of NULL qapi: Improve built-in type documentation qapi-commands: De-duplicate output marshaling functions qapi: De-duplicate parameter list generation qapi: Rename qmp_marshal_input_FOO() to qmp_marshal_FOO() qapi-commands: Rearrange code qapi-visit: Rearrange code a bit qapi: Clean up after recent conversions to QAPISchemaVisitor qapi: Replace dirty is_c_ptr() by method c_null() qapi-event: Convert to QAPISchemaVisitor, fixing data with base qapi-event: Eliminate global variable event_enum_value qapi: De-duplicate enum code generation qapi-commands: Convert to QAPISchemaVisitor qapi-visit: Convert to QAPISchemaVisitor, fixing bugs ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
@@ -34,6 +34,7 @@
|
||||
/qapi-visit.[ch]
|
||||
/qapi-event.[ch]
|
||||
/qmp-commands.h
|
||||
/qmp-introspect.[ch]
|
||||
/qmp-marshal.c
|
||||
/qemu-doc.html
|
||||
/qemu-tech.html
|
||||
|
||||
@@ -52,6 +52,8 @@ endif
|
||||
GENERATED_HEADERS = config-host.h qemu-options.def
|
||||
GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h qapi-event.h
|
||||
GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c qapi-event.c
|
||||
GENERATED_HEADERS += qmp-introspect.h
|
||||
GENERATED_SOURCES += qmp-introspect.c
|
||||
|
||||
GENERATED_HEADERS += trace/generated-events.h
|
||||
GENERATED_SOURCES += trace/generated-events.c
|
||||
@@ -269,7 +271,7 @@ $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
|
||||
|
||||
qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
|
||||
$(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
|
||||
$(SRC_PATH)/qapi/event.json
|
||||
$(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json
|
||||
|
||||
qapi-types.c qapi-types.h :\
|
||||
$(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
|
||||
@@ -291,6 +293,11 @@ $(qapi-modules) $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \
|
||||
$(gen-out-type) -o "." -m $<, \
|
||||
" GEN $@")
|
||||
qmp-introspect.h qmp-introspect.c :\
|
||||
$(qapi-modules) $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py)
|
||||
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-introspect.py \
|
||||
$(gen-out-type) -o "." $<, \
|
||||
" GEN $@")
|
||||
|
||||
QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h)
|
||||
$(qga-obj-y) qemu-ga.o: $(QGALIB_GEN)
|
||||
|
||||
+3
-1
@@ -1,7 +1,8 @@
|
||||
#######################################################################
|
||||
# Common libraries for tools and emulators
|
||||
stub-obj-y = stubs/
|
||||
util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o
|
||||
util-obj-y = util/ qobject/ qapi/
|
||||
util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
|
||||
|
||||
#######################################################################
|
||||
# block-obj-y is code used by both qemu system emulation and qemu-img
|
||||
@@ -92,6 +93,7 @@ common-obj-$(CONFIG_FDT) += device_tree.o
|
||||
# qapi
|
||||
|
||||
common-obj-y += qmp-marshal.o
|
||||
common-obj-y += qmp-introspect.o
|
||||
common-obj-y += qmp.o hmp.o
|
||||
endif
|
||||
|
||||
|
||||
+291
-64
@@ -111,10 +111,7 @@ and field names within a type, should be all lower case with words
|
||||
separated by a hyphen. However, some existing older commands and
|
||||
complex types use underscore; when extending such expressions,
|
||||
consistency is preferred over blindly avoiding underscore. Event
|
||||
names should be ALL_CAPS with words separated by underscore. The
|
||||
special string '**' appears for some commands that manually perform
|
||||
their own type checking rather than relying on the type-safe code
|
||||
produced by the qapi code generators.
|
||||
names should be ALL_CAPS with words separated by underscore.
|
||||
|
||||
Any name (command, event, type, field, or enum value) beginning with
|
||||
"x-" is marked experimental, and may be withdrawn or changed
|
||||
@@ -140,17 +137,25 @@ must have a value that forms a struct name.
|
||||
|
||||
=== Built-in Types ===
|
||||
|
||||
The following types are built-in to the parser:
|
||||
'str' - arbitrary UTF-8 string
|
||||
'int' - 64-bit signed integer (although the C code may place further
|
||||
restrictions on acceptable range)
|
||||
'number' - floating point number
|
||||
'bool' - JSON value of true or false
|
||||
'int8', 'int16', 'int32', 'int64' - like 'int', but enforce maximum
|
||||
bit size
|
||||
'uint8', 'uint16', 'uint32', 'uint64' - unsigned counterparts
|
||||
'size' - like 'uint64', but allows scaled suffix from command line
|
||||
visitor
|
||||
The following types are predefined, and map to C as follows:
|
||||
|
||||
Schema C JSON
|
||||
str char * any JSON string, UTF-8
|
||||
number double any JSON number
|
||||
int int64_t a JSON number without fractional part
|
||||
that fits into the C integer type
|
||||
int8 int8_t likewise
|
||||
int16 int16_t likewise
|
||||
int32 int32_t likewise
|
||||
int64 int64_t likewise
|
||||
uint8 uint8_t likewise
|
||||
uint16 uint16_t likewise
|
||||
uint32 uint32_t likewise
|
||||
uint64 uint64_t likewise
|
||||
size uint64_t like uint64_t, except StringInputVisitor
|
||||
accepts size suffixes
|
||||
bool bool JSON true or false
|
||||
any QObject * any JSON value
|
||||
|
||||
|
||||
=== Includes ===
|
||||
@@ -453,17 +458,14 @@ which would validate this Client JSON Protocol transaction:
|
||||
<= { "return": [ { "value": "one" }, { } ] }
|
||||
|
||||
In rare cases, QAPI cannot express a type-safe representation of a
|
||||
corresponding Client JSON Protocol command. In these cases, if the
|
||||
command expression includes the key 'gen' with boolean value false,
|
||||
then the 'data' or 'returns' member that intends to bypass generated
|
||||
type-safety and do its own manual validation should use an inline
|
||||
dictionary definition, with a value of '**' rather than a valid type
|
||||
name for the keys that the generated code will not validate. Please
|
||||
try to avoid adding new commands that rely on this, and instead use
|
||||
type-safe unions. For an example of bypass usage:
|
||||
corresponding Client JSON Protocol command. You then have to suppress
|
||||
generation of a marshalling function by including a key 'gen' with
|
||||
boolean value false, and instead write your own function. Please try
|
||||
to avoid adding new commands that rely on this, and instead use
|
||||
type-safe unions. For an example of this usage:
|
||||
|
||||
{ 'command': 'netdev_add',
|
||||
'data': {'type': 'str', 'id': 'str', '*props': '**'},
|
||||
'data': {'type': 'str', 'id': 'str'},
|
||||
'gen': false }
|
||||
|
||||
Normally, the QAPI schema is used to describe synchronous exchanges,
|
||||
@@ -500,13 +502,204 @@ Resulting in this JSON object:
|
||||
"timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
|
||||
|
||||
|
||||
== Client JSON Protocol introspection ==
|
||||
|
||||
Clients of a Client JSON Protocol commonly need to figure out what
|
||||
exactly the server (QEMU) supports.
|
||||
|
||||
For this purpose, QMP provides introspection via command
|
||||
query-qmp-schema. QGA currently doesn't support introspection.
|
||||
|
||||
query-qmp-schema returns a JSON array of SchemaInfo objects. These
|
||||
objects together describe the wire ABI, as defined in the QAPI schema.
|
||||
|
||||
However, the SchemaInfo can't reflect all the rules and restrictions
|
||||
that apply to QMP. It's interface introspection (figuring out what's
|
||||
there), not interface specification. The specification is in the QAPI
|
||||
schema. To understand how QMP is to be used, you need to study the
|
||||
QAPI schema.
|
||||
|
||||
Like any other command, query-qmp-schema is itself defined in the QAPI
|
||||
schema, along with the SchemaInfo type. This text attempts to give an
|
||||
overview how things work. For details you need to consult the QAPI
|
||||
schema.
|
||||
|
||||
SchemaInfo objects have common members "name" and "meta-type", and
|
||||
additional variant members depending on the value of meta-type.
|
||||
|
||||
Each SchemaInfo object describes a wire ABI entity of a certain
|
||||
meta-type: a command, event or one of several kinds of type.
|
||||
|
||||
SchemaInfo for commands and events have the same name as in the QAPI
|
||||
schema.
|
||||
|
||||
Command and event names are part of the wire ABI, but type names are
|
||||
not. Therefore, the SchemaInfo for types have auto-generated
|
||||
meaningless names. For readability, the examples in this section use
|
||||
meaningful type names instead.
|
||||
|
||||
To examine a type, start with a command or event using it, then follow
|
||||
references by name.
|
||||
|
||||
QAPI schema definitions not reachable that way are omitted.
|
||||
|
||||
The SchemaInfo for a command has meta-type "command", and variant
|
||||
members "arg-type" and "ret-type". On the wire, the "arguments"
|
||||
member of a client's "execute" command must conform to the object type
|
||||
named by "arg-type". The "return" member that the server passes in a
|
||||
success response conforms to the type named by "ret-type".
|
||||
|
||||
If the command takes no arguments, "arg-type" names an object type
|
||||
without members. Likewise, if the command returns nothing, "ret-type"
|
||||
names an object type without members.
|
||||
|
||||
Example: the SchemaInfo for command query-qmp-schema
|
||||
|
||||
{ "name": "query-qmp-schema", "meta-type": "command",
|
||||
"arg-type": ":empty", "ret-type": "SchemaInfoList" }
|
||||
|
||||
Type ":empty" is an object type without members, and type
|
||||
"SchemaInfoList" is the array of SchemaInfo type.
|
||||
|
||||
The SchemaInfo for an event has meta-type "event", and variant member
|
||||
"arg-type". On the wire, a "data" member that the server passes in an
|
||||
event conforms to the object type named by "arg-type".
|
||||
|
||||
If the event carries no additional information, "arg-type" names an
|
||||
object type without members. The event may not have a data member on
|
||||
the wire then.
|
||||
|
||||
Each command or event defined with dictionary-valued 'data' in the
|
||||
QAPI schema implicitly defines an object type.
|
||||
|
||||
Example: the SchemaInfo for EVENT_C from section Events
|
||||
|
||||
{ "name": "EVENT_C", "meta-type": "event",
|
||||
"arg-type": ":obj-EVENT_C-arg" }
|
||||
|
||||
Type ":obj-EVENT_C-arg" is an implicitly defined object type with
|
||||
the two members from the event's definition.
|
||||
|
||||
The SchemaInfo for struct and union types has meta-type "object".
|
||||
|
||||
The SchemaInfo for a struct type has variant member "members".
|
||||
|
||||
The SchemaInfo for a union type additionally has variant members "tag"
|
||||
and "variants".
|
||||
|
||||
"members" is a JSON array describing the object's common members, if
|
||||
any. Each element is a JSON object with members "name" (the member's
|
||||
name), "type" (the name of its type), and optionally "default". The
|
||||
member is optional if "default" is present. Currently, "default" can
|
||||
only have value null. Other values are reserved for future
|
||||
extensions.
|
||||
|
||||
Example: the SchemaInfo for MyType from section Struct types
|
||||
|
||||
{ "name": "MyType", "meta-type": "object",
|
||||
"members": [
|
||||
{ "name": "member1", "type": "str" },
|
||||
{ "name": "member2", "type": "int" },
|
||||
{ "name": "member3", "type": "str", "default": null } ] }
|
||||
|
||||
"tag" is the name of the common member serving as type tag.
|
||||
"variants" is a JSON array describing the object's variant members.
|
||||
Each element is a JSON object with members "case" (the value of type
|
||||
tag this element applies to) and "type" (the name of an object type
|
||||
that provides the variant members for this type tag value).
|
||||
|
||||
Example: the SchemaInfo for flat union BlockdevOptions from section
|
||||
Union types
|
||||
|
||||
{ "name": "BlockdevOptions", "meta-type": "object",
|
||||
"members": [
|
||||
{ "name": "driver", "type": "BlockdevDriver" },
|
||||
{ "name": "readonly", "type": "bool"} ],
|
||||
"tag": "driver",
|
||||
"variants": [
|
||||
{ "case": "file", "type": "FileOptions" },
|
||||
{ "case": "qcow2", "type": "Qcow2Options" } ] }
|
||||
|
||||
Note that base types are "flattened": its members are included in the
|
||||
"members" array.
|
||||
|
||||
A simple union implicitly defines an enumeration type for its implicit
|
||||
discriminator (called "type" on the wire, see section Union types).
|
||||
|
||||
A simple union implicitly defines an object type for each of its
|
||||
variants.
|
||||
|
||||
Example: the SchemaInfo for simple union BlockdevOptions from section
|
||||
Union types
|
||||
|
||||
{ "name": "BlockdevOptions", "meta-type": "object",
|
||||
"members": [
|
||||
{ "name": "kind", "type": "BlockdevOptionsKind" } ],
|
||||
"tag": "type",
|
||||
"variants": [
|
||||
{ "case": "file", "type": ":obj-FileOptions-wrapper" },
|
||||
{ "case": "qcow2", "type": ":obj-Qcow2Options-wrapper" } ] }
|
||||
|
||||
Enumeration type "BlockdevOptionsKind" and the object types
|
||||
":obj-FileOptions-wrapper", ":obj-Qcow2Options-wrapper" are
|
||||
implicitly defined.
|
||||
|
||||
The SchemaInfo for an alternate type has meta-type "alternate", and
|
||||
variant member "members". "members" is a JSON array. Each element is
|
||||
a JSON object with member "type", which names a type. Values of the
|
||||
alternate type conform to exactly one of its member types.
|
||||
|
||||
Example: the SchemaInfo for BlockRef from section Alternate types
|
||||
|
||||
{ "name": "BlockRef", "meta-type": "alternate",
|
||||
"members": [
|
||||
{ "type": "BlockdevOptions" },
|
||||
{ "type": "str" } ] }
|
||||
|
||||
The SchemaInfo for an array type has meta-type "array", and variant
|
||||
member "element-type", which names the array's element type. Array
|
||||
types are implicitly defined.
|
||||
|
||||
Example: the SchemaInfo for ['str']
|
||||
|
||||
{ "name": "strList", "meta-type": "array",
|
||||
"element-type": "str" }
|
||||
|
||||
The SchemaInfo for an enumeration type has meta-type "enum" and
|
||||
variant member "values".
|
||||
|
||||
Example: the SchemaInfo for MyEnum from section Enumeration types
|
||||
|
||||
{ "name": "MyEnum", "meta-type": "enum",
|
||||
"values": [ "value1", "value2", "value3" ] }
|
||||
|
||||
The SchemaInfo for a built-in type has the same name as the type in
|
||||
the QAPI schema (see section Built-in Types), with one exception
|
||||
detailed below. It has variant member "json-type" that shows how
|
||||
values of this type are encoded on the wire.
|
||||
|
||||
Example: the SchemaInfo for str
|
||||
|
||||
{ "name": "str", "meta-type": "builtin", "json-type": "string" }
|
||||
|
||||
The QAPI schema supports a number of integer types that only differ in
|
||||
how they map to C. They are identical as far as SchemaInfo is
|
||||
concerned. Therefore, they get all mapped to a single type "int" in
|
||||
SchemaInfo.
|
||||
|
||||
As explained above, type names are not part of the wire ABI. Not even
|
||||
the names of built-in types. Clients should examine member
|
||||
"json-type" instead of hard-coding names of built-in types.
|
||||
|
||||
|
||||
== Code generation ==
|
||||
|
||||
Schemas are fed into 3 scripts to generate all the code/files that, paired
|
||||
with the core QAPI libraries, comprise everything required to take JSON
|
||||
commands read in by a Client JSON Protocol server, unmarshal the arguments into
|
||||
the underlying C types, call into the corresponding C function, and map the
|
||||
response back to a Client JSON Protocol response to be returned to the user.
|
||||
Schemas are fed into four scripts to generate all the code/files that,
|
||||
paired with the core QAPI libraries, comprise everything required to
|
||||
take JSON commands read in by a Client JSON Protocol server, unmarshal
|
||||
the arguments into the underlying C types, call into the corresponding
|
||||
C function, and map the response back to a Client JSON Protocol
|
||||
response to be returned to the user.
|
||||
|
||||
As an example, we'll use the following schema, which describes a single
|
||||
complex user-defined type (which will produce a C struct, along with a list
|
||||
@@ -545,22 +738,6 @@ Example:
|
||||
$ cat qapi-generated/example-qapi-types.c
|
||||
[Uninteresting stuff omitted...]
|
||||
|
||||
void qapi_free_UserDefOneList(UserDefOneList *obj)
|
||||
{
|
||||
QapiDeallocVisitor *md;
|
||||
Visitor *v;
|
||||
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
|
||||
md = qapi_dealloc_visitor_new();
|
||||
v = qapi_dealloc_get_visitor(md);
|
||||
visit_type_UserDefOneList(v, &obj, NULL, NULL);
|
||||
qapi_dealloc_visitor_cleanup(md);
|
||||
}
|
||||
|
||||
|
||||
void qapi_free_UserDefOne(UserDefOne *obj)
|
||||
{
|
||||
QapiDeallocVisitor *md;
|
||||
@@ -575,6 +752,21 @@ Example:
|
||||
visit_type_UserDefOne(v, &obj, NULL, NULL);
|
||||
qapi_dealloc_visitor_cleanup(md);
|
||||
}
|
||||
|
||||
void qapi_free_UserDefOneList(UserDefOneList *obj)
|
||||
{
|
||||
QapiDeallocVisitor *md;
|
||||
Visitor *v;
|
||||
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
|
||||
md = qapi_dealloc_visitor_new();
|
||||
v = qapi_dealloc_get_visitor(md);
|
||||
visit_type_UserDefOneList(v, &obj, NULL, NULL);
|
||||
qapi_dealloc_visitor_cleanup(md);
|
||||
}
|
||||
$ cat qapi-generated/example-qapi-types.h
|
||||
[Uninteresting stuff omitted...]
|
||||
|
||||
@@ -585,25 +777,25 @@ Example:
|
||||
|
||||
typedef struct UserDefOne UserDefOne;
|
||||
|
||||
typedef struct UserDefOneList {
|
||||
union {
|
||||
UserDefOne *value;
|
||||
uint64_t padding;
|
||||
};
|
||||
struct UserDefOneList *next;
|
||||
} UserDefOneList;
|
||||
|
||||
|
||||
[Functions on built-in types omitted...]
|
||||
typedef struct UserDefOneList UserDefOneList;
|
||||
|
||||
struct UserDefOne {
|
||||
int64_t integer;
|
||||
char *string;
|
||||
};
|
||||
|
||||
void qapi_free_UserDefOneList(UserDefOneList *obj);
|
||||
void qapi_free_UserDefOne(UserDefOne *obj);
|
||||
|
||||
struct UserDefOneList {
|
||||
union {
|
||||
UserDefOne *value;
|
||||
uint64_t padding;
|
||||
};
|
||||
UserDefOneList *next;
|
||||
};
|
||||
|
||||
void qapi_free_UserDefOneList(UserDefOneList *obj);
|
||||
|
||||
#endif
|
||||
|
||||
=== scripts/qapi-visit.py ===
|
||||
@@ -722,7 +914,7 @@ Example:
|
||||
$ cat qapi-generated/example-qmp-marshal.c
|
||||
[Uninteresting stuff omitted...]
|
||||
|
||||
static void qmp_marshal_output_my_command(UserDefOne *ret_in, QObject **ret_out, Error **errp)
|
||||
static void qmp_marshal_output_UserDefOne(UserDefOne *ret_in, QObject **ret_out, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
QmpOutputVisitor *mo = qmp_output_visitor_new();
|
||||
@@ -745,7 +937,7 @@ Example:
|
||||
qapi_dealloc_visitor_cleanup(md);
|
||||
}
|
||||
|
||||
static void qmp_marshal_input_my_command(QDict *args, QObject **ret, Error **errp)
|
||||
static void qmp_marshal_my_command(QDict *args, QObject **ret, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
UserDefOne *retval;
|
||||
@@ -765,7 +957,7 @@ Example:
|
||||
goto out;
|
||||
}
|
||||
|
||||
qmp_marshal_output_my_command(retval, ret, &local_err);
|
||||
qmp_marshal_output_UserDefOne(retval, ret, &local_err);
|
||||
|
||||
out:
|
||||
error_propagate(errp, local_err);
|
||||
@@ -778,7 +970,7 @@ Example:
|
||||
|
||||
static void qmp_init_marshal(void)
|
||||
{
|
||||
qmp_register_command("my-command", qmp_marshal_input_my_command, QCO_NO_OPTIONS);
|
||||
qmp_register_command("my-command", qmp_marshal_my_command, QCO_NO_OPTIONS);
|
||||
}
|
||||
|
||||
qapi_init(qmp_init_marshal);
|
||||
@@ -830,9 +1022,9 @@ Example:
|
||||
QDECREF(qmp);
|
||||
}
|
||||
|
||||
const char *example_QAPIEvent_lookup[] = {
|
||||
"MY_EVENT",
|
||||
NULL,
|
||||
const char *const example_QAPIEvent_lookup[] = {
|
||||
[EXAMPLE_QAPI_EVENT_MY_EVENT] = "MY_EVENT",
|
||||
[EXAMPLE_QAPI_EVENT_MAX] = NULL,
|
||||
};
|
||||
$ cat qapi-generated/example-qapi-event.h
|
||||
[Uninteresting stuff omitted...]
|
||||
@@ -847,10 +1039,45 @@ Example:
|
||||
|
||||
void qapi_event_send_my_event(Error **errp);
|
||||
|
||||
extern const char *example_QAPIEvent_lookup[];
|
||||
typedef enum example_QAPIEvent {
|
||||
EXAMPLE_QAPI_EVENT_MY_EVENT = 0,
|
||||
EXAMPLE_QAPI_EVENT_MAX = 1,
|
||||
} example_QAPIEvent;
|
||||
|
||||
extern const char *const example_QAPIEvent_lookup[];
|
||||
|
||||
#endif
|
||||
|
||||
=== scripts/qapi-introspect.py ===
|
||||
|
||||
Used to generate the introspection C code for a schema. The following
|
||||
files are created:
|
||||
|
||||
$(prefix)qmp-introspect.c - Defines a string holding a JSON
|
||||
description of the schema.
|
||||
$(prefix)qmp-introspect.h - Declares the above string.
|
||||
|
||||
Example:
|
||||
|
||||
$ python scripts/qapi-introspect.py --output-dir="qapi-generated"
|
||||
--prefix="example-" example-schema.json
|
||||
$ cat qapi-generated/example-qmp-introspect.c
|
||||
[Uninteresting stuff omitted...]
|
||||
|
||||
const char example_qmp_schema_json[] = "["
|
||||
"{\"arg-type\": \"0\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
|
||||
"{\"arg-type\": \"1\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"2\"}, "
|
||||
"{\"members\": [], \"meta-type\": \"object\", \"name\": \"0\"}, "
|
||||
"{\"members\": [{\"name\": \"arg1\", \"type\": \"2\"}], \"meta-type\": \"object\", \"name\": \"1\"}, "
|
||||
"{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, "
|
||||
"{\"json-type\": \"int\", \"meta-type\": \"builtin\", \"name\": \"int\"}, "
|
||||
"{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}]";
|
||||
$ cat qapi-generated/example-qmp-introspect.h
|
||||
[Uninteresting stuff omitted...]
|
||||
|
||||
#ifndef EXAMPLE_QMP_INTROSPECT_H
|
||||
#define EXAMPLE_QMP_INTROSPECT_H
|
||||
|
||||
extern const char example_qmp_schema_json[];
|
||||
|
||||
#endif
|
||||
|
||||
@@ -127,7 +127,7 @@ following at the bottom:
|
||||
{
|
||||
.name = "hello-world",
|
||||
.args_type = "",
|
||||
.mhandler.cmd_new = qmp_marshal_input_hello_world,
|
||||
.mhandler.cmd_new = qmp_marshal_hello_world,
|
||||
},
|
||||
|
||||
You're done. Now build qemu, run it as suggested in the "Testing" section,
|
||||
@@ -179,7 +179,7 @@ The last step is to update the qmp-commands.hx file:
|
||||
{
|
||||
.name = "hello-world",
|
||||
.args_type = "message:s?",
|
||||
.mhandler.cmd_new = qmp_marshal_input_hello_world,
|
||||
.mhandler.cmd_new = qmp_marshal_hello_world,
|
||||
},
|
||||
|
||||
Notice that the "args_type" member got our "message" argument. The character
|
||||
@@ -461,7 +461,7 @@ The last step is to add the correspoding entry in the qmp-commands.hx file:
|
||||
{
|
||||
.name = "query-alarm-clock",
|
||||
.args_type = "",
|
||||
.mhandler.cmd_new = qmp_marshal_input_query_alarm_clock,
|
||||
.mhandler.cmd_new = qmp_marshal_query_alarm_clock,
|
||||
},
|
||||
|
||||
Time to test the new command. Build qemu, run it as described in the "Testing"
|
||||
@@ -607,7 +607,7 @@ To test this you have to add the corresponding qmp-commands.hx entry:
|
||||
{
|
||||
.name = "query-alarm-methods",
|
||||
.args_type = "",
|
||||
.mhandler.cmd_new = qmp_marshal_input_query_alarm_methods,
|
||||
.mhandler.cmd_new = qmp_marshal_query_alarm_methods,
|
||||
},
|
||||
|
||||
Now Build qemu, run it as explained in the "Testing" section and try our new
|
||||
|
||||
@@ -42,9 +42,6 @@ void monitor_read_command(Monitor *mon, int show_prompt);
|
||||
int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
|
||||
void *opaque);
|
||||
|
||||
void qmp_qom_set(QDict *qdict, QObject **ret, Error **errp);
|
||||
void qmp_qom_get(QDict *qdict, QObject **ret, Error **errp);
|
||||
void qmp_object_add(QDict *qdict, QObject **ret, Error **errp);
|
||||
void object_add(const char *type, const char *id, const QDict *qdict,
|
||||
Visitor *v, Error **errp);
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ struct Visitor
|
||||
void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
|
||||
void (*type_number)(Visitor *v, double *obj, const char *name,
|
||||
Error **errp);
|
||||
void (*type_any)(Visitor *v, QObject **obj, const char *name,
|
||||
Error **errp);
|
||||
|
||||
/* May be NULL */
|
||||
void (*optional)(Visitor *v, bool *present, const char *name,
|
||||
|
||||
@@ -58,6 +58,7 @@ void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp);
|
||||
void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp);
|
||||
void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp);
|
||||
void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp);
|
||||
void visit_type_any(Visitor *v, QObject **obj, const char *name, Error **errp);
|
||||
bool visit_start_union(Visitor *v, bool data_present, Error **errp);
|
||||
void visit_end_union(Visitor *v, bool data_present, Error **errp);
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
#include "block/qapi.h"
|
||||
#include "qapi/qmp-event.h"
|
||||
#include "qapi-event.h"
|
||||
#include "qmp-introspect.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
|
||||
/* for hmp_info_irq/pic */
|
||||
@@ -928,6 +929,21 @@ EventInfoList *qmp_query_events(Error **errp)
|
||||
return ev_list;
|
||||
}
|
||||
|
||||
/*
|
||||
* Minor hack: generated marshalling suppressed for this command
|
||||
* ('gen': false in the schema) so we can parse the JSON string
|
||||
* directly into QObject instead of first parsing it with
|
||||
* visit_type_SchemaInfoList() into a SchemaInfoList, then marshal it
|
||||
* to QObject with generated output marshallers, every time. Instead,
|
||||
* we do it in test-qmp-input-visitor.c, just to make sure
|
||||
* qapi-introspect.py's output actually conforms to the schema.
|
||||
*/
|
||||
static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
|
||||
Error **errp)
|
||||
{
|
||||
*ret_data = qobject_from_json(qmp_schema_json);
|
||||
}
|
||||
|
||||
/* set the current CPU defined by the user */
|
||||
int monitor_set_cpu(int cpu_index)
|
||||
{
|
||||
@@ -3912,7 +3928,7 @@ static QObject *get_qmp_greeting(void)
|
||||
{
|
||||
QObject *ver = NULL;
|
||||
|
||||
qmp_marshal_input_query_version(NULL, &ver, NULL);
|
||||
qmp_marshal_query_version(NULL, &ver, NULL);
|
||||
return qobject_from_jsonf("{'QMP':{'version': %p,'capabilities': []}}",ver);
|
||||
}
|
||||
|
||||
|
||||
+13
-12
@@ -17,6 +17,9 @@
|
||||
# Tracing commands
|
||||
{ 'include': 'qapi/trace.json' }
|
||||
|
||||
# QAPI introspection
|
||||
{ 'include': 'qapi/introspect.json' }
|
||||
|
||||
##
|
||||
# @LostTickPolicy:
|
||||
#
|
||||
@@ -1698,8 +1701,7 @@
|
||||
##
|
||||
{ 'command': 'qom-get',
|
||||
'data': { 'path': 'str', 'property': 'str' },
|
||||
'returns': '**',
|
||||
'gen': false }
|
||||
'returns': 'any' }
|
||||
|
||||
##
|
||||
# @qom-set:
|
||||
@@ -1716,8 +1718,7 @@
|
||||
# Since: 1.2
|
||||
##
|
||||
{ 'command': 'qom-set',
|
||||
'data': { 'path': 'str', 'property': 'str', 'value': '**' },
|
||||
'gen': false }
|
||||
'data': { 'path': 'str', 'property': 'str', 'value': 'any' } }
|
||||
|
||||
##
|
||||
# @set_password:
|
||||
@@ -2081,11 +2082,12 @@
|
||||
#
|
||||
# @id: the name of the new network backend
|
||||
#
|
||||
# @props: #optional a list of properties to be passed to the backend in
|
||||
# the format 'name=value', like 'ifname=tap0,script=no'
|
||||
# Additional arguments depend on the type.
|
||||
#
|
||||
# Notes: The semantics of @props is not well defined. Future commands will be
|
||||
# introduced that provide stronger typing for backend creation.
|
||||
# TODO This command effectively bypasses QAPI completely due to its
|
||||
# "additional arguments" business. It shouldn't have been added to
|
||||
# the schema in this form. It should be qapified properly, or
|
||||
# replaced by a properly qapified command.
|
||||
#
|
||||
# Since: 0.14.0
|
||||
#
|
||||
@@ -2093,8 +2095,8 @@
|
||||
# If @type is not a valid network backend, DeviceNotFound
|
||||
##
|
||||
{ 'command': 'netdev_add',
|
||||
'data': {'type': 'str', 'id': 'str', '*props': '**'},
|
||||
'gen': false }
|
||||
'data': {'type': 'str', 'id': 'str'},
|
||||
'gen': false } # so we can get the additional arguments
|
||||
|
||||
##
|
||||
# @netdev_del:
|
||||
@@ -2127,8 +2129,7 @@
|
||||
# Since: 2.0
|
||||
##
|
||||
{ 'command': 'object-add',
|
||||
'data': {'qom-type': 'str', 'id': 'str', '*props': '**'},
|
||||
'gen': false }
|
||||
'data': {'qom-type': 'str', 'id': 'str', '*props': 'any'} }
|
||||
|
||||
##
|
||||
# @object-del:
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
# -*- Mode: Python -*-
|
||||
#
|
||||
# QAPI/QMP introspection
|
||||
#
|
||||
# Copyright (C) 2015 Red Hat, Inc.
|
||||
#
|
||||
# Authors:
|
||||
# Markus Armbruster <armbru@redhat.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
# See the COPYING file in the top-level directory.
|
||||
|
||||
##
|
||||
# @query-qmp-schema
|
||||
#
|
||||
# Command query-qmp-schema exposes the QMP wire ABI as an array of
|
||||
# SchemaInfo. This lets QMP clients figure out what commands and
|
||||
# events are available in this QEMU, and their parameters and results.
|
||||
#
|
||||
# However, the SchemaInfo can't reflect all the rules and restrictions
|
||||
# that apply to QMP. It's interface introspection (figuring out
|
||||
# what's there), not interface specification. The specification is in
|
||||
# the QAPI schema.
|
||||
#
|
||||
# Returns: array of @SchemaInfo, where each element describes an
|
||||
# entity in the ABI: command, event, type, ...
|
||||
#
|
||||
# Note: the QAPI schema is also used to help define *internal*
|
||||
# interfaces, by defining QAPI types. These are not part of the QMP
|
||||
# wire ABI, and therefore not returned by this command.
|
||||
#
|
||||
# Since: 2.5
|
||||
##
|
||||
{ 'command': 'query-qmp-schema',
|
||||
'returns': [ 'SchemaInfo' ],
|
||||
'gen': false } # just to simplify qmp_query_json()
|
||||
|
||||
##
|
||||
# @SchemaMetaType
|
||||
#
|
||||
# This is a @SchemaInfo's meta type, i.e. the kind of entity it
|
||||
# describes.
|
||||
#
|
||||
# @builtin: a predefined type such as 'int' or 'bool'.
|
||||
#
|
||||
# @enum: an enumeration type
|
||||
#
|
||||
# @array: an array type
|
||||
#
|
||||
# @object: an object type (struct or union)
|
||||
#
|
||||
# @alternate: an alternate type
|
||||
#
|
||||
# @command: a QMP command
|
||||
#
|
||||
# @event: a QMP event
|
||||
#
|
||||
# Since: 2.5
|
||||
##
|
||||
{ 'enum': 'SchemaMetaType',
|
||||
'data': [ 'builtin', 'enum', 'array', 'object', 'alternate',
|
||||
'command', 'event' ] }
|
||||
|
||||
##
|
||||
# @SchemaInfoBase
|
||||
#
|
||||
# Members common to any @SchemaInfo.
|
||||
#
|
||||
# Since: 2.5
|
||||
##
|
||||
{ 'struct': 'SchemaInfoBase',
|
||||
'data': { 'name': 'str', 'meta-type': 'SchemaMetaType' } }
|
||||
|
||||
##
|
||||
# @SchemaInfo
|
||||
#
|
||||
# @name: the entity's name, inherited from @base.
|
||||
# Commands and events have the name defined in the QAPI schema.
|
||||
# Unlike command and event names, type names are not part of
|
||||
# the wire ABI. Consequently, type names are meaningless
|
||||
# strings here.
|
||||
#
|
||||
# All references to other SchemaInfo are by name.
|
||||
#
|
||||
# @meta-type: the entity's meta type, inherited from @base.
|
||||
#
|
||||
# Additional members depend on the value of @meta-type.
|
||||
#
|
||||
# Since: 2.5
|
||||
##
|
||||
{ 'union': 'SchemaInfo',
|
||||
'base': 'SchemaInfoBase',
|
||||
'discriminator': 'meta-type',
|
||||
'data': {
|
||||
'builtin': 'SchemaInfoBuiltin',
|
||||
'enum': 'SchemaInfoEnum',
|
||||
'array': 'SchemaInfoArray',
|
||||
'object': 'SchemaInfoObject',
|
||||
'alternate': 'SchemaInfoAlternate',
|
||||
'command': 'SchemaInfoCommand',
|
||||
'event': 'SchemaInfoEvent' } }
|
||||
|
||||
##
|
||||
# @SchemaInfoBuiltin
|
||||
#
|
||||
# Additional SchemaInfo members for meta-type 'builtin'.
|
||||
#
|
||||
# @json-type: the JSON type used for this type on the wire.
|
||||
#
|
||||
# Since: 2.5
|
||||
##
|
||||
{ 'struct': 'SchemaInfoBuiltin',
|
||||
'data': { 'json-type': 'JSONType' } }
|
||||
|
||||
##
|
||||
# @JSONType
|
||||
#
|
||||
# The four primitive and two structured types according to RFC 7159
|
||||
# section 1, plus 'int' (split off 'number'), plus the obvious top
|
||||
# type 'value'.
|
||||
#
|
||||
# Since: 2.5
|
||||
##
|
||||
{ 'enum': 'JSONType',
|
||||
'data': [ 'string', 'number', 'int', 'boolean', 'null',
|
||||
'object', 'array', 'value' ] }
|
||||
|
||||
##
|
||||
# @SchemaInfoEnum
|
||||
#
|
||||
# Additional SchemaInfo members for meta-type 'enum'.
|
||||
#
|
||||
# @values: the enumeration type's values.
|
||||
#
|
||||
# Values of this type are JSON string on the wire.
|
||||
#
|
||||
# Since: 2.5
|
||||
##
|
||||
{ 'struct': 'SchemaInfoEnum',
|
||||
'data': { 'values': ['str'] } }
|
||||
|
||||
##
|
||||
# @SchemaInfoArray
|
||||
#
|
||||
# Additional SchemaInfo members for meta-type 'array'.
|
||||
#
|
||||
# @element-type: the array type's element type.
|
||||
#
|
||||
# Values of this type are JSON array on the wire.
|
||||
#
|
||||
# Since: 2.5
|
||||
##
|
||||
{ 'struct': 'SchemaInfoArray',
|
||||
'data': { 'element-type': 'str' } }
|
||||
|
||||
##
|
||||
# @SchemaInfoObject
|
||||
#
|
||||
# Additional SchemaInfo members for meta-type 'object'.
|
||||
#
|
||||
# @members: the object type's (non-variant) members.
|
||||
#
|
||||
# @tag: #optional the name of the member serving as type tag.
|
||||
# An element of @members with this name must exist.
|
||||
#
|
||||
# @variants: #optional variant members, i.e. additional members that
|
||||
# depend on the type tag's value. Present exactly when
|
||||
# @tag is present.
|
||||
#
|
||||
# Values of this type are JSON object on the wire.
|
||||
#
|
||||
# Since: 2.5
|
||||
##
|
||||
{ 'struct': 'SchemaInfoObject',
|
||||
'data': { 'members': [ 'SchemaInfoObjectMember' ],
|
||||
'*tag': 'str',
|
||||
'*variants': [ 'SchemaInfoObjectVariant' ] } }
|
||||
|
||||
##
|
||||
# @SchemaInfoObjectMember
|
||||
#
|
||||
# An object member.
|
||||
#
|
||||
# @name: the member's name, as defined in the QAPI schema.
|
||||
#
|
||||
# @type: the name of the member's type.
|
||||
#
|
||||
# @default: #optional default when used as command parameter.
|
||||
# If absent, the parameter is mandatory.
|
||||
# If present, the value must be null. The parameter is
|
||||
# optional, and behavior when it's missing is not specified
|
||||
# here.
|
||||
# Future extension: if present and non-null, the parameter
|
||||
# is optional, and defaults to this value.
|
||||
#
|
||||
# Since: 2.5
|
||||
##
|
||||
{ 'struct': 'SchemaInfoObjectMember',
|
||||
'data': { 'name': 'str', 'type': 'str', '*default': 'any' } }
|
||||
# @default's type must be null or match @type
|
||||
|
||||
##
|
||||
# @SchemaInfoObjectVariant
|
||||
#
|
||||
# The variant members for a value of the type tag.
|
||||
#
|
||||
# @case: a value of the type tag.
|
||||
#
|
||||
# @type: the name of the object type that provides the variant members
|
||||
# when the type tag has value @case.
|
||||
#
|
||||
# Since: 2.5
|
||||
##
|
||||
{ 'struct': 'SchemaInfoObjectVariant',
|
||||
'data': { 'case': 'str', 'type': 'str' } }
|
||||
|
||||
##
|
||||
# @SchemaInfoAlternate
|
||||
#
|
||||
# Additional SchemaInfo members for meta-type 'alternate'.
|
||||
#
|
||||
# @members: the alternate type's members.
|
||||
# The members' wire encoding is distinct, see
|
||||
# docs/qapi-code-gen.txt section Alternate types.
|
||||
#
|
||||
# On the wire, this can be any of the members.
|
||||
#
|
||||
# Since: 2.5
|
||||
##
|
||||
{ 'struct': 'SchemaInfoAlternate',
|
||||
'data': { 'members': [ 'SchemaInfoAlternateMember' ] } }
|
||||
|
||||
##
|
||||
# @SchemaInfoAlternateMember
|
||||
#
|
||||
# An alternate member.
|
||||
#
|
||||
# @type: the name of the member's type.
|
||||
#
|
||||
# Since: 2.5
|
||||
##
|
||||
{ 'struct': 'SchemaInfoAlternateMember',
|
||||
'data': { 'type': 'str' } }
|
||||
|
||||
##
|
||||
# @SchemaInfoCommand
|
||||
#
|
||||
# Additional SchemaInfo members for meta-type 'command'.
|
||||
#
|
||||
# @arg-type: the name of the object type that provides the command's
|
||||
# parameters.
|
||||
#
|
||||
# @ret-type: the name of the command's result type.
|
||||
#
|
||||
# TODO @success-response (currently irrelevant, because it's QGA, not QMP)
|
||||
#
|
||||
# Since: 2.5
|
||||
##
|
||||
{ 'struct': 'SchemaInfoCommand',
|
||||
'data': { 'arg-type': 'str', 'ret-type': 'str' } }
|
||||
|
||||
##
|
||||
# @SchemaInfoEvent
|
||||
#
|
||||
# Additional SchemaInfo members for meta-type 'event'.
|
||||
#
|
||||
# @arg-type: the name of the object type that provides the event's
|
||||
# parameters.
|
||||
#
|
||||
# Since: 2.5
|
||||
##
|
||||
{ 'struct': 'SchemaInfoEvent',
|
||||
'data': { 'arg-type': 'str' } }
|
||||
@@ -151,6 +151,14 @@ static void qapi_dealloc_type_number(Visitor *v, double *obj, const char *name,
|
||||
{
|
||||
}
|
||||
|
||||
static void qapi_dealloc_type_anything(Visitor *v, QObject **obj,
|
||||
const char *name, Error **errp)
|
||||
{
|
||||
if (obj) {
|
||||
qobject_decref(*obj);
|
||||
}
|
||||
}
|
||||
|
||||
static void qapi_dealloc_type_size(Visitor *v, uint64_t *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
@@ -216,6 +224,7 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
|
||||
v->visitor.type_bool = qapi_dealloc_type_bool;
|
||||
v->visitor.type_str = qapi_dealloc_type_str;
|
||||
v->visitor.type_number = qapi_dealloc_type_number;
|
||||
v->visitor.type_any = qapi_dealloc_type_anything;
|
||||
v->visitor.type_size = qapi_dealloc_type_size;
|
||||
v->visitor.start_union = qapi_dealloc_start_union;
|
||||
|
||||
|
||||
@@ -260,6 +260,12 @@ void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp)
|
||||
v->type_number(v, obj, name, errp);
|
||||
}
|
||||
|
||||
void visit_type_any(Visitor *v, QObject **obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
v->type_any(v, obj, name, errp);
|
||||
}
|
||||
|
||||
void output_type_enum(Visitor *v, int *obj, const char * const strings[],
|
||||
const char *kind, const char *name,
|
||||
Error **errp)
|
||||
|
||||
@@ -286,6 +286,16 @@ static void qmp_input_type_number(Visitor *v, double *obj, const char *name,
|
||||
}
|
||||
}
|
||||
|
||||
static void qmp_input_type_any(Visitor *v, QObject **obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
QmpInputVisitor *qiv = to_qiv(v);
|
||||
QObject *qobj = qmp_input_get_object(qiv, name, true);
|
||||
|
||||
qobject_incref(qobj);
|
||||
*obj = qobj;
|
||||
}
|
||||
|
||||
static void qmp_input_optional(Visitor *v, bool *present, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
@@ -329,6 +339,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
|
||||
v->visitor.type_bool = qmp_input_type_bool;
|
||||
v->visitor.type_str = qmp_input_type_str;
|
||||
v->visitor.type_number = qmp_input_type_number;
|
||||
v->visitor.type_any = qmp_input_type_any;
|
||||
v->visitor.optional = qmp_input_optional;
|
||||
v->visitor.get_next_type = qmp_input_get_next_type;
|
||||
|
||||
|
||||
@@ -66,9 +66,13 @@ static QObject *qmp_output_first(QmpOutputVisitor *qov)
|
||||
{
|
||||
QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
|
||||
|
||||
/* FIXME - find a better way to deal with NULL values */
|
||||
/*
|
||||
* FIXME Wrong, because qmp_output_get_qobject() will increment
|
||||
* the refcnt *again*. We need to think through how visitors
|
||||
* handle null.
|
||||
*/
|
||||
if (!e) {
|
||||
return NULL;
|
||||
return qnull();
|
||||
}
|
||||
|
||||
return e->value;
|
||||
@@ -186,6 +190,14 @@ static void qmp_output_type_number(Visitor *v, double *obj, const char *name,
|
||||
qmp_output_add(qov, name, qfloat_from_double(*obj));
|
||||
}
|
||||
|
||||
static void qmp_output_type_any(Visitor *v, QObject **obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
QmpOutputVisitor *qov = to_qov(v);
|
||||
qobject_incref(*obj);
|
||||
qmp_output_add_obj(qov, name, *obj);
|
||||
}
|
||||
|
||||
QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
|
||||
{
|
||||
QObject *obj = qmp_output_first(qov);
|
||||
@@ -233,6 +245,7 @@ QmpOutputVisitor *qmp_output_visitor_new(void)
|
||||
v->visitor.type_bool = qmp_output_type_bool;
|
||||
v->visitor.type_str = qmp_output_type_str;
|
||||
v->visitor.type_number = qmp_output_type_number;
|
||||
v->visitor.type_any = qmp_output_type_any;
|
||||
|
||||
QTAILQ_INIT(&v->stack);
|
||||
|
||||
|
||||
+144
-125
File diff suppressed because it is too large
Load Diff
@@ -157,9 +157,9 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp)
|
||||
* #ifdef CONFIG_SPICE. Necessary for an accurate query-commands
|
||||
* result. However, the QAPI schema is blissfully unaware of that,
|
||||
* and the QAPI code generator happily generates a dead
|
||||
* qmp_marshal_input_query_spice() that calls qmp_query_spice().
|
||||
* Provide it one, or else linking fails.
|
||||
* FIXME Educate the QAPI schema on CONFIG_SPICE.
|
||||
* qmp_marshal_query_spice() that calls qmp_query_spice(). Provide it
|
||||
* one, or else linking fails. FIXME Educate the QAPI schema on
|
||||
* CONFIG_SPICE.
|
||||
*/
|
||||
SpiceInfo *qmp_query_spice(Error **errp)
|
||||
{
|
||||
@@ -234,12 +234,9 @@ ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp)
|
||||
return props;
|
||||
}
|
||||
|
||||
/* FIXME: teach qapi about how to pass through Visitors */
|
||||
void qmp_qom_set(QDict *qdict, QObject **ret, Error **errp)
|
||||
void qmp_qom_set(const char *path, const char *property, QObject *value,
|
||||
Error **errp)
|
||||
{
|
||||
const char *path = qdict_get_str(qdict, "path");
|
||||
const char *property = qdict_get_str(qdict, "property");
|
||||
QObject *value = qdict_get(qdict, "value");
|
||||
Object *obj;
|
||||
|
||||
obj = object_resolve_path(path, NULL);
|
||||
@@ -252,20 +249,18 @@ void qmp_qom_set(QDict *qdict, QObject **ret, Error **errp)
|
||||
object_property_set_qobject(obj, value, property, errp);
|
||||
}
|
||||
|
||||
void qmp_qom_get(QDict *qdict, QObject **ret, Error **errp)
|
||||
QObject *qmp_qom_get(const char *path, const char *property, Error **errp)
|
||||
{
|
||||
const char *path = qdict_get_str(qdict, "path");
|
||||
const char *property = qdict_get_str(qdict, "property");
|
||||
Object *obj;
|
||||
|
||||
obj = object_resolve_path(path, NULL);
|
||||
if (!obj) {
|
||||
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
|
||||
"Device '%s' not found", path);
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*ret = object_property_get_qobject(obj, property, errp);
|
||||
return object_property_get_qobject(obj, property, errp);
|
||||
}
|
||||
|
||||
void qmp_set_password(const char *protocol, const char *password,
|
||||
@@ -661,11 +656,9 @@ out:
|
||||
object_unref(obj);
|
||||
}
|
||||
|
||||
void qmp_object_add(QDict *qdict, QObject **ret, Error **errp)
|
||||
void qmp_object_add(const char *type, const char *id,
|
||||
bool has_props, QObject *props, Error **errp)
|
||||
{
|
||||
const char *type = qdict_get_str(qdict, "qom-type");
|
||||
const char *id = qdict_get_str(qdict, "id");
|
||||
QObject *props = qdict_get(qdict, "props");
|
||||
const QDict *pdict = NULL;
|
||||
QmpInputVisitor *qiv;
|
||||
|
||||
|
||||
+156
-140
@@ -12,22 +12,18 @@
|
||||
# This work is licensed under the terms of the GNU GPL, version 2.
|
||||
# See the COPYING file in the top-level directory.
|
||||
|
||||
from ordereddict import OrderedDict
|
||||
from qapi import *
|
||||
import re
|
||||
|
||||
def generate_command_decl(name, args, ret_type):
|
||||
arglist=""
|
||||
for argname, argtype, optional in parse_args(args):
|
||||
argtype = c_type(argtype, is_param=True)
|
||||
if optional:
|
||||
arglist += "bool has_%s, " % c_name(argname)
|
||||
arglist += "%s %s, " % (argtype, c_name(argname))
|
||||
|
||||
def gen_command_decl(name, arg_type, ret_type):
|
||||
return mcgen('''
|
||||
%(ret_type)s qmp_%(name)s(%(args)sError **errp);
|
||||
%(c_type)s qmp_%(c_name)s(%(params)s);
|
||||
''',
|
||||
ret_type=c_type(ret_type), name=c_name(name),
|
||||
args=arglist)
|
||||
c_type=(ret_type and ret_type.c_type()) or 'void',
|
||||
c_name=c_name(name),
|
||||
params=gen_params(arg_type, 'Error **errp'))
|
||||
|
||||
|
||||
def gen_err_check(err):
|
||||
if not err:
|
||||
@@ -39,110 +35,124 @@ if (%(err)s) {
|
||||
''',
|
||||
err=err)
|
||||
|
||||
def gen_sync_call(name, args, ret_type):
|
||||
ret = ""
|
||||
arglist=""
|
||||
retval=""
|
||||
|
||||
def gen_call(name, arg_type, ret_type):
|
||||
ret = ''
|
||||
|
||||
argstr = ''
|
||||
if arg_type:
|
||||
for memb in arg_type.members:
|
||||
if memb.optional:
|
||||
argstr += 'has_%s, ' % c_name(memb.name)
|
||||
argstr += '%s, ' % c_name(memb.name)
|
||||
|
||||
lhs = ''
|
||||
if ret_type:
|
||||
retval = "retval = "
|
||||
for argname, argtype, optional in parse_args(args):
|
||||
if optional:
|
||||
arglist += "has_%s, " % c_name(argname)
|
||||
arglist += "%s, " % (c_name(argname))
|
||||
lhs = 'retval = '
|
||||
|
||||
push_indent()
|
||||
ret = mcgen('''
|
||||
%(retval)sqmp_%(name)s(%(args)s&local_err);
|
||||
|
||||
%(lhs)sqmp_%(c_name)s(%(args)s&local_err);
|
||||
''',
|
||||
name=c_name(name), args=arglist, retval=retval)
|
||||
c_name=c_name(name), args=argstr, lhs=lhs)
|
||||
if ret_type:
|
||||
ret += gen_err_check('local_err')
|
||||
ret += mcgen('''
|
||||
|
||||
qmp_marshal_output_%(c_name)s(retval, ret, &local_err);
|
||||
''',
|
||||
c_name=c_name(name))
|
||||
c_name=ret_type.c_name())
|
||||
pop_indent()
|
||||
return ret
|
||||
|
||||
def gen_visitor_input_containers_decl(args):
|
||||
ret = ""
|
||||
|
||||
def gen_marshal_vars(arg_type, ret_type):
|
||||
ret = mcgen('''
|
||||
Error *local_err = NULL;
|
||||
''')
|
||||
|
||||
push_indent()
|
||||
if len(args) > 0:
|
||||
|
||||
if ret_type:
|
||||
ret += mcgen('''
|
||||
%(c_type)s retval;
|
||||
''',
|
||||
c_type=ret_type.c_type())
|
||||
|
||||
if arg_type:
|
||||
ret += mcgen('''
|
||||
QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args));
|
||||
QapiDeallocVisitor *md;
|
||||
Visitor *v;
|
||||
''')
|
||||
pop_indent()
|
||||
|
||||
return ret
|
||||
for memb in arg_type.members:
|
||||
if memb.optional:
|
||||
ret += mcgen('''
|
||||
bool has_%(c_name)s = false;
|
||||
''',
|
||||
c_name=c_name(memb.name))
|
||||
ret += mcgen('''
|
||||
%(c_type)s %(c_name)s = %(c_null)s;
|
||||
''',
|
||||
c_name=c_name(memb.name),
|
||||
c_type=memb.type.c_type(),
|
||||
c_null=memb.type.c_null())
|
||||
ret += '\n'
|
||||
else:
|
||||
ret += mcgen('''
|
||||
|
||||
def gen_visitor_input_vars_decl(args):
|
||||
ret = ""
|
||||
push_indent()
|
||||
for argname, argtype, optional in parse_args(args):
|
||||
if optional:
|
||||
ret += mcgen('''
|
||||
bool has_%(argname)s = false;
|
||||
''',
|
||||
argname=c_name(argname))
|
||||
if is_c_ptr(argtype):
|
||||
ret += mcgen('''
|
||||
%(argtype)s %(argname)s = NULL;
|
||||
''',
|
||||
argname=c_name(argname), argtype=c_type(argtype))
|
||||
else:
|
||||
ret += mcgen('''
|
||||
%(argtype)s %(argname)s = {0};
|
||||
''',
|
||||
argname=c_name(argname), argtype=c_type(argtype))
|
||||
(void)args;
|
||||
''')
|
||||
|
||||
pop_indent()
|
||||
return ret
|
||||
|
||||
def gen_visitor_input_block(args, dealloc=False):
|
||||
ret = ""
|
||||
errparg = '&local_err'
|
||||
errarg = 'local_err'
|
||||
|
||||
if len(args) == 0:
|
||||
def gen_marshal_input_visit(arg_type, dealloc=False):
|
||||
ret = ''
|
||||
|
||||
if not arg_type:
|
||||
return ret
|
||||
|
||||
push_indent()
|
||||
|
||||
if dealloc:
|
||||
errparg = 'NULL'
|
||||
errarg = None;
|
||||
errarg = None
|
||||
ret += mcgen('''
|
||||
qmp_input_visitor_cleanup(mi);
|
||||
md = qapi_dealloc_visitor_new();
|
||||
v = qapi_dealloc_get_visitor(md);
|
||||
''')
|
||||
else:
|
||||
errparg = '&local_err'
|
||||
errarg = 'local_err'
|
||||
ret += mcgen('''
|
||||
v = qmp_input_get_visitor(mi);
|
||||
''')
|
||||
|
||||
for argname, argtype, optional in parse_args(args):
|
||||
if optional:
|
||||
for memb in arg_type.members:
|
||||
if memb.optional:
|
||||
ret += mcgen('''
|
||||
visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s);
|
||||
''',
|
||||
c_name=c_name(argname), name=argname, errp=errparg)
|
||||
c_name=c_name(memb.name), name=memb.name,
|
||||
errp=errparg)
|
||||
ret += gen_err_check(errarg)
|
||||
ret += mcgen('''
|
||||
if (has_%(c_name)s) {
|
||||
''',
|
||||
c_name=c_name(argname))
|
||||
c_name=c_name(memb.name))
|
||||
push_indent()
|
||||
ret += mcgen('''
|
||||
visit_type_%(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s);
|
||||
visit_type_%(c_type)s(v, &%(c_name)s, "%(name)s", %(errp)s);
|
||||
''',
|
||||
c_name=c_name(argname), name=argname, argtype=argtype,
|
||||
visitor=type_name(argtype), errp=errparg)
|
||||
c_name=c_name(memb.name), name=memb.name,
|
||||
c_type=memb.type.c_name(), errp=errparg)
|
||||
ret += gen_err_check(errarg)
|
||||
if optional:
|
||||
if memb.optional:
|
||||
pop_indent()
|
||||
ret += mcgen('''
|
||||
}
|
||||
@@ -155,12 +165,11 @@ qapi_dealloc_visitor_cleanup(md);
|
||||
pop_indent()
|
||||
return ret
|
||||
|
||||
def gen_marshal_output(name, ret_type):
|
||||
if not ret_type:
|
||||
return ""
|
||||
|
||||
ret = mcgen('''
|
||||
static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp)
|
||||
def gen_marshal_output(ret_type):
|
||||
return mcgen('''
|
||||
|
||||
static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
QmpOutputVisitor *mo = qmp_output_visitor_new();
|
||||
@@ -168,7 +177,7 @@ static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_o
|
||||
Visitor *v;
|
||||
|
||||
v = qmp_output_get_visitor(mo);
|
||||
visit_type_%(visitor)s(v, &ret_in, "unused", &local_err);
|
||||
visit_type_%(c_name)s(v, &ret_in, "unused", &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
}
|
||||
@@ -179,51 +188,40 @@ out:
|
||||
qmp_output_visitor_cleanup(mo);
|
||||
md = qapi_dealloc_visitor_new();
|
||||
v = qapi_dealloc_get_visitor(md);
|
||||
visit_type_%(visitor)s(v, &ret_in, "unused", NULL);
|
||||
visit_type_%(c_name)s(v, &ret_in, "unused", NULL);
|
||||
qapi_dealloc_visitor_cleanup(md);
|
||||
}
|
||||
''',
|
||||
c_ret_type=c_type(ret_type), c_name=c_name(name),
|
||||
visitor=type_name(ret_type))
|
||||
c_type=ret_type.c_type(), c_name=ret_type.c_name())
|
||||
|
||||
return ret
|
||||
|
||||
def gen_marshal_input_decl(name, middle_mode):
|
||||
ret = 'void qmp_marshal_input_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name)
|
||||
def gen_marshal_proto(name):
|
||||
ret = 'void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name)
|
||||
if not middle_mode:
|
||||
ret = "static " + ret
|
||||
ret = 'static ' + ret
|
||||
return ret
|
||||
|
||||
def gen_marshal_input(name, args, ret_type, middle_mode):
|
||||
hdr = gen_marshal_input_decl(name, middle_mode)
|
||||
|
||||
def gen_marshal_decl(name):
|
||||
return mcgen('''
|
||||
%(proto)s;
|
||||
''',
|
||||
proto=gen_marshal_proto(name))
|
||||
|
||||
|
||||
def gen_marshal(name, arg_type, ret_type):
|
||||
ret = mcgen('''
|
||||
%(header)s
|
||||
|
||||
%(proto)s
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
''',
|
||||
header=hdr)
|
||||
proto=gen_marshal_proto(name))
|
||||
|
||||
if ret_type:
|
||||
ret += mcgen('''
|
||||
%(c_type)s retval;
|
||||
''',
|
||||
c_type=c_type(ret_type))
|
||||
ret += gen_marshal_vars(arg_type, ret_type)
|
||||
ret += gen_marshal_input_visit(arg_type)
|
||||
ret += gen_call(name, arg_type, ret_type)
|
||||
|
||||
if len(args) > 0:
|
||||
ret += gen_visitor_input_containers_decl(args)
|
||||
ret += gen_visitor_input_vars_decl(args) + '\n'
|
||||
ret += gen_visitor_input_block(args) + '\n'
|
||||
else:
|
||||
ret += mcgen('''
|
||||
|
||||
(void)args;
|
||||
|
||||
''')
|
||||
|
||||
ret += gen_sync_call(name, args, ret_type)
|
||||
|
||||
if re.search('^ *goto out\\;', ret, re.MULTILINE):
|
||||
if re.search('^ *goto out;', ret, re.MULTILINE):
|
||||
ret += mcgen('''
|
||||
|
||||
out:
|
||||
@@ -231,27 +229,31 @@ out:
|
||||
ret += mcgen('''
|
||||
error_propagate(errp, local_err);
|
||||
''')
|
||||
ret += gen_visitor_input_block(args, dealloc=True)
|
||||
ret += gen_marshal_input_visit(arg_type, dealloc=True)
|
||||
ret += mcgen('''
|
||||
}
|
||||
''')
|
||||
return ret
|
||||
|
||||
def gen_registry(commands):
|
||||
registry=""
|
||||
push_indent()
|
||||
for cmd in commands:
|
||||
options = 'QCO_NO_OPTIONS'
|
||||
if not cmd.get('success-response', True):
|
||||
options = 'QCO_NO_SUCCESS_RESP'
|
||||
|
||||
registry += mcgen('''
|
||||
qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s);
|
||||
''',
|
||||
name=cmd['command'], c_name=c_name(cmd['command']),
|
||||
opts=options)
|
||||
pop_indent()
|
||||
def gen_register_command(name, success_response):
|
||||
push_indent()
|
||||
options = 'QCO_NO_OPTIONS'
|
||||
if not success_response:
|
||||
options = 'QCO_NO_SUCCESS_RESP'
|
||||
|
||||
ret = mcgen('''
|
||||
qmp_register_command("%(name)s", qmp_marshal_%(c_name)s, %(opts)s);
|
||||
''',
|
||||
name=name, c_name=c_name(name),
|
||||
opts=options)
|
||||
pop_indent()
|
||||
return ret
|
||||
|
||||
|
||||
def gen_registry(registry):
|
||||
ret = mcgen('''
|
||||
|
||||
static void qmp_init_marshal(void)
|
||||
{
|
||||
''')
|
||||
@@ -263,6 +265,41 @@ qapi_init(qmp_init_marshal);
|
||||
''')
|
||||
return ret
|
||||
|
||||
|
||||
class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
|
||||
def __init__(self):
|
||||
self.decl = None
|
||||
self.defn = None
|
||||
self._regy = None
|
||||
self._visited_ret_types = None
|
||||
|
||||
def visit_begin(self, schema):
|
||||
self.decl = ''
|
||||
self.defn = ''
|
||||
self._regy = ''
|
||||
self._visited_ret_types = set()
|
||||
|
||||
def visit_end(self):
|
||||
if not middle_mode:
|
||||
self.defn += gen_registry(self._regy)
|
||||
self._regy = None
|
||||
self._visited_ret_types = None
|
||||
|
||||
def visit_command(self, name, info, arg_type, ret_type,
|
||||
gen, success_response):
|
||||
if not gen:
|
||||
return
|
||||
self.decl += gen_command_decl(name, arg_type, ret_type)
|
||||
if ret_type and ret_type not in self._visited_ret_types:
|
||||
self._visited_ret_types.add(ret_type)
|
||||
self.defn += gen_marshal_output(ret_type)
|
||||
if middle_mode:
|
||||
self.decl += gen_marshal_decl(name)
|
||||
self.defn += gen_marshal(name, arg_type, ret_type)
|
||||
if not middle_mode:
|
||||
self._regy += gen_register_command(name, success_response)
|
||||
|
||||
|
||||
middle_mode = False
|
||||
|
||||
(input_file, output_dir, do_c, do_h, prefix, opts) = \
|
||||
@@ -272,10 +309,6 @@ for o, a in opts:
|
||||
if o in ("-m", "--middle"):
|
||||
middle_mode = True
|
||||
|
||||
exprs = parse_schema(input_file)
|
||||
commands = filter(lambda expr: expr.has_key('command'), exprs)
|
||||
commands = filter(lambda expr: not expr.has_key('gen'), commands)
|
||||
|
||||
c_comment = '''
|
||||
/*
|
||||
* schema-defined QMP->QAPI command dispatch
|
||||
@@ -323,7 +356,7 @@ fdef.write(mcgen('''
|
||||
#include "%(prefix)sqmp-commands.h"
|
||||
|
||||
''',
|
||||
prefix=prefix))
|
||||
prefix=prefix))
|
||||
|
||||
fdecl.write(mcgen('''
|
||||
#include "%(prefix)sqapi-types.h"
|
||||
@@ -331,29 +364,12 @@ fdecl.write(mcgen('''
|
||||
#include "qapi/error.h"
|
||||
|
||||
''',
|
||||
prefix=prefix))
|
||||
prefix=prefix))
|
||||
|
||||
for cmd in commands:
|
||||
arglist = []
|
||||
ret_type = None
|
||||
if cmd.has_key('data'):
|
||||
arglist = cmd['data']
|
||||
if cmd.has_key('returns'):
|
||||
ret_type = cmd['returns']
|
||||
ret = generate_command_decl(cmd['command'], arglist, ret_type)
|
||||
fdecl.write(ret)
|
||||
if ret_type:
|
||||
ret = gen_marshal_output(cmd['command'], ret_type) + "\n"
|
||||
fdef.write(ret)
|
||||
|
||||
if middle_mode:
|
||||
fdecl.write('%s;\n' % gen_marshal_input_decl(cmd['command'], middle_mode))
|
||||
|
||||
ret = gen_marshal_input(cmd['command'], arglist, ret_type, middle_mode) + "\n"
|
||||
fdef.write(ret)
|
||||
|
||||
if not middle_mode:
|
||||
ret = gen_registry(commands)
|
||||
fdef.write(ret)
|
||||
schema = QAPISchema(input_file)
|
||||
gen = QAPISchemaGenCommandVisitor()
|
||||
schema.visit(gen)
|
||||
fdef.write(gen.defn)
|
||||
fdecl.write(gen.decl)
|
||||
|
||||
close_output(fdef, fdecl)
|
||||
|
||||
+85
-158
@@ -2,78 +2,64 @@
|
||||
# QAPI event generator
|
||||
#
|
||||
# Copyright (c) 2014 Wenchao Xia
|
||||
# Copyright (c) 2015 Red Hat Inc.
|
||||
#
|
||||
# Authors:
|
||||
# Wenchao Xia <wenchaoqemu@gmail.com>
|
||||
# Markus Armbruster <armbru@redhat.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL, version 2.
|
||||
# See the COPYING file in the top-level directory.
|
||||
|
||||
from ordereddict import OrderedDict
|
||||
from qapi import *
|
||||
|
||||
def _generate_event_api_name(event_name, params):
|
||||
api_name = "void qapi_event_send_%s(" % c_name(event_name).lower();
|
||||
l = len(api_name)
|
||||
|
||||
if params:
|
||||
for argname, argentry, optional in parse_args(params):
|
||||
if optional:
|
||||
api_name += "bool has_%s,\n" % c_name(argname)
|
||||
api_name += "".ljust(l)
|
||||
|
||||
api_name += "%s %s,\n" % (c_type(argentry, is_param=True),
|
||||
c_name(argname))
|
||||
api_name += "".ljust(l)
|
||||
|
||||
api_name += "Error **errp)"
|
||||
return api_name;
|
||||
def gen_event_send_proto(name, arg_type):
|
||||
return 'void qapi_event_send_%(c_name)s(%(param)s)' % {
|
||||
'c_name': c_name(name.lower()),
|
||||
'param': gen_params(arg_type, 'Error **errp')}
|
||||
|
||||
|
||||
# Following are the core functions that generate C APIs to emit event.
|
||||
|
||||
def generate_event_declaration(api_name):
|
||||
def gen_event_send_decl(name, arg_type):
|
||||
return mcgen('''
|
||||
|
||||
%(api_name)s;
|
||||
%(proto)s;
|
||||
''',
|
||||
api_name = api_name)
|
||||
proto=gen_event_send_proto(name, arg_type))
|
||||
|
||||
def generate_event_implement(api_name, event_name, params):
|
||||
# step 1: declare any variables
|
||||
ret = mcgen("""
|
||||
|
||||
%(api_name)s
|
||||
def gen_event_send(name, arg_type):
|
||||
ret = mcgen('''
|
||||
|
||||
%(proto)s
|
||||
{
|
||||
QDict *qmp;
|
||||
Error *local_err = NULL;
|
||||
QMPEventFuncEmit emit;
|
||||
""",
|
||||
api_name = api_name)
|
||||
''',
|
||||
proto=gen_event_send_proto(name, arg_type))
|
||||
|
||||
if params:
|
||||
ret += mcgen("""
|
||||
if arg_type and arg_type.members:
|
||||
ret += mcgen('''
|
||||
QmpOutputVisitor *qov;
|
||||
Visitor *v;
|
||||
QObject *obj;
|
||||
|
||||
""")
|
||||
''')
|
||||
|
||||
# step 2: check emit function, create a dict
|
||||
ret += mcgen("""
|
||||
ret += mcgen('''
|
||||
emit = qmp_event_get_func_emit();
|
||||
if (!emit) {
|
||||
return;
|
||||
}
|
||||
|
||||
qmp = qmp_event_build_dict("%(event_name)s");
|
||||
qmp = qmp_event_build_dict("%(name)s");
|
||||
|
||||
""",
|
||||
event_name = event_name)
|
||||
''',
|
||||
name=name)
|
||||
|
||||
# step 3: visit the params if params != None
|
||||
if params:
|
||||
ret += mcgen("""
|
||||
if arg_type and arg_type.members:
|
||||
ret += mcgen('''
|
||||
qov = qmp_output_visitor_new();
|
||||
g_assert(qov);
|
||||
|
||||
@@ -81,45 +67,46 @@ def generate_event_implement(api_name, event_name, params):
|
||||
g_assert(v);
|
||||
|
||||
/* Fake visit, as if all members are under a structure */
|
||||
visit_start_struct(v, NULL, "", "%(event_name)s", 0, &local_err);
|
||||
visit_start_struct(v, NULL, "", "%(name)s", 0, &local_err);
|
||||
if (local_err) {
|
||||
goto clean;
|
||||
}
|
||||
|
||||
""",
|
||||
event_name = event_name)
|
||||
''',
|
||||
name=name)
|
||||
|
||||
for argname, argentry, optional in parse_args(params):
|
||||
if optional:
|
||||
ret += mcgen("""
|
||||
if (has_%(var)s) {
|
||||
""",
|
||||
var = c_name(argname))
|
||||
for memb in arg_type.members:
|
||||
if memb.optional:
|
||||
ret += mcgen('''
|
||||
if (has_%(c_name)s) {
|
||||
''',
|
||||
c_name=c_name(memb.name))
|
||||
push_indent()
|
||||
|
||||
if argentry == "str":
|
||||
var_type = "(char **)"
|
||||
# Ugly: need to cast away the const
|
||||
if memb.type.name == "str":
|
||||
cast = '(char **)'
|
||||
else:
|
||||
var_type = ""
|
||||
cast = ''
|
||||
|
||||
ret += mcgen("""
|
||||
visit_type_%(type)s(v, %(var_type)s&%(var)s, "%(name)s", &local_err);
|
||||
ret += mcgen('''
|
||||
visit_type_%(c_type)s(v, %(cast)s&%(c_name)s, "%(name)s", &local_err);
|
||||
if (local_err) {
|
||||
goto clean;
|
||||
}
|
||||
""",
|
||||
var_type = var_type,
|
||||
var = c_name(argname),
|
||||
type = type_name(argentry),
|
||||
name = argname)
|
||||
''',
|
||||
cast=cast,
|
||||
c_name=c_name(memb.name),
|
||||
c_type=memb.type.c_name(),
|
||||
name=memb.name)
|
||||
|
||||
if optional:
|
||||
if memb.optional:
|
||||
pop_indent()
|
||||
ret += mcgen("""
|
||||
ret += mcgen('''
|
||||
}
|
||||
""")
|
||||
''')
|
||||
|
||||
ret += mcgen("""
|
||||
ret += mcgen('''
|
||||
|
||||
visit_end_struct(v, &local_err);
|
||||
if (local_err) {
|
||||
@@ -130,86 +117,49 @@ def generate_event_implement(api_name, event_name, params):
|
||||
g_assert(obj != NULL);
|
||||
|
||||
qdict_put_obj(qmp, "data", obj);
|
||||
""")
|
||||
''')
|
||||
|
||||
# step 4: call qmp event api
|
||||
ret += mcgen("""
|
||||
emit(%(event_enum_value)s, qmp, &local_err);
|
||||
ret += mcgen('''
|
||||
emit(%(c_enum)s, qmp, &local_err);
|
||||
|
||||
""",
|
||||
event_enum_value = event_enum_value)
|
||||
''',
|
||||
c_enum=c_enum_const(event_enum_name, name))
|
||||
|
||||
# step 5: clean up
|
||||
if params:
|
||||
ret += mcgen("""
|
||||
if arg_type and arg_type.members:
|
||||
ret += mcgen('''
|
||||
clean:
|
||||
qmp_output_visitor_cleanup(qov);
|
||||
""")
|
||||
ret += mcgen("""
|
||||
''')
|
||||
ret += mcgen('''
|
||||
error_propagate(errp, local_err);
|
||||
QDECREF(qmp);
|
||||
}
|
||||
""")
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
# Following are the functions that generate an enum type for all defined
|
||||
# events, similar to qapi-types.py. Here we already have enum name and
|
||||
# values which were generated before and recorded in event_enum_*. It also
|
||||
# works around the issue that "import qapi-types" can't work.
|
||||
|
||||
def generate_event_enum_decl(event_enum_name, event_enum_values):
|
||||
lookup_decl = mcgen('''
|
||||
|
||||
extern const char *%(event_enum_name)s_lookup[];
|
||||
''',
|
||||
event_enum_name = event_enum_name)
|
||||
|
||||
enum_decl = mcgen('''
|
||||
typedef enum %(event_enum_name)s {
|
||||
''',
|
||||
event_enum_name = event_enum_name)
|
||||
|
||||
# append automatically generated _MAX value
|
||||
enum_max_value = c_enum_const(event_enum_name, "MAX")
|
||||
enum_values = event_enum_values + [ enum_max_value ]
|
||||
|
||||
i = 0
|
||||
for value in enum_values:
|
||||
enum_decl += mcgen('''
|
||||
%(value)s = %(i)d,
|
||||
''',
|
||||
value = value,
|
||||
i = i)
|
||||
i += 1
|
||||
|
||||
enum_decl += mcgen('''
|
||||
} %(event_enum_name)s;
|
||||
''',
|
||||
event_enum_name = event_enum_name)
|
||||
|
||||
return lookup_decl + enum_decl
|
||||
|
||||
def generate_event_enum_lookup(event_enum_name, event_enum_strings):
|
||||
ret = mcgen('''
|
||||
|
||||
const char *%(event_enum_name)s_lookup[] = {
|
||||
''',
|
||||
event_enum_name = event_enum_name)
|
||||
|
||||
for string in event_enum_strings:
|
||||
ret += mcgen('''
|
||||
"%(string)s",
|
||||
''',
|
||||
string = string)
|
||||
|
||||
ret += mcgen('''
|
||||
NULL,
|
||||
};
|
||||
''')
|
||||
return ret
|
||||
|
||||
|
||||
class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
|
||||
def __init__(self):
|
||||
self.decl = None
|
||||
self.defn = None
|
||||
self._event_names = None
|
||||
|
||||
def visit_begin(self, schema):
|
||||
self.decl = ''
|
||||
self.defn = ''
|
||||
self._event_names = []
|
||||
|
||||
def visit_end(self):
|
||||
self.decl += gen_enum(event_enum_name, self._event_names)
|
||||
self.defn += gen_enum_lookup(event_enum_name, self._event_names)
|
||||
self._event_names = None
|
||||
|
||||
def visit_event(self, name, info, arg_type):
|
||||
self.decl += gen_event_send_decl(name, arg_type)
|
||||
self.defn += gen_event_send(name, arg_type)
|
||||
self._event_names.append(name)
|
||||
|
||||
|
||||
(input_file, output_dir, do_c, do_h, prefix, dummy) = parse_command_line()
|
||||
|
||||
c_comment = '''
|
||||
@@ -263,35 +213,12 @@ fdecl.write(mcgen('''
|
||||
''',
|
||||
prefix=prefix))
|
||||
|
||||
exprs = parse_schema(input_file)
|
||||
|
||||
event_enum_name = c_name(prefix + "QAPIEvent", protect=False)
|
||||
event_enum_values = []
|
||||
event_enum_strings = []
|
||||
|
||||
for expr in exprs:
|
||||
if expr.has_key('event'):
|
||||
event_name = expr['event']
|
||||
params = expr.get('data')
|
||||
if params and len(params) == 0:
|
||||
params = None
|
||||
|
||||
api_name = _generate_event_api_name(event_name, params)
|
||||
ret = generate_event_declaration(api_name)
|
||||
fdecl.write(ret)
|
||||
|
||||
# We need an enum value per event
|
||||
event_enum_value = c_enum_const(event_enum_name, event_name)
|
||||
ret = generate_event_implement(api_name, event_name, params)
|
||||
fdef.write(ret)
|
||||
|
||||
# Record it, and generate enum later
|
||||
event_enum_values.append(event_enum_value)
|
||||
event_enum_strings.append(event_name)
|
||||
|
||||
ret = generate_event_enum_decl(event_enum_name, event_enum_values)
|
||||
fdecl.write(ret)
|
||||
ret = generate_event_enum_lookup(event_enum_name, event_enum_strings)
|
||||
fdef.write(ret)
|
||||
schema = QAPISchema(input_file)
|
||||
gen = QAPISchemaGenEventVisitor()
|
||||
schema.visit(gen)
|
||||
fdef.write(gen.defn)
|
||||
fdecl.write(gen.decl)
|
||||
|
||||
close_output(fdef, fdecl)
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
#
|
||||
# QAPI introspection generator
|
||||
#
|
||||
# Copyright (C) 2015 Red Hat, Inc.
|
||||
#
|
||||
# Authors:
|
||||
# Markus Armbruster <armbru@redhat.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL, version 2.
|
||||
# See the COPYING file in the top-level directory.
|
||||
|
||||
from qapi import *
|
||||
|
||||
|
||||
# Caveman's json.dumps() replacement (we're stuck at Python 2.4)
|
||||
# TODO try to use json.dumps() once we get unstuck
|
||||
def to_json(obj, level=0):
|
||||
if obj is None:
|
||||
ret = 'null'
|
||||
elif isinstance(obj, str):
|
||||
ret = '"' + obj.replace('"', r'\"') + '"'
|
||||
elif isinstance(obj, list):
|
||||
elts = [to_json(elt, level + 1)
|
||||
for elt in obj]
|
||||
ret = '[' + ', '.join(elts) + ']'
|
||||
elif isinstance(obj, dict):
|
||||
elts = ['"%s": %s' % (key.replace('"', r'\"'),
|
||||
to_json(obj[key], level + 1))
|
||||
for key in sorted(obj.keys())]
|
||||
ret = '{' + ', '.join(elts) + '}'
|
||||
else:
|
||||
assert False # not implemented
|
||||
if level == 1:
|
||||
ret = '\n' + ret
|
||||
return ret
|
||||
|
||||
|
||||
def to_c_string(string):
|
||||
return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"'
|
||||
|
||||
|
||||
class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor):
|
||||
def __init__(self, unmask):
|
||||
self._unmask = unmask
|
||||
self.defn = None
|
||||
self.decl = None
|
||||
self._schema = None
|
||||
self._jsons = None
|
||||
self._used_types = None
|
||||
self._name_map = None
|
||||
|
||||
def visit_begin(self, schema):
|
||||
self._schema = schema
|
||||
self._jsons = []
|
||||
self._used_types = []
|
||||
self._name_map = {}
|
||||
return QAPISchemaType # don't visit types for now
|
||||
|
||||
def visit_end(self):
|
||||
# visit the types that are actually used
|
||||
jsons = self._jsons
|
||||
self._jsons = []
|
||||
for typ in self._used_types:
|
||||
typ.visit(self)
|
||||
# generate C
|
||||
# TODO can generate awfully long lines
|
||||
jsons.extend(self._jsons)
|
||||
name = prefix + 'qmp_schema_json'
|
||||
self.decl = mcgen('''
|
||||
extern const char %(c_name)s[];
|
||||
''',
|
||||
c_name=c_name(name))
|
||||
lines = to_json(jsons).split('\n')
|
||||
c_string = '\n '.join([to_c_string(line) for line in lines])
|
||||
self.defn = mcgen('''
|
||||
const char %(c_name)s[] = %(c_string)s;
|
||||
''',
|
||||
c_name=c_name(name),
|
||||
c_string=c_string)
|
||||
self._schema = None
|
||||
self._jsons = None
|
||||
self._used_types = None
|
||||
self._name_map = None
|
||||
|
||||
def _name(self, name):
|
||||
if self._unmask:
|
||||
return name
|
||||
if name not in self._name_map:
|
||||
self._name_map[name] = '%d' % len(self._name_map)
|
||||
return self._name_map[name]
|
||||
|
||||
def _use_type(self, typ):
|
||||
# Map the various integer types to plain int
|
||||
if typ.json_type() == 'int':
|
||||
typ = self._schema.lookup_type('int')
|
||||
elif (isinstance(typ, QAPISchemaArrayType) and
|
||||
typ.element_type.json_type() == 'int'):
|
||||
typ = self._schema.lookup_type('intList')
|
||||
# Add type to work queue if new
|
||||
if typ not in self._used_types:
|
||||
self._used_types.append(typ)
|
||||
# Clients should examine commands and events, not types. Hide
|
||||
# type names to reduce the temptation. Also saves a few
|
||||
# characters.
|
||||
if isinstance(typ, QAPISchemaBuiltinType):
|
||||
return typ.name
|
||||
return self._name(typ.name)
|
||||
|
||||
def _gen_json(self, name, mtype, obj):
|
||||
if mtype != 'command' and mtype != 'event' and mtype != 'builtin':
|
||||
name = self._name(name)
|
||||
obj['name'] = name
|
||||
obj['meta-type'] = mtype
|
||||
self._jsons.append(obj)
|
||||
|
||||
def _gen_member(self, member):
|
||||
ret = {'name': member.name, 'type': self._use_type(member.type)}
|
||||
if member.optional:
|
||||
ret['default'] = None
|
||||
return ret
|
||||
|
||||
def _gen_variants(self, tag_name, variants):
|
||||
return {'tag': tag_name,
|
||||
'variants': [self._gen_variant(v) for v in variants]}
|
||||
|
||||
def _gen_variant(self, variant):
|
||||
return {'case': variant.name, 'type': self._use_type(variant.type)}
|
||||
|
||||
def visit_builtin_type(self, name, info, json_type):
|
||||
self._gen_json(name, 'builtin', {'json-type': json_type})
|
||||
|
||||
def visit_enum_type(self, name, info, values, prefix):
|
||||
self._gen_json(name, 'enum', {'values': values})
|
||||
|
||||
def visit_array_type(self, name, info, element_type):
|
||||
self._gen_json(name, 'array',
|
||||
{'element-type': self._use_type(element_type)})
|
||||
|
||||
def visit_object_type_flat(self, name, info, members, variants):
|
||||
obj = {'members': [self._gen_member(m) for m in members]}
|
||||
if variants:
|
||||
obj.update(self._gen_variants(variants.tag_member.name,
|
||||
variants.variants))
|
||||
self._gen_json(name, 'object', obj)
|
||||
|
||||
def visit_alternate_type(self, name, info, variants):
|
||||
self._gen_json(name, 'alternate',
|
||||
{'members': [{'type': self._use_type(m.type)}
|
||||
for m in variants.variants]})
|
||||
|
||||
def visit_command(self, name, info, arg_type, ret_type,
|
||||
gen, success_response):
|
||||
arg_type = arg_type or self._schema.the_empty_object_type
|
||||
ret_type = ret_type or self._schema.the_empty_object_type
|
||||
self._gen_json(name, 'command',
|
||||
{'arg-type': self._use_type(arg_type),
|
||||
'ret-type': self._use_type(ret_type)})
|
||||
|
||||
def visit_event(self, name, info, arg_type):
|
||||
arg_type = arg_type or self._schema.the_empty_object_type
|
||||
self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)})
|
||||
|
||||
# Debugging aid: unmask QAPI schema's type names
|
||||
# We normally mask them, because they're not QMP wire ABI
|
||||
opt_unmask = False
|
||||
|
||||
(input_file, output_dir, do_c, do_h, prefix, opts) = \
|
||||
parse_command_line("u", ["unmask-non-abi-names"])
|
||||
|
||||
for o, a in opts:
|
||||
if o in ("-u", "--unmask-non-abi-names"):
|
||||
opt_unmask = True
|
||||
|
||||
c_comment = '''
|
||||
/*
|
||||
* QAPI/QMP schema introspection
|
||||
*
|
||||
* Copyright (C) 2015 Red Hat, Inc.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
'''
|
||||
h_comment = '''
|
||||
/*
|
||||
* QAPI/QMP schema introspection
|
||||
*
|
||||
* Copyright (C) 2015 Red Hat, Inc.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
'''
|
||||
|
||||
(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
|
||||
'qmp-introspect.c', 'qmp-introspect.h',
|
||||
c_comment, h_comment)
|
||||
|
||||
fdef.write(mcgen('''
|
||||
#include "%(prefix)sqmp-introspect.h"
|
||||
|
||||
''',
|
||||
prefix=prefix))
|
||||
|
||||
schema = QAPISchema(input_file)
|
||||
gen = QAPISchemaGenIntrospectVisitor(opt_unmask)
|
||||
schema.visit(gen)
|
||||
fdef.write(gen.defn)
|
||||
fdecl.write(gen.decl)
|
||||
|
||||
close_output(fdef, fdecl)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user