diff mbox

[v2,22/29] qapi: Generate separate .h, .c for each module

Message ID 20180211093607.27351-23-armbru@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Markus Armbruster Feb. 11, 2018, 9:36 a.m. UTC
Our qapi-schema.json is composed of modules connected by include
directives, but the generated code is monolithic all the same: one
qapi-types.h with all the types, one qapi-visit.h with all the
visitors, and so forth.  These monolithic headers get included all
over the place.  In my "build everything" tree, adding a QAPI type
recompiles about 4800 out of 5100 objects.

We wouldn't write such monolithic headers by hand.  It stands to
reason that we shouldn't generate them, either.

Split up generated qapi-types.h to mirror the schema's modular
structure: one header per module.  Name the main module's header
qapi-types.h, and sub-module D/B.json's header D/qapi-types-B.h.

Mirror the schema's includes in the headers, so that qapi-types.h gets
you everything exactly as before.  If you need less, you can include
one or more of the sub-module headers.  To be exploited shortly.

Split up qapi-types.c, qapi-visit.h, qapi-visit.c, qmp-commands.h,
qmp-commands.c, qapi-event.h, qapi-event.c the same way.
qmp-introspect.h, qmp-introspect.c and qapi.texi remain monolithic.

The split of qmp-commands.c duplicates static helper function
qmp_marshal_output_str() in qapi-commands-char.c and
qapi-commands-misc.c.  This happens when commands returning the same
type occur in multiple modules.  Not worth avoiding.

Since I'm going to rename qapi-event.[ch] to qapi-events.[ch], and
qmp-commands.[ch] to qapi-commands.[ch], name the shards that way
already, to reduce churn.  This requires temporary hacks in
commands.py and events.py.  They'll go away with the rename.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 .gitignore               |  60 ++++++++++++++++++++++++
 Makefile                 | 120 +++++++++++++++++++++++++++++++++++++++++++++++
 Makefile.objs            |  65 ++++++++++++++++++++++++-
 scripts/qapi/commands.py |  35 +++++++++-----
 scripts/qapi/common.py   |  21 +++++++--
 scripts/qapi/events.py   |  19 ++++++--
 6 files changed, 300 insertions(+), 20 deletions(-)

Comments

Eric Blake Feb. 12, 2018, 10:06 p.m. UTC | #1
On 02/11/2018 03:36 AM, Markus Armbruster wrote:
> Our qapi-schema.json is composed of modules connected by include
> directives, but the generated code is monolithic all the same: one
> qapi-types.h with all the types, one qapi-visit.h with all the
> visitors, and so forth.  These monolithic headers get included all
> over the place.  In my "build everything" tree, adding a QAPI type
> recompiles about 4800 out of 5100 objects.
> 
> We wouldn't write such monolithic headers by hand.  It stands to
> reason that we shouldn't generate them, either.
> 
> Split up generated qapi-types.h to mirror the schema's modular
> structure: one header per module.  Name the main module's header
> qapi-types.h, and sub-module D/B.json's header D/qapi-types-B.h.
> 
> Mirror the schema's includes in the headers, so that qapi-types.h gets
> you everything exactly as before.  If you need less, you can include
> one or more of the sub-module headers.  To be exploited shortly.
> 
> Split up qapi-types.c, qapi-visit.h, qapi-visit.c, qmp-commands.h,
> qmp-commands.c, qapi-event.h, qapi-event.c the same way.
> qmp-introspect.h, qmp-introspect.c and qapi.texi remain monolithic.

Make sense.

> 
> The split of qmp-commands.c duplicates static helper function
> qmp_marshal_output_str() in qapi-commands-char.c and
> qapi-commands-misc.c.  This happens when commands returning the same
> type occur in multiple modules.  Not worth avoiding.

