qapi-gen: New common driver for code and doc generators

Whenever qapi-schema.json changes, we run six programs eleven times to
update eleven files.  Similar for qga/qapi-schema.json.  This is
silly.  Replace the six programs by a single program that spits out
all eleven files.

The programs become modules in new Python package qapi, along with the
helper library.  This requires moving them to scripts/qapi/.  While
moving them, consistently drop executable mode bits.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20180211093607.27351-9-armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Michael Roth <mdroth@linux.vnet.ibm.com>
[eblake: move change to one-line 'blurb' earlier in series, mention mode
bit change as intentional, update qapi-code-gen.txt to match actual
generated events.c file]
Signed-off-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
Markus Armbruster 2018-02-26 13:48:58 -06:00 committed by Eric Blake
parent 26df4e7fab
commit fb0bc835e5
16 changed files with 191 additions and 277 deletions

2
.gitignore vendored
View File

@ -28,9 +28,11 @@
/linux-headers/asm /linux-headers/asm
/qga/qapi-generated /qga/qapi-generated
/qapi-generated /qapi-generated
/qapi-gen-timestamp
/qapi-types.[ch] /qapi-types.[ch]
/qapi-visit.[ch] /qapi-visit.[ch]
/qapi-event.[ch] /qapi-event.[ch]
/qapi-doc.texi
/qmp-commands.h /qmp-commands.h
/qmp-introspect.[ch] /qmp-introspect.[ch]
/qmp-marshal.c /qmp-marshal.c

View File

