diff mbox

[RFC,2/3] qapi script: add support of event

Message ID 1384307094-5836-3-git-send-email-xiawenc@linux.vnet.ibm.com
State New
Headers show

Commit Message

Wayne Xia Nov. 13, 2013, 1:44 a.m. UTC
Nested structure is not supported now, so following define is not valid:
{ 'event': 'EVENT_C',
  'data': { 'a': { 'a_a', 'str', 'a_b', 'str' }, 'b': 'int' }

Signed-off-by: Wenchao Xia <xiawenc@linux.vnet.ibm.com>
---
 Makefile              |    9 +-
 Makefile.objs         |    2 +-
 qapi/Makefile.objs    |    1 +
 scripts/qapi-event.py |  355 +++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 363 insertions(+), 4 deletions(-)
 create mode 100644 scripts/qapi-event.py

Comments

Luiz Capitulino Nov. 28, 2013, 12:48 a.m. UTC | #1
On Wed, 13 Nov 2013 09:44:52 +0800
Wenchao Xia <xiawenc@linux.vnet.ibm.com> wrote:

> Nested structure is not supported now, so following define is not valid:
> { 'event': 'EVENT_C',
>   'data': { 'a': { 'a_a', 'str', 'a_b', 'str' }, 'b': 'int' }

I think your general approach is reasonable, but there are a number of
details to fix.

The first one is documentation. This patch's changelog is quite bad,
you don't say anything about what the does. You just mention a corner
case which doesn't happen to work. Please, add full changelog explaining
what this patch is about and please add examples of how an event
entry would look like in the schema and maybe you could also add the
important parts of a generated event function. Also, please add a
"event" section to docs/writing-qmp-commands.txt (in a different patch).

Secondly, for changes like this it's a very good idea to provide
conversion examples (as patches, not as changelog doc) at least
one or two so that we can see how it will look like.

More below.

> Signed-off-by: Wenchao Xia <xiawenc@linux.vnet.ibm.com>
> ---
>  Makefile              |    9 +-
>  Makefile.objs         |    2 +-
>  qapi/Makefile.objs    |    1 +
>  scripts/qapi-event.py |  355 +++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 363 insertions(+), 4 deletions(-)
>  create mode 100644 scripts/qapi-event.py
> 
> diff --git a/Makefile b/Makefile
> index b15003f..a3e465f 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -38,8 +38,8 @@ endif
>  endif
>  
>  GENERATED_HEADERS = config-host.h qemu-options.def
> -GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h
> -GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c
> +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 += trace/generated-events.h
>  GENERATED_SOURCES += trace/generated-events.c
> @@ -178,7 +178,7 @@ Makefile: $(version-obj-y) $(version-lobj-y)
>  # Build libraries
>  
>  libqemustub.a: $(stub-obj-y)
> -libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o
> +libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o qapi-event.o
>  
>  ######################################################################
>  
> @@ -219,6 +219,9 @@ $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
>  qapi-visit.c qapi-visit.h :\
>  $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
>  	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o "." -b < $<, "  GEN   $@")
> +qapi-event.c qapi-event.h :\
> +$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-event.py $(qapi-py)
> +	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py $(gen-out-type) -o "." -b < $<, "  GEN   $@")
>  qmp-commands.h qmp-marshal.c :\
>  $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
>  	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -m -o "." < $<, "  GEN   $@")
> diff --git a/Makefile.objs b/Makefile.objs
> index 2b6c1fe..33f5950 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -12,7 +12,7 @@ block-obj-y += main-loop.o iohandler.o qemu-timer.o
>  block-obj-$(CONFIG_POSIX) += aio-posix.o
>  block-obj-$(CONFIG_WIN32) += aio-win32.o
>  block-obj-y += block/
> -block-obj-y += qapi-types.o qapi-visit.o
> +block-obj-y += qapi-types.o qapi-visit.o qapi-event.o
>  block-obj-y += qemu-io-cmds.o
>  
>  block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
> diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
> index 1f9c973..d14b769 100644
> --- a/qapi/Makefile.objs
> +++ b/qapi/Makefile.objs
> @@ -3,3 +3,4 @@ util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
>  util-obj-y += string-input-visitor.o string-output-visitor.o
>  
>  util-obj-y += opts-visitor.o
> +util-obj-y += qmp-event.o
> diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
> new file mode 100644
> index 0000000..4c6a0fe

I didn't review this hunk, but this series doesn't build for me:

  CC    qapi/string-output-visitor.o
  CC    qapi/opts-visitor.o
make: *** No rule to make target `qapi/qmp-event.o', needed by `libqemuutil.a'.  Stop.
~/work/src/upstream/qmp-unstable/build (tmp|AM)/ 

> --- /dev/null
> +++ b/scripts/qapi-event.py
> @@ -0,0 +1,355 @@
> +#
> +# QAPI event generator
> +#
> +# Copyright IBM, Corp. 2013
> +#
> +# Authors:
> +#  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
> +#
> +# This work is licensed under the terms of the GNU GPLv2.
> +# See the COPYING.LIB file in the top-level directory.
> +
> +from ordereddict import OrderedDict
> +from qapi import *
> +import sys
> +import os
> +import getopt
> +import errno
> +
> +def _generate_event_api_name_with_params(event_name, params):
> +    api_name = "void qapi_event_gen_%s(" % c_fun(event_name);

I'd prefer a name like qmp_notify_NAME() or qmp_send_event_NAME().