As long as it is static, and neither .c file includes the other, we're 
fine (gdb may have a harder time figuring out which copy to put a 
breakpoint on, but that's not too terrible).

> 
> Since I'm going to rename qapi-event.[ch] to qapi-events.[ch], and
> qmp-commands.[ch] to qapi-commands.[ch], name the shards that way
> already, to reduce churn.  This requires temporary hacks in
> commands.py and events.py.  They'll go away with the rename.

The planned rename makes sense; prepping now is fine.

> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>   .gitignore               |  60 ++++++++++++++++++++++++
>   Makefile                 | 120 +++++++++++++++++++++++++++++++++++++++++++++++
>   Makefile.objs            |  65 ++++++++++++++++++++++++-
>   scripts/qapi/commands.py |  35 +++++++++-----
>   scripts/qapi/common.py   |  21 +++++++--
>   scripts/qapi/events.py   |  19 ++++++--
>   6 files changed, 300 insertions(+), 20 deletions(-)
> 
> diff --git a/.gitignore b/.gitignore
> index 9477a08b6b..42c57998fd 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -31,7 +31,67 @@
>   /qapi-gen-timestamp
>   /qapi-builtin-types.[ch]
>   /qapi-builtin-visit.[ch]
> +/qapi/qapi-commands-block-core.[ch]
> +/qapi/qapi-commands-block.[ch]

Is it worth using a glob instead of specific patterns, as in:

/qapi/qapi-commands-*.[ch]

Maybe being precise doesn't hurt, if we aren't adding sub-modules all 
that often; but being generous with a glob makes it less likely that a 
future module addition will forget to update .gitignore.


> +++ b/Makefile
> @@ -92,10 +92,70 @@ include $(SRC_PATH)/rules.mak
>   GENERATED_FILES = qemu-version.h config-host.h qemu-options.def
>   GENERATED_FILES += qapi-builtin-types.h qapi-builtin-types.c
>   GENERATED_FILES += qapi-types.h qapi-types.c
> +GENERATED_FILES += qapi/qapi-types-block-core.h qapi/qapi-types-block-core.c

For the makefile, I'd suggest using an intermediate list of module 
names, and then using make string operations to expand it into larger 
lists, to cut down on the repetition.  Something like (untested):

QAPI_MODULES = block-core block char common ...
GENERATED_FILES += $(addprefix qapi/qapi-types-,$(addsuffix 
.c,$(QAPI_MODULES))
GENERATED_FILES += $(addprefix qapi/qapi-types-,$(addsuffix 
.h,$(QAPI_MODULES))


> @@ -525,10 +585,70 @@ qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
>   
>   qapi-builtin-types.c qapi-builtin-types.h \
>   qapi-types.c qapi-types.h \
> +qapi/qapi-types-block-core.c qapi/qapi-types-block-core.h \
> +qapi/qapi-types-block.c qapi/qapi-types-block.h \

And repeating the list of filenames; again, an intermediate variable 
would let you do:

QAPI_GENERATED_FILES = ...
GENERATED_FILES += $(QAPI_GENERATED_FILES)

$(QAPI_GENERATED_FILES): ...

>   qmp-introspect.h qmp-introspect.c \
>   qapi-doc.texi: \
>   qapi-gen-timestamp ;

Not only does it cut down on the repetition, but it will make adding a 
future module easier.

> diff --git a/Makefile.objs b/Makefile.objs
> index 2813e984fd..7a55d45669 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -3,8 +3,56 @@
>   stub-obj-y = stubs/ crypto/
>   util-obj-y = util/ qobject/ qapi/
>   util-obj-y += qapi-builtin-types.o
> +util-obj-y += qapi-types.o
> +util-obj-y += qapi/qapi-types-block-core.o

Perhaps this can also exploit make text manipulation?

Otherwise looks good.
Markus Armbruster Feb. 23, 2018, 5:41 p.m. UTC | #2
Eric Blake <eblake@redhat.com> writes:

> On 02/11/2018 03:36 AM, Markus Armbruster wrote:
>> Our qapi-schema.json is composed of modules connected by include
>> directives, but the generated code is monolithic all the same: one
>> qapi-types.h with all the types, one qapi-visit.h with all the
>> visitors, and so forth.  These monolithic headers get included all
>> over the place.  In my "build everything" tree, adding a QAPI type
>> recompiles about 4800 out of 5100 objects.
>>
>> We wouldn't write such monolithic headers by hand.  It stands to
>> reason that we shouldn't generate them, either.
>>
>> Split up generated qapi-types.h to mirror the schema's modular
>> structure: one header per module.  Name the main module's header
>> qapi-types.h, and sub-module D/B.json's header D/qapi-types-B.h.
>>
>> Mirror the schema's includes in the headers, so that qapi-types.h gets
>> you everything exactly as before.  If you need less, you can include
>> one or more of the sub-module headers.  To be exploited shortly.
>>
>> Split up qapi-types.c, qapi-visit.h, qapi-visit.c, qmp-commands.h,
>> qmp-commands.c, qapi-event.h, qapi-event.c the same way.
>> qmp-introspect.h, qmp-introspect.c and qapi.texi remain monolithic.
>
> Make sense.
>
>>
>> The split of qmp-commands.c duplicates static helper function
>> qmp_marshal_output_str() in qapi-commands-char.c and
>> qapi-commands-misc.c.  This happens when commands returning the same
>> type occur in multiple modules.  Not worth avoiding.
>
> As long as it is static, and neither .c file includes the other, we're
> fine (gdb may have a harder time figuring out which copy to put a
> breakpoint on, but that's not too terrible).
>
>>
>> Since I'm going to rename qapi-event.[ch] to qapi-events.[ch], and
>> qmp-commands.[ch] to qapi-commands.[ch], name the shards that way
>> already, to reduce churn.  This requires temporary hacks in
>> commands.py and events.py.  They'll go away with the rename.
>
> The planned rename makes sense; prepping now is fine.
>
>>
>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> ---
>>   .gitignore               |  60 ++++++++++++++++++++++++
>>   Makefile                 | 120 +++++++++++++++++++++++++++++++++++++++++++++++
>>   Makefile.objs            |  65 ++++++++++++++++++++++++-
>>   scripts/qapi/commands.py |  35 +++++++++-----
>>   scripts/qapi/common.py   |  21 +++++++--
>>   scripts/qapi/events.py   |  19 ++++++--
>>   6 files changed, 300 insertions(+), 20 deletions(-)
>>
>> diff --git a/.gitignore b/.gitignore
>> index 9477a08b6b..42c57998fd 100644
>> --- a/.gitignore
>> +++ b/.gitignore
>> @@ -31,7 +31,67 @@
>>   /qapi-gen-timestamp
>>   /qapi-builtin-types.[ch]
>>   /qapi-builtin-visit.[ch]
>> +/qapi/qapi-commands-block-core.[ch]
>> +/qapi/qapi-commands-block.[ch]
>
> Is it worth using a glob instead of specific patterns, as in:
>
> /qapi/qapi-commands-*.[ch]
>
> Maybe being precise doesn't hurt, if we aren't adding sub-modules all
> that often; but being generous with a glob makes it less likely that a
> future module addition will forget to update .gitignore.

We should simply ditch building in-tree.  I wanted to post patches for
that for quite some time.

>> +++ b/Makefile
>> @@ -92,10 +92,70 @@ include $(SRC_PATH)/rules.mak
>>   GENERATED_FILES = qemu-version.h config-host.h qemu-options.def
>>   GENERATED_FILES += qapi-builtin-types.h qapi-builtin-types.c
>>   GENERATED_FILES += qapi-types.h qapi-types.c
>> +GENERATED_FILES += qapi/qapi-types-block-core.h qapi/qapi-types-block-core.c
>
> For the makefile, I'd suggest using an intermediate list of module
> names, and then using make string operations to expand it into larger
> lists, to cut down on the repetition.  Something like (untested):
>
> QAPI_MODULES = block-core block char common ...
> GENERATED_FILES += $(addprefix qapi/qapi-types-,$(addsuffix
> .c,$(QAPI_MODULES))
> GENERATED_FILES += $(addprefix qapi/qapi-types-,$(addsuffix
> .h,$(QAPI_MODULES))

I did it the stupid way to save time.  Improvements welcome :)

>> @@ -525,10 +585,70 @@ qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
>>     qapi-builtin-types.c qapi-builtin-types.h \
>>   qapi-types.c qapi-types.h \
>> +qapi/qapi-types-block-core.c qapi/qapi-types-block-core.h \
>> +qapi/qapi-types-block.c qapi/qapi-types-block.h \
>
> And repeating the list of filenames; again, an intermediate variable
> would let you do:
>
> QAPI_GENERATED_FILES = ...
> GENERATED_FILES += $(QAPI_GENERATED_FILES)
>
> $(QAPI_GENERATED_FILES): ...
>
>>   qmp-introspect.h qmp-introspect.c \
>>   qapi-doc.texi: \
>>   qapi-gen-timestamp ;
>
> Not only does it cut down on the repetition, but it will make adding a
> future module easier.
>
>> diff --git a/Makefile.objs b/Makefile.objs
>> index 2813e984fd..7a55d45669 100644
>> --- a/Makefile.objs
>> +++ b/Makefile.objs
>> @@ -3,8 +3,56 @@
>>   stub-obj-y = stubs/ crypto/
>>   util-obj-y = util/ qobject/ qapi/
>>   util-obj-y += qapi-builtin-types.o
>> +util-obj-y += qapi-types.o
>> +util-obj-y += qapi/qapi-types-block-core.o
>
> Perhaps this can also exploit make text manipulation?
>
> Otherwise looks good.

Thanks!
Eric Blake Feb. 26, 2018, 10:39 p.m. UTC | #3
On 02/23/2018 11:41 AM, Markus Armbruster wrote:


>>> +++ b/Makefile
>>> @@ -92,10 +92,70 @@ include $(SRC_PATH)/rules.mak
>>>    GENERATED_FILES = qemu-version.h config-host.h qemu-options.def
>>>    GENERATED_FILES += qapi-builtin-types.h qapi-builtin-types.c
>>>    GENERATED_FILES += qapi-types.h qapi-types.c
>>> +GENERATED_FILES += qapi/qapi-types-block-core.h qapi/qapi-types-block-core.c
>>
>> For the makefile, I'd suggest using an intermediate list of module
>> names, and then using make string operations to expand it into larger
>> lists, to cut down on the repetition.  Something like (untested):
>>
>> QAPI_MODULES = block-core block char common ...
>> GENERATED_FILES += $(addprefix qapi/qapi-types-,$(addsuffix
>> .c,$(QAPI_MODULES))
>> GENERATED_FILES += $(addprefix qapi/qapi-types-,$(addsuffix
>> .h,$(QAPI_MODULES))
> 
> I did it the stupid way to save time.  Improvements welcome :)


>>> +++ b/Makefile.objs
>>> @@ -3,8 +3,56 @@
>>>    stub-obj-y = stubs/ crypto/
>>>    util-obj-y = util/ qobject/ qapi/
>>>    util-obj-y += qapi-builtin-types.o
>>> +util-obj-y += qapi-types.o
>>> +util-obj-y += qapi/qapi-types-block-core.o
>>
>> Perhaps this can also exploit make text manipulation?
>>
>> Otherwise looks good.
> 
> Thanks!

At this point, I'll take your patch as-is, and worry about improvements 
as followups.

Reviewed-by: Eric Blake <eblake@redhat.com>
diff mbox

Patch

diff --git a/.gitignore b/.gitignore
index 9477a08b6b..42c57998fd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,7 +31,67 @@ 
 /qapi-gen-timestamp
 /qapi-builtin-types.[ch]
 /qapi-builtin-visit.[ch]
+/qapi/qapi-commands-block-core.[ch]
+/qapi/qapi-commands-block.[ch]
+/qapi/qapi-commands-char.[ch]
+/qapi/qapi-commands-common.[ch]
+/qapi/qapi-commands-crypto.[ch]
+/qapi/qapi-commands-introspect.[ch]
+/qapi/qapi-commands-migration.[ch]
+/qapi/qapi-commands-net.[ch]
+/qapi/qapi-commands-rocker.[ch]
+/qapi/qapi-commands-run-state.[ch]
+/qapi/qapi-commands-sockets.[ch]
+/qapi/qapi-commands-tpm.[ch]
+/qapi/qapi-commands-trace.[ch]
+/qapi/qapi-commands-transaction.[ch]
+/qapi/qapi-commands-ui.[ch]
+/qapi/qapi-events-block-core.[ch]
+/qapi/qapi-events-block.[ch]
+/qapi/qapi-events-char.[ch]
+/qapi/qapi-events-common.[ch]
+/qapi/qapi-events-crypto.[ch]
+/qapi/qapi-events-introspect.[ch]
+/qapi/qapi-events-migration.[ch]
+/qapi/qapi-events-net.[ch]
+/qapi/qapi-events-rocker.[ch]
+/qapi/qapi-events-run-state.[ch]
+/qapi/qapi-events-sockets.[ch]
+/qapi/qapi-events-tpm.[ch]
+/qapi/qapi-events-trace.[ch]
+/qapi/qapi-events-transaction.[ch]
+/qapi/qapi-events-ui.[ch]
+/qapi/qapi-types-block-core.[ch]
+/qapi/qapi-types-block.[ch]
+/qapi/qapi-types-char.[ch]
+/qapi/qapi-types-common.[ch]
+/qapi/qapi-types-crypto.[ch]
+/qapi/qapi-types-introspect.[ch]
+/qapi/qapi-types-migration.[ch]
+/qapi/qapi-types-net.[ch]
+/qapi/qapi-types-rocker.[ch]
+/qapi/qapi-types-run-state.[ch]
+/qapi/qapi-types-sockets.[ch]
+/qapi/qapi-types-tpm.[ch]
+/qapi/qapi-types-trace.[ch]
+/qapi/qapi-types-transaction.[ch]
+/qapi/qapi-types-ui.[ch]
 /qapi-types.[ch]
+/qapi/qapi-visit-block-core.[ch]
+/qapi/qapi-visit-block.[ch]
+/qapi/qapi-visit-char.[ch]
+/qapi/qapi-visit-common.[ch]
+/qapi/qapi-visit-crypto.[ch]
+/qapi/qapi-visit-introspect.[ch]
+/qapi/qapi-visit-migration.[ch]
+/qapi/qapi-visit-net.[ch]
+/qapi/qapi-visit-rocker.[ch]
+/qapi/qapi-visit-run-state.[ch]
+/qapi/qapi-visit-sockets.[ch]
+/qapi/qapi-visit-tpm.[ch]
+/qapi/qapi-visit-trace.[ch]
+/qapi/qapi-visit-transaction.[ch]
+/qapi/qapi-visit-ui.[ch]
 /qapi-visit.[ch]
 /qapi-event.[ch]
 /qapi-doc.texi
diff --git a/Makefile b/Makefile
index 60ddc9c945..ac9a7627a2 100644
--- a/Makefile
+++ b/Makefile
@@ -92,10 +92,70 @@  include $(SRC_PATH)/rules.mak
 GENERATED_FILES = qemu-version.h config-host.h qemu-options.def
 GENERATED_FILES += qapi-builtin-types.h qapi-builtin-types.c
 GENERATED_FILES += qapi-types.h qapi-types.c
+GENERATED_FILES += qapi/qapi-types-block-core.h qapi/qapi-types-block-core.c
+GENERATED_FILES += qapi/qapi-types-block.h qapi/qapi-types-block.c
+GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c
+GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c
+GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c
+GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c
+GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c
+GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c
+GENERATED_FILES += qapi/qapi-types-rocker.h qapi/qapi-types-rocker.c
+GENERATED_FILES += qapi/qapi-types-run-state.h qapi/qapi-types-run-state.c
+GENERATED_FILES += qapi/qapi-types-sockets.h qapi/qapi-types-sockets.c
+GENERATED_FILES += qapi/qapi-types-tpm.h qapi/qapi-types-tpm.c
+GENERATED_FILES += qapi/qapi-types-trace.h qapi/qapi-types-trace.c
+GENERATED_FILES += qapi/qapi-types-transaction.h qapi/qapi-types-transaction.c
+GENERATED_FILES += qapi/qapi-types-ui.h qapi/qapi-types-ui.c
 GENERATED_FILES += qapi-builtin-visit.h qapi-builtin-visit.c
 GENERATED_FILES += qapi-visit.h qapi-visit.c
+GENERATED_FILES += qapi/qapi-visit-block-core.h qapi/qapi-visit-block-core.c
+GENERATED_FILES += qapi/qapi-visit-block.h qapi/qapi-visit-block.c
+GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c
+GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c
+GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c
+GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c
+GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c
+GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c
+GENERATED_FILES += qapi/qapi-visit-rocker.h qapi/qapi-visit-rocker.c
+GENERATED_FILES += qapi/qapi-visit-run-state.h qapi/qapi-visit-run-state.c
+GENERATED_FILES += qapi/qapi-visit-sockets.h qapi/qapi-visit-sockets.c
+GENERATED_FILES += qapi/qapi-visit-tpm.h qapi/qapi-visit-tpm.c
+GENERATED_FILES += qapi/qapi-visit-trace.h qapi/qapi-visit-trace.c
+GENERATED_FILES += qapi/qapi-visit-transaction.h qapi/qapi-visit-transaction.c
+GENERATED_FILES += qapi/qapi-visit-ui.h qapi/qapi-visit-ui.c
 GENERATED_FILES += qmp-commands.h qmp-commands.c
+GENERATED_FILES += qapi/qapi-commands-block-core.h qapi/qapi-commands-block-core.c
+GENERATED_FILES += qapi/qapi-commands-block.h qapi/qapi-commands-block.c
+GENERATED_FILES += qapi/qapi-commands-char.h qapi/qapi-commands-char.c
+GENERATED_FILES += qapi/qapi-commands-common.h qapi/qapi-commands-common.c
+GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.c
+GENERATED_FILES += qapi/qapi-commands-introspect.h qapi/qapi-commands-introspect.c
+GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c
+GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c
+GENERATED_FILES += qapi/qapi-commands-rocker.h qapi/qapi-commands-rocker.c
+GENERATED_FILES += qapi/qapi-commands-run-state.h qapi/qapi-commands-run-state.c
+GENERATED_FILES += qapi/qapi-commands-sockets.h qapi/qapi-commands-sockets.c
+GENERATED_FILES += qapi/qapi-commands-tpm.h qapi/qapi-commands-tpm.c
+GENERATED_FILES += qapi/qapi-commands-trace.h qapi/qapi-commands-trace.c
+GENERATED_FILES += qapi/qapi-commands-transaction.h qapi/qapi-commands-transaction.c
+GENERATED_FILES += qapi/qapi-commands-ui.h qapi/qapi-commands-ui.c
 GENERATED_FILES += qapi-event.h qapi-event.c
+GENERATED_FILES += qapi/qapi-events-block-core.h qapi/qapi-events-block-core.c
+GENERATED_FILES += qapi/qapi-events-block.h qapi/qapi-events-block.c
+GENERATED_FILES += qapi/qapi-events-char.h qapi/qapi-events-char.c
+GENERATED_FILES += qapi/qapi-events-common.h qapi/qapi-events-common.c
+GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.c
+GENERATED_FILES += qapi/qapi-events-introspect.h qapi/qapi-events-introspect.c
+GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c
+GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c
+GENERATED_FILES += qapi/qapi-events-rocker.h qapi/qapi-events-rocker.c
+GENERATED_FILES += qapi/qapi-events-run-state.h qapi/qapi-events-run-state.c
+GENERATED_FILES += qapi/qapi-events-sockets.h qapi/qapi-events-sockets.c
+GENERATED_FILES += qapi/qapi-events-tpm.h qapi/qapi-events-tpm.c
+GENERATED_FILES += qapi/qapi-events-trace.h qapi/qapi-events-trace.c
+GENERATED_FILES += qapi/qapi-events-transaction.h qapi/qapi-events-transaction.c
+GENERATED_FILES += qapi/qapi-events-ui.h qapi/qapi-events-ui.c
 GENERATED_FILES += qmp-introspect.c qmp-introspect.h
 GENERATED_FILES += qapi-doc.texi
 
@@ -525,10 +585,70 @@  qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
 
 qapi-builtin-types.c qapi-builtin-types.h \
 qapi-types.c qapi-types.h \
+qapi/qapi-types-block-core.c qapi/qapi-types-block-core.h \
+qapi/qapi-types-block.c qapi/qapi-types-block.h \
+qapi/qapi-types-char.c qapi/qapi-types-char.h \
+qapi/qapi-types-common.c qapi/qapi-types-common.h \
+qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \
+qapi/qapi-types-introspect.c qapi/qapi-types-introspect.h \
+qapi/qapi-types-migration.c qapi/qapi-types-migration.h \
+qapi/qapi-types-net.c qapi/qapi-types-net.h \
+qapi/qapi-types-rocker.c qapi/qapi-types-rocker.h \
+qapi/qapi-types-run-state.c qapi/qapi-types-run-state.h \
+qapi/qapi-types-sockets.c qapi/qapi-types-sockets.h \
+qapi/qapi-types-tpm.c qapi/qapi-types-tpm.h \
+qapi/qapi-types-trace.c qapi/qapi-types-trace.h \
+qapi/qapi-types-transaction.c qapi/qapi-types-transaction.h \
+qapi/qapi-types-ui.c qapi/qapi-types-ui.h \
 qapi-builtin-visit.c qapi-builtin-visit.h \
 qapi-visit.c qapi-visit.h \
+qapi/qapi-visit-block-core.c qapi/qapi-visit-block-core.h \
+qapi/qapi-visit-block.c qapi/qapi-visit-block.h \
+qapi/qapi-visit-char.c qapi/qapi-visit-char.h \
+qapi/qapi-visit-common.c qapi/qapi-visit-common.h \
+qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \
+qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.h \
+qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \
+qapi/qapi-visit-net.c qapi/qapi-visit-net.h \
+qapi/qapi-visit-rocker.c qapi/qapi-visit-rocker.h \
+qapi/qapi-visit-run-state.c qapi/qapi-visit-run-state.h \
+qapi/qapi-visit-sockets.c qapi/qapi-visit-sockets.h \
+qapi/qapi-visit-tpm.c qapi/qapi-visit-tpm.h \
+qapi/qapi-visit-trace.c qapi/qapi-visit-trace.h \
+qapi/qapi-visit-transaction.c qapi/qapi-visit-transaction.h \
+qapi/qapi-visit-ui.c qapi/qapi-visit-ui.h \
 qmp-commands.h qmp-commands.c \
+qapi/qapi-commands-block-core.c qapi/qapi-commands-block-core.h \
+qapi/qapi-commands-block.c qapi/qapi-commands-block.h \
+qapi/qapi-commands-char.c qapi/qapi-commands-char.h \
+qapi/qapi-commands-common.c qapi/qapi-commands-common.h \
+qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \
+qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.h \
+qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \
+qapi/qapi-commands-net.c qapi/qapi-commands-net.h \
+qapi/qapi-commands-rocker.c qapi/qapi-commands-rocker.h \
+qapi/qapi-commands-run-state.c qapi/qapi-commands-run-state.h \
+qapi/qapi-commands-sockets.c qapi/qapi-commands-sockets.h \
+qapi/qapi-commands-tpm.c qapi/qapi-commands-tpm.h \
+qapi/qapi-commands-trace.c qapi/qapi-commands-trace.h \
+qapi/qapi-commands-transaction.c qapi/qapi-commands-transaction.h \
+qapi/qapi-commands-ui.c qapi/qapi-commands-ui.h \
 qapi-event.c qapi-event.h \
+qapi/qapi-events-block-core.c qapi/qapi-events-block-core.h \
+qapi/qapi-events-block.c qapi/qapi-events-block.h \
+qapi/qapi-events-char.c qapi/qapi-events-char.h \
+qapi/qapi-events-common.c qapi/qapi-events-common.h \
+qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \
+qapi/qapi-events-introspect.c qapi/qapi-events-introspect.h \
+qapi/qapi-events-migration.c qapi/qapi-events-migration.h \
+qapi/qapi-events-net.c qapi/qapi-events-net.h \
+qapi/qapi-events-rocker.c qapi/qapi-events-rocker.h \
+qapi/qapi-events-run-state.c qapi/qapi-events-run-state.h \
+qapi/qapi-events-sockets.c qapi/qapi-events-sockets.h \
+qapi/qapi-events-tpm.c qapi/qapi-events-tpm.h \
+qapi/qapi-events-trace.c qapi/qapi-events-trace.h \
+qapi/qapi-events-transaction.c qapi/qapi-events-transaction.h \
+qapi/qapi-events-ui.c qapi/qapi-events-ui.h \
 qmp-introspect.h qmp-introspect.c \
 qapi-doc.texi: \
 qapi-gen-timestamp ;
diff --git a/Makefile.objs b/Makefile.objs
index 2813e984fd..7a55d45669 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -3,8 +3,56 @@ 
 stub-obj-y = stubs/ crypto/
 util-obj-y = util/ qobject/ qapi/
 util-obj-y += qapi-builtin-types.o
+util-obj-y += qapi-types.o
+util-obj-y += qapi/qapi-types-block-core.o
+util-obj-y += qapi/qapi-types-block.o
+util-obj-y += qapi/qapi-types-char.o
+util-obj-y += qapi/qapi-types-common.o
+util-obj-y += qapi/qapi-types-crypto.o
+util-obj-y += qapi/qapi-types-introspect.o
+util-obj-y += qapi/qapi-types-migration.o
+util-obj-y += qapi/qapi-types-net.o
+util-obj-y += qapi/qapi-types-rocker.o
+util-obj-y += qapi/qapi-types-run-state.o
+util-obj-y += qapi/qapi-types-sockets.o
+util-obj-y += qapi/qapi-types-tpm.o
+util-obj-y += qapi/qapi-types-trace.o
+util-obj-y += qapi/qapi-types-transaction.o
+util-obj-y += qapi/qapi-types-ui.o
 util-obj-y += qapi-builtin-visit.o
-util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
+util-obj-y += qapi-visit.o
+util-obj-y += qapi/qapi-visit-block-core.o
+util-obj-y += qapi/qapi-visit-block.o
+util-obj-y += qapi/qapi-visit-char.o
+util-obj-y += qapi/qapi-visit-common.o
+util-obj-y += qapi/qapi-visit-crypto.o
+util-obj-y += qapi/qapi-visit-introspect.o
+util-obj-y += qapi/qapi-visit-migration.o
+util-obj-y += qapi/qapi-visit-net.o
+util-obj-y += qapi/qapi-visit-rocker.o
+util-obj-y += qapi/qapi-visit-run-state.o
+util-obj-y += qapi/qapi-visit-sockets.o
+util-obj-y += qapi/qapi-visit-tpm.o
+util-obj-y += qapi/qapi-visit-trace.o
+util-obj-y += qapi/qapi-visit-transaction.o
+util-obj-y += qapi/qapi-visit-ui.o
+util-obj-y += qapi-event.o
+util-obj-y += qapi/qapi-events-block-core.o
+util-obj-y += qapi/qapi-events-block.o
+util-obj-y += qapi/qapi-events-char.o
+util-obj-y += qapi/qapi-events-common.o
+util-obj-y += qapi/qapi-events-crypto.o
+util-obj-y += qapi/qapi-events-introspect.o
+util-obj-y += qapi/qapi-events-migration.o
+util-obj-y += qapi/qapi-events-net.o
+util-obj-y += qapi/qapi-events-rocker.o
+util-obj-y += qapi/qapi-events-run-state.o
+util-obj-y += qapi/qapi-events-sockets.o
+util-obj-y += qapi/qapi-events-tpm.o
+util-obj-y += qapi/qapi-events-trace.o
+util-obj-y += qapi/qapi-events-transaction.o
+util-obj-y += qapi/qapi-events-ui.o
+util-obj-y += qmp-introspect.o
 
 chardev-obj-y = chardev/
 
@@ -81,6 +129,21 @@  common-obj-$(CONFIG_FDT) += device_tree.o
 # qapi
 
 common-obj-y += qmp-commands.o
+common-obj-y += qapi/qapi-commands-block-core.o
+common-obj-y += qapi/qapi-commands-block.o
+common-obj-y += qapi/qapi-commands-char.o
+common-obj-y += qapi/qapi-commands-common.o
+common-obj-y += qapi/qapi-commands-crypto.o
+common-obj-y += qapi/qapi-commands-introspect.o
+common-obj-y += qapi/qapi-commands-migration.o
+common-obj-y += qapi/qapi-commands-net.o
+common-obj-y += qapi/qapi-commands-rocker.o
+common-obj-y += qapi/qapi-commands-run-state.o
+common-obj-y += qapi/qapi-commands-sockets.o
+common-obj-y += qapi/qapi-commands-tpm.o
+common-obj-y += qapi/qapi-commands-trace.o
+common-obj-y += qapi/qapi-commands-transaction.o
+common-obj-y += qapi/qapi-commands-ui.o
 common-obj-y += qmp-introspect.o
 common-obj-y += qmp.o hmp.o
 endif
diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py
index 46757db771..a43bccb190 100644
--- a/scripts/qapi/commands.py
+++ b/scripts/qapi/commands.py
@@ -223,14 +223,24 @@  void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds)
     return ret
 
 
-class QAPISchemaGenCommandVisitor(QAPISchemaMonolithicCVisitor):
+class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
 
     def __init__(self, prefix):
-        QAPISchemaMonolithicCVisitor.__init__(
-            self, prefix, 'qmp-commands',
+        QAPISchemaModularCVisitor.__init__(
+            self, prefix, 'qapi-commands',
             ' * Schema-defined QAPI/QMP commands', __doc__)
         self._regy = ''
-        self._visited_ret_types = set()
+        self._visited_ret_types = {}
+
+    # Temporary HACK:
+    def _module_basename(self, what, name):
+        basename = QAPISchemaModularCVisitor._module_basename(self, what, name)
+        if name == self._main_module:
+            return re.sub(r'qapi-commands', 'qmp-commands', basename)
+        return basename
+
+    def _begin_module(self, name):
+        self._visited_ret_types[self._genc] = set()
         self._genc.add(mcgen('''
 #include "qemu/osdep.h"
 #include "qemu-common.h"
@@ -246,26 +256,29 @@  class QAPISchemaGenCommandVisitor(QAPISchemaMonolithicCVisitor):
 #include "%(prefix)sqmp-commands.h"
 
 ''',
-                             prefix=prefix))
+                             prefix=self._prefix))
         self._genh.add(mcgen('''
 #include "%(prefix)sqapi-types.h"
 #include "qapi/qmp/dispatch.h"
 
-void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
 ''',
-                             prefix=prefix,
-                             c_prefix=c_name(prefix, protect=False)))
+                             prefix=self._prefix))
 
     def visit_end(self):
-        self._genc.add(gen_registry(self._regy, self._prefix))
+        (genc, genh) = self._module[self._main_module]
+        genh.add(mcgen('''
+void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
+''',
+                       c_prefix=c_name(self._prefix, protect=False)))
+        genc.add(gen_registry(self._regy, self._prefix))
 
     def visit_command(self, name, info, arg_type, ret_type,
                       gen, success_response, boxed):
         if not gen:
             return
         self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type))