@ -94,6 +94,7 @@ GENERATED_FILES += qmp-commands.h qapi-types.h qapi-visit.h qapi-event.h
GENERATED_FILES += qmp-marshal.c qapi-types.c qapi-visit.c qapi-event.c GENERATED_FILES += qmp-marshal.c qapi-types.c qapi-visit.c qapi-event.c
GENERATED_FILES += qmp-introspect.h GENERATED_FILES += qmp-introspect.h
GENERATED_FILES += qmp-introspect.c GENERATED_FILES += qmp-introspect.c
GENERATED_FILES += qapi-doc.texi
GENERATED_FILES += trace/generated-tcg-tracers.h GENERATED_FILES += trace/generated-tcg-tracers.h
@ -482,25 +483,26 @@ qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated
qemu-keymap$(EXESUF): LIBS += $(XKBCOMMON_LIBS) qemu-keymap$(EXESUF): LIBS += $(XKBCOMMON_LIBS)
qemu-keymap$(EXESUF): QEMU_CFLAGS += $(XKBCOMMON_CFLAGS) qemu-keymap$(EXESUF): QEMU_CFLAGS += $(XKBCOMMON_CFLAGS)
gen-out-type = $(subst .,-,$(suffix $@)) qapi-py = $(SRC_PATH)/scripts/qapi/commands.py \
$(SRC_PATH)/scripts/qapi/events.py \
$(SRC_PATH)/scripts/qapi/introspect.py \
$(SRC_PATH)/scripts/qapi/types.py \
$(SRC_PATH)/scripts/qapi/visit.py \
$(SRC_PATH)/scripts/qapi/common.py \
$(SRC_PATH)/scripts/qapi/doc.py \
$(SRC_PATH)/scripts/ordereddict.py \
$(SRC_PATH)/scripts/qapi-gen.py
qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \
qga/qapi-generated/qga-qapi-visit.c qga/qapi-generated/qga-qapi-visit.h \
qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h :\ qga/qapi-generated/qga-qmp-commands.h qga/qapi-generated/qga-qmp-marshal.c \
$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py) qga/qapi-generated/qga-qapi-doc.texi: \
$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-types.py \ qga/qapi-generated/qapi-gen-timestamp ;
$(gen-out-type) -o qga/qapi-generated -p "qga-" $<, \ qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py)
"GEN","$@") $(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \
qga/qapi-generated/qga-qapi-visit.c qga/qapi-generated/qga-qapi-visit.h :\ -o qga/qapi-generated -p "qga-" $<, \
$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py) "GEN","$(@:%-timestamp=%)")
$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-visit.py \ @>$@
$(gen-out-type) -o qga/qapi-generated -p "qga-" $<, \
"GEN","$@")
qga/qapi-generated/qga-qmp-commands.h qga/qapi-generated/qga-qmp-marshal.c :\
$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-commands.py \
$(gen-out-type) -o qga/qapi-generated -p "qga-" $<, \
"GEN","$@")
qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \ 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/block.json $(SRC_PATH)/qapi/block-core.json \
@ -517,31 +519,18 @@ qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
$(SRC_PATH)/qapi/transaction.json \ $(SRC_PATH)/qapi/transaction.json \
$(SRC_PATH)/qapi/ui.json $(SRC_PATH)/qapi/ui.json
qapi-types.c qapi-types.h :\ qapi-types.c qapi-types.h \
$(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py) qapi-visit.c qapi-visit.h \
$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-types.py \ qmp-commands.h qmp-marshal.c \
$(gen-out-type) -o "." -b $<, \ qapi-event.c qapi-event.h \
"GEN","$@") qmp-introspect.h qmp-introspect.c \
qapi-visit.c qapi-visit.h :\ qapi-doc.texi: \
$(qapi-modules) $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py) qapi-gen-timestamp ;
$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-visit.py \ qapi-gen-timestamp: $(qapi-modules) $(qapi-py)
$(gen-out-type) -o "." -b $<, \ $(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \
"GEN","$@") -o "." -b $<, \
qapi-event.c qapi-event.h :\ "GEN","$(@:%-timestamp=%)")
$(qapi-modules) $(SRC_PATH)/scripts/qapi-event.py $(qapi-py) @>$@
$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-event.py \
$(gen-out-type) -o "." $<, \
"GEN","$@")
qmp-commands.h qmp-marshal.c :\
$(qapi-modules) $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-commands.py \
$(gen-out-type) -o "." $<, \
"GEN","$@")
qmp-introspect.h qmp-introspect.c :\
$(qapi-modules) $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py)
$(call quiet-command,$(PYTHON_UTF8) $(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) QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h)
$(qga-obj-y): $(QGALIB_GEN) $(qga-obj-y): $(QGALIB_GEN)
@ -601,6 +590,7 @@ clean:
rm -f trace/generated-tracers-dtrace.dtrace* rm -f trace/generated-tracers-dtrace.dtrace*
rm -f trace/generated-tracers-dtrace.h* rm -f trace/generated-tracers-dtrace.h*
rm -f $(foreach f,$(GENERATED_FILES),$(f) $(f)-timestamp) rm -f $(foreach f,$(GENERATED_FILES),$(f) $(f)-timestamp)
rm -f qapi-gen-timestamp
rm -rf qapi-generated rm -rf qapi-generated
rm -rf qga/qapi-generated rm -rf qga/qapi-generated
for d in $(ALL_SUBDIRS); do \ for d in $(ALL_SUBDIRS); do \
@ -809,13 +799,11 @@ qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxt
qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@") $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
docs/interop/qemu-qmp-qapi.texi docs/interop/qemu-ga-qapi.texi: $(SRC_PATH)/scripts/qapi2texi.py $(qapi-py) docs/interop/qemu-qmp-qapi.texi: qapi-doc.texi
@cp -p $< $@
docs/interop/qemu-qmp-qapi.texi: $(qapi-modules) docs/interop/qemu-ga-qapi.texi: qga/qapi-generated/qga-qapi-doc.texi
$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@") @cp -p $< $@
docs/interop/qemu-ga-qapi.texi: $(SRC_PATH)/qga/qapi-schema.json
$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@")
qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi
qemu.1: qemu-option-trace.texi qemu.1: qemu-option-trace.texi

View File