> +    l = len(api_name)
> +
> +    for argname, argentry, optional, structured in parse_args(params):
> +        if structured:
> +            sys.stderr.write("Nested structure define in event is not "
> +                             "supported now, event '%s', argname '%s'\n" %
> +                             (event_name, argname))
> +            sys.exit(1)
> +            continue
> +
> +        if optional:
> +            api_name += "bool has_%s,\n" % c_var(argname)
> +            api_name += "".ljust(l)
> +
> +        if argentry == "str":
> +            api_name += "const "
> +        api_name += "%s %s,\n" % (c_type(argentry), c_var(argname))
> +        api_name += "".ljust(l)
> +
> +    api_name += "Error **errp)"
> +    return api_name;
> +
> +def _generate_event_implement_with_params(api_name, event_name, params):
> +    ret = mcgen("""
> +
> +%(api_name)s
> +{
> +    QmpOutputVisitor *qov;
> +    Visitor *v;
> +    Error *err = NULL;

We usually call it *local_err;

> +    QObject *obj;
> +    QDict *qmp = NULL;

It's not needed to initialize qmp.

> +
> +    if (!qapi_event_functions.emit) {

Better to return an error here instead of silently failing.

> +        return;
> +    }
> +
> +    qmp = qdict_new();
> +    qdict_put(qmp, "event", qstring_from_str("%(event_name)s"));
> +    timestamp_put(qmp);

Maybe it's a good idea to move this to a function? Say
Qdict *qmp_build_event_dict(const char *event_name)?

> +
> +    qov = qmp_output_visitor_new();
> +    g_assert(qov);
> +
> +    v = qmp_output_get_visitor(qov);
> +    g_assert(v);
> +
> +    /* Fake visit, as if all member are under a structure */
> +    visit_start_struct(v, NULL, "", "%(event_name)s", 0, &err);
> +    if (error_is_set(&err)) {
> +        goto clean;
> +    }
> +
> +""",
> +                api_name = api_name,
> +                event_name = event_name)
> +
> +    for argname, argentry, optional, structured in parse_args(params):
> +        if structured:
> +            sys.stderr.write("Nested structure define in event is not "
> +                             "supported now, event '%s', argname '%s'\n" %
> +                             (event_name, argname))
> +            sys.exit(1)
> +            continue
> +
> +        if optional:
> +            ret += mcgen("""
> +    if (has_%(var)s) {
> +""",
> +                         var = c_var(argname))
> +            push_indent()
> +
> +        if argentry == "str":
> +            var_type = "(char **)"
> +        else:
> +            var_type = ""
> +
> +        ret += mcgen("""
> +    visit_type_%(type)s(v, %(var_type)s&%(var)s, "%(name)s", &err);
> +    if (error_is_set(&err)) {
> +        goto clean;
> +    }
> +""",
> +                     var_type = var_type,
> +                     var = c_var(argname),
> +                     type = type_name(argentry),
> +                     name = argname)
> +
> +        if optional:
> +            pop_indent()
> +            ret += mcgen("""
> +    }
> +""")
> +
> +    ret += mcgen("""
> +
> +    visit_end_struct(v, &err);
> +    if (error_is_set(&err)) {
> +        goto clean;
> +    }
> +
> +    obj = qmp_output_get_qobject(qov);
> +    g_assert(obj != NULL);
> +
> +    qdict_put_obj(qmp, "data", obj);
> +
> +    qapi_event_functions.emit(qmp, &err);
> +
> + clean:
> +    error_propagate(errp, err);
> +    qmp_output_visitor_cleanup(qov);
> +    QDECREF(qmp);
> +}
> +""")
> +
> +    return ret
> +
> +def _generate_event_api_name(event_name):
> +    return "void qapi_event_gen_%s(Error **errp)" % c_fun(event_name);
> +
> +def _generate_event_implement(api_name, event_name):
> +    return mcgen("""
> +
> +%(api_name)s
> +{
> +    QDict *qmp = NULL;

It's not needed to initialize qmp.

> +
> +    if (!qapi_event_functions.emit) {
> +        return;
> +    }
> +
> +    qmp = qdict_new();
> +    qdict_put(qmp, "event", qstring_from_str("%(event_name)s"));
> +    timestamp_put(qmp);
> +
> +    qapi_event_functions.emit(qmp, errp);
> +
> +    QDECREF(qmp);
> +}
> +""",
> +                api_name = api_name,
> +                event_name = event_name)
> +
> +
> +def generate_event_declaration(event_name, params):
> +    if params and len(params) > 0:
> +        api_name = _generate_event_api_name_with_params(event_name, params)
> +    else:
> +        api_name = _generate_event_api_name(event_name)
> +
> +    return mcgen('''
> +
> +%(api_name)s;
> +''',
> +                 api_name = api_name)
> +
> +def generate_event_implement(event_name, params):
> +    if params and len(params) > 0:
> +        api_name = _generate_event_api_name_with_params(event_name, params)
> +        ret =  _generate_event_implement_with_params(api_name,
> +                                                     event_name,
> +                                                     params)
> +
> +    else:
> +        api_name = _generate_event_api_name(event_name)
> +        ret =  _generate_event_implement(api_name,
> +                                         event_name)
> +    return ret
> +
> +try:
> +    opts, args = getopt.gnu_getopt(sys.argv[1:], "chbp:o:",
> +                                   ["source", "header", "builtins", "prefix=",
> +                                    "output-dir="])
> +except getopt.GetoptError, err:
> +    print str(err)
> +    sys.exit(1)
> +
> +output_dir = ""
> +prefix = ""
> +c_file = 'qapi-event.c'
> +h_file = 'qapi-event.h'
> +
> +do_c = False
> +do_h = False
> +do_builtins = False
> +
> +for o, a in opts:
> +    if o in ("-p", "--prefix"):
> +        prefix = a
> +    elif o in ("-o", "--output-dir"):
> +        output_dir = a + "/"
> +    elif o in ("-c", "--source"):
> +        do_c = True
> +    elif o in ("-h", "--header"):
> +        do_h = True
> +    elif o in ("-b", "--builtins"):
> +        do_builtins = True
> +
> +if not do_c and not do_h:
> +    do_c = True
> +    do_h = True
> +
> +c_file = output_dir + prefix + c_file
> +h_file = output_dir + prefix + h_file
> +
> +try:
> +    os.makedirs(output_dir)
> +except os.error, e:
> +    if e.errno != errno.EEXIST:
> +        raise
> +
> +def maybe_open(really, name, opt):
> +    if really:
> +        return open(name, opt)
> +    else:
> +        import StringIO
> +        return StringIO.StringIO()
> +
> +fdef = maybe_open(do_c, c_file, 'w')
> +fdecl = maybe_open(do_h, h_file, 'w')
> +
> +fdef.write(mcgen('''
> +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
> +
> +/*
> + * schema-defined QAPI event functions
> + *
> + * Copyright IBM, Corp. 2013
> + *
> + * Authors:
> + *  Wenchao Xia   <xiawenc@linux.vnet.ibm.com>
> + *
> + * 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.
> + *
> + */
> +
> +#include "qemu-common.h"
> +#include "%(header)s"
> +#include "%(prefix)sqapi-visit.h"
> +#include "qapi/qmp-output-visitor.h"
> +#include "qapi/qmp/qstring.h"
> +#include "qapi/qmp/qjson.h"
> +
> +#ifdef _WIN32
> +#include "sysemu/os-win32.h"
> +#endif
> +
> +#ifdef CONFIG_POSIX
> +#include "sysemu/os-posix.h"
> +#endif
> +
> +typedef struct QAPIEventFunctions {
> +    void (*emit)(QDict *dict, Error **errp);
> +} QAPIEventFunctions;
> +
> +QAPIEventFunctions qapi_event_functions;
> +
> +void qapi_event_set_func_emit(qapi_event_emit emit)
> +{
> +    qapi_event_functions.emit = emit;
> +}
> +
> +static void timestamp_put(QDict *qdict)
> +{
> +    int err;
> +    QObject *obj;
> +    qemu_timeval tv;
> +
> +    err = qemu_gettimeofday(&tv);
> +    if (err < 0)
> +        return;
> +
> +    obj = qobject_from_jsonf("{ 'seconds': %(p)s" PRId64 ", "
> +                                "'microseconds': %(p)s" PRId64 " }",
> +                                (int64_t) tv.tv_sec, (int64_t) tv.tv_usec);
> +    qdict_put_obj(qdict, "timestamp", obj);
> +}

Any special reason to generate these functions? Maybe they could be
put in qmp.c instead?

> +
> +''',
> +                 prefix=prefix, header=basename(h_file), p="%"))
> +
> +fdecl.write(mcgen('''
> +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
> +
> +/*
> + * schema-defined QAPI event function
> + *
> + * Copyright IBM, Corp. 2013
> + *
> + * Authors:
> + *  Wenchao Xia  <xiawenc@linux.vnet.ibm.com>
> + *
> + * 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.
> + *
> + */
> +
> +#ifndef %(guard)s
> +#define %(guard)s
> +
> +#include "qapi/qmp/qdict.h"
> +#include "qapi/error.h"
> +#include "qapi/visitor.h"
> +#include "%(prefix)sqapi-types.h"
> +
> +typedef void (*qapi_event_emit)(QDict *d, Error **errp);
> +
> +void qapi_event_set_func_emit(qapi_event_emit emit);
> +
> +''',
> +                  prefix=prefix, guard=guardname(h_file)))
> +
> +exprs = parse_schema(sys.stdin)
> +
> +for expr in exprs:
> +    if expr.has_key('event'):
> +        event_name = expr['event']
> +        params = expr.get('data')
> +
> +        ret = generate_event_declaration(event_name, params)
> +        fdecl.write(ret)
> +
> +        ret = generate_event_implement(event_name, params)
> +        fdef.write(ret)
> +
> +fdecl.write('''
> +#endif
> +''')
> +
> +fdecl.flush()
> +fdecl.close()
> +
> +fdef.flush()
> +fdef.close()
Wayne Xia Nov. 28, 2013, 7:16 a.m. UTC | #2
于 2013/11/28 8:48, Luiz Capitulino 写道:
> On Wed, 13 Nov 2013 09:44:52 +0800
> Wenchao Xia <xiawenc@linux.vnet.ibm.com> wrote:
>
>> Nested structure is not supported now, so following define is not valid:
>> { 'event': 'EVENT_C',
>>    'data': { 'a': { 'a_a', 'str', 'a_b', 'str' }, 'b': 'int' }
>
> I think your general approach is reasonable, but there are a number of
> details to fix.
>
> The first one is documentation. This patch's changelog is quite bad,
> you don't say anything about what the does. You just mention a corner
> case which doesn't happen to work. Please, add full changelog explaining
> what this patch is about and please add examples of how an event
> entry would look like in the schema and maybe you could also add the
> important parts of a generated event function. Also, please add a
> "event" section to docs/writing-qmp-commands.txt (in a different patch).

   OK.

>
> Secondly, for changes like this it's a very good idea to provide
> conversion examples (as patches, not as changelog doc) at least
> one or two so that we can see how it will look like.
>

   Will add some in the intro part. By the way I think test
cases in patch 3 shows a bit.

> More below.
>

>> Signed-off-by: Wenchao Xia <xiawenc@linux.vnet.ibm.com>
>> ---
>>   Makefile              |    9 +-
>>   Makefile.objs         |    2 +-
>>   qapi/Makefile.objs    |    1 +
>>   scripts/qapi-event.py |  355 +++++++++++++++++++++++++++++++++++++++++++++++++
>>   4 files changed, 363 insertions(+), 4 deletions(-)
>>   create mode 100644 scripts/qapi-event.py
>>
>> diff --git a/Makefile b/Makefile
>> index b15003f..a3e465f 100644
>> --- a/Makefile
>> +++ b/Makefile
>> @@ -38,8 +38,8 @@ endif
>>   endif
>>
>>   GENERATED_HEADERS = config-host.h qemu-options.def
>> -GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h
>> -GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c
>> +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 += trace/generated-events.h
>>   GENERATED_SOURCES += trace/generated-events.c
>> @@ -178,7 +178,7 @@ Makefile: $(version-obj-y) $(version-lobj-y)
>>   # Build libraries
>>
>>   libqemustub.a: $(stub-obj-y)
>> -libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o
>> +libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o qapi-event.o
>>
>>   ######################################################################
>>
>> @@ -219,6 +219,9 @@ $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
>>   qapi-visit.c qapi-visit.h :\
>>   $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
>>   	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o "." -b < $<, "  GEN   $@")
>> +qapi-event.c qapi-event.h :\
>> +$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-event.py $(qapi-py)
>> +	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py $(gen-out-type) -o "." -b < $<, "  GEN   $@")
>>   qmp-commands.h qmp-marshal.c :\
>>   $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
>>   	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -m -o "." < $<, "  GEN   $@")
>> diff --git a/Makefile.objs b/Makefile.objs
>> index 2b6c1fe..33f5950 100644
>> --- a/Makefile.objs
>> +++ b/Makefile.objs
>> @@ -12,7 +12,7 @@ block-obj-y += main-loop.o iohandler.o qemu-timer.o
>>   block-obj-$(CONFIG_POSIX) += aio-posix.o
>>   block-obj-$(CONFIG_WIN32) += aio-win32.o
>>   block-obj-y += block/
>> -block-obj-y += qapi-types.o qapi-visit.o
>> +block-obj-y += qapi-types.o qapi-visit.o qapi-event.o
>>   block-obj-y += qemu-io-cmds.o
>>
>>   block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
>> diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
>> index 1f9c973..d14b769 100644
>> --- a/qapi/Makefile.objs
>> +++ b/qapi/Makefile.objs
>> @@ -3,3 +3,4 @@ util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
>>   util-obj-y += string-input-visitor.o string-output-visitor.o
>>
>>   util-obj-y += opts-visitor.o
>> +util-obj-y += qmp-event.o
>> diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
>> new file mode 100644
>> index 0000000..4c6a0fe
>
> I didn't review this hunk, but this series doesn't build for me:
>
>    CC    qapi/string-output-visitor.o
>    CC    qapi/opts-visitor.o
> make: *** No rule to make target `qapi/qmp-event.o', needed by `libqemuutil.a'.  Stop.
> ~/work/src/upstream/qmp-unstable/build (tmp|AM)/
>
   A draft file I forgot to remove in Makefile, will fix.

>> --- /dev/null
>> +++ b/scripts/qapi-event.py
>> @@ -0,0 +1,355 @@
>> +#
>> +# QAPI event generator
>> +#
>> +# Copyright IBM, Corp. 2013
>> +#
>> +# Authors:
>> +#  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
>> +#
>> +# This work is licensed under the terms of the GNU GPLv2.
>> +# See the COPYING.LIB file in the top-level directory.
>> +
>> +from ordereddict import OrderedDict
>> +from qapi import *
>> +import sys
>> +import os
>> +import getopt
>> +import errno
>> +
>> +def _generate_event_api_name_with_params(event_name, params):
>> +    api_name = "void qapi_event_gen_%s(" % c_fun(event_name);
>
> I'd prefer a name like qmp_notify_NAME() or qmp_send_event_NAME().
>

   do you think NAME should be capitalized as:
qmp_notify_SHUTDOWN()?