-        if ret_type and ret_type not in self._visited_ret_types:
-            self._visited_ret_types.add(ret_type)
+        if ret_type and ret_type not in self._visited_ret_types[self._genc]:
+            self._visited_ret_types[self._genc].add(ret_type)
             self._genc.add(gen_marshal_output(ret_type))
         self._genh.add(gen_marshal_decl(name))
         self._genc.add(gen_marshal(name, arg_type, boxed, ret_type))
diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 6e5152b173..b9a52e820d 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -2073,13 +2073,20 @@  class QAPISchemaModularCVisitor(QAPISchemaVisitor):
         self._blurb = blurb
         self._pydoc = pydoc
         self._module = {}
+        self._main_module = None
 
     def _module_basename(self, what, name):
         if name is None:
             return re.sub(r'-', '-builtin-', what)
-        return self._prefix + what
+        basename = os.path.join(os.path.dirname(name),
+                                self._prefix + what)
+        if name == self._main_module:
+            return basename
+        return basename + '-' + os.path.splitext(os.path.basename(name))[0]
 
     def _add_module(self, name, blurb):
+        if self._main_module is None and name is not None:
+            self._main_module = name
         genc = QAPIGenC(blurb, self._pydoc)
         genh = QAPIGenH(blurb, self._pydoc)
         self._module[name] = (genc, genh)