@ -899,12 +899,13 @@ the names of built-in types. Clients should examine member
== Code generation == == Code generation ==
Schemas are fed into five scripts to generate all the code/files that, The QAPI code generator qapi-gen.py generates code and documentation
paired with the core QAPI libraries, comprise everything required to from the schema. Together with the core QAPI libraries, this code
take JSON commands read in by a Client JSON Protocol server, unmarshal provides everything required to take JSON commands read in by a Client
the arguments into the underlying C types, call into the corresponding JSON Protocol server, unmarshal the arguments into the underlying C
C function, map the response back to a Client JSON Protocol response types, call into the corresponding C function, map the response back
to be returned to the user, and introspect the commands. to a Client JSON Protocol response to be returned to the user, and
introspect the commands.
As an example, we'll use the following schema, which describes a As an example, we'll use the following schema, which describes a
single complex user-defined type, along with command which takes a single complex user-defined type, along with command which takes a
@ -922,18 +923,23 @@ qmp_my_command(); everything else is produced by the generator.
{ 'event': 'MY_EVENT' } { 'event': 'MY_EVENT' }
We run qapi-gen.py like this:
$ python scripts/qapi-gen.py --output-dir="qapi-generated" \
--prefix="example-" example-schema.json
For a more thorough look at generated code, the testsuite includes For a more thorough look at generated code, the testsuite includes
tests/qapi-schema/qapi-schema-tests.json that covers more examples of tests/qapi-schema/qapi-schema-tests.json that covers more examples of
what the generator will accept, and compiles the resulting C code as what the generator will accept, and compiles the resulting C code as
part of 'make check-unit'. part of 'make check-unit'.
=== scripts/qapi-types.py === === Code generated for QAPI types ===
Used to generate the C types defined by a schema, along with The following files are created:
supporting code. The following files are created:
$(prefix)qapi-types.h - C types corresponding to types defined in $(prefix)qapi-types.h - C types corresponding to types defined in
the schema you pass in the schema
$(prefix)qapi-types.c - Cleanup functions for the above C types $(prefix)qapi-types.c - Cleanup functions for the above C types
The $(prefix) is an optional parameter used as a namespace to keep the The $(prefix) is an optional parameter used as a namespace to keep the
@ -943,8 +949,6 @@ created code.
Example: Example:
$ python scripts/qapi-types.py --output-dir="qapi-generated" \
--prefix="example-" example-schema.json
$ cat qapi-generated/example-qapi-types.h $ cat qapi-generated/example-qapi-types.h
[Uninteresting stuff omitted...] [Uninteresting stuff omitted...]
@ -1008,28 +1012,26 @@ Example:
visit_free(v); visit_free(v);
} }
=== scripts/qapi-visit.py === === Code generated for visiting QAPI types ===
Used to generate the visitor functions used to walk through and These are the visitor functions used to walk through and convert
convert between a native QAPI C data structure and some other format between a native QAPI C data structure and some other format (such as
(such as QObject); the generated functions are named visit_type_FOO() QObject); the generated functions are named visit_type_FOO() and
and visit_type_FOO_members(). visit_type_FOO_members().
The following files are generated: The following files are generated:
$(prefix)qapi-visit.c: visitor function for a particular C type, used $(prefix)qapi-visit.c: Visitor function for a particular C type, used
to automagically convert QObjects into the to automagically convert QObjects into the
corresponding C type and vice-versa, as well corresponding C type and vice-versa, as well
as for deallocating memory for an existing C as for deallocating memory for an existing C
type type
$(prefix)qapi-visit.h: declarations for previously mentioned visitor $(prefix)qapi-visit.h: Declarations for previously mentioned visitor
functions functions
Example: Example:
$ python scripts/qapi-visit.py --output-dir="qapi-generated"
--prefix="example-" example-schema.json
$ cat qapi-generated/example-qapi-visit.h $ cat qapi-generated/example-qapi-visit.h
[Uninteresting stuff omitted...] [Uninteresting stuff omitted...]
@ -1137,30 +1139,22 @@ Example:
error_propagate(errp, err); error_propagate(errp, err);
} }
=== scripts/qapi-commands.py === === Code generated for commands ===
Used to generate the marshaling/dispatch functions for the commands These are the marshaling/dispatch functions for the commands defined
defined in the schema. The generated code implements in the schema. The generated code provides qmp_marshal_COMMAND(), and
qmp_marshal_COMMAND() (registered automatically), and declares declares qmp_COMMAND() that the user must implement.
qmp_COMMAND() that the user must implement. The following files are
generated:
$(prefix)qmp-marshal.c: command marshal/dispatch functions for each The following files are generated:
QMP command defined in the schema. Functions
generated by qapi-visit.py are used to $(prefix)qmp-marshal.c: Command marshal/dispatch functions for each
convert QObjects received from the wire into QMP command defined in the schema
function parameters, and uses the same
visitor functions to convert native C return
values to QObjects from transmission back
over the wire.
$(prefix)qmp-commands.h: Function prototypes for the QMP commands $(prefix)qmp-commands.h: Function prototypes for the QMP commands
specified in the schema. specified in the schema
Example: Example:
$ python scripts/qapi-commands.py --output-dir="qapi-generated"
--prefix="example-" example-schema.json
$ cat qapi-generated/example-qmp-commands.h $ cat qapi-generated/example-qmp-commands.h
[Uninteresting stuff omitted...] [Uninteresting stuff omitted...]
@ -1242,20 +1236,20 @@ Example:
qmp_marshal_my_command, QCO_NO_OPTIONS); qmp_marshal_my_command, QCO_NO_OPTIONS);
} }
=== scripts/qapi-event.py === === Code generated for events ===
Used to generate the event-related C code defined by a schema, with This is the code related to events defined in the schema, providing
implementations for qapi_event_send_FOO(). The following files are qapi_event_send_EVENT().
created:
The following files are created:
$(prefix)qapi-event.h - Function prototypes for each event type, plus an $(prefix)qapi-event.h - Function prototypes for each event type, plus an
enumeration of all event names enumeration of all event names
$(prefix)qapi-event.c - Implementation of functions to send an event $(prefix)qapi-event.c - Implementation of functions to send an event
Example: Example:
$ python scripts/qapi-event.py --output-dir="qapi-generated"
--prefix="example-" example-schema.json
$ cat qapi-generated/example-qapi-event.h $ cat qapi-generated/example-qapi-event.h
[Uninteresting stuff omitted...] [Uninteresting stuff omitted...]
@ -1301,24 +1295,24 @@ Example:
QDECREF(qmp); QDECREF(qmp);
} }
const char *const example_QAPIEvent_lookup[] = { const QEnumLookup example_QAPIEvent_lookup = {
[EXAMPLE_QAPI_EVENT_MY_EVENT] = "MY_EVENT", .array = (const char *const[]) {
[EXAMPLE_QAPI_EVENT__MAX] = NULL, [EXAMPLE_QAPI_EVENT_MY_EVENT] = "MY_EVENT",
},
.size = EXAMPLE_QAPI_EVENT__MAX
}; };
=== scripts/qapi-introspect.py === === Code generated for introspection ===
Used to generate the introspection C code for a schema. The following The following files are created:
files are created:
$(prefix)qmp-introspect.c - Defines a string holding a JSON $(prefix)qmp-introspect.c - Defines a string holding a JSON
description of the schema. description of the schema
$(prefix)qmp-introspect.h - Declares the above string.
$(prefix)qmp-introspect.h - Declares the above string
Example: Example:
$ python scripts/qapi-introspect.py --output-dir="qapi-generated"
--prefix="example-" example-schema.json
$ cat qapi-generated/example-qmp-introspect.h $ cat qapi-generated/example-qmp-introspect.h
[Uninteresting stuff omitted...] [Uninteresting stuff omitted...]