>> +    l = len(api_name)
>> +
>> +    for argname, argentry, optional, structured in parse_args(params):
>> +        if structured:
>> +            sys.stderr.write("Nested structure define in event is not "
>> +                             "supported now, event '%s', argname '%s'\n" %
>> +                             (event_name, argname))
>> +            sys.exit(1)
>> +            continue
>> +
>> +        if optional:
>> +            api_name += "bool has_%s,\n" % c_var(argname)
>> +            api_name += "".ljust(l)
>> +
>> +        if argentry == "str":
>> +            api_name += "const "
>> +        api_name += "%s %s,\n" % (c_type(argentry), c_var(argname))
>> +        api_name += "".ljust(l)
>> +
>> +    api_name += "Error **errp)"
>> +    return api_name;
>> +
>> +def _generate_event_implement_with_params(api_name, event_name, params):
>> +    ret = mcgen("""
>> +
>> +%(api_name)s
>> +{
>> +    QmpOutputVisitor *qov;
>> +    Visitor *v;
>> +    Error *err = NULL;
>
> We usually call it *local_err;
>

   OK.

>> +    QObject *obj;
>> +    QDict *qmp = NULL;
>
> It's not needed to initialize qmp.
>

   Will fix.

>> +
>> +    if (!qapi_event_functions.emit) {
>
> Better to return an error here instead of silently failing.
>

   The purpose is allowing emit=NULL and skip event code in that case.
If err is set, caller can't distinguish it from real error case.
caller:
     qmp_event_notify_SHUTDOWN(&err);
     if (error_is_set(&err)) {
         ...
     }

     err is always set when emit=NULL, but we may allow emit=NULL,
for example, qemu-img will have emit=NULL.

>> +        return;
>> +    }
>> +
>> +    qmp = qdict_new();
>> +    qdict_put(qmp, "event", qstring_from_str("%(event_name)s"));
>> +    timestamp_put(qmp);
>
> Maybe it's a good idea to move this to a function? Say
> Qdict *qmp_build_event_dict(const char *event_name)?
>

   Seems good, will use it.

>> +
>> +    qov = qmp_output_visitor_new();
>> +    g_assert(qov);
>> +
>> +    v = qmp_output_get_visitor(qov);
>> +    g_assert(v);
>> +
>> +    /* Fake visit, as if all member are under a structure */
>> +    visit_start_struct(v, NULL, "", "%(event_name)s", 0, &err);
>> +    if (error_is_set(&err)) {
>> +        goto clean;
>> +    }
>> +
>> +""",
>> +                api_name = api_name,
>> +                event_name = event_name)
>> +
>> +    for argname, argentry, optional, structured in parse_args(params):
>> +        if structured:
>> +            sys.stderr.write("Nested structure define in event is not "
>> +                             "supported now, event '%s', argname '%s'\n" %
>> +                             (event_name, argname))
>> +            sys.exit(1)
>> +            continue
>> +
>> +        if optional:
>> +            ret += mcgen("""
>> +    if (has_%(var)s) {
>> +""",
>> +                         var = c_var(argname))
>> +            push_indent()
>> +
>> +        if argentry == "str":
>> +            var_type = "(char **)"
>> +        else:
>> +            var_type = ""
>> +
>> +        ret += mcgen("""
>> +    visit_type_%(type)s(v, %(var_type)s&%(var)s, "%(name)s", &err);
>> +    if (error_is_set(&err)) {
>> +        goto clean;
>> +    }
>> +""",
>> +                     var_type = var_type,
>> +                     var = c_var(argname),
>> +                     type = type_name(argentry),
>> +                     name = argname)
>> +
>> +        if optional:
>> +            pop_indent()
>> +            ret += mcgen("""
>> +    }
>> +""")
>> +
>> +    ret += mcgen("""
>> +
>> +    visit_end_struct(v, &err);
>> +    if (error_is_set(&err)) {
>> +        goto clean;
>> +    }
>> +
>> +    obj = qmp_output_get_qobject(qov);
>> +    g_assert(obj != NULL);
>> +
>> +    qdict_put_obj(qmp, "data", obj);
>> +
>> +    qapi_event_functions.emit(qmp, &err);
>> +
>> + clean:
>> +    error_propagate(errp, err);
>> +    qmp_output_visitor_cleanup(qov);
>> +    QDECREF(qmp);
>> +}
>> +""")
>> +
>> +    return ret
>> +
>> +def _generate_event_api_name(event_name):
>> +    return "void qapi_event_gen_%s(Error **errp)" % c_fun(event_name);
>> +
>> +def _generate_event_implement(api_name, event_name):
>> +    return mcgen("""
>> +
>> +%(api_name)s
>> +{
>> +    QDict *qmp = NULL;
>
> It's not needed to initialize qmp.
>

   OK.

>> +
>> +    if (!qapi_event_functions.emit) {
>> +        return;
>> +    }
>> +
>> +    qmp = qdict_new();
>> +    qdict_put(qmp, "event", qstring_from_str("%(event_name)s"));
>> +    timestamp_put(qmp);
>> +
>> +    qapi_event_functions.emit(qmp, errp);
>> +
>> +    QDECREF(qmp);
>> +}
>> +""",
>> +                api_name = api_name,
>> +                event_name = event_name)
>> +
>> +
>> +def generate_event_declaration(event_name, params):
>> +    if params and len(params) > 0:
>> +        api_name = _generate_event_api_name_with_params(event_name, params)
>> +    else:
>> +        api_name = _generate_event_api_name(event_name)
>> +
>> +    return mcgen('''
>> +
>> +%(api_name)s;
>> +''',
>> +                 api_name = api_name)
>> +
>> +def generate_event_implement(event_name, params):
>> +    if params and len(params) > 0:
>> +        api_name = _generate_event_api_name_with_params(event_name, params)
>> +        ret =  _generate_event_implement_with_params(api_name,
>> +                                                     event_name,
>> +                                                     params)
>> +
>> +    else:
>> +        api_name = _generate_event_api_name(event_name)
>> +        ret =  _generate_event_implement(api_name,
>> +                                         event_name)
>> +    return ret
>> +
>> +try:
>> +    opts, args = getopt.gnu_getopt(sys.argv[1:], "chbp:o:",
>> +                                   ["source", "header", "builtins", "prefix=",
>> +                                    "output-dir="])
>> +except getopt.GetoptError, err:
>> +    print str(err)
>> +    sys.exit(1)
>> +
>> +output_dir = ""
>> +prefix = ""
>> +c_file = 'qapi-event.c'
>> +h_file = 'qapi-event.h'
>> +
>> +do_c = False
>> +do_h = False
>> +do_builtins = False
>> +
>> +for o, a in opts:
>> +    if o in ("-p", "--prefix"):
>> +        prefix = a
>> +    elif o in ("-o", "--output-dir"):
>> +        output_dir = a + "/"
>> +    elif o in ("-c", "--source"):
>> +        do_c = True
>> +    elif o in ("-h", "--header"):
>> +        do_h = True
>> +    elif o in ("-b", "--builtins"):
>> +        do_builtins = True
>> +
>> +if not do_c and not do_h:
>> +    do_c = True
>> +    do_h = True
>> +
>> +c_file = output_dir + prefix + c_file
>> +h_file = output_dir + prefix + h_file
>> +
>> +try:
>> +    os.makedirs(output_dir)
>> +except os.error, e:
>> +    if e.errno != errno.EEXIST:
>> +        raise
>> +
>> +def maybe_open(really, name, opt):
>> +    if really:
>> +        return open(name, opt)
>> +    else:
>> +        import StringIO
>> +        return StringIO.StringIO()
>> +
>> +fdef = maybe_open(do_c, c_file, 'w')
>> +fdecl = maybe_open(do_h, h_file, 'w')
>> +
>> +fdef.write(mcgen('''
>> +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
>> +
>> +/*
>> + * schema-defined QAPI event functions
>> + *
>> + * Copyright IBM, Corp. 2013
>> + *
>> + * Authors:
>> + *  Wenchao Xia   <xiawenc@linux.vnet.ibm.com>
>> + *
>> + * 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.
>> + *
>> + */
>> +
>> +#include "qemu-common.h"
>> +#include "%(header)s"
>> +#include "%(prefix)sqapi-visit.h"
>> +#include "qapi/qmp-output-visitor.h"
>> +#include "qapi/qmp/qstring.h"
>> +#include "qapi/qmp/qjson.h"
>> +
>> +#ifdef _WIN32
>> +#include "sysemu/os-win32.h"
>> +#endif
>> +
>> +#ifdef CONFIG_POSIX
>> +#include "sysemu/os-posix.h"
>> +#endif
>> +
>> +typedef struct QAPIEventFunctions {
>> +    void (*emit)(QDict *dict, Error **errp);
>> +} QAPIEventFunctions;
>> +
>> +QAPIEventFunctions qapi_event_functions;
>> +
>> +void qapi_event_set_func_emit(qapi_event_emit emit)
>> +{
>> +    qapi_event_functions.emit = emit;
>> +}
>> +
>> +static void timestamp_put(QDict *qdict)
>> +{
>> +    int err;
>> +    QObject *obj;
>> +    qemu_timeval tv;
>> +
>> +    err = qemu_gettimeofday(&tv);
>> +    if (err < 0)
>> +        return;
>> +
>> +    obj = qobject_from_jsonf("{ 'seconds': %(p)s" PRId64 ", "
>> +                                "'microseconds': %(p)s" PRId64 " }",
>> +                                (int64_t) tv.tv_sec, (int64_t) tv.tv_usec);
>> +    qdict_put_obj(qdict, "timestamp", obj);
>> +}
>
> Any special reason to generate these functions? Maybe they could be
> put in qmp.c instead?
>

   No, I just found no better place to go. qmp.c seems irrelevent to
event, how about new file qapi/qmp-event.c?(which I used before and
triggered the build error you met)

>> +
>> +''',
>> +                 prefix=prefix, header=basename(h_file), p="%"))
>> +
>> +fdecl.write(mcgen('''
>> +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
>> +
>> +/*
>> + * schema-defined QAPI event function
>> + *
>> + * Copyright IBM, Corp. 2013
>> + *
>> + * Authors:
>> + *  Wenchao Xia  <xiawenc@linux.vnet.ibm.com>
>> + *
>> + * 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.
>> + *
>> + */
>> +
>> +#ifndef %(guard)s
>> +#define %(guard)s
>> +
>> +#include "qapi/qmp/qdict.h"
>> +#include "qapi/error.h"
>> +#include "qapi/visitor.h"
>> +#include "%(prefix)sqapi-types.h"
>> +
>> +typedef void (*qapi_event_emit)(QDict *d, Error **errp);
>> +
>> +void qapi_event_set_func_emit(qapi_event_emit emit);
>> +
>> +''',
>> +                  prefix=prefix, guard=guardname(h_file)))
>> +
>> +exprs = parse_schema(sys.stdin)
>> +
>> +for expr in exprs:
>> +    if expr.has_key('event'):
>> +        event_name = expr['event']
>> +        params = expr.get('data')
>> +
>> +        ret = generate_event_declaration(event_name, params)
>> +        fdecl.write(ret)
>> +
>> +        ret = generate_event_implement(event_name, params)
>> +        fdef.write(ret)
>> +
>> +fdecl.write('''
>> +#endif
>> +''')
>> +
>> +fdecl.flush()
>> +fdecl.close()
>> +
>> +fdef.flush()
>> +fdef.close()
>
Luiz Capitulino Nov. 28, 2013, 2:31 p.m. UTC | #3
On Thu, 28 Nov 2013 15:16:08 +0800
Wenchao Xia <xiawenc@linux.vnet.ibm.com> wrote:

> 于 2013/11/28 8:48, Luiz Capitulino 写道:
> > On Wed, 13 Nov 2013 09:44:52 +0800
> > Wenchao Xia <xiawenc@linux.vnet.ibm.com> wrote:
> >
> >> Nested structure is not supported now, so following define is not valid:
> >> { 'event': 'EVENT_C',
> >>    'data': { 'a': { 'a_a', 'str', 'a_b', 'str' }, 'b': 'int' }
> >
> > I think your general approach is reasonable, but there are a number of
> > details to fix.
> >
> > The first one is documentation. This patch's changelog is quite bad,
> > you don't say anything about what the does. You just mention a corner
> > case which doesn't happen to work. Please, add full changelog explaining
> > what this patch is about and please add examples of how an event
> > entry would look like in the schema and maybe you could also add the
> > important parts of a generated event function. Also, please add a
> > "event" section to docs/writing-qmp-commands.txt (in a different patch).
> 
>    OK.
> 
> >
> > Secondly, for changes like this it's a very good idea to provide
> > conversion examples (as patches, not as changelog doc) at least
> > one or two so that we can see how it will look like.
> >
> 
>    Will add some in the intro part. By the way I think test
> cases in patch 3 shows a bit.

I didn't look at the test to be honest (it didn't apply and I
concentrated on the second patch). Having a test is awesome, but
you still have to provide at least one conversion so that we can
see how it will look like.

> 
> > More below.
> >
> 
> >> Signed-off-by: Wenchao Xia <xiawenc@linux.vnet.ibm.com>
> >> ---
> >>   Makefile              |    9 +-
> >>   Makefile.objs         |    2 +-
> >>   qapi/Makefile.objs    |    1 +
> >>   scripts/qapi-event.py |  355 +++++++++++++++++++++++++++++++++++++++++++++++++
> >>   4 files changed, 363 insertions(+), 4 deletions(-)
> >>   create mode 100644 scripts/qapi-event.py
> >>
> >> diff --git a/Makefile b/Makefile
> >> index b15003f..a3e465f 100644
> >> --- a/Makefile
> >> +++ b/Makefile
> >> @@ -38,8 +38,8 @@ endif
> >>   endif
> >>
> >>   GENERATED_HEADERS = config-host.h qemu-options.def
> >> -GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h
> >> -GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c
> >> +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 += trace/generated-events.h
> >>   GENERATED_SOURCES += trace/generated-events.c
> >> @@ -178,7 +178,7 @@ Makefile: $(version-obj-y) $(version-lobj-y)
> >>   # Build libraries
> >>
> >>   libqemustub.a: $(stub-obj-y)
> >> -libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o
> >> +libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o qapi-event.o
> >>
> >>   ######################################################################
> >>
> >> @@ -219,6 +219,9 @@ $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
> >>   qapi-visit.c qapi-visit.h :\
> >>   $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
> >>   	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o "." -b < $<, "  GEN   $@")
> >> +qapi-event.c qapi-event.h :\
> >> +$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-event.py $(qapi-py)
> >> +	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py $(gen-out-type) -o "." -b < $<, "  GEN   $@")
> >>   qmp-commands.h qmp-marshal.c :\
> >>   $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
> >>   	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -m -o "." < $<, "  GEN   $@")
> >> diff --git a/Makefile.objs b/Makefile.objs
> >> index 2b6c1fe..33f5950 100644
> >> --- a/Makefile.objs
> >> +++ b/Makefile.objs
> >> @@ -12,7 +12,7 @@ block-obj-y += main-loop.o iohandler.o qemu-timer.o
> >>   block-obj-$(CONFIG_POSIX) += aio-posix.o
> >>   block-obj-$(CONFIG_WIN32) += aio-win32.o
> >>   block-obj-y += block/
> >> -block-obj-y += qapi-types.o qapi-visit.o
> >> +block-obj-y += qapi-types.o qapi-visit.o qapi-event.o
> >>   block-obj-y += qemu-io-cmds.o
> >>
> >>   block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
> >> diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
> >> index 1f9c973..d14b769 100644
> >> --- a/qapi/Makefile.objs
> >> +++ b/qapi/Makefile.objs
> >> @@ -3,3 +3,4 @@ util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
> >>   util-obj-y += string-input-visitor.o string-output-visitor.o
> >>
> >>   util-obj-y += opts-visitor.o
> >> +util-obj-y += qmp-event.o
> >> diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
> >> new file mode 100644
> >> index 0000000..4c6a0fe
> >
> > I didn't review this hunk, but this series doesn't build for me:
> >
> >    CC    qapi/string-output-visitor.o
> >    CC    qapi/opts-visitor.o
> > make: *** No rule to make target `qapi/qmp-event.o', needed by `libqemuutil.a'.  Stop.
> > ~/work/src/upstream/qmp-unstable/build (tmp|AM)/
> >
>    A draft file I forgot to remove in Makefile, will fix.
> 
> >> --- /dev/null
> >> +++ b/scripts/qapi-event.py
> >> @@ -0,0 +1,355 @@
> >> +#
> >> +# QAPI event generator
> >> +#
> >> +# Copyright IBM, Corp. 2013
> >> +#
> >> +# Authors:
> >> +#  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
> >> +#
> >> +# This work is licensed under the terms of the GNU GPLv2.
> >> +# See the COPYING.LIB file in the top-level directory.
> >> +
> >> +from ordereddict import OrderedDict
> >> +from qapi import *
> >> +import sys
> >> +import os
> >> +import getopt
> >> +import errno
> >> +
> >> +def _generate_event_api_name_with_params(event_name, params):
> >> +    api_name = "void qapi_event_gen_%s(" % c_fun(event_name);
> >
> > I'd prefer a name like qmp_notify_NAME() or qmp_send_event_NAME().
> >
> 
>    do you think NAME should be capitalized as:
> qmp_notify_SHUTDOWN()?

No way :) This kind of detail of the wire format shouldn't be part of
the C interface (and this is an unfortunate detail, btw).