@@ -2088,7 +2095,7 @@  class QAPISchemaModularCVisitor(QAPISchemaVisitor):
     def _set_module(self, name):
         self._genc, self._genh = self._module[name]
 
-    def write(self, output_dir, opt_builtins):
+    def write(self, output_dir, opt_builtins=False):
         for name in self._module:
             if name is None and not opt_builtins:
                 continue
@@ -2101,7 +2108,15 @@  class QAPISchemaModularCVisitor(QAPISchemaVisitor):
         pass
 
     def visit_module(self, name):
-        if len(self._module) != 1:
+        if name in self._module:
+            self._set_module(name)
             return
         self._add_module(name, self._blurb)
         self._begin_module(name)
+
+    def visit_include(self, name, info):
+        basename = self._module_basename(self._what, name)
+        self._genh.preamble_add(mcgen('''
+#include "%(basename)s.h"
+''',
+                                      basename=basename))
diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py
index 81ab3abb30..1e0b990f35 100644
--- a/scripts/qapi/events.py
+++ b/scripts/qapi/events.py
@@ -148,14 +148,23 @@  out:
     return ret
 
 
-class QAPISchemaGenEventVisitor(QAPISchemaMonolithicCVisitor):
+class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
 
     def __init__(self, prefix):
-        QAPISchemaMonolithicCVisitor.__init__(
-            self, prefix, 'qapi-event',
+        QAPISchemaModularCVisitor.__init__(
+            self, prefix, 'qapi-events',
             ' * Schema-defined QAPI/QMP events', __doc__)
         self._enum_name = c_name(prefix + 'QAPIEvent', protect=False)
         self._event_names = []
+
+    # Temporary HACK:
+    def _module_basename(self, what, name):
+        basename = QAPISchemaModularCVisitor._module_basename(self, what, name)
+        if name == self._main_module:
+            return re.sub(r'qapi-events', 'qapi-event', basename)
+        return basename
+
+    def _begin_module(self, name):
         self._genc.add(mcgen('''
 #include "qemu/osdep.h"
 #include "qemu-common.h"
@@ -167,13 +176,13 @@  class QAPISchemaGenEventVisitor(QAPISchemaMonolithicCVisitor):
 #include "qapi/qmp-event.h"
 
 ''',
-                             prefix=prefix))
+                             prefix=self._prefix))
         self._genh.add(mcgen('''
 #include "qapi/util.h"
 #include "%(prefix)sqapi-types.h"
 
 ''',
-                             prefix=prefix))
+                             prefix=self._prefix))
 
     def visit_end(self):
         self._genh.add(gen_enum(self._enum_name, self._event_names))