View File

@ -951,7 +951,7 @@ EventInfoList *qmp_query_events(Error **errp)
* visit_type_SchemaInfoList() into a SchemaInfoList, then marshal it * visit_type_SchemaInfoList() into a SchemaInfoList, then marshal it
* to QObject with generated output marshallers, every time. Instead, * to QObject with generated output marshallers, every time. Instead,
* we do it in test-qobject-input-visitor.c, just to make sure * we do it in test-qobject-input-visitor.c, just to make sure
* qapi-introspect.py's output actually conforms to the schema. * qapi-gen.py's output actually conforms to the schema.
*/ */
static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data, static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
Error **errp) Error **errp)

View File

@ -72,7 +72,7 @@
'q_obj_CpuInfo-base' # CPU, visible through query-cpu 'q_obj_CpuInfo-base' # CPU, visible through query-cpu
] } } ] } }
# Documentation generated with qapi2texi.py is in source order, with # Documentation generated with qapi-gen.py is in source order, with
# included sub-schemas inserted at the first include directive # included sub-schemas inserted at the first include directive
# (subsequent include directives have no effect). To get a sane and # (subsequent include directives have no effect). To get a sane and
# stable order, it's best to include each sub-schema just once, or # stable order, it's best to include each sub-schema just once, or

41
scripts/qapi-gen.py Executable file
View File

@ -0,0 +1,41 @@
#!/usr/bin/env python
# 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.
import sys
from qapi.common import parse_command_line, QAPISchema
from qapi.types import gen_types
from qapi.visit import gen_visit
from qapi.commands import gen_commands
from qapi.events import gen_events
from qapi.introspect import gen_introspect
from qapi.doc import gen_doc
def main(argv):
(input_file, output_dir, prefix, opts) = \
parse_command_line('bu', ['builtins', 'unmask-non-abi-names'])
opt_builtins = False
opt_unmask = False
for o, a in opts:
if o in ('-b', '--builtins'):
opt_builtins = True
if o in ('-u', '--unmask-non-abi-names'):
opt_unmask = True
schema = QAPISchema(input_file)
gen_types(schema, output_dir, prefix, opt_builtins)
gen_visit(schema, output_dir, prefix, opt_builtins)
gen_commands(schema, output_dir, prefix)
gen_events(schema, output_dir, prefix)
gen_introspect(schema, output_dir, prefix, opt_unmask)
gen_doc(schema, output_dir, prefix)
if __name__ == '__main__':
main(sys.argv)