> >> +    l = len(api_name)
> >> +
> >> +    for argname, argentry, optional, structured in parse_args(params):
> >> +        if structured:
> >> +            sys.stderr.write("Nested structure define in event is not "
> >> +                             "supported now, event '%s', argname '%s'\n" %
> >> +                             (event_name, argname))
> >> +            sys.exit(1)
> >> +            continue
> >> +
> >> +        if optional:
> >> +            api_name += "bool has_%s,\n" % c_var(argname)
> >> +            api_name += "".ljust(l)
> >> +
> >> +        if argentry == "str":
> >> +            api_name += "const "
> >> +        api_name += "%s %s,\n" % (c_type(argentry), c_var(argname))
> >> +        api_name += "".ljust(l)
> >> +
> >> +    api_name += "Error **errp)"
> >> +    return api_name;
> >> +
> >> +def _generate_event_implement_with_params(api_name, event_name, params):
> >> +    ret = mcgen("""
> >> +
> >> +%(api_name)s
> >> +{
> >> +    QmpOutputVisitor *qov;
> >> +    Visitor *v;
> >> +    Error *err = NULL;
> >
> > We usually call it *local_err;
> >
> 
>    OK.
> 
> >> +    QObject *obj;
> >> +    QDict *qmp = NULL;
> >
> > It's not needed to initialize qmp.
> >
> 
>    Will fix.
> 
> >> +
> >> +    if (!qapi_event_functions.emit) {
> >
> > Better to return an error here instead of silently failing.
> >
> 
>    The purpose is allowing emit=NULL and skip event code in that case.

But the code will do nothing and the caller won't know that.

Actually, I wonder if the code should even abort() in such a case,
as emit=NULL would be a programming today.

> If err is set, caller can't distinguish it from real error case.
> caller:
>      qmp_event_notify_SHUTDOWN(&err);
>      if (error_is_set(&err)) {
>          ...
>      }
> 
>      err is always set when emit=NULL, but we may allow emit=NULL,
> for example, qemu-img will have emit=NULL.
> 
> >> +        return;
> >> +    }
> >> +
> >> +    qmp = qdict_new();
> >> +    qdict_put(qmp, "event", qstring_from_str("%(event_name)s"));
> >> +    timestamp_put(qmp);
> >
> > Maybe it's a good idea to move this to a function? Say
> > Qdict *qmp_build_event_dict(const char *event_name)?
> >
> 
>    Seems good, will use it.
> 
> >> +
> >> +    qov = qmp_output_visitor_new();
> >> +    g_assert(qov);
> >> +
> >> +    v = qmp_output_get_visitor(qov);
> >> +    g_assert(v);
> >> +
> >> +    /* Fake visit, as if all member are under a structure */
> >> +    visit_start_struct(v, NULL, "", "%(event_name)s", 0, &err);
> >> +    if (error_is_set(&err)) {
> >> +        goto clean;
> >> +    }
> >> +
> >> +""",
> >> +                api_name = api_name,
> >> +                event_name = event_name)
> >> +
> >> +    for argname, argentry, optional, structured in parse_args(params):
> >> +        if structured:
> >> +            sys.stderr.write("Nested structure define in event is not "
> >> +                             "supported now, event '%s', argname '%s'\n" %
> >> +                             (event_name, argname))
> >> +            sys.exit(1)
> >> +            continue
> >> +
> >> +        if optional:
> >> +            ret += mcgen("""
> >> +    if (has_%(var)s) {
> >> +""",
> >> +                         var = c_var(argname))
> >> +            push_indent()
> >> +
> >> +        if argentry == "str":
> >> +            var_type = "(char **)"
> >> +        else:
> >> +            var_type = ""
> >> +
> >> +        ret += mcgen("""
> >> +    visit_type_%(type)s(v, %(var_type)s&%(var)s, "%(name)s", &err);
> >> +    if (error_is_set(&err)) {
> >> +        goto clean;
> >> +    }
> >> +""",
> >> +                     var_type = var_type,
> >> +                     var = c_var(argname),
> >> +                     type = type_name(argentry),
> >> +                     name = argname)
> >> +
> >> +        if optional:
> >> +            pop_indent()
> >> +            ret += mcgen("""
> >> +    }
> >> +""")
> >> +
> >> +    ret += mcgen("""
> >> +
> >> +    visit_end_struct(v, &err);
> >> +    if (error_is_set(&err)) {
> >> +        goto clean;
> >> +    }
> >> +
> >> +    obj = qmp_output_get_qobject(qov);
> >> +    g_assert(obj != NULL);
> >> +
> >> +    qdict_put_obj(qmp, "data", obj);
> >> +
> >> +    qapi_event_functions.emit(qmp, &err);
> >> +
> >> + clean:
> >> +    error_propagate(errp, err);
> >> +    qmp_output_visitor_cleanup(qov);
> >> +    QDECREF(qmp);
> >> +}
> >> +""")
> >> +
> >> +    return ret
> >> +
> >> +def _generate_event_api_name(event_name):
> >> +    return "void qapi_event_gen_%s(Error **errp)" % c_fun(event_name);
> >> +
> >> +def _generate_event_implement(api_name, event_name):
> >> +    return mcgen("""
> >> +
> >> +%(api_name)s
> >> +{
> >> +    QDict *qmp = NULL;
> >
> > It's not needed to initialize qmp.
> >
> 
>    OK.
> 
> >> +
> >> +    if (!qapi_event_functions.emit) {
> >> +        return;
> >> +    }
> >> +
> >> +    qmp = qdict_new();
> >> +    qdict_put(qmp, "event", qstring_from_str("%(event_name)s"));
> >> +    timestamp_put(qmp);
> >> +
> >> +    qapi_event_functions.emit(qmp, errp);
> >> +
> >> +    QDECREF(qmp);
> >> +}
> >> +""",
> >> +                api_name = api_name,
> >> +                event_name = event_name)
> >> +
> >> +
> >> +def generate_event_declaration(event_name, params):
> >> +    if params and len(params) > 0:
> >> +        api_name = _generate_event_api_name_with_params(event_name, params)
> >> +    else:
> >> +        api_name = _generate_event_api_name(event_name)
> >> +
> >> +    return mcgen('''
> >> +
> >> +%(api_name)s;
> >> +''',
> >> +                 api_name = api_name)
> >> +
> >> +def generate_event_implement(event_name, params):
> >> +    if params and len(params) > 0:
> >> +        api_name = _generate_event_api_name_with_params(event_name, params)
> >> +        ret =  _generate_event_implement_with_params(api_name,
> >> +                                                     event_name,
> >> +                                                     params)
> >> +
> >> +    else:
> >> +        api_name = _generate_event_api_name(event_name)
> >> +        ret =  _generate_event_implement(api_name,
> >> +                                         event_name)
> >> +    return ret
> >> +
> >> +try:
> >> +    opts, args = getopt.gnu_getopt(sys.argv[1:], "chbp:o:",
> >> +                                   ["source", "header", "builtins", "prefix=",
> >> +                                    "output-dir="])
> >> +except getopt.GetoptError, err:
> >> +    print str(err)
> >> +    sys.exit(1)
> >> +
> >> +output_dir = ""
> >> +prefix = ""
> >> +c_file = 'qapi-event.c'
> >> +h_file = 'qapi-event.h'
> >> +
> >> +do_c = False
> >> +do_h = False
> >> +do_builtins = False
> >> +
> >> +for o, a in opts:
> >> +    if o in ("-p", "--prefix"):
> >> +        prefix = a
> >> +    elif o in ("-o", "--output-dir"):
> >> +        output_dir = a + "/"
> >> +    elif o in ("-c", "--source"):
> >> +        do_c = True
> >> +    elif o in ("-h", "--header"):
> >> +        do_h = True
> >> +    elif o in ("-b", "--builtins"):
> >> +        do_builtins = True
> >> +
> >> +if not do_c and not do_h:
> >> +    do_c = True
> >> +    do_h = True
> >> +
> >> +c_file = output_dir + prefix + c_file
> >> +h_file = output_dir + prefix + h_file
> >> +
> >> +try:
> >> +    os.makedirs(output_dir)
> >> +except os.error, e:
> >> +    if e.errno != errno.EEXIST:
> >> +        raise
> >> +
> >> +def maybe_open(really, name, opt):
> >> +    if really:
> >> +        return open(name, opt)
> >> +    else:
> >> +        import StringIO
> >> +        return StringIO.StringIO()
> >> +
> >> +fdef = maybe_open(do_c, c_file, 'w')
> >> +fdecl = maybe_open(do_h, h_file, 'w')
> >> +
> >> +fdef.write(mcgen('''
> >> +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
> >> +
> >> +/*
> >> + * schema-defined QAPI event functions
> >> + *
> >> + * Copyright IBM, Corp. 2013
> >> + *
> >> + * Authors:
> >> + *  Wenchao Xia   <xiawenc@linux.vnet.ibm.com>
> >> + *
> >> + * 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.
> >> + *
> >> + */
> >> +
> >> +#include "qemu-common.h"
> >> +#include "%(header)s"
> >> +#include "%(prefix)sqapi-visit.h"
> >> +#include "qapi/qmp-output-visitor.h"
> >> +#include "qapi/qmp/qstring.h"
> >> +#include "qapi/qmp/qjson.h"
> >> +
> >> +#ifdef _WIN32
> >> +#include "sysemu/os-win32.h"
> >> +#endif
> >> +
> >> +#ifdef CONFIG_POSIX
> >> +#include "sysemu/os-posix.h"
> >> +#endif
> >> +
> >> +typedef struct QAPIEventFunctions {
> >> +    void (*emit)(QDict *dict, Error **errp);
> >> +} QAPIEventFunctions;
> >> +
> >> +QAPIEventFunctions qapi_event_functions;
> >> +
> >> +void qapi_event_set_func_emit(qapi_event_emit emit)
> >> +{
> >> +    qapi_event_functions.emit = emit;
> >> +}
> >> +
> >> +static void timestamp_put(QDict *qdict)
> >> +{
> >> +    int err;
> >> +    QObject *obj;
> >> +    qemu_timeval tv;
> >> +
> >> +    err = qemu_gettimeofday(&tv);
> >> +    if (err < 0)
> >> +        return;
> >> +
> >> +    obj = qobject_from_jsonf("{ 'seconds': %(p)s" PRId64 ", "
> >> +                                "'microseconds': %(p)s" PRId64 " }",
> >> +                                (int64_t) tv.tv_sec, (int64_t) tv.tv_usec);
> >> +    qdict_put_obj(qdict, "timestamp", obj);
> >> +}
> >
> > Any special reason to generate these functions? Maybe they could be
> > put in qmp.c instead?
> >
> 
>    No, I just found no better place to go. qmp.c seems irrelevent to
> event, how about new file qapi/qmp-event.c?(which I used before and
> triggered the build error you met)

That works for me.

> 
> >> +
> >> +''',
> >> +                 prefix=prefix, header=basename(h_file), p="%"))
> >> +
> >> +fdecl.write(mcgen('''
> >> +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
> >> +
> >> +/*
> >> + * schema-defined QAPI event function
> >> + *
> >> + * Copyright IBM, Corp. 2013
> >> + *
> >> + * Authors:
> >> + *  Wenchao Xia  <xiawenc@linux.vnet.ibm.com>
> >> + *
> >> + * 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.
> >> + *
> >> + */
> >> +
> >> +#ifndef %(guard)s
> >> +#define %(guard)s
> >> +
> >> +#include "qapi/qmp/qdict.h"
> >> +#include "qapi/error.h"
> >> +#include "qapi/visitor.h"
> >> +#include "%(prefix)sqapi-types.h"
> >> +
> >> +typedef void (*qapi_event_emit)(QDict *d, Error **errp);
> >> +
> >> +void qapi_event_set_func_emit(qapi_event_emit emit);
> >> +
> >> +''',
> >> +                  prefix=prefix, guard=guardname(h_file)))
> >> +
> >> +exprs = parse_schema(sys.stdin)
> >> +
> >> +for expr in exprs:
> >> +    if expr.has_key('event'):
> >> +        event_name = expr['event']
> >> +        params = expr.get('data')
> >> +
> >> +        ret = generate_event_declaration(event_name, params)
> >> +        fdecl.write(ret)
> >> +
> >> +        ret = generate_event_implement(event_name, params)
> >> +        fdef.write(ret)
> >> +
> >> +fdecl.write('''
> >> +#endif
> >> +''')
> >> +
> >> +fdecl.flush()
> >> +fdecl.close()
> >> +
> >> +fdef.flush()
> >> +fdef.close()
> >
>
Wayne Xia Dec. 2, 2013, 6:48 a.m. UTC | #4
>>
>>>> +
>>>> +    if (!qapi_event_functions.emit) {
>>>
>>> Better to return an error here instead of silently failing.
>>>
>>
>>     The purpose is allowing emit=NULL and skip event code in that case.
>
> But the code will do nothing and the caller won't know that.
>
   Now the caller also won't know that useless code will be executed,
when qemu-img link with stub of monitor_event functions. :)

> Actually, I wonder if the code should even abort() in such a case,
> as emit=NULL would be a programming today.
>

   I am not sure why the code should always do something. The code may
actually take CPU resource to do nothing meanful, such as build up a
qdict and release it later, when emit is not a valid function. So I
did this as an improvement: check emit function ahead to escape useless
work.
Wayne Xia Dec. 13, 2013, 3 a.m. UTC | #5
于 2013/12/2 14:48, Wenchao Xia 写道:
>
>>>
>>>>> +
>>>>> +    if (!qapi_event_functions.emit) {
>>>>
>>>> Better to return an error here instead of silently failing.
>>>>
>>>
>>>     The purpose is allowing emit=NULL and skip event code in that case.
>>
>> But the code will do nothing and the caller won't know that.
>>
>    Now the caller also won't know that useless code will be executed,
> when qemu-img link with stub of monitor_event functions. :)
>
>> Actually, I wonder if the code should even abort() in such a case,
>> as emit=NULL would be a programming today.
>>
>
>    I am not sure why the code should always do something. The code may
> actually take CPU resource to do nothing meanful, such as build up a
> qdict and release it later, when emit is not a valid function. So I
> did this as an improvement: check emit function ahead to escape useless
> work.
>
>

   Luiz, do you agree with me?