0
scripts/qapi/__init__.py Normal file
View File

View File

@ -13,7 +13,7 @@ This work is licensed under the terms of the GNU GPL, version 2.
See the COPYING file in the top-level directory. See the COPYING file in the top-level directory.
""" """
from qapi import * from qapi.common import *
def gen_command_decl(name, arg_type, boxed, ret_type): def gen_command_decl(name, arg_type, boxed, ret_type):
@ -255,11 +255,8 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
self._regy += gen_register_command(name, success_response) self._regy += gen_register_command(name, success_response)
def main(argv): def gen_commands(schema, output_dir, prefix):
(input_file, output_dir, do_c, do_h, prefix, opts) = parse_command_line()
blurb = ' * Schema-defined QAPI/QMP commands' blurb = ' * Schema-defined QAPI/QMP commands'
genc = QAPIGenC(blurb, __doc__) genc = QAPIGenC(blurb, __doc__)
genh = QAPIGenH(blurb, __doc__) genh = QAPIGenH(blurb, __doc__)
@ -288,17 +285,9 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
''', ''',
prefix=prefix, c_prefix=c_name(prefix, protect=False))) prefix=prefix, c_prefix=c_name(prefix, protect=False)))
schema = QAPISchema(input_file)
vis = QAPISchemaGenCommandVisitor(prefix) vis = QAPISchemaGenCommandVisitor(prefix)
schema.visit(vis) schema.visit(vis)
genc.add(vis.defn) genc.add(vis.defn)
genh.add(vis.decl) genh.add(vis.decl)
genc.write(output_dir, prefix + 'qmp-marshal.c')
if do_c: genh.write(output_dir, prefix + 'qmp-commands.h')
genc.write(output_dir, prefix + 'qmp-marshal.c')
if do_h:
genh.write(output_dir, prefix + 'qmp-commands.h')
if __name__ == '__main__':
main(sys.argv)

View File

@ -1932,17 +1932,15 @@ def parse_command_line(extra_options='', extra_long_options=[]):
try: try:
opts, args = getopt.gnu_getopt(sys.argv[1:], opts, args = getopt.gnu_getopt(sys.argv[1:],
'chp:o:' + extra_options, 'p:o:' + extra_options,
['source', 'header', 'prefix=', ['prefix=', 'output-dir=']
'output-dir='] + extra_long_options) + extra_long_options)
except getopt.GetoptError as err: except getopt.GetoptError as err:
print("%s: %s" % (sys.argv[0], str(err)), file=sys.stderr) print("%s: %s" % (sys.argv[0], str(err)), file=sys.stderr)
sys.exit(1) sys.exit(1)
output_dir = '' output_dir = ''
prefix = '' prefix = ''
do_c = False
do_h = False
extra_opts = [] extra_opts = []
for oa in opts: for oa in opts:
@ -1956,23 +1954,15 @@ def parse_command_line(extra_options='', extra_long_options=[]):
prefix = a prefix = a
elif o in ('-o', '--output-dir'): elif o in ('-o', '--output-dir'):
output_dir = a + '/' output_dir = a + '/'
elif o in ('-c', '--source'):
do_c = True
elif o in ('-h', '--header'):
do_h = True
else: else:
extra_opts.append(oa) extra_opts.append(oa)
if not do_c and not do_h:
do_c = True
do_h = True
if len(args) != 1: if len(args) != 1:
print("%s: need exactly one argument" % sys.argv[0], file=sys.stderr) print("%s: need exactly one argument" % sys.argv[0], file=sys.stderr)
sys.exit(1) sys.exit(1)
fname = args[0] fname = args[0]
return (fname, output_dir, do_c, do_h, prefix, extra_opts) return (fname, output_dir, prefix, extra_opts)
# #

29
scripts/qapi2texi.py → scripts/qapi/doc.py Executable file → Normal file
View File

@ -4,11 +4,10 @@
# This work is licensed under the terms of the GNU LGPL, version 2+. # This work is licensed under the terms of the GNU LGPL, version 2+.
# See the COPYING file in the top-level directory. # See the COPYING file in the top-level directory.
"""This script produces the documentation of a qapi schema in texinfo format""" """This script produces the documentation of a qapi schema in texinfo format"""
from __future__ import print_function from __future__ import print_function
import re import re
import sys import qapi.common
import qapi
MSG_FMT = """ MSG_FMT = """
@deftypefn {type} {{}} {name} @deftypefn {type} {{}} {name}
@ -197,7 +196,7 @@ def texi_entity(doc, what, base=None, variants=None,
+ texi_sections(doc)) + texi_sections(doc))
class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor): class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor):
def __init__(self): def __init__(self):
self.out = None self.out = None
self.cur_doc = None self.cur_doc = None
@ -272,20 +271,8 @@ def texi_schema(schema):
return gen.out return gen.out
def main(argv): def gen_doc(schema, output_dir, prefix):
"""Takes schema argument, prints result to stdout""" if qapi.common.doc_required:
if len(argv) != 2: gen = qapi.common.QAPIGenDoc()
print("%s: need exactly 1 argument: SCHEMA" % argv[0], file=sys.stderr) gen.add(texi_schema(schema))
sys.exit(1) gen.write(output_dir, prefix + 'qapi-doc.texi')
schema = qapi.QAPISchema(argv[1])
if not qapi.doc_required:
print("%s: need pragma 'doc-required' "
"to generate documentation" % argv[0], file=sys.stderr)
sys.exit(1)
print('@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n')
print(texi_schema(schema), end='')
if __name__ == '__main__':
main(sys.argv)

View File

@ -12,7 +12,7 @@ This work is licensed under the terms of the GNU GPL, version 2.
See the COPYING file in the top-level directory. See the COPYING file in the top-level directory.
""" """
from qapi import * from qapi.common import *
def build_event_send_proto(name, arg_type, boxed): def build_event_send_proto(name, arg_type, boxed):
@ -171,11 +171,8 @@ class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
self._event_names.append(name) self._event_names.append(name)
def main(argv): def gen_events(schema, output_dir, prefix):
(input_file, output_dir, do_c, do_h, prefix, dummy) = parse_command_line()
blurb = ' * Schema-defined QAPI/QMP events' blurb = ' * Schema-defined QAPI/QMP events'
genc = QAPIGenC(blurb, __doc__) genc = QAPIGenC(blurb, __doc__)
genh = QAPIGenH(blurb, __doc__) genh = QAPIGenH(blurb, __doc__)
@ -199,17 +196,9 @@ def main(argv):
''', ''',
prefix=prefix)) prefix=prefix))
schema = QAPISchema(input_file)
vis = QAPISchemaGenEventVisitor(prefix) vis = QAPISchemaGenEventVisitor(prefix)
schema.visit(vis) schema.visit(vis)
genc.add(vis.defn) genc.add(vis.defn)
genh.add(vis.decl) genh.add(vis.decl)
genc.write(output_dir, prefix + 'qapi-event.c')
if do_c: genh.write(output_dir, prefix + 'qapi-event.h')
genc.write(output_dir, prefix + 'qapi-event.c')
if do_h:
genh.write(output_dir, prefix + 'qapi-event.h')
if __name__ == '__main__':
main(sys.argv)

View File

@ -10,7 +10,7 @@ This work is licensed under the terms of the GNU GPL, version 2.
See the COPYING file in the top-level directory. See the COPYING file in the top-level directory.
""" """
from qapi import * from qapi.common import *
# Caveman's json.dumps() replacement (we're stuck at Python 2.4) # Caveman's json.dumps() replacement (we're stuck at Python 2.4)
@ -168,20 +168,8 @@ const char %(c_name)s[] = %(c_string)s;
self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)}) self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)})
def main(argv): def gen_introspect(schema, output_dir, prefix, opt_unmask):
# 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
blurb = ' * QAPI/QMP schema introspection' blurb = ' * QAPI/QMP schema introspection'
genc = QAPIGenC(blurb, __doc__) genc = QAPIGenC(blurb, __doc__)
genh = QAPIGenH(blurb, __doc__) genh = QAPIGenH(blurb, __doc__)
@ -192,17 +180,9 @@ def main(argv):
''', ''',
prefix=prefix)) prefix=prefix))
schema = QAPISchema(input_file)
vis = QAPISchemaGenIntrospectVisitor(prefix, opt_unmask) vis = QAPISchemaGenIntrospectVisitor(prefix, opt_unmask)
schema.visit(vis) schema.visit(vis)
genc.add(vis.defn) genc.add(vis.defn)
genh.add(vis.decl) genh.add(vis.decl)
genc.write(output_dir, prefix + 'qmp-introspect.c')
if do_c: genh.write(output_dir, prefix + 'qmp-introspect.h')
genc.write(output_dir, prefix + 'qmp-introspect.c')
if do_h:
genh.write(output_dir, prefix + 'qmp-introspect.h')
if __name__ == '__main__':
main(sys.argv)