Eric Blake Dec. 13, 2013, 1:31 p.m. UTC | #6
On 11/12/2013 06:44 PM, Wenchao Xia wrote:
> Nested structure is not supported now, so following define is not valid:
> { 'event': 'EVENT_C',
>   'data': { 'a': { 'a_a', 'str', 'a_b', 'str' }, 'b': 'int' }

But what IS valid?  You need to document this in docs/qapi-code-gen.txt
at a bare minimum.

This patch series is hard to review, and still has the RFC subject line.
 At this point, I think it's worth rebasing and resending what you have;
even if it needs more review, it will at least get it to a state that is
easier to review.

> +++ b/scripts/qapi-event.py
> @@ -0,0 +1,355 @@
> +#
> +# QAPI event generator
> +#
> +# Copyright IBM, Corp. 2013
> +#
> +# Authors:
> +#  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
> +#
> +# This work is licensed under the terms of the GNU GPLv2.

Can you please use GPLv2+ (that is, add the "or later" clause)?  We
already have GPLv2-only code, but I don't want to increase the size of
that unfortunate license choice.
Kevin Wolf Dec. 13, 2013, 1:43 p.m. UTC | #7
Am 13.12.2013 um 14:31 hat Eric Blake geschrieben:
> On 11/12/2013 06:44 PM, Wenchao Xia wrote:
> > +++ b/scripts/qapi-event.py
> > @@ -0,0 +1,355 @@
> > +#
> > +# QAPI event generator
> > +#
> > +# Copyright IBM, Corp. 2013
> > +#
> > +# Authors:
> > +#  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
> > +#
> > +# This work is licensed under the terms of the GNU GPLv2.
> 
> Can you please use GPLv2+ (that is, add the "or later" clause)?  We
> already have GPLv2-only code, but I don't want to increase the size of
> that unfortunate license choice.

In fact, it's even worse:

+# This work is licensed under the terms of the GNU GPLv2.
+# See the COPYING.LIB file in the top-level directory.

These two lines contradict each other, COPYING.LIB contains the
LGPL 2.1. The same bad license header is in the other QAPI generator
scripts, so it's only copy&paste here.

This doesn't make things easier, because if things are copied, the
license of the source must be respected. And it seems rather dubious to
me what this license actually is. If it's GPLv2-only, we can't just
change it in the new copy.

Kevin
Wayne Xia Dec. 16, 2013, 2:50 a.m. UTC | #8
于 2013/12/13 21:43, Kevin Wolf 写道:
> Am 13.12.2013 um 14:31 hat Eric Blake geschrieben:
>> On 11/12/2013 06:44 PM, Wenchao Xia wrote:
>>> +++ b/scripts/qapi-event.py
>>> @@ -0,0 +1,355 @@
>>> +#
>>> +# QAPI event generator
>>> +#
>>> +# Copyright IBM, Corp. 2013
>>> +#
>>> +# Authors:
>>> +#  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
>>> +#
>>> +# This work is licensed under the terms of the GNU GPLv2.
>>
>> Can you please use GPLv2+ (that is, add the "or later" clause)?  We
>> already have GPLv2-only code, but I don't want to increase the size of
>> that unfortunate license choice.
>
> In fact, it's even worse:
>
> +# This work is licensed under the terms of the GNU GPLv2.
> +# See the COPYING.LIB file in the top-level directory.
>
> These two lines contradict each other, COPYING.LIB contains the
> LGPL 2.1. The same bad license header is in the other QAPI generator
> scripts, so it's only copy&paste here.
>
> This doesn't make things easier, because if things are copied, the
> license of the source must be respected. And it seems rather dubious to
> me what this license actually is. If it's GPLv2-only, we can't just
> change it in the new copy.
>
> Kevin
>
   ah..I am bad in license problem, will use the doc as LGPL from other
file.
Wayne Xia Dec. 16, 2013, 2:51 a.m. UTC | #9
于 2013/12/13 21:31, Eric Blake 写道:
> On 11/12/2013 06:44 PM, Wenchao Xia wrote:
>> Nested structure is not supported now, so following define is not valid:
>> { 'event': 'EVENT_C',
>>    'data': { 'a': { 'a_a', 'str', 'a_b', 'str' }, 'b': 'int' }
>
> But what IS valid?  You need to document this in docs/qapi-code-gen.txt
> at a bare minimum.
>
> This patch series is hard to review, and still has the RFC subject line.
>   At this point, I think it's worth rebasing and resending what you have;
> even if it needs more review, it will at least get it to a state that is
> easier to review.
>
   Sure, I will wait some time and respin.


>> +++ b/scripts/qapi-event.py
>> @@ -0,0 +1,355 @@
>> +#
>> +# QAPI event generator
>> +#
>> +# Copyright IBM, Corp. 2013
>> +#
>> +# Authors:
>> +#  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
>> +#
>> +# This work is licensed under the terms of the GNU GPLv2.
>
> Can you please use GPLv2+ (that is, add the "or later" clause)?  We
> already have GPLv2-only code, but I don't want to increase the size of
> that unfortunate license choice.
>
Markus Armbruster Dec. 16, 2013, 9:04 a.m. UTC | #10
Wenchao Xia <xiawenc@linux.vnet.ibm.com> writes:

> 于 2013/12/13 21:43, Kevin Wolf 写道:
>> Am 13.12.2013 um 14:31 hat Eric Blake geschrieben:
>>> On 11/12/2013 06:44 PM, Wenchao Xia wrote:
>>>> +++ b/scripts/qapi-event.py
>>>> @@ -0,0 +1,355 @@
>>>> +#
>>>> +# QAPI event generator
>>>> +#
>>>> +# Copyright IBM, Corp. 2013
>>>> +#
>>>> +# Authors:
>>>> +#  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
>>>> +#
>>>> +# This work is licensed under the terms of the GNU GPLv2.
>>>
>>> Can you please use GPLv2+ (that is, add the "or later" clause)?  We
>>> already have GPLv2-only code, but I don't want to increase the size of
>>> that unfortunate license choice.
>>
>> In fact, it's even worse:
>>
>> +# This work is licensed under the terms of the GNU GPLv2.
>> +# See the COPYING.LIB file in the top-level directory.
>>
>> These two lines contradict each other, COPYING.LIB contains the
>> LGPL 2.1. The same bad license header is in the other QAPI generator
>> scripts, so it's only copy&paste here.
>>
>> This doesn't make things easier, because if things are copied, the
>> license of the source must be respected. And it seems rather dubious to
>> me what this license actually is. If it's GPLv2-only, we can't just
>> change it in the new copy.
>>
>> Kevin
>>
>   ah..I am bad in license problem, will use the doc as LGPL from other
> file.

Please use GPLv2+ unless you have a specific reason for another license.
Markus Armbruster Dec. 16, 2013, 9:13 a.m. UTC | #11
[Licensing problem, cc: Anthony]

Kevin Wolf <kwolf@redhat.com> writes:

> Am 13.12.2013 um 14:31 hat Eric Blake geschrieben:
>> On 11/12/2013 06:44 PM, Wenchao Xia wrote:
>> > +++ b/scripts/qapi-event.py
>> > @@ -0,0 +1,355 @@
>> > +#
>> > +# QAPI event generator
>> > +#
>> > +# Copyright IBM, Corp. 2013
>> > +#
>> > +# Authors:
>> > +#  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
>> > +#
>> > +# This work is licensed under the terms of the GNU GPLv2.
>> 
>> Can you please use GPLv2+ (that is, add the "or later" clause)?  We
>> already have GPLv2-only code, but I don't want to increase the size of
>> that unfortunate license choice.
>
> In fact, it's even worse:
>
> +# This work is licensed under the terms of the GNU GPLv2.
> +# See the COPYING.LIB file in the top-level directory.
>
> These two lines contradict each other, COPYING.LIB contains the
> LGPL 2.1. The same bad license header is in the other QAPI generator
> scripts, so it's only copy&paste here.

Specifically:

    File                        Commit
    scripts/qapi-commands.py    c17d9908
    scripts/qapi-visit.py       fb3182ce
    scripts/qapi-types.py       06d64c62
    scripts/qapi.py             0f923be2

All four from Michael Roth via Luiz.

> This doesn't make things easier, because if things are copied, the
> license of the source must be respected. And it seems rather dubious to
> me what this license actually is. If it's GPLv2-only, we can't just
> change it in the new copy.

IANAL, and I wouldn't dare to judge which of the two conflicting license
claims takes precedence.  Possibly neither, and then the files might
technically not be distributable.

Anyway, this mess needs to be addressed.  Michael, what was your
*intended* license?

If it wasn't GPLv2+, then why?

Do we need formal ACKs from all contributors to fix the licensing
comment in these four files?
Markus Armbruster Jan. 7, 2014, 12:14 p.m. UTC | #12
Ping?

Markus Armbruster <armbru@redhat.com> writes:

> [Licensing problem, cc: Anthony]
>
> Kevin Wolf <kwolf@redhat.com> writes:
>
>> Am 13.12.2013 um 14:31 hat Eric Blake geschrieben:
>>> On 11/12/2013 06:44 PM, Wenchao Xia wrote:
>>> > +++ b/scripts/qapi-event.py
>>> > @@ -0,0 +1,355 @@
>>> > +#
>>> > +# QAPI event generator
>>> > +#
>>> > +# Copyright IBM, Corp. 2013
>>> > +#
>>> > +# Authors:
>>> > +#  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
>>> > +#
>>> > +# This work is licensed under the terms of the GNU GPLv2.
>>> 
>>> Can you please use GPLv2+ (that is, add the "or later" clause)?  We
>>> already have GPLv2-only code, but I don't want to increase the size of
>>> that unfortunate license choice.
>>
>> In fact, it's even worse:
>>
>> +# This work is licensed under the terms of the GNU GPLv2.
>> +# See the COPYING.LIB file in the top-level directory.
>>
>> These two lines contradict each other, COPYING.LIB contains the
>> LGPL 2.1. The same bad license header is in the other QAPI generator
>> scripts, so it's only copy&paste here.
>
> Specifically:
>
>     File                        Commit
>     scripts/qapi-commands.py    c17d9908
>     scripts/qapi-visit.py       fb3182ce
>     scripts/qapi-types.py       06d64c62
>     scripts/qapi.py             0f923be2
>
> All four from Michael Roth via Luiz.
>
>> This doesn't make things easier, because if things are copied, the
>> license of the source must be respected. And it seems rather dubious to
>> me what this license actually is. If it's GPLv2-only, we can't just
>> change it in the new copy.
>
> IANAL, and I wouldn't dare to judge which of the two conflicting license
> claims takes precedence.  Possibly neither, and then the files might
> technically not be distributable.
>
> Anyway, this mess needs to be addressed.  Michael, what was your
> *intended* license?
>
> If it wasn't GPLv2+, then why?
>
> Do we need formal ACKs from all contributors to fix the licensing
> comment in these four files?
Markus Armbruster Jan. 13, 2014, 10:08 a.m. UTC | #13
Ping^2!

Markus Armbruster <armbru@redhat.com> writes:

> Ping?
>
> Markus Armbruster <armbru@redhat.com> writes:
>
>> [Licensing problem, cc: Anthony]
>>
>> Kevin Wolf <kwolf@redhat.com> writes:
>>
>>> Am 13.12.2013 um 14:31 hat Eric Blake geschrieben:
>>>> On 11/12/2013 06:44 PM, Wenchao Xia wrote:
>>>> > +++ b/scripts/qapi-event.py
>>>> > @@ -0,0 +1,355 @@
>>>> > +#
>>>> > +# QAPI event generator
>>>> > +#
>>>> > +# Copyright IBM, Corp. 2013
>>>> > +#
>>>> > +# Authors:
>>>> > +#  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
>>>> > +#
>>>> > +# This work is licensed under the terms of the GNU GPLv2.
>>>> 
>>>> Can you please use GPLv2+ (that is, add the "or later" clause)?  We
>>>> already have GPLv2-only code, but I don't want to increase the size of
>>>> that unfortunate license choice.
>>>
>>> In fact, it's even worse:
>>>
>>> +# This work is licensed under the terms of the GNU GPLv2.
>>> +# See the COPYING.LIB file in the top-level directory.
>>>
>>> These two lines contradict each other, COPYING.LIB contains the
>>> LGPL 2.1. The same bad license header is in the other QAPI generator
>>> scripts, so it's only copy&paste here.
>>
>> Specifically:
>>
>>     File                        Commit
>>     scripts/qapi-commands.py    c17d9908
>>     scripts/qapi-visit.py       fb3182ce
>>     scripts/qapi-types.py       06d64c62
>>     scripts/qapi.py             0f923be2
>>
>> All four from Michael Roth via Luiz.
>>
>>> This doesn't make things easier, because if things are copied, the
>>> license of the source must be respected. And it seems rather dubious to
>>> me what this license actually is. If it's GPLv2-only, we can't just
>>> change it in the new copy.
>>
>> IANAL, and I wouldn't dare to judge which of the two conflicting license
>> claims takes precedence.  Possibly neither, and then the files might
>> technically not be distributable.
>>
>> Anyway, this mess needs to be addressed.  Michael, what was your
>> *intended* license?
>>
>> If it wasn't GPLv2+, then why?
>>
>> Do we need formal ACKs from all contributors to fix the licensing
>> comment in these four files?
Wayne Xia Jan. 15, 2014, 1:47 a.m. UTC | #14
于 2014/1/13 18:08, Markus Armbruster 写道:
> Ping^2!
> 
> Markus Armbruster <armbru@redhat.com> writes:
> 
>> Ping?
>>
>> Markus Armbruster <armbru@redhat.com> writes:
>>
>>> [Licensing problem, cc: Anthony]
>>>
>>> Kevin Wolf <kwolf@redhat.com> writes:
>>>
>>>> Am 13.12.2013 um 14:31 hat Eric Blake geschrieben:
>>>>> On 11/12/2013 06:44 PM, Wenchao Xia wrote:
>>>>>> +++ b/scripts/qapi-event.py
>>>>>> @@ -0,0 +1,355 @@
>>>>>> +#
>>>>>> +# QAPI event generator
>>>>>> +#
>>>>>> +# Copyright IBM, Corp. 2013
>>>>>> +#
>>>>>> +# Authors:
>>>>>> +#  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
>>>>>> +#
>>>>>> +# This work is licensed under the terms of the GNU GPLv2.
>>>>>
>>>>> Can you please use GPLv2+ (that is, add the "or later" clause)?  We
>>>>> already have GPLv2-only code, but I don't want to increase the size of
>>>>> that unfortunate license choice.
>>>>
>>>> In fact, it's even worse:
>>>>
>>>> +# This work is licensed under the terms of the GNU GPLv2.
>>>> +# See the COPYING.LIB file in the top-level directory.
>>>>
>>>> These two lines contradict each other, COPYING.LIB contains the
>>>> LGPL 2.1. The same bad license header is in the other QAPI generator
>>>> scripts, so it's only copy&paste here.
>>>
>>> Specifically:
>>>
>>>      File                        Commit
>>>      scripts/qapi-commands.py    c17d9908
>>>      scripts/qapi-visit.py       fb3182ce
>>>      scripts/qapi-types.py       06d64c62
>>>      scripts/qapi.py             0f923be2
>>>
>>> All four from Michael Roth via Luiz.
>>>
>>>> This doesn't make things easier, because if things are copied, the
>>>> license of the source must be respected. And it seems rather dubious to
>>>> me what this license actually is. If it's GPLv2-only, we can't just
>>>> change it in the new copy.
>>>
>>> IANAL, and I wouldn't dare to judge which of the two conflicting license
>>> claims takes precedence.  Possibly neither, and then the files might
>>> technically not be distributable.
>>>
>>> Anyway, this mess needs to be addressed.  Michael, what was your
>>> *intended* license?
>>>
>>> If it wasn't GPLv2+, then why?
>>>
>>> Do we need formal ACKs from all contributors to fix the licensing
>>> comment in these four files?
> 
  I used GPLv2+ in my new files of v2, but not sure about other files.
Michael, do you think other scripts should be changed either?
Michael Roth Jan. 16, 2014, 2:50 a.m. UTC | #15
Quoting Markus Armbruster (2013-12-16 03:13:08)
> [Licensing problem, cc: Anthony]
> 
> Kevin Wolf <kwolf@redhat.com> writes:
> 
> > Am 13.12.2013 um 14:31 hat Eric Blake geschrieben:
> >> On 11/12/2013 06:44 PM, Wenchao Xia wrote:
> >> > +++ b/scripts/qapi-event.py
> >> > @@ -0,0 +1,355 @@
> >> > +#
> >> > +# QAPI event generator
> >> > +#
> >> > +# Copyright IBM, Corp. 2013
> >> > +#
> >> > +# Authors:
> >> > +#  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
> >> > +#
> >> > +# This work is licensed under the terms of the GNU GPLv2.
> >> 
> >> Can you please use GPLv2+ (that is, add the "or later" clause)?  We
> >> already have GPLv2-only code, but I don't want to increase the size of
> >> that unfortunate license choice.
> >
> > In fact, it's even worse:
> >
> > +# This work is licensed under the terms of the GNU GPLv2.
> > +# See the COPYING.LIB file in the top-level directory.
> >
> > These two lines contradict each other, COPYING.LIB contains the
> > LGPL 2.1. The same bad license header is in the other QAPI generator
> > scripts, so it's only copy&paste here.
> 
> Specifically:
> 
>     File                        Commit
>     scripts/qapi-commands.py    c17d9908
>     scripts/qapi-visit.py       fb3182ce
>     scripts/qapi-types.py       06d64c62
>     scripts/qapi.py             0f923be2
> 
> All four from Michael Roth via Luiz.
> 
> > This doesn't make things easier, because if things are copied, the
> > license of the source must be respected. And it seems rather dubious to
> > me what this license actually is. If it's GPLv2-only, we can't just
> > change it in the new copy.
> 
> IANAL, and I wouldn't dare to judge which of the two conflicting license
> claims takes precedence.  Possibly neither, and then the files might
> technically not be distributable.

IAAlsoNAL, but GPLv2 is explicit, whereas the "COPYING.LIB" simply
references a document with no information relevant to GPLv2, so I think a
strong case can be made that the intended license was GPLv2 and the
"clarification" is effectively a no-op.

> Anyway, this mess needs to be addressed.  Michael, what was your
> *intended* license?

GPLv2 was my intention at least (I meant to reference COPYING). But
I think we need Anthony's ack to be certain, since he was the original
author, and I added the screwed up license header after-the-fact under
the assumption that the code was to be GPLv2.

Here's the original:
http://repo.or.cz/w/qemu/aliguori.git/blob_plain/glib:/scripts/qapi-types.py

> 
> If it wasn't GPLv2+, then why?

This was committed prior to the push to switch to GPLv2+, but I'm fine
with relicensing my contributions as GPLv2+ should we opt to do so, but
I think that's a separate issue.

> 
> Do we need formal ACKs from all contributors to fix the licensing
> comment in these four files?

If we were actually re-licensing then yes (at least, that's what we've done
in the past). To clarify the existing license maybe not, but we should
probably err on the side of caution.

Current list seems to be:

mdroth@loki:~/w/qemu.git$ git log --format="%an: %ae" scripts/qapi* | sort | uniq
Amos Kong: akong@redhat.com
Anthony Liguori: aliguori@us.ibm.com
Anthony Liguori: anthony@codemonkey.ws
Avi Kivity: avi@redhat.com
Blue Swirl: blauwirbel@gmail.com
Cole Robinson: crobinso@redhat.com
Federico Simoncelli: fsimonce@redhat.com
Igor Mammedov: imammedo@redhat.com
Kevin Wolf: kwolf@redhat.com
Laszlo Ersek: lersek@redhat.com
Luiz Capitulino: lcapitulino@redhat.com
Markus Armbruster: armbru@redhat.com
Michael Roth: mdroth@linux.vnet.ibm.com
Paolo Bonzini: pbonzini@redhat.com
Peter Maydell: peter.maydell@linaro.org
Richard Henderson: rth@twiddle.net
Stefan Weil: sw@weilnetz.de
Tomoki Sekiyama: tomoki.sekiyama@hds.com

If we go to that effort, it may make sense to try to re-license to GPLv2+
while we're at it, but either way I think this should be done as a separate
patchset, and shouldn't hold up Wenchao's series. I can send that out, since
it's my screw-up.
Paolo Bonzini Jan. 16, 2014, 11:05 a.m. UTC | #16
Il 16/01/2014 03:50, Michael Roth ha scritto:
> If we go to that effort, it may make sense to try to re-license to GPLv2+
> while we're at it, but either way I think this should be done as a separate
> patchset, and shouldn't hold up Wenchao's series. I can send that out, since
> it's my screw-up.
> 

Removing Red Hat, Blue Swirl and Stefan Weil, who have agreed on a
blanket relicensing of GPLv2-only code to GPLv2+ leaves only half a
dozen people:

Anthony Liguori: anthony@codemonkey.ws (IBM?)
Michael Roth: mdroth@linux.vnet.ibm.com (IBM?)
Peter Maydell: peter.maydell@linaro.org
Richard Henderson: rth@twiddle.net
Tomoki Sekiyama: tomoki.sekiyama@hds.com

Paolo
Markus Armbruster Jan. 30, 2014, 10:35 a.m. UTC | #17
Paolo Bonzini <pbonzini@redhat.com> writes:

> Il 16/01/2014 03:50, Michael Roth ha scritto:
>> If we go to that effort, it may make sense to try to re-license to GPLv2+
>> while we're at it, but either way I think this should be done as a separate
>> patchset, and shouldn't hold up Wenchao's series. I can send that out, since
>> it's my screw-up.
>> 
>
> Removing Red Hat, Blue Swirl and Stefan Weil, who have agreed on a
> blanket relicensing of GPLv2-only code to GPLv2+ leaves only half a
> dozen people:
>
> Anthony Liguori: anthony@codemonkey.ws (IBM?)
> Michael Roth: mdroth@linux.vnet.ibm.com (IBM?)
> Peter Maydell: peter.maydell@linaro.org
> Richard Henderson: rth@twiddle.net
> Tomoki Sekiyama: tomoki.sekiyama@hds.com

I sent a patch.  Now we hunt for the required ACKs.
diff mbox

Patch

diff --git a/Makefile b/Makefile
index b15003f..a3e465f 100644
--- a/Makefile
+++ b/Makefile
@@ -38,8 +38,8 @@  endif
 endif
 
 GENERATED_HEADERS = config-host.h qemu-options.def
-GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h
-GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c
+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 += trace/generated-events.h
 GENERATED_SOURCES += trace/generated-events.c
@@ -178,7 +178,7 @@  Makefile: $(version-obj-y) $(version-lobj-y)
 # Build libraries
 
 libqemustub.a: $(stub-obj-y)
-libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o
+libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o qapi-event.o
 
 ######################################################################
 
@@ -219,6 +219,9 @@  $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
 qapi-visit.c qapi-visit.h :\
 $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o "." -b < $<, "  GEN   $@")
+qapi-event.c qapi-event.h :\
+$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-event.py $(qapi-py)
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py $(gen-out-type) -o "." -b < $<, "  GEN   $@")
 qmp-commands.h qmp-marshal.c :\
 $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -m -o "." < $<, "  GEN   $@")