View File

@ -13,7 +13,7 @@ This work is licensed under the terms of the GNU GPL, version 2.
# See the COPYING file in the top-level directory. # See the COPYING file in the top-level directory.
""" """
from qapi import * from qapi.common import *
# variants must be emitted before their container; track what has already # variants must be emitted before their container; track what has already
@ -241,22 +241,8 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
self._gen_type_cleanup(name) self._gen_type_cleanup(name)
def main(argv): def gen_types(schema, output_dir, prefix, opt_builtins):
# If you link code generated from multiple schemata, you want only one
# instance of the code for built-in types. Generate it only when
# opt_builtins, enabled by command line option -b. See also
# QAPISchemaGenTypeVisitor.visit_end().
opt_builtins = False
(input_file, output_dir, do_c, do_h, prefix, opts) = \
parse_command_line('b', ['builtins'])
for o, a in opts:
if o in ('-b', '--builtins'):
opt_builtins = True
blurb = ' * Schema-defined QAPI types' blurb = ' * Schema-defined QAPI types'
genc = QAPIGenC(blurb, __doc__) genc = QAPIGenC(blurb, __doc__)
genh = QAPIGenH(blurb, __doc__) genh = QAPIGenH(blurb, __doc__)
@ -272,17 +258,9 @@ def main(argv):
#include "qapi/util.h" #include "qapi/util.h"
''')) '''))
schema = QAPISchema(input_file)
vis = QAPISchemaGenTypeVisitor(opt_builtins) vis = QAPISchemaGenTypeVisitor(opt_builtins)
schema.visit(vis) schema.visit(vis)
genc.add(vis.defn) genc.add(vis.defn)
genh.add(vis.decl) genh.add(vis.decl)
genc.write(output_dir, prefix + 'qapi-types.c')
if do_c: genh.write(output_dir, prefix + 'qapi-types.h')
genc.write(output_dir, prefix + 'qapi-types.c')
if do_h:
genh.write(output_dir, prefix + 'qapi-types.h')
if __name__ == '__main__':
main(sys.argv)

View File

@ -13,7 +13,7 @@ This work is licensed under the terms of the GNU GPL, version 2.
See the COPYING file in the top-level directory. See the COPYING file in the top-level directory.
""" """
from qapi import * from qapi.common import *
def gen_visit_decl(name, scalar=False): def gen_visit_decl(name, scalar=False):
@ -324,22 +324,8 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
self.defn += gen_visit_alternate(name, variants) self.defn += gen_visit_alternate(name, variants)
def main(argv): def gen_visit(schema, output_dir, prefix, opt_builtins):
# If you link code generated from multiple schemata, you want only one
# instance of the code for built-in types. Generate it only when
# opt_builtins, enabled by command line option -b. See also
# QAPISchemaGenVisitVisitor.visit_end().
opt_builtins = False
(input_file, output_dir, do_c, do_h, prefix, opts) = \
parse_command_line('b', ['builtins'])
for o, a in opts:
if o in ('-b', '--builtins'):
opt_builtins = True
blurb = ' * Schema-defined QAPI visitors' blurb = ' * Schema-defined QAPI visitors'
genc = QAPIGenC(blurb, __doc__) genc = QAPIGenC(blurb, __doc__)
genh = QAPIGenH(blurb, __doc__) genh = QAPIGenH(blurb, __doc__)
@ -359,17 +345,9 @@ def main(argv):
''', ''',
prefix=prefix)) prefix=prefix))
schema = QAPISchema(input_file)
vis = QAPISchemaGenVisitVisitor(opt_builtins) vis = QAPISchemaGenVisitVisitor(opt_builtins)
schema.visit(vis) schema.visit(vis)
genc.add(vis.defn) genc.add(vis.defn)
genh.add(vis.decl) genh.add(vis.decl)
genc.write(output_dir, prefix + 'qapi-visit.c')
if do_c: genh.write(output_dir, prefix + 'qapi-visit.h')
genc.write(output_dir, prefix + 'qapi-visit.c')
if do_h:
genh.write(output_dir, prefix + 'qapi-visit.h')
if __name__ == '__main__':
main(sys.argv)

View File