diff --git a/Makefile.objs b/Makefile.objs
index 2b6c1fe..33f5950 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -12,7 +12,7 @@  block-obj-y += main-loop.o iohandler.o qemu-timer.o
 block-obj-$(CONFIG_POSIX) += aio-posix.o
 block-obj-$(CONFIG_WIN32) += aio-win32.o
 block-obj-y += block/
-block-obj-y += qapi-types.o qapi-visit.o
+block-obj-y += qapi-types.o qapi-visit.o qapi-event.o
 block-obj-y += qemu-io-cmds.o
 
 block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index 1f9c973..d14b769 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -3,3 +3,4 @@  util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
 util-obj-y += string-input-visitor.o string-output-visitor.o
 
 util-obj-y += opts-visitor.o
+util-obj-y += qmp-event.o
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
new file mode 100644
index 0000000..4c6a0fe
--- /dev/null
+++ b/scripts/qapi-event.py
@@ -0,0 +1,355 @@ 
+#
+# QAPI event generator
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPLv2.
+# See the COPYING.LIB file in the top-level directory.
+
+from ordereddict import OrderedDict
+from qapi import *
+import sys
+import os
+import getopt
+import errno
+
+def _generate_event_api_name_with_params(event_name, params):
+    api_name = "void qapi_event_gen_%s(" % c_fun(event_name);
+    l = len(api_name)
+
+    for argname, argentry, optional, structured in parse_args(params):
+        if structured:
+            sys.stderr.write("Nested structure define in event is not "
+                             "supported now, event '%s', argname '%s'\n" %
+                             (event_name, argname))
+            sys.exit(1)
+            continue
+
+        if optional:
+            api_name += "bool has_%s,\n" % c_var(argname)
+            api_name += "".ljust(l)
+
+        if argentry == "str":
+            api_name += "const "
+        api_name += "%s %s,\n" % (c_type(argentry), c_var(argname))
+        api_name += "".ljust(l)
+
+    api_name += "Error **errp)"
+    return api_name;
+
+def _generate_event_implement_with_params(api_name, event_name, params):
+    ret = mcgen("""
+
+%(api_name)s
+{
+    QmpOutputVisitor *qov;
+    Visitor *v;
+    Error *err = NULL;
+    QObject *obj;
+    QDict *qmp = NULL;
+
+    if (!qapi_event_functions.emit) {
+        return;
+    }
+
+    qmp = qdict_new();
+    qdict_put(qmp, "event", qstring_from_str("%(event_name)s"));
+    timestamp_put(qmp);
+
+    qov = qmp_output_visitor_new();
+    g_assert(qov);
+
+    v = qmp_output_get_visitor(qov);
+    g_assert(v);
+
+    /* Fake visit, as if all member are under a structure */
+    visit_start_struct(v, NULL, "", "%(event_name)s", 0, &err);
+    if (error_is_set(&err)) {
+        goto clean;
+    }
+
+""",
+                api_name = api_name,
+                event_name = event_name)
+
+    for argname, argentry, optional, structured in parse_args(params):
+        if structured:
+            sys.stderr.write("Nested structure define in event is not "
+                             "supported now, event '%s', argname '%s'\n" %
+                             (event_name, argname))
+            sys.exit(1)
+            continue
+
+        if optional:
+            ret += mcgen("""
+    if (has_%(var)s) {
+""",
+                         var = c_var(argname))
+            push_indent()
+
+        if argentry == "str":
+            var_type = "(char **)"
+        else:
+            var_type = ""
+
+        ret += mcgen("""
+    visit_type_%(type)s(v, %(var_type)s&%(var)s, "%(name)s", &err);
+    if (error_is_set(&err)) {
+        goto clean;
+    }
+""",
+                     var_type = var_type,
+                     var = c_var(argname),
+                     type = type_name(argentry),
+                     name = argname)
+
+        if optional:
+            pop_indent()
+            ret += mcgen("""
+    }
+""")
+
+    ret += mcgen("""
+
+    visit_end_struct(v, &err);
+    if (error_is_set(&err)) {
+        goto clean;
+    }
+
+    obj = qmp_output_get_qobject(qov);
+    g_assert(obj != NULL);
+
+    qdict_put_obj(qmp, "data", obj);
+
+    qapi_event_functions.emit(qmp, &err);
+
+ clean:
+    error_propagate(errp, err);
+    qmp_output_visitor_cleanup(qov);
+    QDECREF(qmp);
+}
+""")
+
+    return ret
+
+def _generate_event_api_name(event_name):
+    return "void qapi_event_gen_%s(Error **errp)" % c_fun(event_name);
+
+def _generate_event_implement(api_name, event_name):
+    return mcgen("""
+
+%(api_name)s
+{
+    QDict *qmp = NULL;
+
+    if (!qapi_event_functions.emit) {
+        return;
+    }
+
+    qmp = qdict_new();
+    qdict_put(qmp, "event", qstring_from_str("%(event_name)s"));
+    timestamp_put(qmp);
+
+    qapi_event_functions.emit(qmp, errp);
+
+    QDECREF(qmp);
+}
+""",
+                api_name = api_name,
+                event_name = event_name)
+
+
+def generate_event_declaration(event_name, params):
+    if params and len(params) > 0:
+        api_name = _generate_event_api_name_with_params(event_name, params)
+    else:
+        api_name = _generate_event_api_name(event_name)
+
+    return mcgen('''
+
+%(api_name)s;
+''',
+                 api_name = api_name)
+
+def generate_event_implement(event_name, params):
+    if params and len(params) > 0:
+        api_name = _generate_event_api_name_with_params(event_name, params)
+        ret =  _generate_event_implement_with_params(api_name,
+                                                     event_name,
+                                                     params)
+
+    else:
+        api_name = _generate_event_api_name(event_name)
+        ret =  _generate_event_implement(api_name,
+                                         event_name)
+    return ret
+
+try:
+    opts, args = getopt.gnu_getopt(sys.argv[1:], "chbp:o:",
+                                   ["source", "header", "builtins", "prefix=",
+                                    "output-dir="])
+except getopt.GetoptError, err:
+    print str(err)
+    sys.exit(1)
+
+output_dir = ""
+prefix = ""
+c_file = 'qapi-event.c'
+h_file = 'qapi-event.h'
+
+do_c = False
+do_h = False
+do_builtins = False
+
+for o, a in opts:
+    if o in ("-p", "--prefix"):
+        prefix = a
+    elif o in ("-o", "--output-dir"):
+        output_dir = a + "/"
+    elif o in ("-c", "--source"):
+        do_c = True
+    elif o in ("-h", "--header"):
+        do_h = True
+    elif o in ("-b", "--builtins"):
+        do_builtins = True
+
+if not do_c and not do_h:
+    do_c = True
+    do_h = True
+
+c_file = output_dir + prefix + c_file
+h_file = output_dir + prefix + h_file
+
+try:
+    os.makedirs(output_dir)
+except os.error, e:
+    if e.errno != errno.EEXIST:
+        raise
+
+def maybe_open(really, name, opt):
+    if really:
+        return open(name, opt)
+    else:
+        import StringIO
+        return StringIO.StringIO()
+
+fdef = maybe_open(do_c, c_file, 'w')
+fdecl = maybe_open(do_h, h_file, 'w')
+
+fdef.write(mcgen('''
+/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+/*
+ * schema-defined QAPI event functions
+ *
+ * Copyright IBM, Corp. 2013
+ *
+ * Authors:
+ *  Wenchao Xia   <xiawenc@linux.vnet.ibm.com>
+ *
+ * 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.
+ *
+ */
+
+#include "qemu-common.h"
+#include "%(header)s"
+#include "%(prefix)sqapi-visit.h"
+#include "qapi/qmp-output-visitor.h"
+#include "qapi/qmp/qstring.h"
+#include "qapi/qmp/qjson.h"
+
+#ifdef _WIN32
+#include "sysemu/os-win32.h"
+#endif
+
+#ifdef CONFIG_POSIX
+#include "sysemu/os-posix.h"
+#endif
+
+typedef struct QAPIEventFunctions {
+    void (*emit)(QDict *dict, Error **errp);
+} QAPIEventFunctions;
+
+QAPIEventFunctions qapi_event_functions;
+
+void qapi_event_set_func_emit(qapi_event_emit emit)
+{
+    qapi_event_functions.emit = emit;
+}
+
+static void timestamp_put(QDict *qdict)
+{
+    int err;
+    QObject *obj;
+    qemu_timeval tv;
+
+    err = qemu_gettimeofday(&tv);
+    if (err < 0)
+        return;
+
+    obj = qobject_from_jsonf("{ 'seconds': %(p)s" PRId64 ", "
+                                "'microseconds': %(p)s" PRId64 " }",
+                                (int64_t) tv.tv_sec, (int64_t) tv.tv_usec);
+    qdict_put_obj(qdict, "timestamp", obj);
+}
+
+''',
+                 prefix=prefix, header=basename(h_file), p="%"))
+
+fdecl.write(mcgen('''
+/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+
+/*
+ * schema-defined QAPI event function
+ *
+ * Copyright IBM, Corp. 2013
+ *
+ * Authors:
+ *  Wenchao Xia  <xiawenc@linux.vnet.ibm.com>
+ *
+ * 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.
+ *
+ */
+
+#ifndef %(guard)s
+#define %(guard)s
+
+#include "qapi/qmp/qdict.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "%(prefix)sqapi-types.h"
+
+typedef void (*qapi_event_emit)(QDict *d, Error **errp);
+
+void qapi_event_set_func_emit(qapi_event_emit emit);
+
+''',
+                  prefix=prefix, guard=guardname(h_file)))
+
+exprs = parse_schema(sys.stdin)
+
+for expr in exprs:
+    if expr.has_key('event'):
+        event_name = expr['event']
+        params = expr.get('data')
+
+        ret = generate_event_declaration(event_name, params)
+        fdecl.write(ret)
+
+        ret = generate_event_implement(event_name, params)
+        fdef.write(ret)
+
+fdecl.write('''
+#endif
+''')
+
+fdecl.flush()
+fdecl.close()
+
+fdef.flush()
+fdef.close()