@ -23,7 +23,16 @@ check-help:
ifneq ($(wildcard config-host.mak),) ifneq ($(wildcard config-host.mak),)
export SRC_PATH export SRC_PATH
qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py # TODO don't duplicate $(SRC_PATH)/Makefile's qapi-py here
qapi-py = $(SRC_PATH)/scripts/qapi/commands.py \
$(SRC_PATH)/scripts/qapi/events.py \
$(SRC_PATH)/scripts/qapi/introspect.py \
$(SRC_PATH)/scripts/qapi/types.py \
$(SRC_PATH)/scripts/qapi/visit.py \
$(SRC_PATH)/scripts/qapi/common.py \
$(SRC_PATH)/scripts/qapi/doc.py \
$(SRC_PATH)/scripts/ordereddict.py \
$(SRC_PATH)/scripts/qapi-gen.py
# Get the list of all supported sysemu targets # Get the list of all supported sysemu targets
SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \ SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \
@ -649,34 +658,24 @@ tests/test-logging$(EXESUF): tests/test-logging.o $(test-util-obj-y)
tests/test-replication$(EXESUF): tests/test-replication.o $(test-util-obj-y) \ tests/test-replication$(EXESUF): tests/test-replication.o $(test-util-obj-y) \
$(test-block-obj-y) $(test-block-obj-y)
tests/test-qapi-types.c tests/test-qapi-types.h :\ tests/test-qapi-types.c tests/test-qapi-types.h \
$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py) tests/test-qapi-visit.c tests/test-qapi-visit.h \
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py \ tests/test-qmp-commands.h tests/test-qmp-marshal.c \
$(gen-out-type) -o tests -p "test-" $<, \ tests/test-qapi-event.c tests/test-qapi-event.h \
"GEN","$@") tests/test-qmp-introspect.c tests/test-qmp-introspect.h: \
tests/test-qapi-visit.c tests/test-qapi-visit.h :\ tests/test-qapi-gen-timestamp ;
$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py) tests/test-qapi-gen-timestamp: $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(qapi-py)
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py \ $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
$(gen-out-type) -o tests -p "test-" $<, \ -o tests -p "test-" $<, \
"GEN","$@") "GEN","$(@:%-timestamp=%)")
tests/test-qmp-commands.h tests/test-qmp-marshal.c :\ @>$@
$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \
$(gen-out-type) -o tests -p "test-" $<, \
"GEN","$@")
tests/test-qapi-event.c tests/test-qapi-event.h :\
$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-event.py $(qapi-py)
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py \
$(gen-out-type) -o tests -p "test-" $<, \
"GEN","$@")
tests/test-qmp-introspect.c tests/test-qmp-introspect.h :\
$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py)
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-introspect.py \
$(gen-out-type) -o tests -p "test-" $<, \
"GEN","$@")
tests/qapi-schema/doc-good.test.texi: $(SRC_PATH)/tests/qapi-schema/doc-good.json $(SRC_PATH)/scripts/qapi2texi.py $(qapi-py) tests/qapi-schema/doc-good.test.texi: $(SRC_PATH)/tests/qapi-schema/doc-good.json $(qapi-py)
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@") $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
-o tests/qapi-schema -p "doc-good-" $<, \
"GEN","$@")
@mv tests/qapi-schema/doc-good-qapi-doc.texi $@
@rm -f tests/qapi-schema/doc-good-qapi-*.[ch] tests/qapi-schema/doc-good-qmp-*.[ch]
tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y)
tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y)
@ -954,6 +953,7 @@ check-clean:
$(MAKE) -C tests/tcg clean $(MAKE) -C tests/tcg clean
rm -rf $(check-unit-y) tests/*.o $(QEMU_IOTESTS_HELPERS-y) rm -rf $(check-unit-y) tests/*.o $(QEMU_IOTESTS_HELPERS-y)
rm -rf $(sort $(foreach target,$(SYSEMU_TARGET_LIST), $(check-qtest-$(target)-y)) $(check-qtest-generic-y)) rm -rf $(sort $(foreach target,$(SYSEMU_TARGET_LIST), $(check-qtest-$(target)-y)) $(check-qtest-generic-y))
rm -f tests/test-qapi-gen-timestamp
clean: check-clean clean: check-clean

View File

@ -11,10 +11,8 @@
# #
from __future__ import print_function from __future__ import print_function
from qapi import *
from pprint import pprint
import os
import sys import sys
from qapi.common import QAPISchema, QAPISchemaVisitor
class QAPISchemaTestVisitor(QAPISchemaVisitor): class QAPISchemaTestVisitor(QAPISchemaVisitor):