All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code
@ 2017-07-27 15:41 Marc-André Lureau
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 01/26] qapi: fix type_seen key error Marc-André Lureau
                   ` (26 more replies)
  0 siblings, 27 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

Hi,

In order to clean-up some hacks in qapi (having to unregister commands
at runtime), I proposed a "[PATCH v5 02/20] qapi.py: add a simple #ifdef condition"

(see http://lists.gnu.org/archive/html/qemu-devel/2016-08/msg03106.html).

However, we decided to drop that patch from the series and solve the
problem later. The main issues were:
- the syntax was awkward to the JSON schema and documentation
- the evaluation of the condition was done in the qapi scripts, with
  very limited capability
- each target/config would need different generated files.

Instead, it could defer the #if evaluation to the C-preprocessor.

With this series, top-level qapi JSON entity can take 'if' keys:

{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
  'if': 'defined(TEST_IF_STRUCT)' }

Members can be exploded as dictionnary with 'type'/'if' keys:

{ 'struct': 'TestIfStruct', 'data':
  { 'foo': 'int',
    'bar': { 'type': 'int', 'if': 'defined(TEST_IF_STRUCT_BAR)'} } }

Enum values can be exploded as dictionnary with 'type'/'if' keys:

{ 'enum': 'TestIfEnum', 'data':
  [ 'foo',
    { 'name' : 'bar', 'if': 'defined(TEST_IF_ENUM_BAR)' } ] }

A good benefit from having conditional schema is that introspection
will reflect more accurately the capability of the server. Another
benefit is that it may help to remove some dead code when disabling a
functionality.

Starting from patch "qapi: add conditions to VNC type/commands/events
on the schema", the series demonstrate adding conditions, in order to
remove qmp_unregister_commands_hack(). However, it feels like I
cheated a little bit by using per-target NEED_CPU_H in the headers to
avoid #define poison. The alternative could be to split the headers in
common/target?

There are a lot more things we could make conditional in the QAPI
schema, like pci/kvm/xen/numa/vde/slirp/posix/win32/vsock/lzo etc etc,
however I am still evaluating the implication of such changes both
externally and internally, for those interested, I can share my wip
branch.

Comments welcome,

Marc-André Lureau (26):
  qapi: fix type_seen key error
  qobject: replace dump_qobject() by qobject_to_string()
  qboject: add literal qobject type
  qapi: generate a literal qobject for introspection
  visitor: pass size of strings array to enum visitor
  qapi2texi: minor python code simplification
  qapi: add 'if' condition on top-level schema elements
  qapi: add 'if' condition on enum member values
  qapi: add 'if' condition on struct member
  qapi: add 'if' condition on union variant
  qapi: add 'if' condition on alternate variant
  qapi2texi: add 'If:' section to generated documentation
  qapi2texi: add 'If:' condition to enum values
  qapi2texi: add 'If:' condition to struct members
  qapi2texi: add condition to variants
  qapi: add conditions to VNC type/commands/events on the schema
  qapi: add conditions to SPICE type/commands/events on the schema
  qapi: add conditions to REPLICATION type/commands on the schema
  build-sys: move qapi variables in qapi.mak
  tests/qmp-test: add query-qmp-schema test
  build-sys: make qemu qapi objects per-target
  qapi: make rtc-reset-reinjection depend on TARGET_I386
  qapi: make s390 commands depend on TARGET_S390X
  qapi: make query-gic-capabilities depend on TARGET_ARM
  qapi: make query-cpu-model-expansion depend on s390 or x86
  qapi: make query-cpu-definitions depend on specific targets

 qapi-schema.json                                 |  98 ++++++----
 qapi/event.json                                  |  21 +-
 include/qapi/visitor.h                           |   3 +-
 scripts/qapi.py                                  | 232 ++++++++++++++++++-----
 scripts/qapi-commands.py                         |   4 +-
 scripts/qapi-event.py                            |   4 +-
 scripts/qapi-introspect.py                       | 116 +++++++-----
 scripts/qapi-types.py                            |  55 ++++--
 scripts/qapi-visit.py                            |  29 ++-
 scripts/qapi2texi.py                             |  49 ++---
 include/hw/qdev-core.h                           |   1 +
 include/qapi/qmp/qdict.h                         |   2 +
 include/qapi/qmp/qlist.h                         |   2 +
 include/qapi/qmp/qlit.h                          |  53 ++++++
 include/qapi/qmp/qobject.h                       |   7 +
 include/qom/object.h                             |   4 +
 include/sysemu/arch_init.h                       |  11 --
 qapi/qapi-visit-core.c                           |  23 +--
 backends/hostmem.c                               |   1 +
 block/qapi.c                                     |  90 +--------
 crypto/secret.c                                  |   1 +
 crypto/tlscreds.c                                |   1 +
 hmp.c                                            |  14 +-
 hw/core/qdev-properties.c                        |  11 +-
 migration/colo.c                                 |  14 +-
 monitor.c                                        |  70 +------
 net/filter.c                                     |   1 +
 qmp.c                                            |  72 +------
 qobject/qdict.c                                  |  30 +++
 qobject/qlist.c                                  |  23 +++
 qobject/qlit.c                                   |  53 ++++++
 qobject/qobject.c                                |  21 ++
 qom/object.c                                     |  11 +-
 stubs/arch-query-cpu-def.c                       |  10 -
 stubs/arch-query-cpu-model-baseline.c            |  12 --
 stubs/arch-query-cpu-model-comparison.c          |  12 --
 stubs/arch-query-cpu-model-expansion.c           |  12 --
 stubs/qapi-event.c                               |  74 ++++++++
 target/arm/helper.c                              |   3 +-
 target/i386/cpu.c                                |   5 +-
 target/ppc/translate_init.c                      |   3 +-
 target/s390x/cpu_models.c                        |   9 +-
 tests/check-qjson.c                              |  91 +++++----
 tests/check-qlit.c                               |  52 +++++
 tests/check-qom-proplist.c                       |   1 +
 tests/qmp-test.c                                 |  21 ++
 tests/test-qobject-input-visitor.c               |  11 +-
 Makefile                                         |  43 ++---
 Makefile.objs                                    |   9 +-
 Makefile.target                                  |   4 +
 docs/devel/qapi-code-gen.txt                     |  29 ++-
 qapi.mak                                         |  14 ++
 qobject/Makefile.objs                            |   2 +-
 stubs/Makefile.objs                              |   5 +-
 tests/Makefile.include                           |  11 +-
 tests/qapi-schema/alternate-conflict-string.json |   4 +-
 tests/qapi-schema/alternate-dict-invalid.err     |   1 +
 tests/qapi-schema/alternate-dict-invalid.exit    |   1 +
 tests/qapi-schema/alternate-dict-invalid.json    |   4 +
 tests/qapi-schema/alternate-dict-invalid.out     |   0
 tests/qapi-schema/bad-if.err                     |   1 +
 tests/qapi-schema/bad-if.exit                    |   1 +
 tests/qapi-schema/bad-if.json                    |   3 +
 tests/qapi-schema/bad-if.out                     |   0
 tests/qapi-schema/enum-dict-member-invalid.err   |   1 +
 tests/qapi-schema/enum-dict-member-invalid.exit  |   1 +
 tests/qapi-schema/enum-dict-member-invalid.json  |   2 +
 tests/qapi-schema/enum-dict-member-invalid.out   |   0
 tests/qapi-schema/enum-dict-member.err           |   1 -
 tests/qapi-schema/enum-dict-member.exit          |   2 +-
 tests/qapi-schema/enum-dict-member.json          |   3 +-
 tests/qapi-schema/enum-dict-member.out           |   4 +
 tests/qapi-schema/enum-if-invalid.err            |   1 +
 tests/qapi-schema/enum-if-invalid.exit           |   1 +
 tests/qapi-schema/enum-if-invalid.json           |   3 +
 tests/qapi-schema/enum-if-invalid.out            |   0
 tests/qapi-schema/qapi-schema-test.json          |  32 ++++
 tests/qapi-schema/qapi-schema-test.out           |  47 +++++
 tests/qapi-schema/struct-if-invalid.err          |   1 +
 tests/qapi-schema/struct-if-invalid.exit         |   1 +
 tests/qapi-schema/struct-if-invalid.json         |   3 +
 tests/qapi-schema/struct-if-invalid.out          |   0
 tests/qapi-schema/struct-member-type.err         |   0
 tests/qapi-schema/struct-member-type.exit        |   1 +
 tests/qapi-schema/struct-member-type.json        |   1 +
 tests/qapi-schema/struct-member-type.out         |   5 +
 tests/qapi-schema/test-qapi.py                   |  31 ++-
 trace/Makefile.objs                              |   2 +-
 88 files changed, 1094 insertions(+), 624 deletions(-)
 create mode 100644 include/qapi/qmp/qlit.h
 create mode 100644 qobject/qlit.c
 delete mode 100644 stubs/arch-query-cpu-def.c
 delete mode 100644 stubs/arch-query-cpu-model-baseline.c
 delete mode 100644 stubs/arch-query-cpu-model-comparison.c
 delete mode 100644 stubs/arch-query-cpu-model-expansion.c
 create mode 100644 stubs/qapi-event.c
 create mode 100644 tests/check-qlit.c
 create mode 100644 qapi.mak
 create mode 100644 tests/qapi-schema/alternate-dict-invalid.err
 create mode 100644 tests/qapi-schema/alternate-dict-invalid.exit
 create mode 100644 tests/qapi-schema/alternate-dict-invalid.json
 create mode 100644 tests/qapi-schema/alternate-dict-invalid.out
 create mode 100644 tests/qapi-schema/bad-if.err
 create mode 100644 tests/qapi-schema/bad-if.exit
 create mode 100644 tests/qapi-schema/bad-if.json
 create mode 100644 tests/qapi-schema/bad-if.out
 create mode 100644 tests/qapi-schema/enum-dict-member-invalid.err
 create mode 100644 tests/qapi-schema/enum-dict-member-invalid.exit
 create mode 100644 tests/qapi-schema/enum-dict-member-invalid.json
 create mode 100644 tests/qapi-schema/enum-dict-member-invalid.out
 create mode 100644 tests/qapi-schema/enum-if-invalid.err
 create mode 100644 tests/qapi-schema/enum-if-invalid.exit
 create mode 100644 tests/qapi-schema/enum-if-invalid.json
 create mode 100644 tests/qapi-schema/enum-if-invalid.out
 create mode 100644 tests/qapi-schema/struct-if-invalid.err
 create mode 100644 tests/qapi-schema/struct-if-invalid.exit
 create mode 100644 tests/qapi-schema/struct-if-invalid.json
 create mode 100644 tests/qapi-schema/struct-if-invalid.out
 create mode 100644 tests/qapi-schema/struct-member-type.err
 create mode 100644 tests/qapi-schema/struct-member-type.exit
 create mode 100644 tests/qapi-schema/struct-member-type.json
 create mode 100644 tests/qapi-schema/struct-member-type.out

-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 01/26] qapi: fix type_seen key error
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-08-15 14:40   ` Markus Armbruster
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 02/26] qobject: replace dump_qobject() by qobject_to_string() Marc-André Lureau
                   ` (25 subsequent siblings)
  26 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Markus Armbruster, Michael Roth

The type_seen member can be of a different type than the 'qtype' being
checked, since a string create several conflicts. Lookup the real
conflicting type in the conflict set, that one must be present in
type_seen.

This fixes the following error, reproducible with the modified test:

Traceback (most recent call last):
  File "/home/elmarco/src/qq/tests/qapi-schema/test-qapi.py", line 56, in <module>
    schema = QAPISchema(sys.argv[1])
  File "/home/elmarco/src/qq/scripts/qapi.py", line 1470, in __init__
    self.exprs = check_exprs(parser.exprs)
  File "/home/elmarco/src/qq/scripts/qapi.py", line 959, in check_exprs
    check_alternate(expr, info)
  File "/home/elmarco/src/qq/scripts/qapi.py", line 831, in check_alternate
    % (name, key, types_seen[qtype]))
KeyError: 'QTYPE_QSTRING'

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi.py                                  | 4 +++-
 tests/qapi-schema/alternate-conflict-string.json | 4 ++--
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 8aa2775f12..4ecc19e944 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -825,7 +825,9 @@ def check_alternate(expr, info):
             else:
                 conflicting.add('QTYPE_QNUM')
                 conflicting.add('QTYPE_QBOOL')
-        if conflicting & set(types_seen):
+        conflict = conflicting & set(types_seen)
+        if conflict:
+            qtype = list(conflict)[0]
             raise QAPISemError(info, "Alternate '%s' member '%s' can't "
                                "be distinguished from member '%s'"
                                % (name, key, types_seen[qtype]))
diff --git a/tests/qapi-schema/alternate-conflict-string.json b/tests/qapi-schema/alternate-conflict-string.json
index 85adbd4adc..bb2702978e 100644
--- a/tests/qapi-schema/alternate-conflict-string.json
+++ b/tests/qapi-schema/alternate-conflict-string.json
@@ -1,4 +1,4 @@
 # alternate branches of 'str' type conflict with all scalar types
 { 'alternate': 'Alt',
-  'data': { 'one': 'str',
-            'two': 'int' } }
+  'data': { 'one': 'int',
+            'two': 'str' } }
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 02/26] qobject: replace dump_qobject() by qobject_to_string()
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 01/26] qapi: fix type_seen key error Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-08-16  9:02   ` Markus Armbruster
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 03/26] qboject: add literal qobject type Marc-André Lureau
                   ` (24 subsequent siblings)
  26 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: Marc-André Lureau, Markus Armbruster, Kevin Wolf, Max Reitz,
	open list:Block layer core

The dump functions is generally useful for any qobject user, for
testing, debugging etc.

The callback-based output is replaced by string allocation. Trading
efficiency for ease-of-use is okay here.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/qapi/qmp/qdict.h   |  2 ++
 include/qapi/qmp/qlist.h   |  2 ++
 include/qapi/qmp/qobject.h |  7 ++++
 block/qapi.c               | 90 +++-------------------------------------------
 qobject/qdict.c            | 30 ++++++++++++++++
 qobject/qlist.c            | 23 ++++++++++++
 qobject/qobject.c          | 21 +++++++++++
 tests/check-qjson.c        | 19 ++++++++++
 8 files changed, 108 insertions(+), 86 deletions(-)

diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h
index 363e431106..c9c4038435 100644
--- a/include/qapi/qmp/qdict.h
+++ b/include/qapi/qmp/qdict.h
@@ -86,4 +86,6 @@ QObject *qdict_crumple(const QDict *src, Error **errp);
 
 void qdict_join(QDict *dest, QDict *src, bool overwrite);
 
+char *qdict_to_string(QDict *dict, int indent);
+
 #endif /* QDICT_H */
diff --git a/include/qapi/qmp/qlist.h b/include/qapi/qmp/qlist.h
index c4b5fdad9b..c93ac3e15b 100644
--- a/include/qapi/qmp/qlist.h
+++ b/include/qapi/qmp/qlist.h
@@ -60,6 +60,8 @@ size_t qlist_size(const QList *qlist);
 QList *qobject_to_qlist(const QObject *obj);
 void qlist_destroy_obj(QObject *obj);
 
+char *qlist_to_string(QList *list, int indent);
+
 static inline const QListEntry *qlist_first(const QList *qlist)
 {
     return QTAILQ_FIRST(&qlist->head);
diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h
index eab29edd12..3365eb73c9 100644
--- a/include/qapi/qmp/qobject.h
+++ b/include/qapi/qmp/qobject.h
@@ -105,4 +105,11 @@ static inline QNull *qnull(void)
     return &qnull_;
 }
 
+char *qobject_to_string_indent(QObject *obj, int indent);
+
+static inline char *qobject_to_string(QObject *obj)
+{
+    return qobject_to_string_indent(obj, 0);
+}
+
 #endif /* QOBJECT_H */
diff --git a/block/qapi.c b/block/qapi.c
index d2b18ee9df..08b5a666d1 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -623,101 +623,19 @@ void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f,
     }
 }
 
-static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation,
-                       QDict *dict);
-static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation,
-                       QList *list);
-
-static void dump_qobject(fprintf_function func_fprintf, void *f,
-                         int comp_indent, QObject *obj)
-{
-    switch (qobject_type(obj)) {
-        case QTYPE_QNUM: {
-            QNum *value = qobject_to_qnum(obj);
-            char *tmp = qnum_to_string(value);
-            func_fprintf(f, "%s", tmp);
-            g_free(tmp);
-            break;
-        }
-        case QTYPE_QSTRING: {
-            QString *value = qobject_to_qstring(obj);
-            func_fprintf(f, "%s", qstring_get_str(value));
-            break;
-        }
-        case QTYPE_QDICT: {
-            QDict *value = qobject_to_qdict(obj);
-            dump_qdict(func_fprintf, f, comp_indent, value);
-            break;
-        }
-        case QTYPE_QLIST: {
-            QList *value = qobject_to_qlist(obj);
-            dump_qlist(func_fprintf, f, comp_indent, value);
-            break;
-        }
-        case QTYPE_QBOOL: {
-            QBool *value = qobject_to_qbool(obj);
-            func_fprintf(f, "%s", qbool_get_bool(value) ? "true" : "false");
-            break;
-        }
-        default:
-            abort();
-    }
-}
-
-static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation,
-                       QList *list)
-{
-    const QListEntry *entry;
-    int i = 0;
-
-    for (entry = qlist_first(list); entry; entry = qlist_next(entry), i++) {
-        QType type = qobject_type(entry->value);
-        bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
-        func_fprintf(f, "%*s[%i]:%c", indentation * 4, "", i,
-                     composite ? '\n' : ' ');
-        dump_qobject(func_fprintf, f, indentation + 1, entry->value);
-        if (!composite) {
-            func_fprintf(f, "\n");
-        }
-    }
-}
-
-static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation,
-                       QDict *dict)
-{
-    const QDictEntry *entry;
-
-    for (entry = qdict_first(dict); entry; entry = qdict_next(dict, entry)) {
-        QType type = qobject_type(entry->value);
-        bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
-        char *key = g_malloc(strlen(entry->key) + 1);
-        int i;
-
-        /* replace dashes with spaces in key (variable) names */
-        for (i = 0; entry->key[i]; i++) {
-            key[i] = entry->key[i] == '-' ? ' ' : entry->key[i];
-        }
-        key[i] = 0;
-        func_fprintf(f, "%*s%s:%c", indentation * 4, "", key,
-                     composite ? '\n' : ' ');
-        dump_qobject(func_fprintf, f, indentation + 1, entry->value);
-        if (!composite) {
-            func_fprintf(f, "\n");
-        }
-        g_free(key);
-    }
-}
-
 void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
                                    ImageInfoSpecific *info_spec)
 {
     QObject *obj, *data;
     Visitor *v = qobject_output_visitor_new(&obj);
+    char *tmp;
 
     visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort);
     visit_complete(v, &obj);
     data = qdict_get(qobject_to_qdict(obj), "data");
-    dump_qobject(func_fprintf, f, 1, data);
+    tmp = qobject_to_string_indent(data, 1);
+    func_fprintf(f, "%s", tmp);
+    g_free(tmp);
     qobject_decref(obj);
     visit_free(v);
 }
diff --git a/qobject/qdict.c b/qobject/qdict.c
index 576018e531..1cb5cab5e0 100644
--- a/qobject/qdict.c
+++ b/qobject/qdict.c
@@ -1022,3 +1022,33 @@ void qdict_join(QDict *dest, QDict *src, bool overwrite)
         entry = next;
     }
 }
+
+char *qdict_to_string(QDict *dict, int indent)
+{
+    const QDictEntry *entry;
+    GString *str = g_string_new(NULL);
+
+    for (entry = qdict_first(dict); entry; entry = qdict_next(dict, entry)) {
+        QType type = qobject_type(entry->value);
+        bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
+        char *key = g_malloc(strlen(entry->key) + 1);
+        char *val = qobject_to_string_indent(entry->value, indent + 1);
+        int i;
+
+        /* replace dashes with spaces in key (variable) names */
+        for (i = 0; entry->key[i]; i++) {
+            key[i] = entry->key[i] == '-' ? ' ' : entry->key[i];
+        }
+        key[i] = 0;
+        g_string_append_printf(str, "%*s%s:", indent * 4, "", key);
+        g_string_append_c(str, composite ? '\n' : ' ');
+        g_string_append(str, val);
+        if (!composite) {
+            g_string_append_c(str, '\n');
+        }
+        g_free(val);
+        g_free(key);
+    }
+
+    return g_string_free(str, false);
+}
diff --git a/qobject/qlist.c b/qobject/qlist.c
index 86b60cb88c..b769248290 100644
--- a/qobject/qlist.c
+++ b/qobject/qlist.c
@@ -158,3 +158,26 @@ void qlist_destroy_obj(QObject *obj)
 
     g_free(qlist);
 }
+
+char *qlist_to_string(QList *list, int indent)
+{
+    GString *str = g_string_new(NULL);
+    const QListEntry *entry;
+    int i = 0;
+
+    for (entry = qlist_first(list); entry; entry = qlist_next(entry), i++) {
+        QType type = qobject_type(entry->value);
+        bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
+        char *val = qobject_to_string_indent(entry->value, indent + 1);
+
+        g_string_append_printf(str, "%*s[%i]:", indent * 4, "", i);
+        g_string_append_c(str, composite ? '\n' : ' ');
+        g_string_append(str, val);
+        if (!composite) {
+            g_string_append_c(str, '\n');
+        }
+        g_free(val);
+    }
+
+    return g_string_free(str, false);
+}
diff --git a/qobject/qobject.c b/qobject/qobject.c
index b0cafb66f1..b70bef9ccb 100644
--- a/qobject/qobject.c
+++ b/qobject/qobject.c
@@ -27,3 +27,24 @@ void qobject_destroy(QObject *obj)
     assert(QTYPE_QNULL < obj->type && obj->type < QTYPE__MAX);
     qdestroy[obj->type](obj);
 }
+
+char *qobject_to_string_indent(QObject *obj, int indent)
+{
+    switch (qobject_type(obj)) {
+    case QTYPE_QNULL:
+        return g_strdup("null");
+    case QTYPE_QNUM:
+        return qnum_to_string(qobject_to_qnum(obj));
+    case QTYPE_QSTRING:
+        return g_strdup(qstring_get_str(qobject_to_qstring(obj)));
+    case QTYPE_QDICT:
+        return qdict_to_string(qobject_to_qdict(obj), indent);
+    case QTYPE_QLIST:
+        return qlist_to_string(qobject_to_qlist(obj), indent);
+    case QTYPE_QBOOL:
+        return g_strdup(qbool_get_bool(qobject_to_qbool(obj)) ?
+                        "true" : "false");
+    default:
+        abort();
+    }
+}
diff --git a/tests/check-qjson.c b/tests/check-qjson.c
index a3a97b0d99..9c42a46b7d 100644
--- a/tests/check-qjson.c
+++ b/tests/check-qjson.c
@@ -1372,6 +1372,23 @@ static void simple_whitespace(void)
     }
 }
 
+static void qobject_to_string_test(void)
+{
+    QObject *obj;
+    char *tmp;
+
+    obj = qobject_from_json("[ 43, { 'c': { 'd' : 12 } }, [ 1, 2 ], 42 ]",
+                            &error_abort);
+    tmp = qobject_to_string(obj);
+    g_assert_cmpstr(tmp, ==,
+                    "[0]: 43\n"
+                    "[1]:\n    c:\n        d: 12\n"
+                    "[2]:\n    [0]: 1\n    [1]: 2\n"
+                    "[3]: 42\n");
+    g_free(tmp);
+    qobject_decref(obj);
+}
+
 static void simple_varargs(void)
 {
     QObject *embedded_obj;
@@ -1545,5 +1562,7 @@ int main(int argc, char **argv)
     g_test_add_func("/errors/unterminated/literal", unterminated_literal);
     g_test_add_func("/errors/limits/nesting", limits_nesting);
 
+    g_test_add_func("/qobject/to_string", qobject_to_string_test);
+
     return g_test_run();
 }
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 03/26] qboject: add literal qobject type
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 01/26] qapi: fix type_seen key error Marc-André Lureau
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 02/26] qobject: replace dump_qobject() by qobject_to_string() Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-08-16  8:59   ` Markus Armbruster
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 04/26] qapi: generate a literal qobject for introspection Marc-André Lureau
                   ` (23 subsequent siblings)
  26 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Markus Armbruster

Promote LiteralQObject from tests/check-qjson.c to qobject/qlit.c,
allowing to statically declare complex qobjects.

Add a helper qobject_from_qlit() to instantiate a literal qobject to a
real QObject. Add some simple test.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/qapi/qmp/qlit.h | 53 ++++++++++++++++++++++++++++++++++++
 qobject/qlit.c          | 53 ++++++++++++++++++++++++++++++++++++
 tests/check-qjson.c     | 72 +++++++++++++++++--------------------------------
 tests/check-qlit.c      | 52 +++++++++++++++++++++++++++++++++++
 qobject/Makefile.objs   |  2 +-
 tests/Makefile.include  |  5 +++-
 6 files changed, 187 insertions(+), 50 deletions(-)
 create mode 100644 include/qapi/qmp/qlit.h
 create mode 100644 qobject/qlit.c
 create mode 100644 tests/check-qlit.c

diff --git a/include/qapi/qmp/qlit.h b/include/qapi/qmp/qlit.h
new file mode 100644
index 0000000000..fd6bfc3e69
--- /dev/null
+++ b/include/qapi/qmp/qlit.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright IBM, Corp. 2009
+ * Copyright (c) 2013, 2015 Red Hat Inc.
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Markus Armbruster <armbru@redhat.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 QLIT_H_
+#define QLIT_H_
+
+#include "qapi-types.h"
+#include "qobject.h"
+
+typedef struct QLitDictEntry QLitDictEntry;
+typedef struct QLitObject QLitObject;
+
+struct QLitObject {
+    int type;
+    union {
+        bool qbool;
+        int64_t qnum;
+        const char *qstr;
+        QLitDictEntry *qdict;
+        QLitObject *qlist;
+    } value;
+};
+
+struct QLitDictEntry {
+    const char *key;
+    QLitObject value;
+};
+
+#define QLIT_QNULL \
+    { .type = QTYPE_QNULL }
+#define QLIT_QBOOL(val) \
+    { .type = QTYPE_QBOOL, .value.qbool = (val) }
+#define QLIT_QNUM(val) \
+    { .type = QTYPE_QNUM, .value.qnum = (val) }
+#define QLIT_QSTR(val) \
+    { .type = QTYPE_QSTRING, .value.qstr = (val) }
+#define QLIT_QDICT(val) \
+    { .type = QTYPE_QDICT, .value.qdict = (val) }
+#define QLIT_QLIST(val) \
+    { .type = QTYPE_QLIST, .value.qlist = (val) }
+
+QObject *qobject_from_qlit(const QLitObject *qlit);
+
+#endif /* QLIT_H_ */
diff --git a/qobject/qlit.c b/qobject/qlit.c
new file mode 100644
index 0000000000..d7407b4b34
--- /dev/null
+++ b/qobject/qlit.c
@@ -0,0 +1,53 @@
+/*
+ * QLit literal qobject
+ *
+ * Copyright (C) 2017 Red Hat Inc.
+ *
+ * Authors:
+ *  Marc-André Lureau <marcandre.lureau@redhat.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/osdep.h"
+
+#include "qapi/qmp/qlit.h"
+#include "qapi/qmp/types.h"
+
+QObject *qobject_from_qlit(const QLitObject *qlit)
+{
+    int i;
+
+    switch (qlit->type) {
+    case QTYPE_QNULL:
+        return QOBJECT(qnull());
+    case QTYPE_QNUM:
+        return QOBJECT(qnum_from_int(qlit->value.qnum));
+    case QTYPE_QSTRING:
+        return QOBJECT(qstring_from_str(qlit->value.qstr));
+    case QTYPE_QDICT: {
+        QDict *qdict = qdict_new();
+        for (i = 0; qlit->value.qdict[i].value.type != QTYPE_NONE; i++) {
+            QLitDictEntry *e = &qlit->value.qdict[i];
+
+            qdict_put_obj(qdict, e->key, qobject_from_qlit(&e->value));
+        }
+        return QOBJECT(qdict);
+    }
+    case QTYPE_QLIST: {
+        QList *qlist = qlist_new();
+
+        for (i = 0; qlit->value.qlist[i].type != QTYPE_NONE; i++) {
+            qlist_append_obj(qlist, qobject_from_qlit(&qlit->value.qlist[i]));
+        }
+        return QOBJECT(qlist);
+    }
+    case QTYPE_QBOOL:
+        return QOBJECT(qbool_from_bool(qlit->value.qbool));
+    case QTYPE_NONE:
+        assert(0);
+    }
+
+    return NULL;
+}
diff --git a/tests/check-qjson.c b/tests/check-qjson.c
index 9c42a46b7d..1a95aff2ba 100644
--- a/tests/check-qjson.c
+++ b/tests/check-qjson.c
@@ -16,6 +16,7 @@
 #include "qapi/error.h"
 #include "qapi/qmp/types.h"
 #include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qlit.h"
 #include "qemu-common.h"
 
 static void escaped_string(void)
@@ -1059,39 +1060,14 @@ static void keyword_literal(void)
     QDECREF(null);
 }
 
-typedef struct LiteralQDictEntry LiteralQDictEntry;
-typedef struct LiteralQObject LiteralQObject;
-
-struct LiteralQObject
-{
-    int type;
-    union {
-        int64_t qnum;
-        const char *qstr;
-        LiteralQDictEntry *qdict;
-        LiteralQObject *qlist;
-    } value;
-};
-
-struct LiteralQDictEntry
-{
-    const char *key;
-    LiteralQObject value;
-};
-
-#define QLIT_QNUM(val) (LiteralQObject){.type = QTYPE_QNUM, .value.qnum = (val)}
-#define QLIT_QSTR(val) (LiteralQObject){.type = QTYPE_QSTRING, .value.qstr = (val)}
-#define QLIT_QDICT(val) (LiteralQObject){.type = QTYPE_QDICT, .value.qdict = (val)}
-#define QLIT_QLIST(val) (LiteralQObject){.type = QTYPE_QLIST, .value.qlist = (val)}
-
 typedef struct QListCompareHelper
 {
     int index;
-    LiteralQObject *objs;
+    QLitObject *objs;
     int result;
 } QListCompareHelper;
 
-static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs);
+static int compare_litqobj_to_qobj(QLitObject *lhs, QObject *rhs);
 
 static void compare_helper(QObject *obj, void *opaque)
 {
@@ -1109,7 +1085,7 @@ static void compare_helper(QObject *obj, void *opaque)
     helper->result = compare_litqobj_to_qobj(&helper->objs[helper->index++], obj);
 }
 
-static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs)
+static int compare_litqobj_to_qobj(QLitObject *lhs, QObject *rhs)
 {
     int64_t val;
 
@@ -1159,23 +1135,23 @@ static void simple_dict(void)
     int i;
     struct {
         const char *encoded;
-        LiteralQObject decoded;
+        QLitObject decoded;
     } test_cases[] = {
         {
             .encoded = "{\"foo\": 42, \"bar\": \"hello world\"}",
-            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
+            .decoded = QLIT_QDICT(((QLitDictEntry[]){
                         { "foo", QLIT_QNUM(42) },
                         { "bar", QLIT_QSTR("hello world") },
                         { }
                     })),
         }, {
             .encoded = "{}",
-            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
+            .decoded = QLIT_QDICT(((QLitDictEntry[]){
                         { }
                     })),
         }, {
             .encoded = "{\"foo\": 43}",
-            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
+            .decoded = QLIT_QDICT(((QLitDictEntry[]){
                         { "foo", QLIT_QNUM(43) },
                         { }
                     })),
@@ -1257,11 +1233,11 @@ static void simple_list(void)
     int i;
     struct {
         const char *encoded;
-        LiteralQObject decoded;
+        QLitObject decoded;
     } test_cases[] = {
         {
             .encoded = "[43,42]",
-            .decoded = QLIT_QLIST(((LiteralQObject[]){
+            .decoded = QLIT_QLIST(((QLitObject[]){
                         QLIT_QNUM(43),
                         QLIT_QNUM(42),
                         { }
@@ -1269,21 +1245,21 @@ static void simple_list(void)
         },
         {
             .encoded = "[43]",
-            .decoded = QLIT_QLIST(((LiteralQObject[]){
+            .decoded = QLIT_QLIST(((QLitObject[]){
                         QLIT_QNUM(43),
                         { }
                     })),
         },
         {
             .encoded = "[]",
-            .decoded = QLIT_QLIST(((LiteralQObject[]){
+            .decoded = QLIT_QLIST(((QLitObject[]){
                         { }
                     })),
         },
         {
             .encoded = "[{}]",
-            .decoded = QLIT_QLIST(((LiteralQObject[]){
-                        QLIT_QDICT(((LiteralQDictEntry[]){
+            .decoded = QLIT_QLIST(((QLitObject[]){
+                        QLIT_QDICT(((QLitDictEntry[]){
                                     {},
                                         })),
                         {},
@@ -1314,11 +1290,11 @@ static void simple_whitespace(void)
     int i;
     struct {
         const char *encoded;
-        LiteralQObject decoded;
+        QLitObject decoded;
     } test_cases[] = {
         {
             .encoded = " [ 43 , 42 ]",
-            .decoded = QLIT_QLIST(((LiteralQObject[]){
+            .decoded = QLIT_QLIST(((QLitObject[]){
                         QLIT_QNUM(43),
                         QLIT_QNUM(42),
                         { }
@@ -1326,12 +1302,12 @@ static void simple_whitespace(void)
         },
         {
             .encoded = " [ 43 , { 'h' : 'b' }, [ ], 42 ]",
-            .decoded = QLIT_QLIST(((LiteralQObject[]){
+            .decoded = QLIT_QLIST(((QLitObject[]){
                         QLIT_QNUM(43),
-                        QLIT_QDICT(((LiteralQDictEntry[]){
+                        QLIT_QDICT(((QLitDictEntry[]){
                                     { "h", QLIT_QSTR("b") },
                                     { }})),
-                        QLIT_QLIST(((LiteralQObject[]){
+                        QLIT_QLIST(((QLitObject[]){
                                     { }})),
                         QLIT_QNUM(42),
                         { }
@@ -1339,13 +1315,13 @@ static void simple_whitespace(void)
         },
         {
             .encoded = " [ 43 , { 'h' : 'b' , 'a' : 32 }, [ ], 42 ]",
-            .decoded = QLIT_QLIST(((LiteralQObject[]){
+            .decoded = QLIT_QLIST(((QLitObject[]){
                         QLIT_QNUM(43),
-                        QLIT_QDICT(((LiteralQDictEntry[]){
+                        QLIT_QDICT(((QLitDictEntry[]){
                                     { "h", QLIT_QSTR("b") },
                                     { "a", QLIT_QNUM(32) },
                                     { }})),
-                        QLIT_QLIST(((LiteralQObject[]){
+                        QLIT_QLIST(((QLitObject[]){
                                     { }})),
                         QLIT_QNUM(42),
                         { }
@@ -1393,10 +1369,10 @@ static void simple_varargs(void)
 {
     QObject *embedded_obj;
     QObject *obj;
-    LiteralQObject decoded = QLIT_QLIST(((LiteralQObject[]){
+    QLitObject decoded = QLIT_QLIST(((QLitObject[]){
             QLIT_QNUM(1),
             QLIT_QNUM(2),
-            QLIT_QLIST(((LiteralQObject[]){
+            QLIT_QLIST(((QLitObject[]){
                         QLIT_QNUM(32),
                         QLIT_QNUM(42),
                         {}})),
diff --git a/tests/check-qlit.c b/tests/check-qlit.c
new file mode 100644
index 0000000000..cda4620f92
--- /dev/null
+++ b/tests/check-qlit.c
@@ -0,0 +1,52 @@
+/*
+ * QLit unit-tests.
+ *
+ * Copyright (C) 2017 Red Hat Inc.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "qapi/qmp/qlit.h"
+
+static void qobject_from_qlit_test(void)
+{
+    char *str;
+    QObject *qobj = NULL;
+    QLitObject qlit = QLIT_QDICT((
+        (QLitDictEntry[]) {
+            { "foo", QLIT_QNUM(42) },
+            { "bar", QLIT_QSTR("hello world") },
+            { "baz", QLIT_QNULL },
+            { "bee", QLIT_QLIST((
+                (QLitObject[]) {
+                    QLIT_QNUM(43),
+                    QLIT_QNUM(44),
+                    QLIT_QBOOL(true),
+                    { },
+                }))
+            },
+            { },
+        }));
+
+    qobj = qobject_from_qlit(&qlit);
+
+    str = qobject_to_string(qobj);
+    g_assert_cmpstr(str, ==,
+                    "bee:\n    [0]: 43\n    [1]: 44\n    [2]: true\n"   \
+                    "baz: null\nbar: hello world\nfoo: 42\n");
+
+    g_free(str);
+    qobject_decref(qobj);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_func("/qlit/qobject_from_qlit", qobject_from_qlit_test);
+
+    return g_test_run();
+}
diff --git a/qobject/Makefile.objs b/qobject/Makefile.objs
index fc8885c9a4..002d25873a 100644
--- a/qobject/Makefile.objs
+++ b/qobject/Makefile.objs
@@ -1,2 +1,2 @@
-util-obj-y = qnull.o qnum.o qstring.o qdict.o qlist.o qbool.o
+util-obj-y = qnull.o qnum.o qstring.o qdict.o qlist.o qbool.o qlit.o
 util-obj-y += qjson.o qobject.o json-lexer.o json-streamer.o json-parser.o
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 7af278db55..960ab8c6dd 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -12,6 +12,8 @@ check-unit-y += tests/test-char$(EXESUF)
 gcov-files-check-qdict-y = chardev/char.c
 check-unit-y += tests/check-qnum$(EXESUF)
 gcov-files-check-qnum-y = qobject/qnum.c
+check-unit-y += tests/check-qlit$(EXESUF)
+gcov-files-check-qlit-y = qobject/qlit.c
 check-unit-y += tests/check-qstring$(EXESUF)
 gcov-files-check-qstring-y = qobject/qstring.c
 check-unit-y += tests/check-qlist$(EXESUF)
@@ -523,7 +525,7 @@ test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \
 	tests/rcutorture.o tests/test-rcu-list.o \
 	tests/test-qdist.o tests/test-shift128.o \
 	tests/test-qht.o tests/qht-bench.o tests/test-qht-par.o \
-	tests/atomic_add-bench.o
+	tests/atomic_add-bench.o tests/check-qlit.o
 
 $(test-obj-y): QEMU_INCLUDES += -Itests
 QEMU_CFLAGS += -I$(SRC_PATH)/tests
@@ -541,6 +543,7 @@ test-io-obj-y = $(io-obj-y) $(test-crypto-obj-y)
 test-block-obj-y = $(block-obj-y) $(test-io-obj-y) tests/iothread.o
 
 tests/check-qnum$(EXESUF): tests/check-qnum.o $(test-util-obj-y)
+tests/check-qlit$(EXESUF): tests/check-qlit.o $(test-util-obj-y)
 tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y)
 tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y)
 tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y)
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 04/26] qapi: generate a literal qobject for introspection
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (2 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 03/26] qboject: add literal qobject type Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-08-16 10:21   ` Markus Armbruster
  2017-08-17 11:48   ` Markus Armbruster
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 05/26] visitor: pass size of strings array to enum visitor Marc-André Lureau
                   ` (22 subsequent siblings)
  26 siblings, 2 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: Marc-André Lureau, Markus Armbruster,
	Dr. David Alan Gilbert, Michael Roth

Replace the generated json string with a literal qobject. The later is
easier to deal with, at run time, as well as compile time: #if blocks
can be more easily added than in a json string.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi-introspect.py         | 83 +++++++++++++++++++++-----------------
 monitor.c                          |  2 +-
 tests/test-qobject-input-visitor.c | 10 +++--
 docs/devel/qapi-code-gen.txt       | 29 ++++++++-----
 4 files changed, 72 insertions(+), 52 deletions(-)

diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
index 032bcea491..fc72cdb66d 100644
--- a/scripts/qapi-introspect.py
+++ b/scripts/qapi-introspect.py
@@ -12,72 +12,74 @@
 from qapi import *
 
 
-# Caveman's json.dumps() replacement (we're stuck at Python 2.4)
-# TODO try to use json.dumps() once we get unstuck
-def to_json(obj, level=0):
+def to_qlit(obj, level=0, first_indent=True):
+    def indent(level):
+        return level * 4 * ' '
+    ret = ''
+    if first_indent:
+        ret += indent(level)
     if obj is None:
-        ret = 'null'
+        ret += 'QLIT_QNULL'
     elif isinstance(obj, str):
-        ret = '"' + obj.replace('"', r'\"') + '"'
+        ret += 'QLIT_QSTR(' + '"' + obj.replace('"', r'\"') + '"' + ')'
     elif isinstance(obj, list):
-        elts = [to_json(elt, level + 1)
+        elts = [to_qlit(elt, level + 1)
                 for elt in obj]
-        ret = '[' + ', '.join(elts) + ']'
+        elts.append(indent(level + 1) + "{ }")
+        ret += 'QLIT_QLIST(((QLitObject[]) {\n'
+        ret += ',\n'.join(elts) + '\n'
+        ret += indent(level) + '}))'
     elif isinstance(obj, dict):
-        elts = ['"%s": %s' % (key.replace('"', r'\"'),
-                              to_json(obj[key], level + 1))
-                for key in sorted(obj.keys())]
-        ret = '{' + ', '.join(elts) + '}'
+        elts = [ indent(level + 1) + '{ "%s", %s }' %
+                 (key.replace('"', r'\"'), to_qlit(obj[key], level + 1, False))
+                 for key in sorted(obj.keys())]
+        elts.append(indent(level + 1) + '{ }')
+        ret += 'QLIT_QDICT(((QLitDictEntry[]) {\n'
+        ret += ',\n'.join(elts) + '\n'
+        ret += indent(level) + '}))'
     else:
         assert False                # not implemented
-    if level == 1:
-        ret = '\n' + ret
     return ret
 
 
-def to_c_string(string):
-    return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"'
-
-
 class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor):
     def __init__(self, unmask):
         self._unmask = unmask
         self.defn = None
         self.decl = None
         self._schema = None
-        self._jsons = None
+        self._qlits = None
         self._used_types = None
         self._name_map = None
 
     def visit_begin(self, schema):
         self._schema = schema
-        self._jsons = []
+        self._qlits = []
         self._used_types = []
         self._name_map = {}
 
     def visit_end(self):
         # visit the types that are actually used
-        jsons = self._jsons
-        self._jsons = []
+        qlits = self._qlits
+        self._qlits = []
         for typ in self._used_types:
             typ.visit(self)
         # generate C
         # TODO can generate awfully long lines
-        jsons.extend(self._jsons)
-        name = c_name(prefix, protect=False) + 'qmp_schema_json'
+        qlits.extend(self._qlits)
+        name = c_name(prefix, protect=False) + 'qmp_schema_qlit'
         self.decl = mcgen('''
-extern const char %(c_name)s[];
+extern const QLitObject %(c_name)s;
 ''',
                           c_name=c_name(name))
-        lines = to_json(jsons).split('\n')
-        c_string = '\n    '.join([to_c_string(line) for line in lines])
+        c_string = to_qlit(qlits)
         self.defn = mcgen('''
-const char %(c_name)s[] = %(c_string)s;
+const QLitObject %(c_name)s = %(c_string)s;
 ''',
                           c_name=c_name(name),
                           c_string=c_string)
         self._schema = None
-        self._jsons = None
+        self._qlits = None
         self._used_types = None
         self._name_map = None
 
@@ -111,12 +113,12 @@ const char %(c_name)s[] = %(c_string)s;
             return '[' + self._use_type(typ.element_type) + ']'
         return self._name(typ.name)
 
-    def _gen_json(self, name, mtype, obj):
+    def _gen_qlit(self, name, mtype, obj):
         if mtype not in ('command', 'event', 'builtin', 'array'):
             name = self._name(name)
         obj['name'] = name
         obj['meta-type'] = mtype
-        self._jsons.append(obj)
+        self._qlits.append(obj)
 
     def _gen_member(self, member):
         ret = {'name': member.name, 'type': self._use_type(member.type)}
@@ -132,24 +134,24 @@ const char %(c_name)s[] = %(c_string)s;
         return {'case': variant.name, 'type': self._use_type(variant.type)}
 
     def visit_builtin_type(self, name, info, json_type):
-        self._gen_json(name, 'builtin', {'json-type': json_type})
+        self._gen_qlit(name, 'builtin', {'json-type': json_type})
 
     def visit_enum_type(self, name, info, values, prefix):
-        self._gen_json(name, 'enum', {'values': values})
+        self._gen_qlit(name, 'enum', {'values': values})
 
     def visit_array_type(self, name, info, element_type):
         element = self._use_type(element_type)
-        self._gen_json('[' + element + ']', 'array', {'element-type': element})
+        self._gen_qlit('[' + element + ']', 'array', {'element-type': element})
 
     def visit_object_type_flat(self, name, info, members, variants):
         obj = {'members': [self._gen_member(m) for m in members]}
         if variants:
             obj.update(self._gen_variants(variants.tag_member.name,
                                           variants.variants))
-        self._gen_json(name, 'object', obj)
+        self._gen_qlit(name, 'object', obj)
 
     def visit_alternate_type(self, name, info, variants):
-        self._gen_json(name, 'alternate',
+        self._gen_qlit(name, 'alternate',
                        {'members': [{'type': self._use_type(m.type)}
                                     for m in variants.variants]})
 
@@ -157,13 +159,13 @@ const char %(c_name)s[] = %(c_string)s;
                       gen, success_response, boxed):
         arg_type = arg_type or self._schema.the_empty_object_type
         ret_type = ret_type or self._schema.the_empty_object_type
-        self._gen_json(name, 'command',
+        self._gen_qlit(name, 'command',
                        {'arg-type': self._use_type(arg_type),
                         'ret-type': self._use_type(ret_type)})
 
     def visit_event(self, name, info, arg_type, boxed):
         arg_type = arg_type or self._schema.the_empty_object_type
-        self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)})
+        self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)})
 
 # Debugging aid: unmask QAPI schema's type names
 # We normally mask them, because they're not QMP wire ABI
@@ -205,11 +207,18 @@ h_comment = '''
 
 fdef.write(mcgen('''
 #include "qemu/osdep.h"
+#include "qapi/qmp/qlit.h"
 #include "%(prefix)sqmp-introspect.h"
 
 ''',
                  prefix=prefix))
 
+fdecl.write(mcgen('''
+#include "qemu/osdep.h"
+#include "qapi/qmp/qlit.h"
+
+'''))
+
 schema = QAPISchema(input_file)
 gen = QAPISchemaGenIntrospectVisitor(opt_unmask)
 schema.visit(gen)
diff --git a/monitor.c b/monitor.c
index 6d040e620f..a1773d5bc7 100644
--- a/monitor.c
+++ b/monitor.c
@@ -953,7 +953,7 @@ EventInfoList *qmp_query_events(Error **errp)
 static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
                                  Error **errp)
 {
-    *ret_data = qobject_from_json(qmp_schema_json, &error_abort);
+    *ret_data = qobject_from_qlit(&qmp_schema_qlit);
 }
 
 /*
diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c
index bcf02617dc..1969733971 100644
--- a/tests/test-qobject-input-visitor.c
+++ b/tests/test-qobject-input-visitor.c
@@ -1247,24 +1247,26 @@ static void test_visitor_in_fail_alternate(TestInputVisitorData *data,
 }
 
 static void do_test_visitor_in_qmp_introspect(TestInputVisitorData *data,
-                                              const char *schema_json)
+                                              const QLitObject *qlit)
 {
     SchemaInfoList *schema = NULL;
+    QObject *obj = qobject_from_qlit(qlit);
     Visitor *v;
 
-    v = visitor_input_test_init_raw(data, schema_json);
+    v = qobject_input_visitor_new(obj);
 
     visit_type_SchemaInfoList(v, NULL, &schema, &error_abort);
     g_assert(schema);
 
     qapi_free_SchemaInfoList(schema);
+    qobject_decref(obj);
 }
 
 static void test_visitor_in_qmp_introspect(TestInputVisitorData *data,
                                            const void *unused)
 {
-    do_test_visitor_in_qmp_introspect(data, test_qmp_schema_json);
-    do_test_visitor_in_qmp_introspect(data, qmp_schema_json);
+    do_test_visitor_in_qmp_introspect(data, &test_qmp_schema_qlit);
+    do_test_visitor_in_qmp_introspect(data, &qmp_schema_qlit);
 }
 
 int main(int argc, char **argv)
diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt
index 9903ac4c19..885c61b52f 100644
--- a/docs/devel/qapi-code-gen.txt
+++ b/docs/devel/qapi-code-gen.txt
@@ -1295,18 +1295,27 @@ Example:
     #ifndef EXAMPLE_QMP_INTROSPECT_H
     #define EXAMPLE_QMP_INTROSPECT_H
 
-    extern const char example_qmp_schema_json[];
+    extern const QLitObject qmp_schema_qlit;
 
     #endif
     $ cat qapi-generated/example-qmp-introspect.c
 [Uninteresting stuff omitted...]
 
-    const char example_qmp_schema_json[] = "["
-        "{\"arg-type\": \"0\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
-        "{\"arg-type\": \"1\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"2\"}, "
-        "{\"members\": [], \"meta-type\": \"object\", \"name\": \"0\"}, "
-        "{\"members\": [{\"name\": \"arg1\", \"type\": \"[2]\"}], \"meta-type\": \"object\", \"name\": \"1\"}, "
-        "{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"default\": null, \"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, "
-        "{\"element-type\": \"2\", \"meta-type\": \"array\", \"name\": \"[2]\"}, "
-        "{\"json-type\": \"int\", \"meta-type\": \"builtin\", \"name\": \"int\"}, "
-        "{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}]";
+    const QLitObject example_qmp_schema_qlit = QLIT_QLIST(((QLitObject[]) {
+        QLIT_QDICT(((QLitDictEntry[]) {
+            { "arg-type", QLIT_QSTR("0") },
+            { "meta-type", QLIT_QSTR("event") },
+            { "name", QLIT_QSTR("Event") },
+            { }
+        })),
+        QLIT_QDICT(((QLitDictEntry[]) {
+            { "members", QLIT_QLIST(((QLitObject[]) {
+                { }
+            })) },
+            { "meta-type", QLIT_QSTR("object") },
+            { "name", QLIT_QSTR("0") },
+            { }
+        })),
+        ....
+        { }
+    }));
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 05/26] visitor: pass size of strings array to enum visitor
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (3 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 04/26] qapi: generate a literal qobject for introspection Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-08-16 12:54   ` Markus Armbruster
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 06/26] qapi2texi: minor python code simplification Marc-André Lureau
                   ` (21 subsequent siblings)
  26 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: Marc-André Lureau, Eduardo Habkost, Igor Mammedov,
	Daniel P. Berrange, Markus Armbruster, Michael Roth,
	Andreas Färber, Jason Wang

The size is known at compile time, this avoids having to compute to
check array boundaries.

Additionally, the following conditional enum entry change will create
"hole" in the generated _lookup tables, that should be skipped.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/qapi/visitor.h             |  3 ++-
 scripts/qapi-visit.py              | 10 +++++-----
 include/hw/qdev-core.h             |  1 +
 include/qom/object.h               |  4 ++++
 qapi/qapi-visit-core.c             | 23 ++++++++++++-----------
 backends/hostmem.c                 |  1 +
 crypto/secret.c                    |  1 +
 crypto/tlscreds.c                  |  1 +
 hw/core/qdev-properties.c          | 11 +++++++++--
 net/filter.c                       |  1 +
 qom/object.c                       | 11 ++++++++---
 tests/check-qom-proplist.c         |  1 +
 tests/test-qobject-input-visitor.c |  2 +-
 13 files changed, 47 insertions(+), 23 deletions(-)

diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index fe9faf469f..a2d9786c52 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -469,7 +469,8 @@ bool visit_optional(Visitor *v, const char *name, bool *present);
  * that visit_type_str() must have no unwelcome side effects.
  */
 void visit_type_enum(Visitor *v, const char *name, int *obj,
-                     const char *const strings[], Error **errp);
+                     const char *const strings[], int nstrings,
+                     Error **errp);
 
 /*
  * Check if visitor is an input visitor.
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index bd0b742236..60850a6cdd 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -147,17 +147,17 @@ out:
                  c_name=c_name(name), c_elt_type=element_type.c_name())
 
 
-def gen_visit_enum(name):
+def gen_visit_enum(name, prefix):
     return mcgen('''
 
 void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error **errp)
 {
     int value = *obj;
-    visit_type_enum(v, name, &value, %(c_name)s_lookup, errp);
+    visit_type_enum(v, name, &value, %(c_name)s_lookup, %(c_max)s, errp);
     *obj = value;
 }
 ''',
-                 c_name=c_name(name))
+                 c_name=c_name(name), c_max=c_enum_const(name, '_MAX', prefix))
 
 
 def gen_visit_alternate(name, variants):
@@ -288,10 +288,10 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
         if not info:
             self._btin += gen_visit_decl(name, scalar=True)
             if do_builtins:
-                self.defn += gen_visit_enum(name)
+                self.defn += gen_visit_enum(name, prefix)
         else:
             self.decl += gen_visit_decl(name, scalar=True)
-            self.defn += gen_visit_enum(name)
+            self.defn += gen_visit_enum(name, prefix)
 
     def visit_array_type(self, name, info, element_type):
         decl = gen_visit_decl(name)
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index ae317286a4..f86a0e1a75 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -250,6 +250,7 @@ struct PropertyInfo {
     const char *name;
     const char *description;
     const char * const *enum_table;
+    int enum_table_size;
     int (*print)(DeviceState *dev, Property *prop, char *dest, size_t len);
     void (*set_default_value)(Object *obj, const Property *prop);
     void (*create)(Object *obj, Property *prop, Error **errp);
diff --git a/include/qom/object.h b/include/qom/object.h
index 1b828994fa..53d807e1e6 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -1406,6 +1406,8 @@ void object_class_property_add_bool(ObjectClass *klass, const char *name,
  * @obj: the object to add a property to
  * @name: the name of the property
  * @typename: the name of the enum data type
+ * @strings: an array of strings for the enum
+ * @nstrings: the size of @strings
  * @get: the getter or %NULL if the property is write-only.
  * @set: the setter or %NULL if the property is read-only
  * @errp: if an error occurs, a pointer to an area to store the error
@@ -1416,6 +1418,7 @@ void object_class_property_add_bool(ObjectClass *klass, const char *name,
 void object_property_add_enum(Object *obj, const char *name,
                               const char *typename,
                               const char * const *strings,
+                              int nstrings,
                               int (*get)(Object *, Error **),
                               void (*set)(Object *, int, Error **),
                               Error **errp);
@@ -1423,6 +1426,7 @@ void object_property_add_enum(Object *obj, const char *name,
 void object_class_property_add_enum(ObjectClass *klass, const char *name,
                                     const char *typename,
                                     const char * const *strings,
+                                    int nstrings,
                                     int (*get)(Object *, Error **),
                                     void (*set)(Object *, int, Error **),
                                     Error **errp);
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index ed6d2af462..dc0b9f2cee 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -333,14 +333,13 @@ void visit_type_null(Visitor *v, const char *name, QNull **obj,
 }
 
 static void output_type_enum(Visitor *v, const char *name, int *obj,
-                             const char *const strings[], Error **errp)
+                             const char *const strings[],
+                             int nstrings, Error **errp)
 {
-    int i = 0;
     int value = *obj;
     char *enum_str;
 
-    while (strings[i++] != NULL);
-    if (value < 0 || value >= i - 1) {
+    if (value < 0 || value >= nstrings) {
         error_setg(errp, QERR_INVALID_PARAMETER, name ? name : "null");
         return;
     }
@@ -350,7 +349,8 @@ static void output_type_enum(Visitor *v, const char *name, int *obj,
 }
 
 static void input_type_enum(Visitor *v, const char *name, int *obj,
-                            const char *const strings[], Error **errp)
+                            const char *const strings[],
+                            int nstrings, Error **errp)
 {
     Error *local_err = NULL;
     int64_t value = 0;
@@ -362,14 +362,14 @@ static void input_type_enum(Visitor *v, const char *name, int *obj,
         return;
     }
 
-    while (strings[value] != NULL) {
-        if (strcmp(strings[value], enum_str) == 0) {
+    while (value < nstrings) {
+        if (strings[value] && strcmp(strings[value], enum_str) == 0) {
             break;
         }
         value++;
     }
 
-    if (strings[value] == NULL) {
+    if (value >= nstrings || strings[value] == NULL) {
         error_setg(errp, QERR_INVALID_PARAMETER, enum_str);
         g_free(enum_str);
         return;
@@ -380,16 +380,17 @@ static void input_type_enum(Visitor *v, const char *name, int *obj,
 }
 
 void visit_type_enum(Visitor *v, const char *name, int *obj,
-                     const char *const strings[], Error **errp)
+                     const char *const strings[], int nstrings,
+                     Error **errp)
 {
     assert(obj && strings);
     trace_visit_type_enum(v, name, obj);
     switch (v->type) {
     case VISITOR_INPUT:
-        input_type_enum(v, name, obj, strings, errp);
+        input_type_enum(v, name, obj, strings, nstrings, errp);
         break;
     case VISITOR_OUTPUT:
-        output_type_enum(v, name, obj, strings, errp);
+        output_type_enum(v, name, obj, strings, nstrings, errp);
         break;
     case VISITOR_CLONE:
         /* nothing further to do, scalar value was already copied by
diff --git a/backends/hostmem.c b/backends/hostmem.c
index 4606b73849..fc475a5387 100644
--- a/backends/hostmem.c
+++ b/backends/hostmem.c
@@ -396,6 +396,7 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
         NULL, NULL, &error_abort);
     object_class_property_add_enum(oc, "policy", "HostMemPolicy",
         HostMemPolicy_lookup,
+        HOST_MEM_POLICY__MAX,
         host_memory_backend_get_policy,
         host_memory_backend_set_policy, &error_abort);
     object_class_property_add_str(oc, "id", get_id, set_id, &error_abort);
diff --git a/crypto/secret.c b/crypto/secret.c
index 285ab7a63c..b5382cb7e3 100644
--- a/crypto/secret.c
+++ b/crypto/secret.c
@@ -379,6 +379,7 @@ qcrypto_secret_class_init(ObjectClass *oc, void *data)
     object_class_property_add_enum(oc, "format",
                                    "QCryptoSecretFormat",
                                    QCryptoSecretFormat_lookup,
+                                   QCRYPTO_SECRET_FORMAT__MAX,
                                    qcrypto_secret_prop_get_format,
                                    qcrypto_secret_prop_set_format,
                                    NULL);
diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
index a8965531b6..8c060127ea 100644
--- a/crypto/tlscreds.c
+++ b/crypto/tlscreds.c
@@ -234,6 +234,7 @@ qcrypto_tls_creds_class_init(ObjectClass *oc, void *data)
     object_class_property_add_enum(oc, "endpoint",
                                    "QCryptoTLSCredsEndpoint",
                                    QCryptoTLSCredsEndpoint_lookup,
+                                   QCRYPTO_TLS_CREDS_ENDPOINT__MAX,
                                    qcrypto_tls_creds_prop_get_endpoint,
                                    qcrypto_tls_creds_prop_set_endpoint,
                                    NULL);
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
index 078fc5d239..696fed5b5b 100644
--- a/hw/core/qdev-properties.c
+++ b/hw/core/qdev-properties.c
@@ -52,7 +52,8 @@ static void get_enum(Object *obj, Visitor *v, const char *name, void *opaque,
     Property *prop = opaque;
     int *ptr = qdev_get_prop_ptr(dev, prop);
 
-    visit_type_enum(v, prop->name, ptr, prop->info->enum_table, errp);
+    visit_type_enum(v, prop->name, ptr, prop->info->enum_table,
+                    prop->info->enum_table_size, errp);
 }
 
 static void set_enum(Object *obj, Visitor *v, const char *name, void *opaque,
@@ -67,7 +68,8 @@ static void set_enum(Object *obj, Visitor *v, const char *name, void *opaque,
         return;
     }
 
-    visit_type_enum(v, prop->name, ptr, prop->info->enum_table, errp);
+    visit_type_enum(v, prop->name, ptr, prop->info->enum_table,
+                    prop->info->enum_table_size, errp);
 }
 
 static void set_default_value_enum(Object *obj, const Property *prop)
@@ -586,6 +588,7 @@ const PropertyInfo qdev_prop_on_off_auto = {
     .name = "OnOffAuto",
     .description = "on/off/auto",
     .enum_table = OnOffAuto_lookup,
+    .enum_table_size = ON_OFF_AUTO__MAX,
     .get = get_enum,
     .set = set_enum,
     .set_default_value = set_default_value_enum,
@@ -598,6 +601,7 @@ QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int));
 const PropertyInfo qdev_prop_losttickpolicy = {
     .name  = "LostTickPolicy",
     .enum_table  = LostTickPolicy_lookup,
+    .enum_table_size = LOST_TICK_POLICY__MAX,
     .get   = get_enum,
     .set   = set_enum,
     .set_default_value = set_default_value_enum,
@@ -612,6 +616,7 @@ const PropertyInfo qdev_prop_blockdev_on_error = {
     .description = "Error handling policy, "
                    "report/ignore/enospc/stop/auto",
     .enum_table = BlockdevOnError_lookup,
+    .enum_table_size = BLOCKDEV_ON_ERROR__MAX,
     .get = get_enum,
     .set = set_enum,
     .set_default_value = set_default_value_enum,
@@ -626,6 +631,7 @@ const PropertyInfo qdev_prop_bios_chs_trans = {
     .description = "Logical CHS translation algorithm, "
                    "auto/none/lba/large/rechs",
     .enum_table = BiosAtaTranslation_lookup,
+    .enum_table_size = BIOS_ATA_TRANSLATION__MAX,
     .get = get_enum,
     .set = set_enum,
     .set_default_value = set_default_value_enum,
@@ -638,6 +644,7 @@ const PropertyInfo qdev_prop_fdc_drive_type = {
     .description = "FDC drive type, "
                    "144/288/120/none/auto",
     .enum_table = FloppyDriveType_lookup,
+    .enum_table_size = FLOPPY_DRIVE_TYPE__MAX,
     .get = get_enum,
     .set = set_enum,
     .set_default_value = set_default_value_enum,
diff --git a/net/filter.c b/net/filter.c
index 1dfd2caa23..cf62851344 100644
--- a/net/filter.c
+++ b/net/filter.c
@@ -180,6 +180,7 @@ static void netfilter_init(Object *obj)
                             NULL);
     object_property_add_enum(obj, "queue", "NetFilterDirection",
                              NetFilterDirection_lookup,
+                             NET_FILTER_DIRECTION__MAX,
                              netfilter_get_direction, netfilter_set_direction,
                              NULL);
     object_property_add_str(obj, "status",
diff --git a/qom/object.c b/qom/object.c
index fe6e744b4d..425bae3a2a 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1247,6 +1247,7 @@ uint64_t object_property_get_uint(Object *obj, const char *name,
 
 typedef struct EnumProperty {
     const char * const *strings;
+    int nstrings;
     int (*get)(Object *, Error **);
     void (*set)(Object *, int, Error **);
 } EnumProperty;
@@ -1284,7 +1285,7 @@ int object_property_get_enum(Object *obj, const char *name,
     visit_complete(v, &str);
     visit_free(v);
     v = string_input_visitor_new(str);
-    visit_type_enum(v, name, &ret, enumprop->strings, errp);
+    visit_type_enum(v, name, &ret, enumprop->strings, enumprop->nstrings, errp);
 
     g_free(str);
     visit_free(v);
@@ -1950,7 +1951,7 @@ static void property_get_enum(Object *obj, Visitor *v, const char *name,
         return;
     }
 
-    visit_type_enum(v, name, &value, prop->strings, errp);
+    visit_type_enum(v, name, &value, prop->strings, prop->nstrings, errp);
 }
 
 static void property_set_enum(Object *obj, Visitor *v, const char *name,
@@ -1960,7 +1961,7 @@ static void property_set_enum(Object *obj, Visitor *v, const char *name,
     int value;
     Error *err = NULL;
 
-    visit_type_enum(v, name, &value, prop->strings, &err);
+    visit_type_enum(v, name, &value, prop->strings, prop->nstrings, &err);
     if (err) {
         error_propagate(errp, err);
         return;
@@ -1978,6 +1979,7 @@ static void property_release_enum(Object *obj, const char *name,
 void object_property_add_enum(Object *obj, const char *name,
                               const char *typename,
                               const char * const *strings,
+                              int nstrings,
                               int (*get)(Object *, Error **),
                               void (*set)(Object *, int, Error **),
                               Error **errp)
@@ -1986,6 +1988,7 @@ void object_property_add_enum(Object *obj, const char *name,
     EnumProperty *prop = g_malloc(sizeof(*prop));
 
     prop->strings = strings;
+    prop->nstrings = nstrings;
     prop->get = get;
     prop->set = set;
 
@@ -2003,6 +2006,7 @@ void object_property_add_enum(Object *obj, const char *name,
 void object_class_property_add_enum(ObjectClass *klass, const char *name,
                                     const char *typename,
                                     const char * const *strings,
+                                    int nstrings,
                                     int (*get)(Object *, Error **),
                                     void (*set)(Object *, int, Error **),
                                     Error **errp)
@@ -2011,6 +2015,7 @@ void object_class_property_add_enum(ObjectClass *klass, const char *name,
     EnumProperty *prop = g_malloc(sizeof(*prop));
 
     prop->strings = strings;
+    prop->nstrings = nstrings;
     prop->get = get;
     prop->set = set;
 
diff --git a/tests/check-qom-proplist.c b/tests/check-qom-proplist.c
index 432b66585f..1179030248 100644
--- a/tests/check-qom-proplist.c
+++ b/tests/check-qom-proplist.c
@@ -143,6 +143,7 @@ static void dummy_class_init(ObjectClass *cls, void *data)
     object_class_property_add_enum(cls, "av",
                                    "DummyAnimal",
                                    dummy_animal_map,
+                                   DUMMY_LAST,
                                    dummy_get_av,
                                    dummy_set_av,
                                    NULL);
diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c
index 1969733971..4da5d02c35 100644
--- a/tests/test-qobject-input-visitor.c
+++ b/tests/test-qobject-input-visitor.c
@@ -1110,7 +1110,7 @@ static void test_visitor_in_fail_struct_missing(TestInputVisitorData *data,
     error_free_or_abort(&err);
     visit_optional(v, "optional", &present);
     g_assert(!present);
-    visit_type_enum(v, "enum", &en, EnumOne_lookup, &err);
+    visit_type_enum(v, "enum", &en, EnumOne_lookup, ENUM_ONE__MAX, &err);
     error_free_or_abort(&err);
     visit_type_int(v, "i64", &i64, &err);
     error_free_or_abort(&err);
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 06/26] qapi2texi: minor python code simplification
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (4 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 05/26] visitor: pass size of strings array to enum visitor Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-08-16 12:55   ` Markus Armbruster
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 07/26] qapi: add 'if' condition on top-level schema elements Marc-André Lureau
                   ` (20 subsequent siblings)
  26 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Markus Armbruster, Michael Roth

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi2texi.py | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py
index 9e015002ef..639eb1d042 100755
--- a/scripts/qapi2texi.py
+++ b/scripts/qapi2texi.py
@@ -136,10 +136,9 @@ def texi_enum_value(value):
 def texi_member(member, suffix=''):
     """Format a table of members item for an object type member"""
     typ = member.type.doc_type()
-    return '@item @code{%s%s%s}%s%s\n' % (
+    return '@item @code{%s%s}%s%s\n' % (
         member.name,
-        ': ' if typ else '',
-        typ if typ else '',
+        ': %s' % typ if typ else '',
         ' (optional)' if member.optional else '',
         suffix)
 
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 07/26] qapi: add 'if' condition on top-level schema elements
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (5 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 06/26] qapi2texi: minor python code simplification Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-08-16 15:43   ` Markus Armbruster
  2017-08-17 11:51   ` Markus Armbruster
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 08/26] qapi: add 'if' condition on enum member values Marc-André Lureau
                   ` (19 subsequent siblings)
  26 siblings, 2 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Markus Armbruster, Michael Roth

Add 'if' c-preprocessor condition on top-level schema elements:
struct, enum, union, alternate, command, event.

Variants objects types are created outside of #if blocks, since they
may be shared by various types. Even if unused, this shouldn't be an
issue, since those are internal types.

Note that there is no checks in qapi scripts to verify that elements
have compatible conditions (ex: if-struct used by a if-foo
command). This may thus fail at C build time if they don't share the
same subset of conditions.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi.py                         | 153 +++++++++++++++++++++++++-------
 scripts/qapi-commands.py                |   4 +-
 scripts/qapi-event.py                   |   4 +-
 scripts/qapi-introspect.py              |  46 ++++++----
 scripts/qapi-types.py                   |  51 +++++++----
 scripts/qapi-visit.py                   |  13 ++-
 scripts/qapi2texi.py                    |  10 +--
 tests/Makefile.include                  |   1 +
 tests/qapi-schema/bad-if.err            |   1 +
 tests/qapi-schema/bad-if.exit           |   1 +
 tests/qapi-schema/bad-if.json           |   3 +
 tests/qapi-schema/bad-if.out            |   0
 tests/qapi-schema/qapi-schema-test.json |  20 +++++
 tests/qapi-schema/qapi-schema-test.out  |  31 +++++++
 tests/qapi-schema/test-qapi.py          |  21 +++--
 15 files changed, 272 insertions(+), 87 deletions(-)
 create mode 100644 tests/qapi-schema/bad-if.err
 create mode 100644 tests/qapi-schema/bad-if.exit
 create mode 100644 tests/qapi-schema/bad-if.json
 create mode 100644 tests/qapi-schema/bad-if.out

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 4ecc19e944..79ba1e93da 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -639,6 +639,16 @@ def add_name(name, info, meta, implicit=False):
     all_names[name] = meta
 
 
+def check_if(expr, info):
+    ifcond = expr.get('if')
+    if not ifcond or isinstance(ifcond, str):
+        return
+    if (not isinstance(ifcond, list) or
+        any([not isinstance(elt, str) for elt in ifcond])):
+        raise QAPISemError(info, "'if' condition requires a string or "
+                           "a list of string")
+
+
 def check_type(info, source, value, allow_array=False,
                allow_dict=False, allow_optional=False,
                allow_metas=[]):
@@ -865,6 +875,7 @@ def check_keys(expr_elem, meta, required, optional=[]):
     expr = expr_elem['expr']
     info = expr_elem['info']
     name = expr[meta]
+    optional.append('if')
     if not isinstance(name, str):
         raise QAPISemError(info, "'%s' key must have a string value" % meta)
     required = required + [meta]
@@ -880,6 +891,8 @@ def check_keys(expr_elem, meta, required, optional=[]):
             raise QAPISemError(info,
                                "'%s' of %s '%s' should only use true value"
                                % (key, meta, name))
+        if key == 'if':
+            check_if(expr, info)
     for key in required:
         if key not in expr:
             raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
@@ -989,6 +1002,10 @@ class QAPISchemaEntity(object):
         # such place).
         self.info = info
         self.doc = doc
+        self.ifcond = None
+
+    def set_ifcond(self, ifcond):
+        self.ifcond = ifcond
 
     def c_name(self):
         return c_name(self.name)
@@ -1017,26 +1034,26 @@ class QAPISchemaVisitor(object):
     def visit_builtin_type(self, name, info, json_type):
         pass
 
-    def visit_enum_type(self, name, info, values, prefix):
+    def visit_enum_type(self, name, info, values, prefix, ifcond):
         pass
 
-    def visit_array_type(self, name, info, element_type):
+    def visit_array_type(self, name, info, element_type, ifcond):
         pass
 
-    def visit_object_type(self, name, info, base, members, variants):
+    def visit_object_type(self, name, info, base, members, variants, ifcond):
         pass
 
-    def visit_object_type_flat(self, name, info, members, variants):
+    def visit_object_type_flat(self, name, info, members, variants, ifcond):
         pass
 
-    def visit_alternate_type(self, name, info, variants):
+    def visit_alternate_type(self, name, info, variants, ifcond):
         pass
 
     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response, boxed):
+                      gen, success_response, boxed, ifcond):
         pass
 
-    def visit_event(self, name, info, arg_type, boxed):
+    def visit_event(self, name, info, arg_type, boxed, ifcond):
         pass
 
 
@@ -1136,7 +1153,7 @@ class QAPISchemaEnumType(QAPISchemaType):
 
     def visit(self, visitor):
         visitor.visit_enum_type(self.name, self.info,
-                                self.member_names(), self.prefix)
+                                self.member_names(), self.prefix, self.ifcond)
 
 
 class QAPISchemaArrayType(QAPISchemaType):
@@ -1149,6 +1166,7 @@ class QAPISchemaArrayType(QAPISchemaType):
     def check(self, schema):
         self.element_type = schema.lookup_type(self._element_type_name)
         assert self.element_type
+        self.ifcond = self.element_type.ifcond
 
     def is_implicit(self):
         return True
@@ -1166,7 +1184,8 @@ class QAPISchemaArrayType(QAPISchemaType):
         return 'array of ' + elt_doc_type
 
     def visit(self, visitor):
-        visitor.visit_array_type(self.name, self.info, self.element_type)
+        visitor.visit_array_type(self.name, self.info, self.element_type,
+                                 self.ifcond)
 
 
 class QAPISchemaObjectType(QAPISchemaType):
@@ -1247,9 +1266,10 @@ class QAPISchemaObjectType(QAPISchemaType):
 
     def visit(self, visitor):
         visitor.visit_object_type(self.name, self.info,
-                                  self.base, self.local_members, self.variants)
+                                  self.base, self.local_members, self.variants,
+                                  self.ifcond)
         visitor.visit_object_type_flat(self.name, self.info,
-                                       self.members, self.variants)
+                                       self.members, self.variants, self.ifcond)
 
 
 class QAPISchemaMember(object):
@@ -1392,7 +1412,8 @@ class QAPISchemaAlternateType(QAPISchemaType):
         return 'value'
 
     def visit(self, visitor):
-        visitor.visit_alternate_type(self.name, self.info, self.variants)
+        visitor.visit_alternate_type(self.name, self.info,
+                                     self.variants, self.ifcond)
 
     def is_empty(self):
         return False
@@ -1434,7 +1455,8 @@ class QAPISchemaCommand(QAPISchemaEntity):
     def visit(self, visitor):
         visitor.visit_command(self.name, self.info,
                               self.arg_type, self.ret_type,
-                              self.gen, self.success_response, self.boxed)
+                              self.gen, self.success_response, self.boxed,
+                              self.ifcond)
 
 
 class QAPISchemaEvent(QAPISchemaEntity):
@@ -1462,7 +1484,8 @@ class QAPISchemaEvent(QAPISchemaEntity):
             raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
 
     def visit(self, visitor):
-        visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
+        visitor.visit_event(self.name, self.info, self.arg_type, self.boxed,
+                            self.ifcond)
 
 
 class QAPISchema(object):
@@ -1481,11 +1504,12 @@ class QAPISchema(object):
             print >>sys.stderr, err
             exit(1)
 
-    def _def_entity(self, ent):
+    def _def_entity(self, ent, ifcond=None):
         # Only the predefined types are allowed to not have info
         assert ent.info or self._predefining
         assert ent.name not in self._entity_dict
         self._entity_dict[ent.name] = ent
+        ent.set_ifcond(ifcond)
 
     def lookup_entity(self, name, typ=None):
         ent = self._entity_dict.get(name)
@@ -1534,11 +1558,11 @@ class QAPISchema(object):
     def _make_enum_members(self, values):
         return [QAPISchemaMember(v) for v in values]
 
-    def _make_implicit_enum_type(self, name, info, values):
+    def _make_implicit_enum_type(self, name, info, values, ifcond):
         # See also QAPISchemaObjectTypeMember._pretty_owner()
         name = name + 'Kind'   # Use namespace reserved by add_name()
         self._def_entity(QAPISchemaEnumType(
-            name, info, None, self._make_enum_members(values), None))
+            name, info, None, self._make_enum_members(values), None), ifcond)
         return name
 
     def _make_array_type(self, element_type, info):
@@ -1547,22 +1571,26 @@ class QAPISchema(object):
             self._def_entity(QAPISchemaArrayType(name, info, element_type))
         return name
 
-    def _make_implicit_object_type(self, name, info, doc, role, members):
+    def _make_implicit_object_type(self, name, info, doc, role, members,
+                                   ifcond=None):
         if not members:
             return None
         # See also QAPISchemaObjectTypeMember._pretty_owner()
         name = 'q_obj_%s-%s' % (name, role)
-        if not self.lookup_entity(name, QAPISchemaObjectType):
+        if self.lookup_entity(name, QAPISchemaObjectType):
+            assert ifcond is None
+        else:
             self._def_entity(QAPISchemaObjectType(name, info, doc, None,
-                                                  members, None))
+                                                  members, None), ifcond)
         return name
 
     def _def_enum_type(self, expr, info, doc):
         name = expr['enum']
         data = expr['data']
         prefix = expr.get('prefix')
-        self._def_entity(QAPISchemaEnumType(
-            name, info, doc, self._make_enum_members(data), prefix))
+        return self._def_entity(QAPISchemaEnumType(
+            name, info, doc, self._make_enum_members(data), prefix),
+                                expr.get('if'))
 
     def _make_member(self, name, typ, info):
         optional = False
@@ -1584,7 +1612,8 @@ class QAPISchema(object):
         data = expr['data']
         self._def_entity(QAPISchemaObjectType(name, info, doc, base,
                                               self._make_members(data, info),
-                                              None))
+                                              None),
+                         expr.get('if'))
 
     def _make_variant(self, case, typ):
         return QAPISchemaObjectTypeVariant(case, typ)
@@ -1593,8 +1622,10 @@ class QAPISchema(object):
         if isinstance(typ, list):
             assert len(typ) == 1
             typ = self._make_array_type(typ[0], info)
+        type_entity = self.lookup_type(typ)
         typ = self._make_implicit_object_type(
-            typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
+            typ, info, None, 'wrapper',
+            [self._make_member('data', typ, info)], type_entity.ifcond)
         return QAPISchemaObjectTypeVariant(case, typ)
 
     def _def_union_type(self, expr, info, doc):
@@ -1604,8 +1635,9 @@ class QAPISchema(object):
         tag_name = expr.get('discriminator')
         tag_member = None
         if isinstance(base, dict):
-            base = (self._make_implicit_object_type(
-                name, info, doc, 'base', self._make_members(base, info)))
+            base = self._make_implicit_object_type(
+                name, info, doc, 'base', self._make_members(base, info),
+                expr.get('if'))
         if tag_name:
             variants = [self._make_variant(key, value)
                         for (key, value) in data.iteritems()]
@@ -1614,14 +1646,16 @@ class QAPISchema(object):
             variants = [self._make_simple_variant(key, value, info)
                         for (key, value) in data.iteritems()]
             typ = self._make_implicit_enum_type(name, info,
-                                                [v.name for v in variants])
+                                                [v.name for v in variants],
+                                                expr.get('if'))
             tag_member = QAPISchemaObjectTypeMember('type', typ, False)
             members = [tag_member]
         self._def_entity(
             QAPISchemaObjectType(name, info, doc, base, members,
                                  QAPISchemaObjectTypeVariants(tag_name,
                                                               tag_member,
-                                                              variants)))
+                                                              variants)),
+            expr.get('if'))
 
     def _def_alternate_type(self, expr, info, doc):
         name = expr['alternate']
@@ -1633,7 +1667,8 @@ class QAPISchema(object):
             QAPISchemaAlternateType(name, info, doc,
                                     QAPISchemaObjectTypeVariants(None,
                                                                  tag_member,
-                                                                 variants)))
+                                                                 variants)),
+            expr.get('if'))
 
     def _def_command(self, expr, info, doc):
         name = expr['command']
@@ -1644,12 +1679,14 @@ class QAPISchema(object):
         boxed = expr.get('boxed', False)
         if isinstance(data, OrderedDict):
             data = self._make_implicit_object_type(
-                name, info, doc, 'arg', self._make_members(data, info))
+                name, info, doc, 'arg', self._make_members(data, info),
+                expr.get('if'))
         if isinstance(rets, list):
             assert len(rets) == 1
             rets = self._make_array_type(rets[0], info)
         self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
-                                           gen, success_response, boxed))
+                                           gen, success_response, boxed),
+                         expr.get('if'))
 
     def _def_event(self, expr, info, doc):
         name = expr['event']
@@ -1657,8 +1694,10 @@ class QAPISchema(object):
         boxed = expr.get('boxed', False)
         if isinstance(data, OrderedDict):
             data = self._make_implicit_object_type(
-                name, info, doc, 'arg', self._make_members(data, info))
-        self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
+                name, info, doc, 'arg', self._make_members(data, info),
+                expr.get('if'))
+        self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed),
+                         expr.get('if'))
 
     def _def_exprs(self):
         for expr_elem in self.exprs:
@@ -1848,6 +1887,54 @@ def guardend(name):
                  name=guardname(name))
 
 
+def gen_if(ifcond, func=''):
+    if not ifcond:
+        return ''
+    if isinstance(ifcond, str):
+        ifcond = [ifcond]
+    ret = '\n'
+    for ifc in ifcond:
+        ret += mcgen('#if %(ifcond)s /* %(func)s */\n', ifcond=ifc, func=func)
+    ret += '\n'
+    return ret
+
+
+def gen_endif(ifcond, func=''):
+    if not ifcond:
+        return ''
+    if isinstance(ifcond, str):
+        ifcond = [ifcond]
+    ret = '\n'
+    for ifc in reversed(ifcond):
+        ret += mcgen('#endif /* %(ifcond)s %(func)s */\n',
+                     ifcond=ifc, func=func)
+    ret += '\n'
+    return ret
+
+
+# wrap a method to add #if / #endif to generated code
+# self must have 'if_members' listing the attributes to wrap
+# the last argument of the wrapped function must be the 'ifcond'
+def if_wrap(func):
+    def func_wrapper(self, *args, **kwargs):
+        funcname = self.__class__.__name__ + '.' + func.__name__
+        ifcond = args[-1]
+        save = {}
+        for mem in self.if_members:
+            save[mem] = getattr(self, mem)
+        func(self, *args, **kwargs)
+        for mem, val in save.items():
+            newval = getattr(self, mem)
+            if newval != val:
+                assert newval.startswith(val)
+                newval = newval[len(val):]
+                val += gen_if(ifcond, funcname)
+                val += newval
+                val += gen_endif(ifcond, funcname)
+            setattr(self, mem, val)
+
+    return func_wrapper
+
 def gen_enum_lookup(name, values, prefix=None):
     ret = mcgen('''
 
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 974d0a4a80..19b1bb9b88 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -228,6 +228,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
         self.defn = None
         self._regy = None
         self._visited_ret_types = None
+        self.if_members = ['decl', 'defn', '_regy']
 
     def visit_begin(self, schema):
         self.decl = ''
@@ -240,8 +241,9 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
         self._regy = None
         self._visited_ret_types = None
 
+    @if_wrap
     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response, boxed):
+                      gen, success_response, boxed, ifcond):
         if not gen:
             return
         self.decl += gen_command_decl(name, arg_type, boxed, ret_type)
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index bcbef1035f..cad9ece790 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -152,6 +152,7 @@ class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
         self.decl = None
         self.defn = None
         self._event_names = None
+        self.if_members = ['decl', 'defn']
 
     def visit_begin(self, schema):
         self.decl = ''
@@ -163,7 +164,8 @@ class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
         self.defn += gen_enum_lookup(event_enum_name, self._event_names)
         self._event_names = None
 
-    def visit_event(self, name, info, arg_type, boxed):
+    @if_wrap
+    def visit_event(self, name, info, arg_type, boxed, ifcond):
         self.decl += gen_event_send_decl(name, arg_type, boxed)
         self.defn += gen_event_send(name, arg_type, boxed)
         self._event_names.append(name)
diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
index fc72cdb66d..ecfb0f2752 100644
--- a/scripts/qapi-introspect.py
+++ b/scripts/qapi-introspect.py
@@ -12,7 +12,7 @@
 from qapi import *
 
 
-def to_qlit(obj, level=0, first_indent=True):
+def to_qlit(obj, level=0, first_indent=True, suffix=''):
     def indent(level):
         return level * 4 * ' '
     ret = ''
@@ -20,14 +20,20 @@ def to_qlit(obj, level=0, first_indent=True):
         ret += indent(level)
     if obj is None:
         ret += 'QLIT_QNULL'
+    elif isinstance(obj, tuple):
+        obj, ifcond =  obj
+        ret += gen_if(ifcond)
+        ret += to_qlit(obj, level, False) + suffix
+        ret += gen_endif(ifcond)
+        suffix = ''
     elif isinstance(obj, str):
         ret += 'QLIT_QSTR(' + '"' + obj.replace('"', r'\"') + '"' + ')'
     elif isinstance(obj, list):
-        elts = [to_qlit(elt, level + 1)
+        elts = [to_qlit(elt, level + 1, True, ",")
                 for elt in obj]
         elts.append(indent(level + 1) + "{ }")
         ret += 'QLIT_QLIST(((QLitObject[]) {\n'
-        ret += ',\n'.join(elts) + '\n'
+        ret += '\n'.join(elts) + '\n'
         ret += indent(level) + '}))'
     elif isinstance(obj, dict):
         elts = [ indent(level + 1) + '{ "%s", %s }' %
@@ -39,7 +45,7 @@ def to_qlit(obj, level=0, first_indent=True):
         ret += indent(level) + '}))'
     else:
         assert False                # not implemented
-    return ret
+    return ret + suffix
 
 
 class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor):
@@ -113,12 +119,12 @@ const QLitObject %(c_name)s = %(c_string)s;
             return '[' + self._use_type(typ.element_type) + ']'
         return self._name(typ.name)
 
-    def _gen_qlit(self, name, mtype, obj):
+    def _gen_qlit(self, name, mtype, obj, ifcond):
         if mtype not in ('command', 'event', 'builtin', 'array'):
             name = self._name(name)
         obj['name'] = name
         obj['meta-type'] = mtype
-        self._qlits.append(obj)
+        self._qlits.append((obj, ifcond))
 
     def _gen_member(self, member):
         ret = {'name': member.name, 'type': self._use_type(member.type)}
@@ -134,38 +140,40 @@ const QLitObject %(c_name)s = %(c_string)s;
         return {'case': variant.name, 'type': self._use_type(variant.type)}
 
     def visit_builtin_type(self, name, info, json_type):
-        self._gen_qlit(name, 'builtin', {'json-type': json_type})
+        self._gen_qlit(name, 'builtin', {'json-type': json_type}, None)
 
-    def visit_enum_type(self, name, info, values, prefix):
-        self._gen_qlit(name, 'enum', {'values': values})
+    def visit_enum_type(self, name, info, values, prefix, ifcond):
+        self._gen_qlit(name, 'enum', {'values': values}, ifcond)
 
-    def visit_array_type(self, name, info, element_type):
+    def visit_array_type(self, name, info, element_type, ifcond):
         element = self._use_type(element_type)
-        self._gen_qlit('[' + element + ']', 'array', {'element-type': element})
+        self._gen_qlit('[' + element + ']', 'array', {'element-type': element},
+                       ifcond)
 
-    def visit_object_type_flat(self, name, info, members, variants):
+    def visit_object_type_flat(self, name, info, members, variants, ifcond):
         obj = {'members': [self._gen_member(m) for m in members]}
         if variants:
             obj.update(self._gen_variants(variants.tag_member.name,
                                           variants.variants))
-        self._gen_qlit(name, 'object', obj)
+        self._gen_qlit(name, 'object', obj, ifcond)
 
-    def visit_alternate_type(self, name, info, variants):
+    def visit_alternate_type(self, name, info, variants, ifcond):
         self._gen_qlit(name, 'alternate',
                        {'members': [{'type': self._use_type(m.type)}
-                                    for m in variants.variants]})
+                                    for m in variants.variants]}, ifcond)
 
     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response, boxed):
+                      gen, success_response, boxed, ifcond):
         arg_type = arg_type or self._schema.the_empty_object_type
         ret_type = ret_type or self._schema.the_empty_object_type
         self._gen_qlit(name, 'command',
                        {'arg-type': self._use_type(arg_type),
-                        'ret-type': self._use_type(ret_type)})
+                        'ret-type': self._use_type(ret_type)}, ifcond)
 
-    def visit_event(self, name, info, arg_type, boxed):
+    def visit_event(self, name, info, arg_type, boxed, ifcond):
         arg_type = arg_type or self._schema.the_empty_object_type
-        self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)})
+        self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)},
+                       ifcond)
 
 # Debugging aid: unmask QAPI schema's type names
 # We normally mask them, because they're not QMP wire ABI
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index b45e7b5634..d0d2eb917c 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -53,19 +53,22 @@ def gen_struct_members(members):
     return ret
 
 
-def gen_object(name, base, members, variants):
-    if name in objects_seen:
-        return ''
-    objects_seen.add(name)
-
+def gen_variants_objects(variants):
     ret = ''
     if variants:
         for v in variants.variants:
             if isinstance(v.type, QAPISchemaObjectType):
+                ret += gen_variants_objects(v.type.variants)
                 ret += gen_object(v.type.name, v.type.base,
                                   v.type.local_members, v.type.variants)
+    return ret
 
-    ret += mcgen('''
+def gen_object(name, base, members, variants):
+    if name in objects_seen:
+        return ''
+    objects_seen.add(name)
+
+    ret = mcgen('''
 
 struct %(c_name)s {
 ''',
@@ -171,6 +174,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
         self.defn = None
         self._fwdecl = None
         self._btin = None
+        self.if_members = ['decl', 'defn', '_fwdecl', '_btin']
 
     def visit_begin(self, schema):
         # gen_object() is recursive, ensure it doesn't visit the empty type
@@ -191,11 +195,13 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
         self.decl = self._btin + self.decl
         self._btin = None
 
-    def _gen_type_cleanup(self, name):
+    @if_wrap
+    def _gen_type_cleanup(self, name, ifcond):
         self.decl += gen_type_cleanup_decl(name)
         self.defn += gen_type_cleanup(name)
 
-    def visit_enum_type(self, name, info, values, prefix):
+    @if_wrap
+    def visit_enum_type(self, name, info, values, prefix, ifcond):
         # Special case for our lone builtin enum type
         # TODO use something cleaner than existence of info
         if not info:
@@ -206,7 +212,8 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
             self._fwdecl += gen_enum(name, values, prefix)
             self.defn += gen_enum_lookup(name, values, prefix)
 
-    def visit_array_type(self, name, info, element_type):
+    @if_wrap
+    def visit_array_type(self, name, info, element_type, ifcond):
         if isinstance(element_type, QAPISchemaBuiltinType):
             self._btin += gen_fwd_object_or_array(name)
             self._btin += gen_array(name, element_type)
@@ -216,13 +223,10 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
         else:
             self._fwdecl += gen_fwd_object_or_array(name)
             self.decl += gen_array(name, element_type)
-            self._gen_type_cleanup(name)
+            self._gen_type_cleanup(name, ifcond)
 
-    def visit_object_type(self, name, info, base, members, variants):
-        # Nothing to do for the special empty builtin
-        if name == 'q_empty':
-            return
-        self._fwdecl += gen_fwd_object_or_array(name)
+    @if_wrap
+    def _gen_object(self, name, info, base, members, variants, ifcond):
         self.decl += gen_object(name, base, members, variants)
         if base and not base.is_implicit():
             self.decl += gen_upcast(name, base)
@@ -230,12 +234,21 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
         # directly use rather than repeat type.is_implicit()?
         if not name.startswith('q_'):
             # implicit types won't be directly allocated/freed
-            self._gen_type_cleanup(name)
+            self._gen_type_cleanup(name, ifcond)
+
+    def visit_object_type(self, name, info, base, members, variants, ifcond):
+        # Nothing to do for the special empty builtin
+        if name == 'q_empty':
+            return
+        self._fwdecl += gen_fwd_object_or_array(name)
+        self.decl += gen_variants_objects(variants)
+        self._gen_object(name, info, base, members, variants, ifcond)
 
-    def visit_alternate_type(self, name, info, variants):
+    def visit_alternate_type(self, name, info, variants, ifcond):
         self._fwdecl += gen_fwd_object_or_array(name)
-        self.decl += gen_object(name, None, [variants.tag_member], variants)
-        self._gen_type_cleanup(name)
+        self.decl += gen_variants_objects(variants)
+        self._gen_object(name, info, None, [variants.tag_member],
+                         variants, ifcond)
 
 # If you link code generated from multiple schemata, you want only one
 # instance of the code for built-in types.  Generate it only when
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 60850a6cdd..335407d078 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -267,6 +267,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
         self.decl = None
         self.defn = None
         self._btin = None
+        self.if_members = ['decl', 'defn', '_btin']
 
     def visit_begin(self, schema):
         self.decl = ''
@@ -282,7 +283,8 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
         self.decl = self._btin + self.decl
         self._btin = None
 
-    def visit_enum_type(self, name, info, values, prefix):
+    @if_wrap
+    def visit_enum_type(self, name, info, values, prefix, ifcond):
         # Special case for our lone builtin enum type
         # TODO use something cleaner than existence of info
         if not info:
@@ -293,7 +295,8 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
             self.decl += gen_visit_decl(name, scalar=True)
             self.defn += gen_visit_enum(name, prefix)
 
-    def visit_array_type(self, name, info, element_type):
+    @if_wrap
+    def visit_array_type(self, name, info, element_type, ifcond):
         decl = gen_visit_decl(name)
         defn = gen_visit_list(name, element_type)
         if isinstance(element_type, QAPISchemaBuiltinType):
@@ -304,7 +307,8 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
             self.decl += decl
             self.defn += defn
 
-    def visit_object_type(self, name, info, base, members, variants):
+    @if_wrap
+    def visit_object_type(self, name, info, base, members, variants, ifcond):
         # Nothing to do for the special empty builtin
         if name == 'q_empty':
             return
@@ -317,7 +321,8 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
             self.decl += gen_visit_decl(name)
             self.defn += gen_visit_object(name, base, members, variants)
 
-    def visit_alternate_type(self, name, info, variants):
+    @if_wrap
+    def visit_alternate_type(self, name, info, variants, ifcond):
         self.decl += gen_visit_decl(name)
         self.defn += gen_visit_alternate(name, variants)
 
diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py
index 639eb1d042..5aa2f48bfd 100755
--- a/scripts/qapi2texi.py
+++ b/scripts/qapi2texi.py
@@ -207,7 +207,7 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
     def visit_begin(self, schema):
         self.out = ''
 
-    def visit_enum_type(self, name, info, values, prefix):
+    def visit_enum_type(self, name, info, values, prefix, ifcond):
         doc = self.cur_doc
         if self.out:
             self.out += '\n'
@@ -216,7 +216,7 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
                              body=texi_entity(doc, 'Values',
                                               member_func=texi_enum_value))
 
-    def visit_object_type(self, name, info, base, members, variants):
+    def visit_object_type(self, name, info, base, members, variants, ifcond):
         doc = self.cur_doc
         if base and base.is_implicit():
             base = None
@@ -226,7 +226,7 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
                              name=doc.symbol,
                              body=texi_entity(doc, 'Members', base, variants))
 
-    def visit_alternate_type(self, name, info, variants):
+    def visit_alternate_type(self, name, info, variants, ifcond):
         doc = self.cur_doc
         if self.out:
             self.out += '\n'
@@ -235,7 +235,7 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
                              body=texi_entity(doc, 'Members'))
 
     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response, boxed):
+                      gen, success_response, boxed, ifcond):
         doc = self.cur_doc
         if self.out:
             self.out += '\n'
@@ -249,7 +249,7 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
                             name=doc.symbol,
                             body=body)
 
-    def visit_event(self, name, info, arg_type, boxed):
+    def visit_event(self, name, info, arg_type, boxed, ifcond):
         doc = self.cur_doc
         if self.out:
             self.out += '\n'
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 960ab8c6dd..0fc7088b2c 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -373,6 +373,7 @@ qapi-schema += args-unknown.json
 qapi-schema += bad-base.json
 qapi-schema += bad-data.json
 qapi-schema += bad-ident.json
+qapi-schema += bad-if.json
 qapi-schema += bad-type-bool.json
 qapi-schema += bad-type-dict.json
 qapi-schema += bad-type-int.json
diff --git a/tests/qapi-schema/bad-if.err b/tests/qapi-schema/bad-if.err
new file mode 100644
index 0000000000..8054fbb143
--- /dev/null
+++ b/tests/qapi-schema/bad-if.err
@@ -0,0 +1 @@
+tests/qapi-schema/bad-if.json:2: 'if' condition requires a string or a list of string
diff --git a/tests/qapi-schema/bad-if.exit b/tests/qapi-schema/bad-if.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/bad-if.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/bad-if.json b/tests/qapi-schema/bad-if.json
new file mode 100644
index 0000000000..3edd1a0bf2
--- /dev/null
+++ b/tests/qapi-schema/bad-if.json
@@ -0,0 +1,3 @@
+# check invalid 'if' type
+{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
+  'if': { 'value': 'defined(TEST_IF_STRUCT)' } }
diff --git a/tests/qapi-schema/bad-if.out b/tests/qapi-schema/bad-if.out
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index c72dbd8050..dc2c444fc1 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -188,3 +188,23 @@
   'data': { 'a': ['__org.qemu_x-Enum'], 'b': ['__org.qemu_x-Struct'],
             'c': '__org.qemu_x-Union2', 'd': '__org.qemu_x-Alt' },
   'returns': '__org.qemu_x-Union1' }
+
+# test 'if' condition handling
+
+{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
+  'if': 'defined(TEST_IF_STRUCT)' }
+
+{ 'enum': 'TestIfEnum', 'data': [ 'foo', 'bar' ],
+  'if': 'defined(TEST_IF_ENUM)' }
+
+{ 'union': 'TestIfUnion', 'data': { 'foo': 'TestStruct' },
+  'if': 'defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)' }
+
+{ 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 'TestStruct' },
+  'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' }
+
+{ 'command': 'TestIfCmd', 'data': { 'foo': 'TestIfStruct' },
+  'if': 'defined(TEST_IF_CMD) && defined(TEST_IF_STRUCT)' }
+
+{ 'event': 'TestIfEvent', 'data': { 'foo': 'TestIfStruct' },
+  'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' }
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 3b1e9082d3..fc5fd25f1b 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -52,6 +52,29 @@ enum QEnumTwo ['value1', 'value2']
     prefix QENUM_TWO
 enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
     prefix QTYPE
+alternate TestIfAlternate
+    tag type
+    case foo: int
+    case bar: TestStruct
+    if defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)
+command TestIfCmd q_obj_TestIfCmd-arg -> None
+   gen=True success_response=True boxed=False
+    if defined(TEST_IF_CMD) && defined(TEST_IF_STRUCT)
+enum TestIfEnum ['foo', 'bar']
+    if defined(TEST_IF_ENUM)
+event TestIfEvent q_obj_TestIfEvent-arg
+   boxed=False
+    if defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)
+object TestIfStruct
+    member foo: int optional=False
+    if defined(TEST_IF_STRUCT)
+object TestIfUnion
+    member type: TestIfUnionKind optional=False
+    tag type
+    case foo: q_obj_TestStruct-wrapper
+    if defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)
+enum TestIfUnionKind ['foo']
+    if defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)
 object TestStruct
     member integer: int optional=False
     member boolean: bool optional=False
@@ -172,6 +195,14 @@ object q_obj_EVENT_D-arg
     member b: str optional=False
     member c: str optional=True
     member enum3: EnumOne optional=True
+object q_obj_TestIfCmd-arg
+    member foo: TestIfStruct optional=False
+    if defined(TEST_IF_CMD) && defined(TEST_IF_STRUCT)
+object q_obj_TestIfEvent-arg
+    member foo: TestIfStruct optional=False
+    if defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)
+object q_obj_TestStruct-wrapper
+    member data: TestStruct optional=False
 object q_obj_UserDefFlatUnion2-base
     member integer: int optional=True
     member string: str optional=False
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index c7724d3437..17fd975812 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -17,12 +17,13 @@ import sys
 
 
 class QAPISchemaTestVisitor(QAPISchemaVisitor):
-    def visit_enum_type(self, name, info, values, prefix):
+    def visit_enum_type(self, name, info, values, prefix, ifcond):
         print 'enum %s %s' % (name, values)
         if prefix:
             print '    prefix %s' % prefix
+        self._print_if(ifcond)
 
-    def visit_object_type(self, name, info, base, members, variants):
+    def visit_object_type(self, name, info, base, members, variants, ifcond):
         print 'object %s' % name
         if base:
             print '    base %s' % base.name
@@ -30,21 +31,25 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
             print '    member %s: %s optional=%s' % \
                 (m.name, m.type.name, m.optional)
         self._print_variants(variants)
+        self._print_if(ifcond)
 
-    def visit_alternate_type(self, name, info, variants):
+    def visit_alternate_type(self, name, info, variants, ifcond):
         print 'alternate %s' % name
         self._print_variants(variants)
+        self._print_if(ifcond)
 
     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response, boxed):
+                      gen, success_response, boxed, ifcond):
         print 'command %s %s -> %s' % \
             (name, arg_type and arg_type.name, ret_type and ret_type.name)
         print '   gen=%s success_response=%s boxed=%s' % \
             (gen, success_response, boxed)
+        self._print_if(ifcond)
 
-    def visit_event(self, name, info, arg_type, boxed):
+    def visit_event(self, name, info, arg_type, boxed, ifcond):
         print 'event %s %s' % (name, arg_type and arg_type.name)
         print '   boxed=%s' % boxed
+        self._print_if(ifcond)
 
     @staticmethod
     def _print_variants(variants):
@@ -53,6 +58,12 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
             for v in variants.variants:
                 print '    case %s: %s' % (v.name, v.type.name)
 
+    @staticmethod
+    def _print_if(ifcond):
+        if ifcond:
+            print '    if %s' % ifcond
+
+
 schema = QAPISchema(sys.argv[1])
 schema.visit(QAPISchemaTestVisitor())
 
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 08/26] qapi: add 'if' condition on enum member values
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (6 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 07/26] qapi: add 'if' condition on top-level schema elements Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 09/26] qapi: add 'if' condition on struct member Marc-André Lureau
                   ` (18 subsequent siblings)
  26 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Markus Armbruster, Michael Roth

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi.py                                 | 39 +++++++++++++++++++++----
 tests/Makefile.include                          |  2 ++
 tests/qapi-schema/enum-dict-member-invalid.err  |  1 +
 tests/qapi-schema/enum-dict-member-invalid.exit |  1 +
 tests/qapi-schema/enum-dict-member-invalid.json |  2 ++
 tests/qapi-schema/enum-dict-member-invalid.out  |  0
 tests/qapi-schema/enum-dict-member.err          |  1 -
 tests/qapi-schema/enum-dict-member.exit         |  2 +-
 tests/qapi-schema/enum-dict-member.json         |  3 +-
 tests/qapi-schema/enum-dict-member.out          |  4 +++
 tests/qapi-schema/enum-if-invalid.err           |  1 +
 tests/qapi-schema/enum-if-invalid.exit          |  1 +
 tests/qapi-schema/enum-if-invalid.json          |  3 ++
 tests/qapi-schema/enum-if-invalid.out           |  0
 tests/qapi-schema/qapi-schema-test.json         |  5 ++--
 tests/qapi-schema/qapi-schema-test.out          |  3 +-
 tests/qapi-schema/test-qapi.py                  |  4 ++-
 17 files changed, 58 insertions(+), 14 deletions(-)
 create mode 100644 tests/qapi-schema/enum-dict-member-invalid.err
 create mode 100644 tests/qapi-schema/enum-dict-member-invalid.exit
 create mode 100644 tests/qapi-schema/enum-dict-member-invalid.json
 create mode 100644 tests/qapi-schema/enum-dict-member-invalid.out
 create mode 100644 tests/qapi-schema/enum-if-invalid.err
 create mode 100644 tests/qapi-schema/enum-if-invalid.exit
 create mode 100644 tests/qapi-schema/enum-if-invalid.json
 create mode 100644 tests/qapi-schema/enum-if-invalid.out

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 79ba1e93da..93d07283e6 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -729,6 +729,10 @@ def check_event(expr, info):
                allow_metas=meta)
 
 
+def enum_get_values(expr):
+    return [e if isinstance(e, str) else e['name'] for e in expr['data']]
+
+
 def check_union(expr, info):
     name = expr['union']
     base = expr.get('base')
@@ -788,7 +792,7 @@ def check_union(expr, info):
         # If the discriminator names an enum type, then all members
         # of 'data' must also be members of the enum type.
         if enum_define:
-            if key not in enum_define['data']:
+            if key not in enum_get_values(enum_define):
                 raise QAPISemError(info,
                                    "Discriminator value '%s' is not found in "
                                    "enum '%s'"
@@ -796,7 +800,7 @@ def check_union(expr, info):
 
     # If discriminator is user-defined, ensure all values are covered
     if enum_define:
-        for value in enum_define['data']:
+        for value in enum_get_values(enum_define):
             if value not in members.keys():
                 raise QAPISemError(info, "Union '%s' data missing '%s' branch"
                                    % (name, value))
@@ -827,7 +831,7 @@ def check_alternate(expr, info):
         if qtype == 'QTYPE_QSTRING':
             enum_expr = enum_types.get(value)
             if enum_expr:
-                for v in enum_expr['data']:
+                for v in enum_get_values(enum_expr):
                     if v in ['on', 'off']:
                         conflicting.add('QTYPE_QBOOL')
                     if re.match(r'[-+0-9.]', v): # lazy, could be tightened
@@ -857,6 +861,12 @@ def check_enum(expr, info):
         raise QAPISemError(info,
                            "Enum '%s' requires a string for 'prefix'" % name)
     for member in members:
+        if isinstance(member, dict):
+            if not 'name' in member:
+                raise QAPISemError(info, "Dictionary member of enum '%s' must "
+                                   "have a 'name' key" % name)
+            check_if(member, info)
+            member = member['name']
         check_name(info, "Member of enum '%s'" % name, member,
                    enum_member=True)
 
@@ -1148,12 +1158,15 @@ class QAPISchemaEnumType(QAPISchemaType):
     def member_names(self):
         return [v.name for v in self.values]
 
+    def members(self):
+        return [(v.name, v.ifcond) for v in self.values]
+
     def json_type(self):
         return 'string'
 
     def visit(self, visitor):
         visitor.visit_enum_type(self.name, self.info,
-                                self.member_names(), self.prefix, self.ifcond)
+                                self.members(), self.prefix, self.ifcond)
 
 
 class QAPISchemaArrayType(QAPISchemaType):
@@ -1275,9 +1288,11 @@ class QAPISchemaObjectType(QAPISchemaType):
 class QAPISchemaMember(object):
     role = 'member'
 
-    def __init__(self, name):
+    def __init__(self, name, ifcond=None):
         assert isinstance(name, str)
+        assert ifcond is None or isinstance(ifcond, str)
         self.name = name
+        self.ifcond = ifcond
         self.owner = None
 
     def set_owner(self, name):
@@ -1556,7 +1571,9 @@ class QAPISchema(object):
                                             qtype_values, 'QTYPE'))
 
     def _make_enum_members(self, values):
-        return [QAPISchemaMember(v) for v in values]
+        return [QAPISchemaMember(v['name'], v.get('if')) if isinstance(v, dict)
+                else QAPISchemaMember(v)
+                for v in values]
 
     def _make_implicit_enum_type(self, name, info, values, ifcond):
         # See also QAPISchemaObjectTypeMember._pretty_owner()
@@ -1942,11 +1959,16 @@ const char *const %(c_name)s_lookup[] = {
 ''',
                 c_name=c_name(name))
     for value in values:
+        ifcond = None
+        if isinstance(value, tuple):
+            value, ifcond = value
+        ret += gen_if(ifcond)
         index = c_enum_const(name, value, prefix)
         ret += mcgen('''
     [%(index)s] = "%(value)s",
 ''',
                      index=index, value=value)
+        ret += gen_endif(ifcond)
 
     max_index = c_enum_const(name, '_MAX', prefix)
     ret += mcgen('''
@@ -1969,12 +1991,17 @@ typedef enum %(c_name)s {
 
     i = 0
     for value in enum_values:
+        ifcond = None
+        if isinstance(value, tuple):
+            value, ifcond = value
+        ret += gen_if(ifcond)
         ret += mcgen('''
     %(c_enum)s = %(i)d,
 ''',
                      c_enum=c_enum_const(name, value, prefix),
                      i=i)
         i += 1
+        ret += gen_endif(ifcond)
 
     ret += mcgen('''
 } %(c_name)s;
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 0fc7088b2c..cb5daf0009 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -413,6 +413,8 @@ qapi-schema += enum-bad-name.json
 qapi-schema += enum-bad-prefix.json
 qapi-schema += enum-clash-member.json
 qapi-schema += enum-dict-member.json
+qapi-schema += enum-dict-member-invalid.json
+qapi-schema += enum-if-invalid.json
 qapi-schema += enum-int-member.json
 qapi-schema += enum-member-case.json
 qapi-schema += enum-missing-data.json
diff --git a/tests/qapi-schema/enum-dict-member-invalid.err b/tests/qapi-schema/enum-dict-member-invalid.err
new file mode 100644
index 0000000000..d12cca0df3
--- /dev/null
+++ b/tests/qapi-schema/enum-dict-member-invalid.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-dict-member-invalid.json:2: Dictionary member of enum 'MyEnum' must have a 'name' key
diff --git a/tests/qapi-schema/enum-dict-member-invalid.exit b/tests/qapi-schema/enum-dict-member-invalid.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/enum-dict-member-invalid.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-dict-member-invalid.json b/tests/qapi-schema/enum-dict-member-invalid.json
new file mode 100644
index 0000000000..9cf8406867
--- /dev/null
+++ b/tests/qapi-schema/enum-dict-member-invalid.json
@@ -0,0 +1,2 @@
+# we reject any enum member that is not a string or a dict with 'name'
+{ 'enum': 'MyEnum', 'data': [ { 'value': 'str' } ] }
diff --git a/tests/qapi-schema/enum-dict-member-invalid.out b/tests/qapi-schema/enum-dict-member-invalid.out
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/qapi-schema/enum-dict-member.err b/tests/qapi-schema/enum-dict-member.err
index 8ca146ea59..e69de29bb2 100644
--- a/tests/qapi-schema/enum-dict-member.err
+++ b/tests/qapi-schema/enum-dict-member.err
@@ -1 +0,0 @@
-tests/qapi-schema/enum-dict-member.json:2: Member of enum 'MyEnum' requires a string name
diff --git a/tests/qapi-schema/enum-dict-member.exit b/tests/qapi-schema/enum-dict-member.exit
index d00491fd7e..573541ac97 100644
--- a/tests/qapi-schema/enum-dict-member.exit
+++ b/tests/qapi-schema/enum-dict-member.exit
@@ -1 +1 @@
-1
+0
diff --git a/tests/qapi-schema/enum-dict-member.json b/tests/qapi-schema/enum-dict-member.json
index 79672e0f09..b09a83061c 100644
--- a/tests/qapi-schema/enum-dict-member.json
+++ b/tests/qapi-schema/enum-dict-member.json
@@ -1,2 +1 @@
-# we reject any enum member that is not a string
-{ 'enum': 'MyEnum', 'data': [ { 'value': 'str' } ] }
+{ 'enum': 'TestEnum', 'data': [ { 'name': 'foo' }, { 'name': 'bar' } ]}
diff --git a/tests/qapi-schema/enum-dict-member.out b/tests/qapi-schema/enum-dict-member.out
index e69de29bb2..cf8e3cce2b 100644
--- a/tests/qapi-schema/enum-dict-member.out
+++ b/tests/qapi-schema/enum-dict-member.out
@@ -0,0 +1,4 @@
+enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
+    prefix QTYPE
+enum TestEnum ['foo', 'bar']
+object q_empty
diff --git a/tests/qapi-schema/enum-if-invalid.err b/tests/qapi-schema/enum-if-invalid.err
new file mode 100644
index 0000000000..236b703e88
--- /dev/null
+++ b/tests/qapi-schema/enum-if-invalid.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-if-invalid.json:2: 'if' condition requires a string or a list of string
diff --git a/tests/qapi-schema/enum-if-invalid.exit b/tests/qapi-schema/enum-if-invalid.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/enum-if-invalid.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-if-invalid.json b/tests/qapi-schema/enum-if-invalid.json
new file mode 100644
index 0000000000..60bd0ef1d7
--- /dev/null
+++ b/tests/qapi-schema/enum-if-invalid.json
@@ -0,0 +1,3 @@
+# check invalid 'if' type
+{ 'enum': 'TestIfEnum', 'data':
+  [ 'foo', { 'name' : 'bar', 'if': { 'val': 'foo' } } ] }
diff --git a/tests/qapi-schema/enum-if-invalid.out b/tests/qapi-schema/enum-if-invalid.out
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index dc2c444fc1..ad2b405d83 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -194,7 +194,8 @@
 { 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
   'if': 'defined(TEST_IF_STRUCT)' }
 
-{ 'enum': 'TestIfEnum', 'data': [ 'foo', 'bar' ],
+{ 'enum': 'TestIfEnum', 'data':
+  [ 'foo', { 'name' : 'bar', 'if': 'defined(TEST_IF_ENUM_BAR)' } ],
   'if': 'defined(TEST_IF_ENUM)' }
 
 { 'union': 'TestIfUnion', 'data': { 'foo': 'TestStruct' },
@@ -203,7 +204,7 @@
 { 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 'TestStruct' },
   'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' }
 
-{ 'command': 'TestIfCmd', 'data': { 'foo': 'TestIfStruct' },
+{ 'command': 'TestIfCmd', 'data': { 'foo': 'TestIfStruct', 'bar': 'TestIfEnum' },
   'if': 'defined(TEST_IF_CMD) && defined(TEST_IF_STRUCT)' }
 
 { 'event': 'TestIfEvent', 'data': { 'foo': 'TestIfStruct' },
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index fc5fd25f1b..211c367632 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -60,7 +60,7 @@ alternate TestIfAlternate
 command TestIfCmd q_obj_TestIfCmd-arg -> None
    gen=True success_response=True boxed=False
     if defined(TEST_IF_CMD) && defined(TEST_IF_STRUCT)
-enum TestIfEnum ['foo', 'bar']
+enum TestIfEnum ['foo', ('bar', 'defined(TEST_IF_ENUM_BAR)')]
     if defined(TEST_IF_ENUM)
 event TestIfEvent q_obj_TestIfEvent-arg
    boxed=False
@@ -197,6 +197,7 @@ object q_obj_EVENT_D-arg
     member enum3: EnumOne optional=True
 object q_obj_TestIfCmd-arg
     member foo: TestIfStruct optional=False
+    member bar: TestIfEnum optional=False
     if defined(TEST_IF_CMD) && defined(TEST_IF_STRUCT)
 object q_obj_TestIfEvent-arg
     member foo: TestIfStruct optional=False
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 17fd975812..70054848f0 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -18,7 +18,9 @@ import sys
 
 class QAPISchemaTestVisitor(QAPISchemaVisitor):
     def visit_enum_type(self, name, info, values, prefix, ifcond):
-        print 'enum %s %s' % (name, values)
+        values = ', '.join(["'%s'" % v[0] if not v[1] else str(v)
+                            for v in values])
+        print 'enum %s [%s]' % (name, values)
         if prefix:
             print '    prefix %s' % prefix
         self._print_if(ifcond)
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 09/26] qapi: add 'if' condition on struct member
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (7 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 08/26] qapi: add 'if' condition on enum member values Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 10/26] qapi: add 'if' condition on union variant Marc-André Lureau
                   ` (17 subsequent siblings)
  26 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Markus Armbruster, Michael Roth

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi.py                           | 18 ++++++++++++++----
 scripts/qapi-introspect.py                |  2 ++
 scripts/qapi-types.py                     |  2 ++
 scripts/qapi-visit.py                     |  2 ++
 tests/Makefile.include                    |  2 ++
 tests/qapi-schema/qapi-schema-test.json   |  9 ++++++---
 tests/qapi-schema/qapi-schema-test.out    |  4 +++-
 tests/qapi-schema/struct-if-invalid.err   |  1 +
 tests/qapi-schema/struct-if-invalid.exit  |  1 +
 tests/qapi-schema/struct-if-invalid.json  |  3 +++
 tests/qapi-schema/struct-if-invalid.out   |  0
 tests/qapi-schema/struct-member-type.err  |  0
 tests/qapi-schema/struct-member-type.exit |  1 +
 tests/qapi-schema/struct-member-type.json |  1 +
 tests/qapi-schema/struct-member-type.out  |  5 +++++
 tests/qapi-schema/test-qapi.py            |  3 ++-
 16 files changed, 45 insertions(+), 9 deletions(-)
 create mode 100644 tests/qapi-schema/struct-if-invalid.err
 create mode 100644 tests/qapi-schema/struct-if-invalid.exit
 create mode 100644 tests/qapi-schema/struct-if-invalid.json
 create mode 100644 tests/qapi-schema/struct-if-invalid.out
 create mode 100644 tests/qapi-schema/struct-member-type.err
 create mode 100644 tests/qapi-schema/struct-member-type.exit
 create mode 100644 tests/qapi-schema/struct-member-type.json
 create mode 100644 tests/qapi-schema/struct-member-type.out

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 93d07283e6..1eb40590fb 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -678,7 +678,13 @@ def check_type(info, source, value, allow_array=False,
         return
 
     if not allow_dict:
-        raise QAPISemError(info, "%s should be a type name" % source)
+        if isinstance(value, dict) and 'type' in value:
+            check_type(info, source, value['type'], allow_array,
+                       allow_dict, allow_optional, allow_metas)
+            check_if(value, info)
+            return
+        else:
+            raise QAPISemError(info, "%s should be a type name" % source)
 
     if not isinstance(value, OrderedDict):
         raise QAPISemError(info,
@@ -1333,8 +1339,8 @@ class QAPISchemaMember(object):
 
 
 class QAPISchemaObjectTypeMember(QAPISchemaMember):
-    def __init__(self, name, typ, optional):
-        QAPISchemaMember.__init__(self, name)
+    def __init__(self, name, typ, optional, ifcond=None):
+        QAPISchemaMember.__init__(self, name, ifcond)
         assert isinstance(typ, str)
         assert isinstance(optional, bool)
         self._type_name = typ
@@ -1611,13 +1617,17 @@ class QAPISchema(object):
 
     def _make_member(self, name, typ, info):
         optional = False
+        ifcond = None
         if name.startswith('*'):
             name = name[1:]
             optional = True
+        if isinstance(typ, dict):
+            ifcond = typ.get('if')
+            typ = typ['type']
         if isinstance(typ, list):
             assert len(typ) == 1
             typ = self._make_array_type(typ[0], info)
-        return QAPISchemaObjectTypeMember(name, typ, optional)
+        return QAPISchemaObjectTypeMember(name, typ, optional, ifcond)
 
     def _make_members(self, data, info):
         return [self._make_member(key, value, info)
diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
index ecfb0f2752..98b320a79e 100644
--- a/scripts/qapi-introspect.py
+++ b/scripts/qapi-introspect.py
@@ -130,6 +130,8 @@ const QLitObject %(c_name)s = %(c_string)s;
         ret = {'name': member.name, 'type': self._use_type(member.type)}
         if member.optional:
             ret['default'] = None
+        if member.ifcond:
+            ret = (ret, member.ifcond)
         return ret
 
     def _gen_variants(self, tag_name, variants):
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index d0d2eb917c..659fb1da86 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -41,6 +41,7 @@ struct %(c_name)s {
 def gen_struct_members(members):
     ret = ''
     for memb in members:
+        ret += gen_if(memb.ifcond)
         if memb.optional:
             ret += mcgen('''
     bool has_%(c_name)s;
@@ -50,6 +51,7 @@ def gen_struct_members(members):
     %(c_type)s %(c_name)s;
 ''',
                      c_type=memb.type.c_type(), c_name=c_name(memb.name))
+        ret += gen_endif(memb.ifcond)
     return ret
 
 
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 335407d078..f400cef13c 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -53,6 +53,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
                      c_type=base.c_name())
 
     for memb in members:
+        ret += gen_if(memb.ifcond)
         if memb.optional:
             ret += mcgen('''
     if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
@@ -72,6 +73,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
             ret += mcgen('''
     }
 ''')
+        ret += gen_endif(memb.ifcond)
 
     if variants:
         ret += mcgen('''
diff --git a/tests/Makefile.include b/tests/Makefile.include
index cb5daf0009..816cc5c5e3 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -489,7 +489,9 @@ qapi-schema += returns-whitelist.json
 qapi-schema += struct-base-clash-deep.json
 qapi-schema += struct-base-clash.json
 qapi-schema += struct-data-invalid.json
+qapi-schema += struct-if-invalid.json
 qapi-schema += struct-member-invalid.json
+qapi-schema += struct-member-type.json
 qapi-schema += trailing-comma-list.json
 qapi-schema += trailing-comma-object.json
 qapi-schema += type-bypass-bad-gen.json
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index ad2b405d83..bb515280d2 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -191,7 +191,8 @@
 
 # test 'if' condition handling
 
-{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
+{ 'struct': 'TestIfStruct', 'data':
+  { 'foo': 'int', 'bar': { 'type': 'int', 'if': 'defined(TEST_IF_STRUCT_BAR)'} },
   'if': 'defined(TEST_IF_STRUCT)' }
 
 { 'enum': 'TestIfEnum', 'data':
@@ -204,8 +205,10 @@
 { 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 'TestStruct' },
   'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' }
 
-{ 'command': 'TestIfCmd', 'data': { 'foo': 'TestIfStruct', 'bar': 'TestIfEnum' },
+{ 'command': 'TestIfCmd', 'data':
+  { 'foo': 'TestIfStruct', 'bar': { 'type': 'TestIfEnum', 'if': 'TEST_IF_CMD_BAR' } },
   'if': 'defined(TEST_IF_CMD) && defined(TEST_IF_STRUCT)' }
 
-{ 'event': 'TestIfEvent', 'data': { 'foo': 'TestIfStruct' },
+{ 'event': 'TestIfEvent', 'data':
+  { 'foo': 'TestIfStruct', 'bar': { 'type': 'TestIfEnum', 'if': 'TEST_IF_EVT_BAR' } },
   'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' }
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 211c367632..b17c32a45f 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -67,6 +67,7 @@ event TestIfEvent q_obj_TestIfEvent-arg
     if defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)
 object TestIfStruct
     member foo: int optional=False
+    member bar: int optional=False if=defined(TEST_IF_STRUCT_BAR)
     if defined(TEST_IF_STRUCT)
 object TestIfUnion
     member type: TestIfUnionKind optional=False
@@ -197,10 +198,11 @@ object q_obj_EVENT_D-arg
     member enum3: EnumOne optional=True
 object q_obj_TestIfCmd-arg
     member foo: TestIfStruct optional=False
-    member bar: TestIfEnum optional=False
+    member bar: TestIfEnum optional=False if=TEST_IF_CMD_BAR
     if defined(TEST_IF_CMD) && defined(TEST_IF_STRUCT)
 object q_obj_TestIfEvent-arg
     member foo: TestIfStruct optional=False
+    member bar: TestIfEnum optional=False if=TEST_IF_EVT_BAR
     if defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)
 object q_obj_TestStruct-wrapper
     member data: TestStruct optional=False
diff --git a/tests/qapi-schema/struct-if-invalid.err b/tests/qapi-schema/struct-if-invalid.err
new file mode 100644
index 0000000000..42245262a9
--- /dev/null
+++ b/tests/qapi-schema/struct-if-invalid.err
@@ -0,0 +1 @@
+tests/qapi-schema/struct-if-invalid.json:2: Member 'bar' of 'data' for struct 'TestIfStruct' should be a type name
diff --git a/tests/qapi-schema/struct-if-invalid.exit b/tests/qapi-schema/struct-if-invalid.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/struct-if-invalid.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/struct-if-invalid.json b/tests/qapi-schema/struct-if-invalid.json
new file mode 100644
index 0000000000..466cdb61e1
--- /dev/null
+++ b/tests/qapi-schema/struct-if-invalid.json
@@ -0,0 +1,3 @@
+# check missing 'type'
+{ 'struct': 'TestIfStruct', 'data':
+  { 'foo': 'int', 'bar': { 'if': 'defined(TEST_IF_STRUCT_BAR)' } } }
diff --git a/tests/qapi-schema/struct-if-invalid.out b/tests/qapi-schema/struct-if-invalid.out
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/qapi-schema/struct-member-type.err b/tests/qapi-schema/struct-member-type.err
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/qapi-schema/struct-member-type.exit b/tests/qapi-schema/struct-member-type.exit
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/tests/qapi-schema/struct-member-type.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/struct-member-type.json b/tests/qapi-schema/struct-member-type.json
new file mode 100644
index 0000000000..07de38fa4a
--- /dev/null
+++ b/tests/qapi-schema/struct-member-type.json
@@ -0,0 +1 @@
+{ 'struct': 'foo', 'data': { 'a': { 'type': 'str' } } }
diff --git a/tests/qapi-schema/struct-member-type.out b/tests/qapi-schema/struct-member-type.out
new file mode 100644
index 0000000000..2ea68909a9
--- /dev/null
+++ b/tests/qapi-schema/struct-member-type.out
@@ -0,0 +1,5 @@
+enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
+    prefix QTYPE
+object foo
+    member a: str optional=False
+object q_empty
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 70054848f0..5d2f67a1d3 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -31,7 +31,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
             print '    base %s' % base.name
         for m in members:
             print '    member %s: %s optional=%s' % \
-                (m.name, m.type.name, m.optional)
+                (m.name, m.type.name, m.optional) + \
+                (' if=%s' % m.ifcond if m.ifcond else '')
         self._print_variants(variants)
         self._print_if(ifcond)
 
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 10/26] qapi: add 'if' condition on union variant
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (8 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 09/26] qapi: add 'if' condition on struct member Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 11/26] qapi: add 'if' condition on alternate variant Marc-André Lureau
                   ` (16 subsequent siblings)
  26 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Markus Armbruster, Michael Roth

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi.py                         | 14 +++++++++-----
 scripts/qapi-introspect.py              |  3 ++-
 scripts/qapi-types.py                   |  2 ++
 scripts/qapi-visit.py                   |  4 ++++
 tests/qapi-schema/qapi-schema-test.json |  6 +++++-
 tests/qapi-schema/qapi-schema-test.out  |  9 ++++++++-
 tests/qapi-schema/test-qapi.py          |  3 ++-
 7 files changed, 32 insertions(+), 9 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 1eb40590fb..333a5c0d1e 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1398,8 +1398,8 @@ class QAPISchemaObjectTypeVariants(object):
 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
     role = 'branch'
 
-    def __init__(self, name, typ):
-        QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
+    def __init__(self, name, typ, ifcond=None):
+        QAPISchemaObjectTypeMember.__init__(self, name, typ, False, ifcond)
 
 
 class QAPISchemaAlternateType(QAPISchemaType):
@@ -1646,6 +1646,10 @@ class QAPISchema(object):
         return QAPISchemaObjectTypeVariant(case, typ)
 
     def _make_simple_variant(self, case, typ, info):
+        ifcond = None
+        if isinstance(typ, dict):
+            ifcond = typ.get('if')
+            typ = typ['type']
         if isinstance(typ, list):
             assert len(typ) == 1
             typ = self._make_array_type(typ[0], info)
@@ -1653,7 +1657,7 @@ class QAPISchema(object):
         typ = self._make_implicit_object_type(
             typ, info, None, 'wrapper',
             [self._make_member('data', typ, info)], type_entity.ifcond)
-        return QAPISchemaObjectTypeVariant(case, typ)
+        return QAPISchemaObjectTypeVariant(case, typ, ifcond)
 
     def _def_union_type(self, expr, info, doc):
         name = expr['union']
@@ -1672,8 +1676,8 @@ class QAPISchema(object):
         else:
             variants = [self._make_simple_variant(key, value, info)
                         for (key, value) in data.iteritems()]
-            typ = self._make_implicit_enum_type(name, info,
-                                                [v.name for v in variants],
+            values = [{'name': v.name, 'if': v.ifcond} for v in variants]
+            typ = self._make_implicit_enum_type(name, info, values,
                                                 expr.get('if'))
             tag_member = QAPISchemaObjectTypeMember('type', typ, False)
             members = [tag_member]
diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
index 98b320a79e..a867ea5de1 100644
--- a/scripts/qapi-introspect.py
+++ b/scripts/qapi-introspect.py
@@ -139,7 +139,8 @@ const QLitObject %(c_name)s = %(c_string)s;
                 'variants': [self._gen_variant(v) for v in variants]}
 
     def _gen_variant(self, variant):
-        return {'case': variant.name, 'type': self._use_type(variant.type)}
+        return ({'case': variant.name, 'type': self._use_type(variant.type)},
+                variant.ifcond)
 
     def visit_builtin_type(self, name, info, json_type):
         self._gen_qlit(name, 'builtin', {'json-type': json_type}, None)
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 659fb1da86..d6c8feb55a 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -128,11 +128,13 @@ def gen_variants(variants):
                 c_name=c_name(variants.tag_member.name))
 
     for var in variants.variants:
+        ret += gen_if(var.ifcond)
         ret += mcgen('''
         %(c_type)s %(c_name)s;
 ''',
                      c_type=var.type.c_unboxed_type(),
                      c_name=c_name(var.name))
+        ret += gen_endif(var.ifcond)
 
     ret += mcgen('''
     } u;
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index f400cef13c..0f5cae33e4 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -82,6 +82,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
                      c_name=c_name(variants.tag_member.name))
 
         for var in variants.variants:
+            ret += gen_if(var.ifcond)
             ret += mcgen('''
     case %(case)s:
         visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, &err);
@@ -92,6 +93,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
                                            variants.tag_member.type.prefix),
                          c_type=var.type.c_name(), c_name=c_name(var.name))
 
+            ret += gen_endif(var.ifcond)
         ret += mcgen('''
     default:
         abort();
@@ -182,6 +184,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
                  c_name=c_name(name))
 
     for var in variants.variants:
+        ret += gen_if(var.ifcond)
         ret += mcgen('''
     case %(case)s:
 ''',
@@ -209,6 +212,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
         ret += mcgen('''
         break;
 ''')
+        ret += gen_endif(var.ifcond)
 
     ret += mcgen('''
     case QTYPE_NONE:
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index bb515280d2..01f7db58dc 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -199,9 +199,13 @@
   [ 'foo', { 'name' : 'bar', 'if': 'defined(TEST_IF_ENUM_BAR)' } ],
   'if': 'defined(TEST_IF_ENUM)' }
 
-{ 'union': 'TestIfUnion', 'data': { 'foo': 'TestStruct' },
+{ 'union': 'TestIfUnion', 'data':
+  { 'foo': 'TestStruct', 'union_bar': { 'type': 'str', 'if': 'defined(TEST_IF_UNION_BAR)'} },
   'if': 'defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)' }
 
+{ 'command': 'TestIfUnionCmd', 'data': { 'union_cmd_arg': 'TestIfUnion' },
+  'if': 'defined(TEST_IF_UNION)' }
+
 { 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 'TestStruct' },
   'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' }
 
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index b17c32a45f..7908eed6fc 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -73,8 +73,12 @@ object TestIfUnion
     member type: TestIfUnionKind optional=False
     tag type
     case foo: q_obj_TestStruct-wrapper
+    case union_bar: q_obj_str-wrapper if=defined(TEST_IF_UNION_BAR)
     if defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)
-enum TestIfUnionKind ['foo']
+command TestIfUnionCmd q_obj_TestIfUnionCmd-arg -> None
+   gen=True success_response=True boxed=False
+    if defined(TEST_IF_UNION)
+enum TestIfUnionKind ['foo', ('union_bar', 'defined(TEST_IF_UNION_BAR)')]
     if defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)
 object TestStruct
     member integer: int optional=False
@@ -204,6 +208,9 @@ object q_obj_TestIfEvent-arg
     member foo: TestIfStruct optional=False
     member bar: TestIfEnum optional=False if=TEST_IF_EVT_BAR
     if defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)
+object q_obj_TestIfUnionCmd-arg
+    member union_cmd_arg: TestIfUnion optional=False
+    if defined(TEST_IF_UNION)
 object q_obj_TestStruct-wrapper
     member data: TestStruct optional=False
 object q_obj_UserDefFlatUnion2-base
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 5d2f67a1d3..b360d44df1 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -59,7 +59,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
         if variants:
             print '    tag %s' % variants.tag_member.name
             for v in variants.variants:
-                print '    case %s: %s' % (v.name, v.type.name)
+                print '    case %s: %s' % (v.name, v.type.name) + \
+                    (' if=%s' % v.ifcond if v.ifcond else '')
 
     @staticmethod
     def _print_if(ifcond):
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 11/26] qapi: add 'if' condition on alternate variant
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (9 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 10/26] qapi: add 'if' condition on union variant Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 12/26] qapi2texi: add 'If:' section to generated documentation Marc-André Lureau
                   ` (15 subsequent siblings)
  26 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Markus Armbruster, Michael Roth

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi.py                               | 8 +++++++-
 scripts/qapi-introspect.py                    | 2 +-
 tests/Makefile.include                        | 1 +
 tests/qapi-schema/alternate-dict-invalid.err  | 1 +
 tests/qapi-schema/alternate-dict-invalid.exit | 1 +
 tests/qapi-schema/alternate-dict-invalid.json | 4 ++++
 tests/qapi-schema/alternate-dict-invalid.out  | 0
 tests/qapi-schema/qapi-schema-test.json       | 6 +++++-
 tests/qapi-schema/qapi-schema-test.out        | 8 +++++++-
 9 files changed, 27 insertions(+), 4 deletions(-)
 create mode 100644 tests/qapi-schema/alternate-dict-invalid.err
 create mode 100644 tests/qapi-schema/alternate-dict-invalid.exit
 create mode 100644 tests/qapi-schema/alternate-dict-invalid.json
 create mode 100644 tests/qapi-schema/alternate-dict-invalid.out

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 333a5c0d1e..7f0d56fb01 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -829,6 +829,8 @@ def check_alternate(expr, info):
         check_type(info, "Member '%s' of alternate '%s'" % (key, name),
                    value,
                    allow_metas=['built-in', 'union', 'struct', 'enum'])
+        if isinstance(value, dict):
+            value = value['type']
         qtype = find_alternate_member_qtype(value)
         if not qtype:
             raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
@@ -1643,7 +1645,11 @@ class QAPISchema(object):
                          expr.get('if'))
 
     def _make_variant(self, case, typ):
-        return QAPISchemaObjectTypeVariant(case, typ)
+        ifcond = None
+        if isinstance(typ, dict):
+            ifcond = typ.get('if')
+            typ = typ['type']
+        return QAPISchemaObjectTypeVariant(case, typ, ifcond)
 
     def _make_simple_variant(self, case, typ, info):
         ifcond = None
diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
index a867ea5de1..cac5a6a392 100644
--- a/scripts/qapi-introspect.py
+++ b/scripts/qapi-introspect.py
@@ -162,7 +162,7 @@ const QLitObject %(c_name)s = %(c_string)s;
 
     def visit_alternate_type(self, name, info, variants, ifcond):
         self._gen_qlit(name, 'alternate',
-                       {'members': [{'type': self._use_type(m.type)}
+                       {'members': [({'type': self._use_type(m.type)}, m.ifcond)
                                     for m in variants.variants]}, ifcond)
 
     def visit_command(self, name, info, arg_type, ret_type,
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 816cc5c5e3..9462deb02b 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -351,6 +351,7 @@ qapi-schema += alternate-conflict-dict.json
 qapi-schema += alternate-conflict-enum-bool.json
 qapi-schema += alternate-conflict-enum-int.json
 qapi-schema += alternate-conflict-string.json
+qapi-schema += alternate-dict-invalid.json
 qapi-schema += alternate-empty.json
 qapi-schema += alternate-nested.json
 qapi-schema += alternate-unknown.json
diff --git a/tests/qapi-schema/alternate-dict-invalid.err b/tests/qapi-schema/alternate-dict-invalid.err
new file mode 100644
index 0000000000..707c40f0f6
--- /dev/null
+++ b/tests/qapi-schema/alternate-dict-invalid.err
@@ -0,0 +1 @@
+tests/qapi-schema/alternate-dict-invalid.json:2: Member 'two' of alternate 'Alt' should be a type name
diff --git a/tests/qapi-schema/alternate-dict-invalid.exit b/tests/qapi-schema/alternate-dict-invalid.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/alternate-dict-invalid.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/alternate-dict-invalid.json b/tests/qapi-schema/alternate-dict-invalid.json
new file mode 100644
index 0000000000..45f2c8ebef
--- /dev/null
+++ b/tests/qapi-schema/alternate-dict-invalid.json
@@ -0,0 +1,4 @@
+# invalid field dictionnary, missing type
+{ 'alternate': 'Alt',
+  'data': { 'one': 'str',
+            'two': { 'if': 'foo' } } }
diff --git a/tests/qapi-schema/alternate-dict-invalid.out b/tests/qapi-schema/alternate-dict-invalid.out
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 01f7db58dc..46a4d1c3e5 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -206,9 +206,13 @@
 { 'command': 'TestIfUnionCmd', 'data': { 'union_cmd_arg': 'TestIfUnion' },
   'if': 'defined(TEST_IF_UNION)' }
 
-{ 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 'TestStruct' },
+{ 'alternate': 'TestIfAlternate', 'data':
+  { 'foo': 'int', 'alt_bar': { 'type': 'TestStruct', 'if': 'defined(TEST_IF_ALT_BAR)'} },
   'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' }
 
+{ 'command': 'TestIfAlternateCmd', 'data': { 'alt_cmd_arg': 'TestIfAlternate' },
+  'if': 'defined(TEST_IF_ALT)' }
+
 { 'command': 'TestIfCmd', 'data':
   { 'foo': 'TestIfStruct', 'bar': { 'type': 'TestIfEnum', 'if': 'TEST_IF_CMD_BAR' } },
   'if': 'defined(TEST_IF_CMD) && defined(TEST_IF_STRUCT)' }
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 7908eed6fc..a51c6ac15c 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -55,8 +55,11 @@ enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
 alternate TestIfAlternate
     tag type
     case foo: int
-    case bar: TestStruct
+    case alt_bar: TestStruct if=defined(TEST_IF_ALT_BAR)
     if defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)
+command TestIfAlternateCmd q_obj_TestIfAlternateCmd-arg -> None
+   gen=True success_response=True boxed=False
+    if defined(TEST_IF_ALT)
 command TestIfCmd q_obj_TestIfCmd-arg -> None
    gen=True success_response=True boxed=False
     if defined(TEST_IF_CMD) && defined(TEST_IF_STRUCT)
@@ -200,6 +203,9 @@ object q_obj_EVENT_D-arg
     member b: str optional=False
     member c: str optional=True
     member enum3: EnumOne optional=True
+object q_obj_TestIfAlternateCmd-arg
+    member alt_cmd_arg: TestIfAlternate optional=False
+    if defined(TEST_IF_ALT)
 object q_obj_TestIfCmd-arg
     member foo: TestIfStruct optional=False
     member bar: TestIfEnum optional=False if=TEST_IF_CMD_BAR
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 12/26] qapi2texi: add 'If:' section to generated documentation
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (10 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 11/26] qapi: add 'if' condition on alternate variant Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 13/26] qapi2texi: add 'If:' condition to enum values Marc-André Lureau
                   ` (14 subsequent siblings)
  26 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Markus Armbruster, Michael Roth

The documentation is generated only once, and doesn't know C
pre-conditions.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi2texi.py | 24 +++++++++++++-----------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py
index 5aa2f48bfd..bfb8cd7c52 100755
--- a/scripts/qapi2texi.py
+++ b/scripts/qapi2texi.py
@@ -132,7 +132,6 @@ def texi_enum_value(value):
     """Format a table of members item for an enumeration value"""
     return '@item @code{%s}\n' % value.name
 
-
 def texi_member(member, suffix=''):
     """Format a table of members item for an object type member"""
     typ = member.type.doc_type()
@@ -175,7 +174,7 @@ def texi_members(doc, what, base, variants, member_func):
     return '\n@b{%s:}\n@table @asis\n%s@end table\n' % (what, items)
 
 
-def texi_sections(doc):
+def texi_sections(doc, ifcond):
     """Format additional sections following arguments"""
     body = ''
     for section in doc.sections:
@@ -189,14 +188,16 @@ def texi_sections(doc):
             body += '\n\n@b{%s:}\n' % name
 
         body += func(doc)
+    if ifcond:
+        body += '\n\n@b{If:} @code{%s}' % ifcond
     return body
 
 
-def texi_entity(doc, what, base=None, variants=None,
+def texi_entity(doc, what, ifcond, base=None, variants=None,
                 member_func=texi_member):
     return (texi_body(doc)
             + texi_members(doc, what, base, variants, member_func)
-            + texi_sections(doc))
+            + texi_sections(doc, ifcond))
 
 
 class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
@@ -213,7 +214,7 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
             self.out += '\n'
         self.out += TYPE_FMT(type='Enum',
                              name=doc.symbol,
-                             body=texi_entity(doc, 'Values',
+                             body=texi_entity(doc, 'Values', ifcond,
                                               member_func=texi_enum_value))
 
     def visit_object_type(self, name, info, base, members, variants, ifcond):
@@ -224,7 +225,8 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
             self.out += '\n'
         self.out += TYPE_FMT(type='Object',
                              name=doc.symbol,
-                             body=texi_entity(doc, 'Members', base, variants))
+                             body=texi_entity(doc, 'Members', ifcond,
+                                              base, variants))
 
     def visit_alternate_type(self, name, info, variants, ifcond):
         doc = self.cur_doc
@@ -232,7 +234,7 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
             self.out += '\n'
         self.out += TYPE_FMT(type='Alternate',
                              name=doc.symbol,
-                             body=texi_entity(doc, 'Members'))
+                             body=texi_entity(doc, 'Members', ifcond))
 
     def visit_command(self, name, info, arg_type, ret_type,
                       gen, success_response, boxed, ifcond):
@@ -242,9 +244,9 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
         if boxed:
             body = texi_body(doc)
             body += '\n@b{Arguments:} the members of @code{%s}' % arg_type.name
-            body += texi_sections(doc)
+            body += texi_sections(doc, ifcond)
         else:
-            body = texi_entity(doc, 'Arguments')
+            body = texi_entity(doc, 'Arguments', ifcond)
         self.out += MSG_FMT(type='Command',
                             name=doc.symbol,
                             body=body)
@@ -255,7 +257,7 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
             self.out += '\n'
         self.out += MSG_FMT(type='Event',
                             name=doc.symbol,
-                            body=texi_entity(doc, 'Arguments'))
+                            body=texi_entity(doc, 'Arguments', ifcond))
 
     def symbol(self, doc, entity):
         self.cur_doc = doc
@@ -266,7 +268,7 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor):
         assert not doc.args
         if self.out:
             self.out += '\n'
-        self.out += texi_body(doc) + texi_sections(doc)
+        self.out += texi_body(doc) + texi_sections(doc, None)
 
 
 def texi_schema(schema):
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 13/26] qapi2texi: add 'If:' condition to enum values
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (11 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 12/26] qapi2texi: add 'If:' section to generated documentation Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 14/26] qapi2texi: add 'If:' condition to struct members Marc-André Lureau
                   ` (13 subsequent siblings)
  26 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Markus Armbruster, Michael Roth

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi2texi.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py
index bfb8cd7c52..340a55c30d 100755
--- a/scripts/qapi2texi.py
+++ b/scripts/qapi2texi.py
@@ -130,7 +130,9 @@ def texi_body(doc):
 
 def texi_enum_value(value):
     """Format a table of members item for an enumeration value"""
-    return '@item @code{%s}\n' % value.name
+    return '@item @code{%s}%s\n' % (
+        value.name,
+        '\n@b{If:} @code{%s}\n' % value.ifcond if value.ifcond else '')
 
 def texi_member(member, suffix=''):
     """Format a table of members item for an object type member"""
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 14/26] qapi2texi: add 'If:' condition to struct members
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (12 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 13/26] qapi2texi: add 'If:' condition to enum values Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 15/26] qapi2texi: add condition to variants Marc-André Lureau
                   ` (12 subsequent siblings)
  26 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Markus Armbruster, Michael Roth

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi2texi.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py
index 340a55c30d..b2c9a9f335 100755
--- a/scripts/qapi2texi.py
+++ b/scripts/qapi2texi.py
@@ -137,10 +137,11 @@ def texi_enum_value(value):
 def texi_member(member, suffix=''):
     """Format a table of members item for an object type member"""
     typ = member.type.doc_type()
-    return '@item @code{%s%s}%s%s\n' % (
+    return '@item @code{%s%s}%s%s%s\n' % (
         member.name,
         ': %s' % typ if typ else '',
         ' (optional)' if member.optional else '',
+        '\n@b{If:} @code{%s}\n' % member.ifcond if member.ifcond else '',
         suffix)
 
 
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 15/26] qapi2texi: add condition to variants
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (13 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 14/26] qapi2texi: add 'If:' condition to struct members Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 16/26] qapi: add conditions to VNC type/commands/events on the schema Marc-André Lureau
                   ` (11 subsequent siblings)
  26 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Markus Armbruster, Michael Roth

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi2texi.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py
index b2c9a9f335..502f1e7c6a 100755
--- a/scripts/qapi2texi.py
+++ b/scripts/qapi2texi.py
@@ -163,8 +163,9 @@ def texi_members(doc, what, base, variants, member_func):
         items += '@item The members of @code{%s}\n' % base.doc_type()
     if variants:
         for v in variants.variants:
-            when = ' when @code{%s} is @t{"%s"}' % (
-                variants.tag_member.name, v.name)
+            when = ' when @code{%s} is @t{"%s"}%s' % (
+                variants.tag_member.name, v.name,
+                ' (and @code{%s})' % v.ifcond if v.ifcond else '')
             if v.type.is_implicit():
                 assert not v.type.base and not v.type.variants
                 for m in v.type.local_members:
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 16/26] qapi: add conditions to VNC type/commands/events on the schema
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (14 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 15/26] qapi2texi: add condition to variants Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-07-28 19:00   ` Dr. David Alan Gilbert
  2017-08-17  7:04   ` Markus Armbruster
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 17/26] qapi: add conditions to SPICE " Marc-André Lureau
                   ` (10 subsequent siblings)
  26 siblings, 2 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: Marc-André Lureau, Dr. David Alan Gilbert, Eric Blake,
	Markus Armbruster

Add #if defined(CONFIG_VNC) in generated code, and adjust the
qmp/hmp code accordingly.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qapi-schema.json | 34 ++++++++++++++++++++++------------
 qapi/event.json  |  9 ++++++---
 hmp.c            | 14 +++++++++++++-
 qmp.c            | 30 ++++--------------------------
 4 files changed, 45 insertions(+), 42 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index 9c6c3e1a53..829c66f9eb 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1660,7 +1660,8 @@
   'data': { 'host': 'str',
             'service': 'str',
             'family': 'NetworkAddressFamily',
-            'websocket': 'bool' } }
+            'websocket': 'bool' },
+  'if': 'defined(CONFIG_VNC)' }
 
 ##
 # @VncServerInfo:
@@ -1674,7 +1675,8 @@
 ##
 { 'struct': 'VncServerInfo',
   'base': 'VncBasicInfo',
-  'data': { '*auth': 'str' } }
+  'data': { '*auth': 'str' },
+  'if': 'defined(CONFIG_VNC)' }
 
 ##
 # @VncClientInfo:
@@ -1691,7 +1693,8 @@
 ##
 { 'struct': 'VncClientInfo',
   'base': 'VncBasicInfo',
-  'data': { '*x509_dname': 'str', '*sasl_username': 'str' } }
+  'data': { '*x509_dname': 'str', '*sasl_username': 'str' },
+  'if': 'defined(CONFIG_VNC)' }
 
 ##
 # @VncInfo:
@@ -1732,7 +1735,8 @@
 { 'struct': 'VncInfo',
   'data': {'enabled': 'bool', '*host': 'str',
            '*family': 'NetworkAddressFamily',
-           '*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']} }
+           '*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']},
+  'if': 'defined(CONFIG_VNC)' }
 
 ##
 # @VncPrimaryAuth:
@@ -1743,7 +1747,8 @@
 ##
 { 'enum': 'VncPrimaryAuth',
   'data': [ 'none', 'vnc', 'ra2', 'ra2ne', 'tight', 'ultra',
-            'tls', 'vencrypt', 'sasl' ] }
+            'tls', 'vencrypt', 'sasl' ],
+  'if': 'defined(CONFIG_VNC)' }
 
 ##
 # @VncVencryptSubAuth:
@@ -1757,7 +1762,8 @@
             'tls-none',  'x509-none',
             'tls-vnc',   'x509-vnc',
             'tls-plain', 'x509-plain',
-            'tls-sasl',  'x509-sasl' ] }
+            'tls-sasl',  'x509-sasl' ],
+  'if': 'defined(CONFIG_VNC)' }
 
 
 ##
@@ -1775,7 +1781,8 @@
 { 'struct': 'VncServerInfo2',
   'base': 'VncBasicInfo',
   'data': { 'auth'      : 'VncPrimaryAuth',
-            '*vencrypt' : 'VncVencryptSubAuth' } }
+            '*vencrypt' : 'VncVencryptSubAuth' },
+  'if': 'defined(CONFIG_VNC)' }
 
 
 ##
@@ -1808,7 +1815,8 @@
             'clients'   : ['VncClientInfo'],
             'auth'      : 'VncPrimaryAuth',
             '*vencrypt' : 'VncVencryptSubAuth',
-            '*display'  : 'str' } }
+            '*display'  : 'str' },
+  'if': 'defined(CONFIG_VNC)' }
 
 ##
 # @query-vnc:
@@ -1839,7 +1847,8 @@
 #    }
 #
 ##
-{ 'command': 'query-vnc', 'returns': 'VncInfo' }
+{ 'command': 'query-vnc', 'returns': 'VncInfo',
+  'if': 'defined(CONFIG_VNC)' }
 
 ##
 # @query-vnc-servers:
@@ -1850,7 +1859,8 @@
 #
 # Since: 2.3
 ##
-{ 'command': 'query-vnc-servers', 'returns': ['VncInfo2'] }
+{ 'command': 'query-vnc-servers', 'returns': ['VncInfo2'],
+  'if': 'defined(CONFIG_VNC)' }
 
 ##
 # @SpiceBasicInfo:
@@ -3077,8 +3087,8 @@
 # Notes:  An empty password in this command will set the password to the empty
 #         string.  Existing clients are unaffected by executing this command.
 ##
-{ 'command': 'change-vnc-password', 'data': {'password': 'str'} }
-
+{ 'command': 'change-vnc-password', 'data': {'password': 'str'},
+  'if': 'defined(CONFIG_VNC)' }
 ##
 # @change:
 #
diff --git a/qapi/event.json b/qapi/event.json
index 6d22b025cc..c8b8e9f384 100644
--- a/qapi/event.json
+++ b/qapi/event.json
@@ -263,7 +263,8 @@
 ##
 { 'event': 'VNC_CONNECTED',
   'data': { 'server': 'VncServerInfo',
-            'client': 'VncBasicInfo' } }
+            'client': 'VncBasicInfo' },
+  'if': 'defined(CONFIG_VNC)' }
 
 ##
 # @VNC_INITIALIZED:
@@ -290,7 +291,8 @@
 ##
 { 'event': 'VNC_INITIALIZED',
   'data': { 'server': 'VncServerInfo',
-            'client': 'VncClientInfo' } }
+            'client': 'VncClientInfo' },
+  'if': 'defined(CONFIG_VNC)' }
 
 ##
 # @VNC_DISCONNECTED:
@@ -316,7 +318,8 @@
 ##
 { 'event': 'VNC_DISCONNECTED',
   'data': { 'server': 'VncServerInfo',
-            'client': 'VncClientInfo' } }
+            'client': 'VncClientInfo' },
+  'if': 'defined(CONFIG_VNC)' }
 
 ##
 # @SPICE_CONNECTED:
diff --git a/hmp.c b/hmp.c
index fd80dce758..9454c634bd 100644
--- a/hmp.c
+++ b/hmp.c
@@ -605,6 +605,7 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict)
     qapi_free_BlockStatsList(stats_list);
 }
 
+#ifdef CONFIG_VNC
 /* Helper for hmp_info_vnc_clients, _servers */
 static void hmp_info_VncBasicInfo(Monitor *mon, VncBasicInfo *info,
                                   const char *name)
@@ -692,6 +693,12 @@ void hmp_info_vnc(Monitor *mon, const QDict *qdict)
     qapi_free_VncInfo2List(info2l);
 
 }
+#else
+void hmp_info_vnc(Monitor *mon, const QDict *qdict)
+{
+    warn_report("VNC support is disabled");
+}
+#endif
 
 #ifdef CONFIG_SPICE
 void hmp_info_spice(Monitor *mon, const QDict *qdict)
@@ -1708,12 +1715,14 @@ void hmp_eject(Monitor *mon, const QDict *qdict)
     hmp_handle_error(mon, &err);
 }
 
+#ifdef CONFIG_VNC
 static void hmp_change_read_arg(void *opaque, const char *password,
                                 void *readline_opaque)
 {
     qmp_change_vnc_password(password, NULL);
     monitor_read_command(opaque, 1);
 }
+#endif
 
 void hmp_change(Monitor *mon, const QDict *qdict)
 {
@@ -1724,6 +1733,7 @@ void hmp_change(Monitor *mon, const QDict *qdict)
     BlockdevChangeReadOnlyMode read_only_mode = 0;
     Error *err = NULL;
 
+#ifdef CONFIG_VNC
     if (strcmp(device, "vnc") == 0) {
         if (read_only) {
             monitor_printf(mon,
@@ -1738,7 +1748,9 @@ void hmp_change(Monitor *mon, const QDict *qdict)
             }
         }
         qmp_change("vnc", target, !!arg, arg, &err);
-    } else {
+    } else
+#endif
+    {
         if (read_only) {
             read_only_mode =
                 qapi_enum_parse(BlockdevChangeReadOnlyMode_lookup,
diff --git a/qmp.c b/qmp.c
index b86201e349..2c90dacb56 100644
--- a/qmp.c
+++ b/qmp.c
@@ -130,22 +130,6 @@ void qmp_cpu_add(int64_t id, Error **errp)
     }
 }
 
-#ifndef CONFIG_VNC
-/* If VNC support is enabled, the "true" query-vnc command is
-   defined in the VNC subsystem */
-VncInfo *qmp_query_vnc(Error **errp)
-{
-    error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
-    return NULL;
-};
-
-VncInfo2List *qmp_query_vnc_servers(Error **errp)
-{
-    error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
-    return NULL;
-};
-#endif
-
 #ifndef CONFIG_SPICE
 /*
  * qmp-commands.hx ensures that QMP command query-spice exists only
@@ -403,23 +387,17 @@ static void qmp_change_vnc(const char *target, bool has_arg, const char *arg,
         qmp_change_vnc_listen(target, errp);
     }
 }
-#else
-void qmp_change_vnc_password(const char *password, Error **errp)
-{
-    error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
-}
-static void qmp_change_vnc(const char *target, bool has_arg, const char *arg,
-                           Error **errp)
-{
-    error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
-}
 #endif /* !CONFIG_VNC */
 
 void qmp_change(const char *device, const char *target,
                 bool has_arg, const char *arg, Error **errp)
 {
     if (strcmp(device, "vnc") == 0) {
+#ifdef CONFIG_VNC
         qmp_change_vnc(target, has_arg, arg, errp);
+#else
+        error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
+#endif
     } else {
         qmp_blockdev_change_medium(true, device, false, NULL, target,
                                    has_arg, arg, false, 0, errp);
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 17/26] qapi: add conditions to SPICE type/commands/events on the schema
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (15 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 16/26] qapi: add conditions to VNC type/commands/events on the schema Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-08-17  8:10   ` Markus Armbruster
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 18/26] qapi: add conditions to REPLICATION type/commands " Marc-André Lureau
                   ` (9 subsequent siblings)
  26 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: Marc-André Lureau, Markus Armbruster,
	Dr. David Alan Gilbert, Eric Blake

Add #if defined(CONFIG_SPICE) in generated code, and adjust the
qmp/hmp code accordingly.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qapi-schema.json | 28 ++++++++++++++++++----------
 qapi/event.json  | 12 ++++++++----
 monitor.c        |  3 ---
 qmp.c            | 16 ----------------
 4 files changed, 26 insertions(+), 33 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index 829c66f9eb..bcee3157b0 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1878,7 +1878,8 @@
 { 'struct': 'SpiceBasicInfo',
   'data': { 'host': 'str',
             'port': 'str',
-            'family': 'NetworkAddressFamily' } }
+            'family': 'NetworkAddressFamily' },
+  'if': 'defined(CONFIG_SPICE)' }
 
 ##
 # @SpiceServerInfo:
@@ -1891,7 +1892,8 @@
 ##
 { 'struct': 'SpiceServerInfo',
   'base': 'SpiceBasicInfo',
-  'data': { '*auth': 'str' } }
+  'data': { '*auth': 'str' },
+  'if': 'defined(CONFIG_SPICE)' }
 
 ##
 # @SpiceChannel:
@@ -1916,7 +1918,8 @@
 { 'struct': 'SpiceChannel',
   'base': 'SpiceBasicInfo',
   'data': {'connection-id': 'int', 'channel-type': 'int', 'channel-id': 'int',
-           'tls': 'bool'} }
+           'tls': 'bool'},
+  'if': 'defined(CONFIG_SPICE)' }
 
 ##
 # @SpiceQueryMouseMode:
@@ -1935,7 +1938,8 @@
 # Since: 1.1
 ##
 { 'enum': 'SpiceQueryMouseMode',
-  'data': [ 'client', 'server', 'unknown' ] }
+  'data': [ 'client', 'server', 'unknown' ],
+  'if': 'defined(CONFIG_SPICE)' }
 
 ##
 # @SpiceInfo:
@@ -1972,7 +1976,8 @@
 { 'struct': 'SpiceInfo',
   'data': {'enabled': 'bool', 'migrated': 'bool', '*host': 'str', '*port': 'int',
            '*tls-port': 'int', '*auth': 'str', '*compiled-version': 'str',
-           'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']} }
+           'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']},
+  'if': 'defined(CONFIG_SPICE)' }
 
 ##
 # @query-spice:
@@ -2017,7 +2022,8 @@
 #    }
 #
 ##
-{ 'command': 'query-spice', 'returns': 'SpiceInfo' }
+{ 'command': 'query-spice', 'returns': 'SpiceInfo',
+  'if': 'defined(CONFIG_SPICE)' }
 
 ##
 # @BalloonInfo:
@@ -5067,7 +5073,8 @@
 # Since: 1.5
 ##
 { 'struct': 'ChardevSpiceChannel', 'data': { 'type'  : 'str' },
-  'base': 'ChardevCommon' }
+  'base': 'ChardevCommon',
+  'if': 'defined(CONFIG_SPICE)' }
 
 ##
 # @ChardevSpicePort:
@@ -5079,7 +5086,8 @@
 # Since: 1.5
 ##
 { 'struct': 'ChardevSpicePort', 'data': { 'fqdn'  : 'str' },
-  'base': 'ChardevCommon' }
+  'base': 'ChardevCommon',
+  'if': 'defined(CONFIG_SPICE)' }
 
 ##
 # @ChardevVC:
@@ -5133,8 +5141,8 @@
                                        'testdev': 'ChardevCommon',
                                        'stdio'  : 'ChardevStdio',
                                        'console': 'ChardevCommon',
-                                       'spicevmc' : 'ChardevSpiceChannel',
-                                       'spiceport' : 'ChardevSpicePort',
+                                       'spicevmc' : { 'type': 'ChardevSpiceChannel', 'if': 'defined(CONFIG_SPICE)' },
+                                       'spiceport' : { 'type': 'ChardevSpicePort', 'if': 'defined(CONFIG_SPICE)' },
                                        'vc'     : 'ChardevVC',
                                        'ringbuf': 'ChardevRingbuf',
                                        # next one is just for compatibility
diff --git a/qapi/event.json b/qapi/event.json
index c8b8e9f384..ff59551914 100644
--- a/qapi/event.json
+++ b/qapi/event.json
@@ -344,7 +344,8 @@
 ##
 { 'event': 'SPICE_CONNECTED',
   'data': { 'server': 'SpiceBasicInfo',
-            'client': 'SpiceBasicInfo' } }
+            'client': 'SpiceBasicInfo' },
+  'if': 'defined(CONFIG_SPICE)' }
 
 ##
 # @SPICE_INITIALIZED:
@@ -372,7 +373,8 @@
 ##
 { 'event': 'SPICE_INITIALIZED',
   'data': { 'server': 'SpiceServerInfo',
-            'client': 'SpiceChannel' } }
+            'client': 'SpiceChannel' },
+  'if': 'defined(CONFIG_SPICE)' }
 
 ##
 # @SPICE_DISCONNECTED:
@@ -397,7 +399,8 @@
 ##
 { 'event': 'SPICE_DISCONNECTED',
   'data': { 'server': 'SpiceBasicInfo',
-            'client': 'SpiceBasicInfo' } }
+            'client': 'SpiceBasicInfo' },
+  'if': 'defined(CONFIG_SPICE)' }
 
 ##
 # @SPICE_MIGRATE_COMPLETED:
@@ -412,7 +415,8 @@
 #      "event": "SPICE_MIGRATE_COMPLETED" }
 #
 ##
-{ 'event': 'SPICE_MIGRATE_COMPLETED' }
+{ 'event': 'SPICE_MIGRATE_COMPLETED',
+  'if': 'defined(CONFIG_SPICE)' }
 
 ##
 # @MIGRATION:
diff --git a/monitor.c b/monitor.c
index a1773d5bc7..4bf6a3ea2e 100644
--- a/monitor.c
+++ b/monitor.c
@@ -970,9 +970,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
  */
 static void qmp_unregister_commands_hack(void)
 {
-#ifndef CONFIG_SPICE
-    qmp_unregister_command(&qmp_commands, "query-spice");
-#endif
 #ifndef CONFIG_REPLICATION
     qmp_unregister_command(&qmp_commands, "xen-set-replication");
     qmp_unregister_command(&qmp_commands, "query-xen-replication-status");
diff --git a/qmp.c b/qmp.c
index 2c90dacb56..90816ba283 100644
--- a/qmp.c
+++ b/qmp.c
@@ -130,22 +130,6 @@ void qmp_cpu_add(int64_t id, Error **errp)
     }
 }
 
-#ifndef CONFIG_SPICE
-/*
- * qmp-commands.hx ensures that QMP command query-spice exists only
- * #ifdef CONFIG_SPICE.  Necessary for an accurate query-commands
- * result.  However, the QAPI schema is blissfully unaware of that,
- * and the QAPI code generator happily generates a dead
- * qmp_marshal_query_spice() that calls qmp_query_spice().  Provide it
- * one, or else linking fails.  FIXME Educate the QAPI schema on
- * CONFIG_SPICE.
- */
-SpiceInfo *qmp_query_spice(Error **errp)
-{
-    abort();
-};
-#endif
-
 void qmp_cont(Error **errp)
 {
     BlockBackend *blk;
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 18/26] qapi: add conditions to REPLICATION type/commands on the schema
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (16 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 17/26] qapi: add conditions to SPICE " Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-08-17  9:16   ` Markus Armbruster
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 19/26] build-sys: move qapi variables in qapi.mak Marc-André Lureau
                   ` (8 subsequent siblings)
  26 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: Marc-André Lureau, zhanghailiang, Juan Quintela,
	Dr. David Alan Gilbert, Markus Armbruster, Eric Blake

Add #if defined(CONFIG_REPLICATION) in generated code, and adjust the
code accordingly.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qapi-schema.json | 12 ++++++++----
 migration/colo.c | 14 ++------------
 monitor.c        |  5 -----
 3 files changed, 10 insertions(+), 21 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index bcee3157b0..2f4528c769 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -6337,7 +6337,8 @@
 # Since: 2.9
 ##
 { 'command': 'xen-set-replication',
-  'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' } }
+  'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' },
+  'if': 'defined(CONFIG_REPLICATION)' }
 
 ##
 # @ReplicationStatus:
@@ -6352,7 +6353,8 @@
 # Since: 2.9
 ##
 { 'struct': 'ReplicationStatus',
-  'data': { 'error': 'bool', '*desc': 'str' } }
+  'data': { 'error': 'bool', '*desc': 'str' },
+  'if': 'defined(CONFIG_REPLICATION)' }
 
 ##
 # @query-xen-replication-status:
@@ -6369,7 +6371,8 @@
 # Since: 2.9
 ##
 { 'command': 'query-xen-replication-status',
-  'returns': 'ReplicationStatus' }
+  'returns': 'ReplicationStatus',
+  'if': 'defined(CONFIG_REPLICATION)' }
 
 ##
 # @xen-colo-do-checkpoint:
@@ -6385,7 +6388,8 @@
 #
 # Since: 2.9
 ##
-{ 'command': 'xen-colo-do-checkpoint' }
+{ 'command': 'xen-colo-do-checkpoint',
+  'if': 'defined(CONFIG_REPLICATION)' }
 
 ##
 # @GICCapability:
diff --git a/migration/colo.c b/migration/colo.c
index a4255432ac..3bff9fc9a4 100644
--- a/migration/colo.c
+++ b/migration/colo.c
@@ -147,11 +147,11 @@ void colo_do_failover(MigrationState *s)
     }
 }
 
+#ifdef CONFIG_REPLICATION
 void qmp_xen_set_replication(bool enable, bool primary,
                              bool has_failover, bool failover,
                              Error **errp)
 {
-#ifdef CONFIG_REPLICATION
     ReplicationMode mode = primary ?
                            REPLICATION_MODE_PRIMARY :
                            REPLICATION_MODE_SECONDARY;
@@ -170,14 +170,10 @@ void qmp_xen_set_replication(bool enable, bool primary,
         }
         replication_stop_all(failover, failover ? NULL : errp);
     }
-#else
-    abort();
-#endif
 }
 
 ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
 {
-#ifdef CONFIG_REPLICATION
     Error *err = NULL;
     ReplicationStatus *s = g_new0(ReplicationStatus, 1);
 
@@ -192,19 +188,13 @@ ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
 
     error_free(err);
     return s;
-#else
-    abort();
-#endif
 }
 
 void qmp_xen_colo_do_checkpoint(Error **errp)
 {
-#ifdef CONFIG_REPLICATION
     replication_do_checkpoint_all(errp);
-#else
-    abort();
-#endif
 }
+#endif
 
 static void colo_send_message(QEMUFile *f, COLOMessage msg,
                               Error **errp)
diff --git a/monitor.c b/monitor.c
index 4bf6a3ea2e..383c84d162 100644
--- a/monitor.c
+++ b/monitor.c
@@ -970,11 +970,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
  */
 static void qmp_unregister_commands_hack(void)
 {
-#ifndef CONFIG_REPLICATION
-    qmp_unregister_command(&qmp_commands, "xen-set-replication");
-    qmp_unregister_command(&qmp_commands, "query-xen-replication-status");
-    qmp_unregister_command(&qmp_commands, "xen-colo-do-checkpoint");
-#endif
 #ifndef TARGET_I386
     qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection");
 #endif
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 19/26] build-sys: move qapi variables in qapi.mak
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (17 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 18/26] qapi: add conditions to REPLICATION type/commands " Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-08-17  9:19   ` Markus Armbruster
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 20/26] tests/qmp-test: add query-qmp-schema test Marc-André Lureau
                   ` (7 subsequent siblings)
  26 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 Makefile | 43 +++++++++++++++++--------------------------
 qapi.mak | 14 ++++++++++++++
 2 files changed, 31 insertions(+), 26 deletions(-)
 create mode 100644 qapi.mak

diff --git a/Makefile b/Makefile
index ef721480eb..8cd30fd88e 100644
--- a/Makefile
+++ b/Makefile
@@ -50,6 +50,7 @@ endif
 endif
 
 include $(SRC_PATH)/rules.mak
+include $(SRC_PATH)/qapi.mak
 
 GENERATED_FILES = qemu-version.h config-host.h qemu-options.def
 GENERATED_FILES += qmp-commands.h qapi-types.h qapi-visit.h qapi-event.h
@@ -390,56 +391,46 @@ qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
 qemu-ga$(EXESUF): LIBS = $(LIBS_QGA)
 qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated
 
-gen-out-type = $(subst .,-,$(suffix $@))
-
-qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py
-
 qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h :\
-$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
+$(SRC_PATH)/qga/qapi-schema.json $(qapi-types-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py \
-		$(gen-out-type) -o qga/qapi-generated -p "qga-" $<, \
+		$(qapi-gen-type) -o qga/qapi-generated -p "qga-" $<, \
 		"GEN","$@")
 qga/qapi-generated/qga-qapi-visit.c qga/qapi-generated/qga-qapi-visit.h :\
-$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
+$(SRC_PATH)/qga/qapi-schema.json $(qapi-visit-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py \
-		$(gen-out-type) -o qga/qapi-generated -p "qga-" $<, \
+		$(qapi-gen-type) -o qga/qapi-generated -p "qga-" $<, \
 		"GEN","$@")
 qga/qapi-generated/qga-qmp-commands.h qga/qapi-generated/qga-qmp-marshal.c :\
-$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
+$(SRC_PATH)/qga/qapi-schema.json $(qapi-commands-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \
-		$(gen-out-type) -o qga/qapi-generated -p "qga-" $<, \
+		$(qapi-gen-type) -o qga/qapi-generated -p "qga-" $<, \
 		"GEN","$@")
 
-qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
-               $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
-               $(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json \
-               $(SRC_PATH)/qapi/crypto.json $(SRC_PATH)/qapi/rocker.json \
-               $(SRC_PATH)/qapi/trace.json
-
 qapi-types.c qapi-types.h :\
-$(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
+$(qapi-modules) $(qapi-types-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py \
-		$(gen-out-type) -o "." -b $<, \
+		$(qapi-gen-type) -o "." -b $<, \
 		"GEN","$@")
 qapi-visit.c qapi-visit.h :\
-$(qapi-modules) $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
+$(qapi-modules) $(qapi-visit-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py \
-		$(gen-out-type) -o "." -b $<, \
+		$(qapi-gen-type) -o "." -b $<, \
 		"GEN","$@")
 qapi-event.c qapi-event.h :\
-$(qapi-modules) $(SRC_PATH)/scripts/qapi-event.py $(qapi-py)
+$(qapi-modules) $(qapi-event-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py \
-		$(gen-out-type) -o "." $<, \
+		$(qapi-gen-type) -o "." $<, \
 		"GEN","$@")
 qmp-commands.h qmp-marshal.c :\
-$(qapi-modules) $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
+$(qapi-modules) $(qapi-commands-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \
-		$(gen-out-type) -o "." $<, \
+		$(qapi-gen-type) -o "." $<, \
 		"GEN","$@")
 qmp-introspect.h qmp-introspect.c :\
-$(qapi-modules) $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py)
+$(qapi-modules) $(qapi-introspect-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-introspect.py \
-		$(gen-out-type) -o "." $<, \
+		$(qapi-gen-type) -o "." $<, \
 		"GEN","$@")
 
 QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h)
diff --git a/qapi.mak b/qapi.mak
new file mode 100644
index 0000000000..70196127d9
--- /dev/null
+++ b/qapi.mak
@@ -0,0 +1,14 @@
+qapi-gen-type = $(subst .,-,$(suffix $@))
+
+qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
+       $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
+       $(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json \
+       $(SRC_PATH)/qapi/crypto.json $(SRC_PATH)/qapi/rocker.json \
+       $(SRC_PATH)/qapi/trace.json
+
+qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py
+qapi-types-py = $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
+qapi-visit-py = $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
+qapi-commands-py = $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
+qapi-introspect-py = $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py)
+
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 20/26] tests/qmp-test: add query-qmp-schema test
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (18 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 19/26] build-sys: move qapi variables in qapi.mak Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 21/26] build-sys: make qemu qapi objects per-target Marc-André Lureau
                   ` (6 subsequent siblings)
  26 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, Markus Armbruster

The following patch is going to make qmp introspection per-target, and
test-qobject-input-visitor.c can no longer link with
qmp_schema_qlit. Use a run-time QMP test instead to validate the
query-qmp-schema schema.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 tests/qmp-test.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/tests/qmp-test.c b/tests/qmp-test.c
index 5d0260b2be..1fd37092b5 100644
--- a/tests/qmp-test.c
+++ b/tests/qmp-test.c
@@ -129,11 +129,32 @@ static void test_qmp_protocol(void)
     qtest_end();
 }
 
+static void test_qmp_query_schema(void)
+{
+    SchemaInfoList *schema;
+    QDict *resp;
+    Visitor *v;
+
+    qtest_start(common_args);
+
+    resp = qmp("{'execute': 'query-qmp-schema'}");
+    v = qobject_input_visitor_new(qdict_get(resp, "return"));
+    visit_type_SchemaInfoList(v, NULL, &schema, &error_abort);
+    g_assert(schema);
+
+    qapi_free_SchemaInfoList(schema);
+    visit_free(v);
+    QDECREF(resp);
+
+    qtest_end();
+}
+
 int main(int argc, char *argv[])
 {
     g_test_init(&argc, &argv, NULL);
 
     qtest_add_func("qmp/protocol", test_qmp_protocol);
+    qtest_add_func("qmp/query-schema", test_qmp_query_schema);
 
     return g_test_run();
 }
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 21/26] build-sys: make qemu qapi objects per-target
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (19 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 20/26] tests/qmp-test: add query-qmp-schema test Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-08-17 11:44   ` Markus Armbruster
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 22/26] qapi: make rtc-reset-reinjection depend on TARGET_I386 Marc-André Lureau
                   ` (5 subsequent siblings)
  26 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: Marc-André Lureau, Paolo Bonzini, Markus Armbruster,
	Michael Roth, Stefan Hajnoczi

The qapi schema has per-target definitions. Move qapi objects in the
per-target build, so they can be configured at compile time.

Keep qapi-types.o qapi-visit.o in util-obj as they are necessary for
common code, but they will be overwritten during the target link. Add
some stubs for block events, in code shared by tools & qemu.

The following patch will configure the schema to conditionally remove
per-target disabled features.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 stubs/qapi-event.c                 | 74 ++++++++++++++++++++++++++++++++++++++
 tests/test-qobject-input-visitor.c |  1 -
 Makefile.objs                      |  9 +----
 Makefile.target                    |  4 +++
 stubs/Makefile.objs                |  1 +
 trace/Makefile.objs                |  2 +-
 6 files changed, 81 insertions(+), 10 deletions(-)
 create mode 100644 stubs/qapi-event.c

diff --git a/stubs/qapi-event.c b/stubs/qapi-event.c
new file mode 100644
index 0000000000..9415299f3a
--- /dev/null
+++ b/stubs/qapi-event.c
@@ -0,0 +1,74 @@
+#include "qemu/osdep.h"
+#include "qapi-event.h"
+
+void qapi_event_send_device_tray_moved(const char *device, const char *id,
+                                       bool tray_open, Error **errp)
+{
+}
+
+void qapi_event_send_quorum_report_bad(QuorumOpType type, bool has_error,
+                                       const char *error, const char *node_name,
+                                       int64_t sector_num,
+                                       int64_t sectors_count, Error **errp)
+{
+}
+
+void qapi_event_send_quorum_failure(const char *reference, int64_t sector_num,
+                                    int64_t sectors_count, Error **errp)
+{
+}
+
+void qapi_event_send_block_job_cancelled(BlockJobType type, const char *device,
+                                         int64_t len, int64_t offset,
+                                         int64_t speed, Error **errp)
+{
+}
+
+void qapi_event_send_block_job_completed(BlockJobType type, const char *device,
+                                         int64_t len, int64_t offset,
+                                         int64_t speed, bool has_error,
+                                         const char *error, Error **errp)
+{
+}
+
+void qapi_event_send_block_job_error(const char *device,
+                                     IoOperationType operation,
+                                     BlockErrorAction action, Error **errp)
+{
+}
+
+void qapi_event_send_block_job_ready(BlockJobType type, const char *device,
+                                     int64_t len, int64_t offset, int64_t speed,
+                                     Error **errp)
+{
+}
+
+void qapi_event_send_block_io_error(const char *device, const char *node_name,
+                                    IoOperationType operation,
+                                    BlockErrorAction action, bool has_nospace,
+                                    bool nospace, const char *reason,
+                                    Error **errp)
+{
+}
+
+void qapi_event_send_block_image_corrupted(const char *device,
+                                           bool has_node_name,
+                                           const char *node_name,
+                                           const char *msg, bool has_offset,
+                                           int64_t offset, bool has_size,
+                                           int64_t size, bool fatal,
+                                           Error **errp)
+{
+}
+
+void qapi_event_send_block_write_threshold(const char *node_name,
+                                           uint64_t amount_exceeded,
+                                           uint64_t write_threshold,
+                                           Error **errp)
+{
+}
+
+void qapi_event_send_device_deleted(bool has_device, const char *device,
+                                    const char *path, Error **errp)
+{
+}
diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c
index 4da5d02c35..0a9352c5c1 100644
--- a/tests/test-qobject-input-visitor.c
+++ b/tests/test-qobject-input-visitor.c
@@ -1266,7 +1266,6 @@ static void test_visitor_in_qmp_introspect(TestInputVisitorData *data,
                                            const void *unused)
 {
     do_test_visitor_in_qmp_introspect(data, &test_qmp_schema_qlit);
-    do_test_visitor_in_qmp_introspect(data, &qmp_schema_qlit);
 }
 
 int main(int argc, char **argv)
diff --git a/Makefile.objs b/Makefile.objs
index 24a4ea08b8..2664720f9b 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -2,7 +2,7 @@
 # Common libraries for tools and emulators
 stub-obj-y = stubs/ crypto/
 util-obj-y = util/ qobject/ qapi/
-util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
+util-obj-y += qapi-types.o qapi-visit.o
 
 chardev-obj-y = chardev/
 
@@ -72,13 +72,6 @@ common-obj-y += chardev/
 common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o
 
 common-obj-$(CONFIG_FDT) += device_tree.o
-
-######################################################################
-# qapi
-
-common-obj-y += qmp-marshal.o
-common-obj-y += qmp-introspect.o
-common-obj-y += qmp.o hmp.o
 endif
 
 #######################################################################
diff --git a/Makefile.target b/Makefile.target
index 2baec9252f..a97dd056ad 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -154,6 +154,10 @@ endif
 
 GENERATED_FILES += hmp-commands.h hmp-commands-info.h
 
+obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
+obj-y += qmp-marshal.o qmp-introspect.o
+obj-y += qmp.o hmp.o
+
 endif # CONFIG_SOFTMMU
 
 # Workaround for http://gcc.gnu.org/PR55489, see configure.
diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
index f5b47bfd74..1b2bef99c9 100644
--- a/stubs/Makefile.objs
+++ b/stubs/Makefile.objs
@@ -21,6 +21,7 @@ stub-obj-y += machine-init-done.o
 stub-obj-y += migr-blocker.o
 stub-obj-y += monitor.o
 stub-obj-y += notify-event.o
+stub-obj-y += qapi-event.o
 stub-obj-y += qtest.o
 stub-obj-y += replay.o
 stub-obj-y += runstate-check.o
diff --git a/trace/Makefile.objs b/trace/Makefile.objs
index afd571c3ec..6447729d60 100644
--- a/trace/Makefile.objs
+++ b/trace/Makefile.objs
@@ -56,4 +56,4 @@ util-obj-$(CONFIG_TRACE_SIMPLE) += simple.o
 util-obj-$(CONFIG_TRACE_FTRACE) += ftrace.o
 util-obj-y += control.o
 target-obj-y += control-target.o
-util-obj-y += qmp.o
+target-obj-y += qmp.o
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 22/26] qapi: make rtc-reset-reinjection depend on TARGET_I386
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (20 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 21/26] build-sys: make qemu qapi objects per-target Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-08-17 11:57   ` Markus Armbruster
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 23/26] qapi: make s390 commands depend on TARGET_S390X Marc-André Lureau
                   ` (4 subsequent siblings)
  26 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: Marc-André Lureau, Markus Armbruster,
	Dr. David Alan Gilbert, Eric Blake

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qapi-schema.json |  3 ++-
 monitor.c        | 10 ----------
 2 files changed, 2 insertions(+), 11 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index 2f4528c769..2361c13fc8 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -6270,7 +6270,8 @@
 # <- { "return": {} }
 #
 ##
-{ 'command': 'rtc-reset-reinjection' }
+{ 'command': 'rtc-reset-reinjection',
+  'if': ['defined(NEED_CPU_H)', 'defined(TARGET_I386)'] }
 
 # Rocker ethernet network switch
 { 'include': 'qapi/rocker.json' }
diff --git a/monitor.c b/monitor.c
index 383c84d162..f3dafafa22 100644
--- a/monitor.c
+++ b/monitor.c
@@ -970,9 +970,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
  */
 static void qmp_unregister_commands_hack(void)
 {
-#ifndef TARGET_I386
-    qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection");
-#endif
 #ifndef TARGET_S390X
     qmp_unregister_command(&qmp_commands, "dump-skeys");
 #endif
@@ -4151,13 +4148,6 @@ QemuOptsList qemu_mon_opts = {
     },
 };
 
-#ifndef TARGET_I386
-void qmp_rtc_reset_reinjection(Error **errp)
-{
-    error_setg(errp, QERR_FEATURE_DISABLED, "rtc-reset-reinjection");
-}
-#endif
-
 #ifndef TARGET_S390X
 void qmp_dump_skeys(const char *filename, Error **errp)
 {
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 23/26] qapi: make s390 commands depend on TARGET_S390X
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (21 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 22/26] qapi: make rtc-reset-reinjection depend on TARGET_I386 Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-08-17 12:13   ` Markus Armbruster
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 24/26] qapi: make query-gic-capabilities depend on TARGET_ARM Marc-André Lureau
                   ` (3 subsequent siblings)
  26 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: Marc-André Lureau, Markus Armbruster,
	Dr. David Alan Gilbert, Eric Blake, Paolo Bonzini,
	Richard Henderson, Alexander Graf

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qapi-schema.json                        | 10 +++++++---
 include/sysemu/arch_init.h              |  6 ------
 monitor.c                               | 14 --------------
 qmp.c                                   | 14 --------------
 stubs/arch-query-cpu-model-baseline.c   | 12 ------------
 stubs/arch-query-cpu-model-comparison.c | 12 ------------
 target/s390x/cpu_models.c               |  4 ++--
 stubs/Makefile.objs                     |  2 --
 8 files changed, 9 insertions(+), 65 deletions(-)
 delete mode 100644 stubs/arch-query-cpu-model-baseline.c
 delete mode 100644 stubs/arch-query-cpu-model-comparison.c

diff --git a/qapi-schema.json b/qapi-schema.json
index 2361c13fc8..278d7e2aa3 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3577,7 +3577,8 @@
 #
 ##
 { 'command': 'dump-skeys',
-  'data': { 'filename': 'str' } }
+  'data': { 'filename': 'str' },
+  'if': ['defined(NEED_CPU_H)', 'defined(TARGET_S390X)']}
 
 ##
 # @netdev_add:
@@ -4621,7 +4622,9 @@
 ##
 { 'command': 'query-cpu-model-comparison',
   'data': { 'modela': 'CpuModelInfo', 'modelb': 'CpuModelInfo' },
-  'returns': 'CpuModelCompareInfo' }
+  'returns': 'CpuModelCompareInfo',
+  'if': ['defined(NEED_CPU_H)', 'defined(TARGET_S390X)']}
+
 
 ##
 # @CpuModelBaselineInfo:
@@ -4673,7 +4676,8 @@
 { 'command': 'query-cpu-model-baseline',
   'data': { 'modela': 'CpuModelInfo',
             'modelb': 'CpuModelInfo' },
-  'returns': 'CpuModelBaselineInfo' }
+  'returns': 'CpuModelBaselineInfo',
+  'if': ['defined(NEED_CPU_H)', 'defined(TARGET_S390X)']}
 
 ##
 # @AddfdInfo:
diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h
index 8751c468ed..e9f1ea0cca 100644
--- a/include/sysemu/arch_init.h
+++ b/include/sysemu/arch_init.h
@@ -35,11 +35,5 @@ CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp);
 CpuModelExpansionInfo *arch_query_cpu_model_expansion(CpuModelExpansionType type,
                                                       CpuModelInfo *mode,
                                                       Error **errp);
-CpuModelCompareInfo *arch_query_cpu_model_comparison(CpuModelInfo *modela,
-                                                     CpuModelInfo *modelb,
-                                                     Error **errp);
-CpuModelBaselineInfo *arch_query_cpu_model_baseline(CpuModelInfo *modela,
-                                                    CpuModelInfo *modelb,
-                                                    Error **errp);
 
 #endif
diff --git a/monitor.c b/monitor.c
index f3dafafa22..505ee5c58d 100644
--- a/monitor.c
+++ b/monitor.c
@@ -970,19 +970,12 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
  */
 static void qmp_unregister_commands_hack(void)
 {
-#ifndef TARGET_S390X
-    qmp_unregister_command(&qmp_commands, "dump-skeys");
-#endif
 #ifndef TARGET_ARM
     qmp_unregister_command(&qmp_commands, "query-gic-capabilities");
 #endif
 #if !defined(TARGET_S390X) && !defined(TARGET_I386)
     qmp_unregister_command(&qmp_commands, "query-cpu-model-expansion");
 #endif
-#if !defined(TARGET_S390X)
-    qmp_unregister_command(&qmp_commands, "query-cpu-model-baseline");
-    qmp_unregister_command(&qmp_commands, "query-cpu-model-comparison");
-#endif
 #if !defined(TARGET_PPC) && !defined(TARGET_ARM) && !defined(TARGET_I386) \
     && !defined(TARGET_S390X)
     qmp_unregister_command(&qmp_commands, "query-cpu-definitions");
@@ -4148,13 +4141,6 @@ QemuOptsList qemu_mon_opts = {
     },
 };
 
-#ifndef TARGET_S390X
-void qmp_dump_skeys(const char *filename, Error **errp)
-{
-    error_setg(errp, QERR_FEATURE_DISABLED, "dump-skeys");
-}
-#endif
-
 #ifndef TARGET_ARM
 GICCapabilityList *qmp_query_gic_capabilities(Error **errp)
 {
diff --git a/qmp.c b/qmp.c
index 90816ba283..7b6861846f 100644
--- a/qmp.c
+++ b/qmp.c
@@ -553,20 +553,6 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type,
     return arch_query_cpu_model_expansion(type, model, errp);
 }
 
-CpuModelCompareInfo *qmp_query_cpu_model_comparison(CpuModelInfo *modela,
-                                                    CpuModelInfo *modelb,
-                                                    Error **errp)
-{
-    return arch_query_cpu_model_comparison(modela, modelb, errp);
-}
-
-CpuModelBaselineInfo *qmp_query_cpu_model_baseline(CpuModelInfo *modela,
-                                                   CpuModelInfo *modelb,
-                                                   Error **errp)
-{
-    return arch_query_cpu_model_baseline(modela, modelb, errp);
-}
-
 void qmp_add_client(const char *protocol, const char *fdname,
                     bool has_skipauth, bool skipauth, bool has_tls, bool tls,
                     Error **errp)
diff --git a/stubs/arch-query-cpu-model-baseline.c b/stubs/arch-query-cpu-model-baseline.c
deleted file mode 100644
index 094ec13c2c..0000000000
--- a/stubs/arch-query-cpu-model-baseline.c
+++ /dev/null
@@ -1,12 +0,0 @@
-#include "qemu/osdep.h"
-#include "qemu-common.h"
-#include "sysemu/arch_init.h"
-#include "qapi/qmp/qerror.h"
-
-CpuModelBaselineInfo *arch_query_cpu_model_baseline(CpuModelInfo *modela,
-                                                    CpuModelInfo *modelb,
-                                                    Error **errp)
-{
-    error_setg(errp, QERR_UNSUPPORTED);
-    return NULL;
-}
diff --git a/stubs/arch-query-cpu-model-comparison.c b/stubs/arch-query-cpu-model-comparison.c
deleted file mode 100644
index d5486ae980..0000000000
--- a/stubs/arch-query-cpu-model-comparison.c
+++ /dev/null
@@ -1,12 +0,0 @@
-#include "qemu/osdep.h"
-#include "qemu-common.h"
-#include "sysemu/arch_init.h"
-#include "qapi/qmp/qerror.h"
-
-CpuModelCompareInfo *arch_query_cpu_model_comparison(CpuModelInfo *modela,
-                                                     CpuModelInfo *modelb,
-                                                     Error **errp)
-{
-    error_setg(errp, QERR_UNSUPPORTED);
-    return NULL;
-}
diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
index fa1338fc72..cdff9cdd3b 100644
--- a/target/s390x/cpu_models.c
+++ b/target/s390x/cpu_models.c
@@ -559,7 +559,7 @@ static void list_add_feat(const char *name, void *opaque)
     *last = entry;
 }
 
-CpuModelCompareInfo *arch_query_cpu_model_comparison(CpuModelInfo *infoa,
+CpuModelCompareInfo *qmp_query_cpu_model_comparison(CpuModelInfo *infoa,
                                                      CpuModelInfo *infob,
                                                      Error **errp)
 {
@@ -632,7 +632,7 @@ CpuModelCompareInfo *arch_query_cpu_model_comparison(CpuModelInfo *infoa,
     return compare_info;
 }
 
-CpuModelBaselineInfo *arch_query_cpu_model_baseline(CpuModelInfo *infoa,
+CpuModelBaselineInfo *qmp_query_cpu_model_baseline(CpuModelInfo *infoa,
                                                     CpuModelInfo *infob,
                                                     Error **errp)
 {
diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
index 1b2bef99c9..049d389966 100644
--- a/stubs/Makefile.objs
+++ b/stubs/Makefile.objs
@@ -1,7 +1,5 @@
 stub-obj-y += arch-query-cpu-def.o
 stub-obj-y += arch-query-cpu-model-expansion.o
-stub-obj-y += arch-query-cpu-model-comparison.o
-stub-obj-y += arch-query-cpu-model-baseline.o
 stub-obj-y += bdrv-next-monitor-owned.o
 stub-obj-y += blk-commit-all.o
 stub-obj-y += blockdev-close-all-bdrv-states.o
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 24/26] qapi: make query-gic-capabilities depend on TARGET_ARM
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (22 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 23/26] qapi: make s390 commands depend on TARGET_S390X Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 25/26] qapi: make query-cpu-model-expansion depend on s390 or x86 Marc-André Lureau
                   ` (2 subsequent siblings)
  26 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: Marc-André Lureau, Dr. David Alan Gilbert,
	Markus Armbruster, Eric Blake

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qapi-schema.json |  3 ++-
 monitor.c        | 11 -----------
 2 files changed, 2 insertions(+), 12 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index 278d7e2aa3..a5c28bc9ad 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -6437,7 +6437,8 @@
 #                 { "version": 3, "emulated": false, "kernel": true } ] }
 #
 ##
-{ 'command': 'query-gic-capabilities', 'returns': ['GICCapability'] }
+{ 'command': 'query-gic-capabilities', 'returns': ['GICCapability'],
+  'if': ['defined(NEED_CPU_H)', 'defined(TARGET_ARM)']}
 
 ##
 # @CpuInstanceProperties:
diff --git a/monitor.c b/monitor.c
index 505ee5c58d..3bdae8d9d0 100644
--- a/monitor.c
+++ b/monitor.c
@@ -970,9 +970,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
  */
 static void qmp_unregister_commands_hack(void)
 {
-#ifndef TARGET_ARM
-    qmp_unregister_command(&qmp_commands, "query-gic-capabilities");
-#endif
 #if !defined(TARGET_S390X) && !defined(TARGET_I386)
     qmp_unregister_command(&qmp_commands, "query-cpu-model-expansion");
 #endif
@@ -4141,14 +4138,6 @@ QemuOptsList qemu_mon_opts = {
     },
 };
 
-#ifndef TARGET_ARM
-GICCapabilityList *qmp_query_gic_capabilities(Error **errp)
-{
-    error_setg(errp, QERR_FEATURE_DISABLED, "query-gic-capabilities");
-    return NULL;
-}
-#endif
-
 HotpluggableCPUList *qmp_query_hotpluggable_cpus(Error **errp)
 {
     MachineState *ms = MACHINE(qdev_get_machine());
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 25/26] qapi: make query-cpu-model-expansion depend on s390 or x86
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (23 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 24/26] qapi: make query-gic-capabilities depend on TARGET_ARM Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 26/26] qapi: make query-cpu-definitions depend on specific targets Marc-André Lureau
  2017-08-17 13:55 ` [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Markus Armbruster
  26 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: Marc-André Lureau, Dr. David Alan Gilbert,
	Markus Armbruster, Eric Blake, Paolo Bonzini, Richard Henderson,
	Eduardo Habkost, Alexander Graf

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qapi-schema.json                       |  4 +++-
 include/sysemu/arch_init.h             |  3 ---
 monitor.c                              |  3 ---
 qmp.c                                  |  7 -------
 stubs/arch-query-cpu-model-expansion.c | 12 ------------
 target/i386/cpu.c                      |  2 +-
 target/s390x/cpu_models.c              |  3 ++-
 stubs/Makefile.objs                    |  1 -
 8 files changed, 6 insertions(+), 29 deletions(-)
 delete mode 100644 stubs/arch-query-cpu-model-expansion.c

diff --git a/qapi-schema.json b/qapi-schema.json
index a5c28bc9ad..f5e1acff83 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -4535,7 +4535,9 @@
 { 'command': 'query-cpu-model-expansion',
   'data': { 'type': 'CpuModelExpansionType',
             'model': 'CpuModelInfo' },
-  'returns': 'CpuModelExpansionInfo' }
+  'returns': 'CpuModelExpansionInfo',
+  'if': ['defined(NEED_CPU_H)',
+         'defined(TARGET_S390X) || defined(TARGET_I386)']}
 
 ##
 # @CpuModelCompareResult:
diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h
index e9f1ea0cca..fb3d20a1b8 100644
--- a/include/sysemu/arch_init.h
+++ b/include/sysemu/arch_init.h
@@ -32,8 +32,5 @@ int kvm_available(void);
 int xen_available(void);
 
 CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp);
-CpuModelExpansionInfo *arch_query_cpu_model_expansion(CpuModelExpansionType type,
-                                                      CpuModelInfo *mode,
-                                                      Error **errp);
 
 #endif
diff --git a/monitor.c b/monitor.c
index 3bdae8d9d0..b134c39144 100644
--- a/monitor.c
+++ b/monitor.c
@@ -970,9 +970,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
  */
 static void qmp_unregister_commands_hack(void)
 {
-#if !defined(TARGET_S390X) && !defined(TARGET_I386)
-    qmp_unregister_command(&qmp_commands, "query-cpu-model-expansion");
-#endif
 #if !defined(TARGET_PPC) && !defined(TARGET_ARM) && !defined(TARGET_I386) \
     && !defined(TARGET_S390X)
     qmp_unregister_command(&qmp_commands, "query-cpu-definitions");
diff --git a/qmp.c b/qmp.c
index 7b6861846f..afa266ec1e 100644
--- a/qmp.c
+++ b/qmp.c
@@ -546,13 +546,6 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
     return arch_query_cpu_definitions(errp);
 }
 
-CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type,
-                                                     CpuModelInfo *model,
-                                                     Error **errp)
-{
-    return arch_query_cpu_model_expansion(type, model, errp);
-}
-
 void qmp_add_client(const char *protocol, const char *fdname,
                     bool has_skipauth, bool skipauth, bool has_tls, bool tls,
                     Error **errp)
diff --git a/stubs/arch-query-cpu-model-expansion.c b/stubs/arch-query-cpu-model-expansion.c
deleted file mode 100644
index ae7cf554d1..0000000000
--- a/stubs/arch-query-cpu-model-expansion.c
+++ /dev/null
@@ -1,12 +0,0 @@
-#include "qemu/osdep.h"
-#include "qemu-common.h"
-#include "sysemu/arch_init.h"
-#include "qapi/qmp/qerror.h"
-
-CpuModelExpansionInfo *arch_query_cpu_model_expansion(CpuModelExpansionType type,
-                                                      CpuModelInfo *mode,
-                                                      Error **errp)
-{
-    error_setg(errp, QERR_UNSUPPORTED);
-    return NULL;
-}
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index ddc45abd70..d683e70a13 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -2574,7 +2574,7 @@ out:
 }
 
 CpuModelExpansionInfo *
-arch_query_cpu_model_expansion(CpuModelExpansionType type,
+qmp_query_cpu_model_expansion(CpuModelExpansionType type,
                                                       CpuModelInfo *model,
                                                       Error **errp)
 {
diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
index cdff9cdd3b..863dce064f 100644
--- a/target/s390x/cpu_models.c
+++ b/target/s390x/cpu_models.c
@@ -22,6 +22,7 @@
 #ifndef CONFIG_USER_ONLY
 #include "sysemu/arch_init.h"
 #endif
+#include "qmp-commands.h"
 
 #define CPUDEF_INIT(_type, _gen, _ec_ga, _mha_pow, _hmfai, _name, _desc) \
     {                                                                    \
@@ -520,7 +521,7 @@ static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model,
     }
 }
 
-CpuModelExpansionInfo *arch_query_cpu_model_expansion(CpuModelExpansionType type,
+CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type,
                                                       CpuModelInfo *model,
                                                       Error **errp)
 {
diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
index 049d389966..dcfe6f49f9 100644
--- a/stubs/Makefile.objs
+++ b/stubs/Makefile.objs
@@ -1,5 +1,4 @@
 stub-obj-y += arch-query-cpu-def.o
-stub-obj-y += arch-query-cpu-model-expansion.o
 stub-obj-y += bdrv-next-monitor-owned.o
 stub-obj-y += blk-commit-all.o
 stub-obj-y += blockdev-close-all-bdrv-states.o
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [Qemu-devel] [PATCH 26/26] qapi: make query-cpu-definitions depend on specific targets
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (24 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 25/26] qapi: make query-cpu-model-expansion depend on s390 or x86 Marc-André Lureau
@ 2017-07-27 15:41 ` Marc-André Lureau
  2017-08-17 12:30   ` Markus Armbruster
  2017-08-17 13:55 ` [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Markus Armbruster
  26 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2017-07-27 15:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: Marc-André Lureau, Markus Armbruster,
	Dr. David Alan Gilbert, Eric Blake, Paolo Bonzini, Peter Maydell,
	Richard Henderson, Eduardo Habkost, David Gibson, Alexander Graf,
	open list:ARM, open list:PowerPC

It depends on TARGET_PPC || TARGET_ARM || TARGET_I386 || TARGET_S390X.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qapi-schema.json            |  4 +++-
 include/sysemu/arch_init.h  |  2 --
 monitor.c                   | 22 ----------------------
 qmp.c                       |  5 -----
 stubs/arch-query-cpu-def.c  | 10 ----------
 target/arm/helper.c         |  3 ++-
 target/i386/cpu.c           |  3 ++-
 target/ppc/translate_init.c |  3 ++-
 target/s390x/cpu_models.c   |  2 +-
 stubs/Makefile.objs         |  1 -
 10 files changed, 10 insertions(+), 45 deletions(-)
 delete mode 100644 stubs/arch-query-cpu-def.c

diff --git a/qapi-schema.json b/qapi-schema.json
index f5e1acff83..8e3949bca8 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -4433,7 +4433,9 @@
 #
 # Since: 1.2.0
 ##
-{ 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'] }
+{ 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'],
+  'if': ['defined(NEED_CPU_H)',
+         'defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_I386) || defined(TARGET_S390X)'] }
 
 ##
 # @CpuModelInfo:
diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h
index fb3d20a1b8..e9721b9ce8 100644
--- a/include/sysemu/arch_init.h
+++ b/include/sysemu/arch_init.h
@@ -31,6 +31,4 @@ extern const uint32_t arch_type;
 int kvm_available(void);
 int xen_available(void);
 
-CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp);
-
 #endif
diff --git a/monitor.c b/monitor.c
index b134c39144..6600819599 100644
--- a/monitor.c
+++ b/monitor.c
@@ -956,26 +956,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
     *ret_data = qobject_from_qlit(&qmp_schema_qlit);
 }
 
-/*
- * We used to define commands in qmp-commands.hx in addition to the
- * QAPI schema.  This permitted defining some of them only in certain
- * configurations.  query-commands has always reflected that (good,
- * because it lets QMP clients figure out what's actually available),
- * while query-qmp-schema never did (not so good).  This function is a
- * hack to keep the configuration-specific commands defined exactly as
- * before, even though qmp-commands.hx is gone.
- *
- * FIXME Educate the QAPI schema on configuration-specific commands,
- * and drop this hack.
- */
-static void qmp_unregister_commands_hack(void)
-{
-#if !defined(TARGET_PPC) && !defined(TARGET_ARM) && !defined(TARGET_I386) \
-    && !defined(TARGET_S390X)
-    qmp_unregister_command(&qmp_commands, "query-cpu-definitions");
-#endif
-}
-
 void monitor_init_qmp_commands(void)
 {
     /*
@@ -995,8 +975,6 @@ void monitor_init_qmp_commands(void)
     qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add,
                          QCO_NO_OPTIONS);
 
-    qmp_unregister_commands_hack();
-
     QTAILQ_INIT(&qmp_cap_negotiation_commands);
     qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities",
                          qmp_marshal_qmp_capabilities, QCO_NO_OPTIONS);
diff --git a/qmp.c b/qmp.c
index afa266ec1e..d57ccf1251 100644
--- a/qmp.c
+++ b/qmp.c
@@ -541,11 +541,6 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
     return prop_list;
 }
 
-CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
-{
-    return arch_query_cpu_definitions(errp);
-}
-
 void qmp_add_client(const char *protocol, const char *fdname,
                     bool has_skipauth, bool skipauth, bool has_tls, bool tls,
                     Error **errp)
diff --git a/stubs/arch-query-cpu-def.c b/stubs/arch-query-cpu-def.c
deleted file mode 100644
index cefe4beb82..0000000000
--- a/stubs/arch-query-cpu-def.c
+++ /dev/null
@@ -1,10 +0,0 @@
-#include "qemu/osdep.h"
-#include "qemu-common.h"
-#include "sysemu/arch_init.h"
-#include "qapi/qmp/qerror.h"
-
-CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
-{
-    error_setg(errp, QERR_UNSUPPORTED);
-    return NULL;
-}
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 4ed32c56b8..ec644f3930 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -15,6 +15,7 @@
 #include <zlib.h> /* For crc32 */
 #include "exec/semihost.h"
 #include "sysemu/kvm.h"
+#include "qmp-commands.h"
 
 #define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */
 
@@ -5336,7 +5337,7 @@ static void arm_cpu_add_definition(gpointer data, gpointer user_data)
     *cpu_list = entry;
 }
 
-CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
+CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
 {
     CpuDefinitionInfoList *cpu_list = NULL;
     GSList *list;
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index d683e70a13..e5f61f6bff 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -36,6 +36,7 @@
 #include "qapi/visitor.h"
 #include "qom/qom-qobject.h"
 #include "sysemu/arch_init.h"
+#include "qmp-commands.h"
 
 #if defined(CONFIG_KVM)
 #include <linux/kvm_para.h>
@@ -2318,7 +2319,7 @@ static void x86_cpu_definition_entry(gpointer data, gpointer user_data)
     *cpu_list = entry;
 }
 
-CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
+CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
 {
     CpuDefinitionInfoList *cpu_list = NULL;
     GSList *list = get_sorted_cpu_model_list();
diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c
index 01723bdfec..2a2d62e5bb 100644
--- a/target/ppc/translate_init.c
+++ b/target/ppc/translate_init.c
@@ -34,6 +34,7 @@
 #include "hw/ppc/ppc.h"
 #include "mmu-book3s-v3.h"
 #include "sysemu/qtest.h"
+#include "qmp-commands.h"
 
 //#define PPC_DUMP_CPU
 //#define PPC_DEBUG_SPR
@@ -10391,7 +10392,7 @@ static void ppc_cpu_defs_entry(gpointer data, gpointer user_data)
     *first = entry;
 }
 
-CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
+CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
 {
     CpuDefinitionInfoList *cpu_list = NULL;
     GSList *list;
diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
index 863dce064f..8021dda341 100644
--- a/target/s390x/cpu_models.c
+++ b/target/s390x/cpu_models.c
@@ -387,7 +387,7 @@ static void create_cpu_model_list(ObjectClass *klass, void *opaque)
     *cpu_list = entry;
 }
 
-CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
+CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
 {
     struct CpuDefinitionInfoListData list_data = {
         .list = NULL,
diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
index dcfe6f49f9..71af433f6b 100644
--- a/stubs/Makefile.objs
+++ b/stubs/Makefile.objs
@@ -1,4 +1,3 @@
-stub-obj-y += arch-query-cpu-def.o
 stub-obj-y += bdrv-next-monitor-owned.o
 stub-obj-y += blk-commit-all.o
 stub-obj-y += blockdev-close-all-bdrv-states.o
-- 
2.14.0.rc0.1.g40ca67566

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 16/26] qapi: add conditions to VNC type/commands/events on the schema
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 16/26] qapi: add conditions to VNC type/commands/events on the schema Marc-André Lureau
@ 2017-07-28 19:00   ` Dr. David Alan Gilbert
  2017-08-17  6:32     ` Markus Armbruster
  2017-08-17  7:04   ` Markus Armbruster
  1 sibling, 1 reply; 69+ messages in thread
From: Dr. David Alan Gilbert @ 2017-07-28 19:00 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Eric Blake, Markus Armbruster

* Marc-André Lureau (marcandre.lureau@redhat.com) wrote:
> Add #if defined(CONFIG_VNC) in generated code, and adjust the
> qmp/hmp code accordingly.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

> diff --git a/hmp.c b/hmp.c
> index fd80dce758..9454c634bd 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -605,6 +605,7 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict)
>      qapi_free_BlockStatsList(stats_list);
>  }
>  
> +#ifdef CONFIG_VNC
>  /* Helper for hmp_info_vnc_clients, _servers */
>  static void hmp_info_VncBasicInfo(Monitor *mon, VncBasicInfo *info,
>                                    const char *name)
> @@ -692,6 +693,12 @@ void hmp_info_vnc(Monitor *mon, const QDict *qdict)
>      qapi_free_VncInfo2List(info2l);
>  
>  }
> +#else
> +void hmp_info_vnc(Monitor *mon, const QDict *qdict)
> +{
> +    warn_report("VNC support is disabled");
> +}
> +#endif

I'm OK with this, so

Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>

although you might just be able to add a #ifdef in hmp-commands-info.hx
and avoid the is disabled function, or you might find that with the QMP
returning an error the HMP just passes that error on.

Dave

>  #ifdef CONFIG_SPICE
>  void hmp_info_spice(Monitor *mon, const QDict *qdict)
> @@ -1708,12 +1715,14 @@ void hmp_eject(Monitor *mon, const QDict *qdict)
>      hmp_handle_error(mon, &err);
>  }
>  
> +#ifdef CONFIG_VNC
>  static void hmp_change_read_arg(void *opaque, const char *password,
>                                  void *readline_opaque)
>  {
>      qmp_change_vnc_password(password, NULL);
>      monitor_read_command(opaque, 1);
>  }
> +#endif
>  
>  void hmp_change(Monitor *mon, const QDict *qdict)
>  {
> @@ -1724,6 +1733,7 @@ void hmp_change(Monitor *mon, const QDict *qdict)
>      BlockdevChangeReadOnlyMode read_only_mode = 0;
>      Error *err = NULL;
>  
> +#ifdef CONFIG_VNC
>      if (strcmp(device, "vnc") == 0) {
>          if (read_only) {
>              monitor_printf(mon,
> @@ -1738,7 +1748,9 @@ void hmp_change(Monitor *mon, const QDict *qdict)
>              }
>          }
>          qmp_change("vnc", target, !!arg, arg, &err);
> -    } else {
> +    } else
> +#endif
> +    {
>          if (read_only) {
>              read_only_mode =
>                  qapi_enum_parse(BlockdevChangeReadOnlyMode_lookup,
> diff --git a/qmp.c b/qmp.c
> index b86201e349..2c90dacb56 100644
> --- a/qmp.c
> +++ b/qmp.c
> @@ -130,22 +130,6 @@ void qmp_cpu_add(int64_t id, Error **errp)
>      }
>  }
>  
> -#ifndef CONFIG_VNC
> -/* If VNC support is enabled, the "true" query-vnc command is
> -   defined in the VNC subsystem */
> -VncInfo *qmp_query_vnc(Error **errp)
> -{
> -    error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
> -    return NULL;
> -};
> -
> -VncInfo2List *qmp_query_vnc_servers(Error **errp)
> -{
> -    error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
> -    return NULL;
> -};
> -#endif
> -
>  #ifndef CONFIG_SPICE
>  /*
>   * qmp-commands.hx ensures that QMP command query-spice exists only
> @@ -403,23 +387,17 @@ static void qmp_change_vnc(const char *target, bool has_arg, const char *arg,
>          qmp_change_vnc_listen(target, errp);
>      }
>  }
> -#else
> -void qmp_change_vnc_password(const char *password, Error **errp)
> -{
> -    error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
> -}
> -static void qmp_change_vnc(const char *target, bool has_arg, const char *arg,
> -                           Error **errp)
> -{
> -    error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
> -}
>  #endif /* !CONFIG_VNC */
>  
>  void qmp_change(const char *device, const char *target,
>                  bool has_arg, const char *arg, Error **errp)
>  {
>      if (strcmp(device, "vnc") == 0) {
> +#ifdef CONFIG_VNC
>          qmp_change_vnc(target, has_arg, arg, errp);
> +#else
> +        error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
> +#endif
>      } else {
>          qmp_blockdev_change_medium(true, device, false, NULL, target,
>                                     has_arg, arg, false, 0, errp);
> -- 
> 2.14.0.rc0.1.g40ca67566
> 
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 01/26] qapi: fix type_seen key error
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 01/26] qapi: fix type_seen key error Marc-André Lureau
@ 2017-08-15 14:40   ` Markus Armbruster
  2017-08-17 23:17     ` Marc-André Lureau
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2017-08-15 14:40 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> The type_seen member can be of a different type than the 'qtype' being
> checked, since a string create several conflicts. Lookup the real
> conflicting type in the conflict set, that one must be present in
> type_seen.
>
> This fixes the following error, reproducible with the modified test:
>
> Traceback (most recent call last):
>   File "/home/elmarco/src/qq/tests/qapi-schema/test-qapi.py", line 56, in <module>
>     schema = QAPISchema(sys.argv[1])
>   File "/home/elmarco/src/qq/scripts/qapi.py", line 1470, in __init__
>     self.exprs = check_exprs(parser.exprs)
>   File "/home/elmarco/src/qq/scripts/qapi.py", line 959, in check_exprs
>     check_alternate(expr, info)
>   File "/home/elmarco/src/qq/scripts/qapi.py", line 831, in check_alternate
>     % (name, key, types_seen[qtype]))
> KeyError: 'QTYPE_QSTRING'
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  scripts/qapi.py                                  | 4 +++-
>  tests/qapi-schema/alternate-conflict-string.json | 4 ++--
>  2 files changed, 5 insertions(+), 3 deletions(-)
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 8aa2775f12..4ecc19e944 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -825,7 +825,9 @@ def check_alternate(expr, info):
>              else:
>                  conflicting.add('QTYPE_QNUM')
>                  conflicting.add('QTYPE_QBOOL')
> -        if conflicting & set(types_seen):
> +        conflict = conflicting & set(types_seen)
> +        if conflict:
> +            qtype = list(conflict)[0]
>              raise QAPISemError(info, "Alternate '%s' member '%s' can't "
>                                 "be distinguished from member '%s'"
>                                 % (name, key, types_seen[qtype]))

I see.

The fix is uninvasive, but I'm not thrilled by repurposing @qtype, and
the meaning of list(dict) is less obvious than dict.keys().  What about

           conflict = conflicting & set(types_seen)
           if conflict:
               conflicting_member_name = conflict.keys()[0]
               raise QAPISemError(info, "Alternate '%s' member '%s' can't "
                                  "be distinguished from member '%s'"
                                  % (name, key, conflicting_member_name))

Alternatively, list all conflicting names (I wouldn't bother).

> diff --git a/tests/qapi-schema/alternate-conflict-string.json b/tests/qapi-schema/alternate-conflict-string.json
> index 85adbd4adc..bb2702978e 100644
> --- a/tests/qapi-schema/alternate-conflict-string.json
> +++ b/tests/qapi-schema/alternate-conflict-string.json
> @@ -1,4 +1,4 @@
>  # alternate branches of 'str' type conflict with all scalar types
>  { 'alternate': 'Alt',
> -  'data': { 'one': 'str',
> -            'two': 'int' } }
> +  'data': { 'one': 'int',
> +            'two': 'str' } }

I had to think for several minutes to convince myself that this is a
better test, not just a test that happens to demonstrate a particular
bug.  It's hot, I'm slow :)

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 03/26] qboject: add literal qobject type
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 03/26] qboject: add literal qobject type Marc-André Lureau
@ 2017-08-16  8:59   ` Markus Armbruster
  2017-08-22 11:16     ` Marc-André Lureau
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2017-08-16  8:59 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

In the subject: s/qboject: add/qobject: Add/

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Promote LiteralQObject from tests/check-qjson.c to qobject/qlit.c,
> allowing to statically declare complex qobjects.
>
> Add a helper qobject_from_qlit() to instantiate a literal qobject to a
> real QObject. Add some simple test.

Suggest

* to also move the existing function making use of LiteralQObject
  compare_litqobj_to_qobj(), so other tests can use it to verify a
  QObject matches expectations, and

* to split the patch into the move and the addition of
  qobject_from_qlit().

>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  include/qapi/qmp/qlit.h | 53 ++++++++++++++++++++++++++++++++++++
>  qobject/qlit.c          | 53 ++++++++++++++++++++++++++++++++++++
>  tests/check-qjson.c     | 72 +++++++++++++++++--------------------------------
>  tests/check-qlit.c      | 52 +++++++++++++++++++++++++++++++++++
>  qobject/Makefile.objs   |  2 +-
>  tests/Makefile.include  |  5 +++-
>  6 files changed, 187 insertions(+), 50 deletions(-)
>  create mode 100644 include/qapi/qmp/qlit.h
>  create mode 100644 qobject/qlit.c
>  create mode 100644 tests/check-qlit.c
>
> diff --git a/include/qapi/qmp/qlit.h b/include/qapi/qmp/qlit.h
> new file mode 100644
> index 0000000000..fd6bfc3e69
> --- /dev/null
> +++ b/include/qapi/qmp/qlit.h
> @@ -0,0 +1,53 @@
> +/*
> + * Copyright IBM, Corp. 2009
> + * Copyright (c) 2013, 2015 Red Hat Inc.
> + *
> + * Authors:
> + *  Anthony Liguori   <aliguori@us.ibm.com>
> + *  Markus Armbruster <armbru@redhat.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 QLIT_H_
> +#define QLIT_H_
> +
> +#include "qapi-types.h"
> +#include "qobject.h"
> +
> +typedef struct QLitDictEntry QLitDictEntry;
> +typedef struct QLitObject QLitObject;
> +
> +struct QLitObject {
> +    int type;
> +    union {
> +        bool qbool;
> +        int64_t qnum;

Not this patch's fault: QLitObject restricts numbers to signed integers.

> +        const char *qstr;
> +        QLitDictEntry *qdict;
> +        QLitObject *qlist;
> +    } value;
> +};
> +
> +struct QLitDictEntry {
> +    const char *key;
> +    QLitObject value;
> +};
> +
> +#define QLIT_QNULL \
> +    { .type = QTYPE_QNULL }
> +#define QLIT_QBOOL(val) \
> +    { .type = QTYPE_QBOOL, .value.qbool = (val) }
> +#define QLIT_QNUM(val) \
> +    { .type = QTYPE_QNUM, .value.qnum = (val) }
> +#define QLIT_QSTR(val) \
> +    { .type = QTYPE_QSTRING, .value.qstr = (val) }
> +#define QLIT_QDICT(val) \
> +    { .type = QTYPE_QDICT, .value.qdict = (val) }
> +#define QLIT_QLIST(val) \
> +    { .type = QTYPE_QLIST, .value.qlist = (val) }
> +
> +QObject *qobject_from_qlit(const QLitObject *qlit);
> +
> +#endif /* QLIT_H_ */
> diff --git a/qobject/qlit.c b/qobject/qlit.c
> new file mode 100644
> index 0000000000..d7407b4b34
> --- /dev/null
> +++ b/qobject/qlit.c
> @@ -0,0 +1,53 @@
> +/*
> + * QLit literal qobject
> + *
> + * Copyright (C) 2017 Red Hat Inc.
> + *
> + * Authors:
> + *  Marc-André Lureau <marcandre.lureau@redhat.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/osdep.h"
> +
> +#include "qapi/qmp/qlit.h"
> +#include "qapi/qmp/types.h"
> +
> +QObject *qobject_from_qlit(const QLitObject *qlit)
> +{
> +    int i;
> +
> +    switch (qlit->type) {
> +    case QTYPE_QNULL:
> +        return QOBJECT(qnull());
> +    case QTYPE_QNUM:
> +        return QOBJECT(qnum_from_int(qlit->value.qnum));
> +    case QTYPE_QSTRING:
> +        return QOBJECT(qstring_from_str(qlit->value.qstr));
> +    case QTYPE_QDICT: {
> +        QDict *qdict = qdict_new();
> +        for (i = 0; qlit->value.qdict[i].value.type != QTYPE_NONE; i++) {
> +            QLitDictEntry *e = &qlit->value.qdict[i];
> +
> +            qdict_put_obj(qdict, e->key, qobject_from_qlit(&e->value));
> +        }
> +        return QOBJECT(qdict);
> +    }
> +    case QTYPE_QLIST: {
> +        QList *qlist = qlist_new();
> +
> +        for (i = 0; qlit->value.qlist[i].type != QTYPE_NONE; i++) {
> +            qlist_append_obj(qlist, qobject_from_qlit(&qlit->value.qlist[i]));
> +        }
> +        return QOBJECT(qlist);
> +    }
> +    case QTYPE_QBOOL:
> +        return QOBJECT(qbool_from_bool(qlit->value.qbool));
> +    case QTYPE_NONE:
> +        assert(0);
> +    }
> +
> +    return NULL;
> +}
> diff --git a/tests/check-qjson.c b/tests/check-qjson.c
> index 9c42a46b7d..1a95aff2ba 100644
> --- a/tests/check-qjson.c
> +++ b/tests/check-qjson.c
> @@ -16,6 +16,7 @@
>  #include "qapi/error.h"
>  #include "qapi/qmp/types.h"
>  #include "qapi/qmp/qjson.h"
> +#include "qapi/qmp/qlit.h"
>  #include "qemu-common.h"
>  
>  static void escaped_string(void)
> @@ -1059,39 +1060,14 @@ static void keyword_literal(void)
>      QDECREF(null);
>  }
>  
> -typedef struct LiteralQDictEntry LiteralQDictEntry;
> -typedef struct LiteralQObject LiteralQObject;
> -
> -struct LiteralQObject
> -{
> -    int type;
> -    union {
> -        int64_t qnum;
> -        const char *qstr;
> -        LiteralQDictEntry *qdict;
> -        LiteralQObject *qlist;
> -    } value;
> -};
> -
> -struct LiteralQDictEntry
> -{
> -    const char *key;
> -    LiteralQObject value;
> -};
> -
> -#define QLIT_QNUM(val) (LiteralQObject){.type = QTYPE_QNUM, .value.qnum = (val)}
> -#define QLIT_QSTR(val) (LiteralQObject){.type = QTYPE_QSTRING, .value.qstr = (val)}
> -#define QLIT_QDICT(val) (LiteralQObject){.type = QTYPE_QDICT, .value.qdict = (val)}
> -#define QLIT_QLIST(val) (LiteralQObject){.type = QTYPE_QLIST, .value.qlist = (val)}
> -
>  typedef struct QListCompareHelper
>  {
>      int index;
> -    LiteralQObject *objs;
> +    QLitObject *objs;
>      int result;
>  } QListCompareHelper;
>  
> -static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs);
> +static int compare_litqobj_to_qobj(QLitObject *lhs, QObject *rhs);
>  
>  static void compare_helper(QObject *obj, void *opaque)
>  {
> @@ -1109,7 +1085,7 @@ static void compare_helper(QObject *obj, void *opaque)
>      helper->result = compare_litqobj_to_qobj(&helper->objs[helper->index++], obj);
>  }
>  
> -static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs)
> +static int compare_litqobj_to_qobj(QLitObject *lhs, QObject *rhs)
>  {
>      int64_t val;
>  
> @@ -1159,23 +1135,23 @@ static void simple_dict(void)
>      int i;
>      struct {
>          const char *encoded;
> -        LiteralQObject decoded;
> +        QLitObject decoded;
>      } test_cases[] = {
>          {
>              .encoded = "{\"foo\": 42, \"bar\": \"hello world\"}",
> -            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
> +            .decoded = QLIT_QDICT(((QLitDictEntry[]){
>                          { "foo", QLIT_QNUM(42) },
>                          { "bar", QLIT_QSTR("hello world") },
>                          { }
>                      })),
>          }, {
>              .encoded = "{}",
> -            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
> +            .decoded = QLIT_QDICT(((QLitDictEntry[]){
>                          { }
>                      })),
>          }, {
>              .encoded = "{\"foo\": 43}",
> -            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
> +            .decoded = QLIT_QDICT(((QLitDictEntry[]){
>                          { "foo", QLIT_QNUM(43) },
>                          { }
>                      })),
> @@ -1257,11 +1233,11 @@ static void simple_list(void)
>      int i;
>      struct {
>          const char *encoded;
> -        LiteralQObject decoded;
> +        QLitObject decoded;
>      } test_cases[] = {
>          {
>              .encoded = "[43,42]",
> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
> +            .decoded = QLIT_QLIST(((QLitObject[]){
>                          QLIT_QNUM(43),
>                          QLIT_QNUM(42),
>                          { }
> @@ -1269,21 +1245,21 @@ static void simple_list(void)
>          },
>          {
>              .encoded = "[43]",
> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
> +            .decoded = QLIT_QLIST(((QLitObject[]){
>                          QLIT_QNUM(43),
>                          { }
>                      })),
>          },
>          {
>              .encoded = "[]",
> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
> +            .decoded = QLIT_QLIST(((QLitObject[]){
>                          { }
>                      })),
>          },
>          {
>              .encoded = "[{}]",
> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
> -                        QLIT_QDICT(((LiteralQDictEntry[]){
> +            .decoded = QLIT_QLIST(((QLitObject[]){
> +                        QLIT_QDICT(((QLitDictEntry[]){
>                                      {},
>                                          })),
>                          {},
> @@ -1314,11 +1290,11 @@ static void simple_whitespace(void)
>      int i;
>      struct {
>          const char *encoded;
> -        LiteralQObject decoded;
> +        QLitObject decoded;
>      } test_cases[] = {
>          {
>              .encoded = " [ 43 , 42 ]",
> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
> +            .decoded = QLIT_QLIST(((QLitObject[]){
>                          QLIT_QNUM(43),
>                          QLIT_QNUM(42),
>                          { }
> @@ -1326,12 +1302,12 @@ static void simple_whitespace(void)
>          },
>          {
>              .encoded = " [ 43 , { 'h' : 'b' }, [ ], 42 ]",
> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
> +            .decoded = QLIT_QLIST(((QLitObject[]){
>                          QLIT_QNUM(43),
> -                        QLIT_QDICT(((LiteralQDictEntry[]){
> +                        QLIT_QDICT(((QLitDictEntry[]){
>                                      { "h", QLIT_QSTR("b") },
>                                      { }})),
> -                        QLIT_QLIST(((LiteralQObject[]){
> +                        QLIT_QLIST(((QLitObject[]){
>                                      { }})),
>                          QLIT_QNUM(42),
>                          { }
> @@ -1339,13 +1315,13 @@ static void simple_whitespace(void)
>          },
>          {
>              .encoded = " [ 43 , { 'h' : 'b' , 'a' : 32 }, [ ], 42 ]",
> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
> +            .decoded = QLIT_QLIST(((QLitObject[]){
>                          QLIT_QNUM(43),
> -                        QLIT_QDICT(((LiteralQDictEntry[]){
> +                        QLIT_QDICT(((QLitDictEntry[]){
>                                      { "h", QLIT_QSTR("b") },
>                                      { "a", QLIT_QNUM(32) },
>                                      { }})),
> -                        QLIT_QLIST(((LiteralQObject[]){
> +                        QLIT_QLIST(((QLitObject[]){
>                                      { }})),
>                          QLIT_QNUM(42),
>                          { }
> @@ -1393,10 +1369,10 @@ static void simple_varargs(void)
>  {
>      QObject *embedded_obj;
>      QObject *obj;
> -    LiteralQObject decoded = QLIT_QLIST(((LiteralQObject[]){
> +    QLitObject decoded = QLIT_QLIST(((QLitObject[]){
>              QLIT_QNUM(1),
>              QLIT_QNUM(2),
> -            QLIT_QLIST(((LiteralQObject[]){
> +            QLIT_QLIST(((QLitObject[]){
>                          QLIT_QNUM(32),
>                          QLIT_QNUM(42),
>                          {}})),
> diff --git a/tests/check-qlit.c b/tests/check-qlit.c
> new file mode 100644
> index 0000000000..cda4620f92
> --- /dev/null
> +++ b/tests/check-qlit.c
> @@ -0,0 +1,52 @@
> +/*
> + * QLit unit-tests.
> + *
> + * Copyright (C) 2017 Red Hat Inc.
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +
> +#include "qapi/qmp/qlit.h"
> +
> +static void qobject_from_qlit_test(void)
> +{
> +    char *str;
> +    QObject *qobj = NULL;
> +    QLitObject qlit = QLIT_QDICT((
> +        (QLitDictEntry[]) {
> +            { "foo", QLIT_QNUM(42) },
> +            { "bar", QLIT_QSTR("hello world") },
> +            { "baz", QLIT_QNULL },
> +            { "bee", QLIT_QLIST((
> +                (QLitObject[]) {
> +                    QLIT_QNUM(43),
> +                    QLIT_QNUM(44),
> +                    QLIT_QBOOL(true),
> +                    { },
> +                }))
> +            },
> +            { },
> +        }));
> +
> +    qobj = qobject_from_qlit(&qlit);
> +
> +    str = qobject_to_string(qobj);
> +    g_assert_cmpstr(str, ==,
> +                    "bee:\n    [0]: 43\n    [1]: 44\n    [2]: true\n"   \
> +                    "baz: null\nbar: hello world\nfoo: 42\n");

I don't like this.  The order of QDict members in @str depends on
qdict_first()/qdict_next() iteration order, which is unspecified.

Here's how we check elsewhere that a QObject matches expectations:

    static void qobject_from_qlit_test(void)
    {
        QLitObject qlit = QLIT_QDICT((
            (QLitDictEntry[]) {
                { "foo", QLIT_QNUM(42) },
                { "bar", QLIT_QSTR("hello world") },
                { "baz", QLIT_QNULL },
                { "bee", QLIT_QLIST((
                    (QLitObject[]) {
                        QLIT_QNUM(43),
                        QLIT_QNUM(44),
                        QLIT_QBOOL(true),
                        { },
                    }))
                },
                { },
            }));
        QObject *qobj = qobject_from_qlit(&qlit);
        QDict *qdict;
        QList *baz;

        qdict = qobject_to_qdict(qobj);
        g_assert_cmpint(qdict_get_int(qdict, "foo"), ==, 42);
        g_assert_cmpstr(qdict_get_str(qdict, "bar"), ==, "hello world");
        g_assert(qobject_type(qdict_get(qdict, "baz")) == QTYPE_QNULL);
        baz = qdict_get_qlist(qdict, "bee");
        g_assert_cmpint(qnum_get_int(qobject_to_qnum(qlist_pop(baz))), ==, 43);
        g_assert_cmpint(qnum_get_int(qobject_to_qnum(qlist_pop(baz))), ==, 44);
        g_assert_cmpint(qbool_get_bool(qobject_to_qbool(qlist_pop(baz))), ==, 1);

        qobject_decref(qobj);
    }

Robust, just tedious.

check-qjson.c uses a differently tedious technique: compare expected
QLitObject to actual QObject with compare_litqobj_to_qobj().  I like
that better, because I find the QLIT... initializers easier to read, and
less error prone to write.  You might prefer not to use QLit to test
QLit, though.

> +
> +    g_free(str);
> +    qobject_decref(qobj);
> +}
> +
> +int main(int argc, char **argv)
> +{
> +    g_test_init(&argc, &argv, NULL);
> +
> +    g_test_add_func("/qlit/qobject_from_qlit", qobject_from_qlit_test);
> +
> +    return g_test_run();
> +}
> diff --git a/qobject/Makefile.objs b/qobject/Makefile.objs
> index fc8885c9a4..002d25873a 100644
> --- a/qobject/Makefile.objs
> +++ b/qobject/Makefile.objs
> @@ -1,2 +1,2 @@
> -util-obj-y = qnull.o qnum.o qstring.o qdict.o qlist.o qbool.o
> +util-obj-y = qnull.o qnum.o qstring.o qdict.o qlist.o qbool.o qlit.o
>  util-obj-y += qjson.o qobject.o json-lexer.o json-streamer.o json-parser.o
> diff --git a/tests/Makefile.include b/tests/Makefile.include
> index 7af278db55..960ab8c6dd 100644
> --- a/tests/Makefile.include
> +++ b/tests/Makefile.include
> @@ -12,6 +12,8 @@ check-unit-y += tests/test-char$(EXESUF)
>  gcov-files-check-qdict-y = chardev/char.c
>  check-unit-y += tests/check-qnum$(EXESUF)
>  gcov-files-check-qnum-y = qobject/qnum.c
> +check-unit-y += tests/check-qlit$(EXESUF)
> +gcov-files-check-qlit-y = qobject/qlit.c
>  check-unit-y += tests/check-qstring$(EXESUF)
>  gcov-files-check-qstring-y = qobject/qstring.c
>  check-unit-y += tests/check-qlist$(EXESUF)
> @@ -523,7 +525,7 @@ test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \
>  	tests/rcutorture.o tests/test-rcu-list.o \
>  	tests/test-qdist.o tests/test-shift128.o \
>  	tests/test-qht.o tests/qht-bench.o tests/test-qht-par.o \
> -	tests/atomic_add-bench.o
> +	tests/atomic_add-bench.o tests/check-qlit.o

Please add it right next to the other qobject-related tests:

   test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \
 	tests/check-qlist.o tests/check-qnull.o \
-	tests/check-qjson.o \
+	tests/check-qjson.o tests/check-qlit.o \

>  
>  $(test-obj-y): QEMU_INCLUDES += -Itests
>  QEMU_CFLAGS += -I$(SRC_PATH)/tests
> @@ -541,6 +543,7 @@ test-io-obj-y = $(io-obj-y) $(test-crypto-obj-y)
>  test-block-obj-y = $(block-obj-y) $(test-io-obj-y) tests/iothread.o
>  
>  tests/check-qnum$(EXESUF): tests/check-qnum.o $(test-util-obj-y)
> +tests/check-qlit$(EXESUF): tests/check-qlit.o $(test-util-obj-y)
>  tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y)
>  tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y)
>  tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y)

Same order as in test-obj-y, please.

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 02/26] qobject: replace dump_qobject() by qobject_to_string()
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 02/26] qobject: replace dump_qobject() by qobject_to_string() Marc-André Lureau
@ 2017-08-16  9:02   ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2017-08-16  9:02 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Kevin Wolf, open list:Block layer core, Max Reitz

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> The dump functions is generally useful for any qobject user, for
> testing, debugging etc.
>
> The callback-based output is replaced by string allocation. Trading
> efficiency for ease-of-use is okay here.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

As far as I can tell, this patch is in this series only for the next
patch's check-qlit.c.  I don't like its use there, so I'm ignoring this
patch for now.

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 04/26] qapi: generate a literal qobject for introspection
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 04/26] qapi: generate a literal qobject for introspection Marc-André Lureau
@ 2017-08-16 10:21   ` Markus Armbruster
  2017-08-22 11:17     ` Marc-André Lureau
  2017-08-17 11:48   ` Markus Armbruster
  1 sibling, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2017-08-16 10:21 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth, Dr. David Alan Gilbert

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Replace the generated json string with a literal qobject. The later is
> easier to deal with, at run time, as well as compile time:

at run time as well as compile time

>                                                            #if blocks
> can be more easily added than in a json string.

Future tense, e.g. "adding #if conditionals will be easier".

>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  scripts/qapi-introspect.py         | 83 +++++++++++++++++++++-----------------
>  monitor.c                          |  2 +-
>  tests/test-qobject-input-visitor.c | 10 +++--
>  docs/devel/qapi-code-gen.txt       | 29 ++++++++-----
>  4 files changed, 72 insertions(+), 52 deletions(-)
>
> diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
> index 032bcea491..fc72cdb66d 100644
> --- a/scripts/qapi-introspect.py
> +++ b/scripts/qapi-introspect.py
> @@ -12,72 +12,74 @@
>  from qapi import *
>  
>  
> -# Caveman's json.dumps() replacement (we're stuck at Python 2.4)
> -# TODO try to use json.dumps() once we get unstuck
> -def to_json(obj, level=0):
> +def to_qlit(obj, level=0, first_indent=True):

Suggest suppress_indent=False.  I prefer defaulting to False.

> +    def indent(level):
> +        return level * 4 * ' '

Blank line before and after nested function definition, please.

> +    ret = ''
> +    if first_indent:
> +        ret += indent(level)
>      if obj is None:
> -        ret = 'null'
> +        ret += 'QLIT_QNULL'
>      elif isinstance(obj, str):
> -        ret = '"' + obj.replace('"', r'\"') + '"'
> +        ret += 'QLIT_QSTR(' + '"' + obj.replace('"', r'\"') + '"' + ')'

Why not 

           ret += 'QLIT_QSTR("' + obj.replace('"', r'\"') + '")'

Hmm, make that

           ret += 'QLIT_QSTR(' + to_c_string(obj) + ')'

because it makes the meaning more obvious, and it's also more robust: it
doubles backslashes.

>      elif isinstance(obj, list):
> -        elts = [to_json(elt, level + 1)
> +        elts = [to_qlit(elt, level + 1)
>                  for elt in obj]
> -        ret = '[' + ', '.join(elts) + ']'
> +        elts.append(indent(level + 1) + "{ }")

I'd prefer "{}".  More of the same below.

> +        ret += 'QLIT_QLIST(((QLitObject[]) {\n'
> +        ret += ',\n'.join(elts) + '\n'
> +        ret += indent(level) + '}))'

The extra pair of parenthesis in QLIT_QLIST(( ... )) is slightly ugly.
Not this patch's fault.  Same for QLIT_QDICT(( ... )) below.

>      elif isinstance(obj, dict):
> -        elts = ['"%s": %s' % (key.replace('"', r'\"'),
> -                              to_json(obj[key], level + 1))
> -                for key in sorted(obj.keys())]
> -        ret = '{' + ', '.join(elts) + '}'
> +        elts = [ indent(level + 1) + '{ "%s", %s }' %
> +                 (key.replace('"', r'\"'), to_qlit(obj[key], level + 1, False))

$ pep8 scripts/qapi-introspect.py
scripts/qapi-introspect.py:33:17: E201 whitespace after '['

Also, break lines at operators with the least precedence, not in the
middle of sub-expressions.

However

           elts = [indent(level + 1)
                   + ('{ "%s", %s }'
                      % (to_c_string(key),
                         to_qlit(obj[key], level + 1, suppress_indent=True)))
                   for key in sorted(obj.keys())]

is still illegible.  I'm afraid this is simply too complex for a list
comprehension.  Try rewriting as a loop.

Another option would be separating off indentation: generate the C
initializer unindented, then feed it to a stupid indenter that counts
parentheses (round, square and curly).

> +                 for key in sorted(obj.keys())]
> +        elts.append(indent(level + 1) + '{ }')
> +        ret += 'QLIT_QDICT(((QLitDictEntry[]) {\n'
> +        ret += ',\n'.join(elts) + '\n'
> +        ret += indent(level) + '}))'
>      else:
>          assert False                # not implemented
> -    if level == 1:
> -        ret = '\n' + ret
>      return ret
>  
>  
> -def to_c_string(string):
> -    return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"'
> -
> -
>  class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor):
>      def __init__(self, unmask):
>          self._unmask = unmask
>          self.defn = None
>          self.decl = None
>          self._schema = None
> -        self._jsons = None
> +        self._qlits = None
>          self._used_types = None
>          self._name_map = None
>  
>      def visit_begin(self, schema):
>          self._schema = schema
> -        self._jsons = []
> +        self._qlits = []
>          self._used_types = []
>          self._name_map = {}
>  
>      def visit_end(self):
>          # visit the types that are actually used
> -        jsons = self._jsons
> -        self._jsons = []
> +        qlits = self._qlits
> +        self._qlits = []
>          for typ in self._used_types:
>              typ.visit(self)
>          # generate C
>          # TODO can generate awfully long lines
> -        jsons.extend(self._jsons)
> -        name = c_name(prefix, protect=False) + 'qmp_schema_json'
> +        qlits.extend(self._qlits)
> +        name = c_name(prefix, protect=False) + 'qmp_schema_qlit'
>          self.decl = mcgen('''
> -extern const char %(c_name)s[];
> +extern const QLitObject %(c_name)s;
>  ''',
>                            c_name=c_name(name))
> -        lines = to_json(jsons).split('\n')
> -        c_string = '\n    '.join([to_c_string(line) for line in lines])
> +        c_string = to_qlit(qlits)

The value is simple, and it's used just once.  Let's eliminate the
variable.

>          self.defn = mcgen('''
> -const char %(c_name)s[] = %(c_string)s;
> +const QLitObject %(c_name)s = %(c_string)s;
>  ''',
>                            c_name=c_name(name),
>                            c_string=c_string)
>          self._schema = None
> -        self._jsons = None
> +        self._qlits = None
>          self._used_types = None
>          self._name_map = None
>  
> @@ -111,12 +113,12 @@ const char %(c_name)s[] = %(c_string)s;
>              return '[' + self._use_type(typ.element_type) + ']'
>          return self._name(typ.name)
>  
> -    def _gen_json(self, name, mtype, obj):
> +    def _gen_qlit(self, name, mtype, obj):
>          if mtype not in ('command', 'event', 'builtin', 'array'):
>              name = self._name(name)
>          obj['name'] = name
>          obj['meta-type'] = mtype
> -        self._jsons.append(obj)
> +        self._qlits.append(obj)
>  
>      def _gen_member(self, member):
>          ret = {'name': member.name, 'type': self._use_type(member.type)}
> @@ -132,24 +134,24 @@ const char %(c_name)s[] = %(c_string)s;
>          return {'case': variant.name, 'type': self._use_type(variant.type)}
>  
>      def visit_builtin_type(self, name, info, json_type):
> -        self._gen_json(name, 'builtin', {'json-type': json_type})
> +        self._gen_qlit(name, 'builtin', {'json-type': json_type})
>  
>      def visit_enum_type(self, name, info, values, prefix):
> -        self._gen_json(name, 'enum', {'values': values})
> +        self._gen_qlit(name, 'enum', {'values': values})
>  
>      def visit_array_type(self, name, info, element_type):
>          element = self._use_type(element_type)
> -        self._gen_json('[' + element + ']', 'array', {'element-type': element})
> +        self._gen_qlit('[' + element + ']', 'array', {'element-type': element})
>  
>      def visit_object_type_flat(self, name, info, members, variants):
>          obj = {'members': [self._gen_member(m) for m in members]}
>          if variants:
>              obj.update(self._gen_variants(variants.tag_member.name,
>                                            variants.variants))
> -        self._gen_json(name, 'object', obj)
> +        self._gen_qlit(name, 'object', obj)
>  
>      def visit_alternate_type(self, name, info, variants):
> -        self._gen_json(name, 'alternate',
> +        self._gen_qlit(name, 'alternate',
>                         {'members': [{'type': self._use_type(m.type)}
>                                      for m in variants.variants]})
>  
> @@ -157,13 +159,13 @@ const char %(c_name)s[] = %(c_string)s;
>                        gen, success_response, boxed):
>          arg_type = arg_type or self._schema.the_empty_object_type
>          ret_type = ret_type or self._schema.the_empty_object_type
> -        self._gen_json(name, 'command',
> +        self._gen_qlit(name, 'command',
>                         {'arg-type': self._use_type(arg_type),
>                          'ret-type': self._use_type(ret_type)})
>  
>      def visit_event(self, name, info, arg_type, boxed):
>          arg_type = arg_type or self._schema.the_empty_object_type
> -        self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)})
> +        self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)})
>  
>  # Debugging aid: unmask QAPI schema's type names
>  # We normally mask them, because they're not QMP wire ABI
> @@ -205,11 +207,18 @@ h_comment = '''
>  
>  fdef.write(mcgen('''
>  #include "qemu/osdep.h"
> +#include "qapi/qmp/qlit.h"
>  #include "%(prefix)sqmp-introspect.h"
>  
>  ''',
>                   prefix=prefix))
>  
> +fdecl.write(mcgen('''
> +#include "qemu/osdep.h"
> +#include "qapi/qmp/qlit.h"
> +
> +'''))
> +
>  schema = QAPISchema(input_file)
>  gen = QAPISchemaGenIntrospectVisitor(opt_unmask)
>  schema.visit(gen)
> diff --git a/monitor.c b/monitor.c
> index 6d040e620f..a1773d5bc7 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -953,7 +953,7 @@ EventInfoList *qmp_query_events(Error **errp)
>  static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
>                                   Error **errp)
>  {
> -    *ret_data = qobject_from_json(qmp_schema_json, &error_abort);
> +    *ret_data = qobject_from_qlit(&qmp_schema_qlit);
>  }
>  
>  /*
> diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c
> index bcf02617dc..1969733971 100644
> --- a/tests/test-qobject-input-visitor.c
> +++ b/tests/test-qobject-input-visitor.c
> @@ -1247,24 +1247,26 @@ static void test_visitor_in_fail_alternate(TestInputVisitorData *data,
>  }
>  
>  static void do_test_visitor_in_qmp_introspect(TestInputVisitorData *data,
> -                                              const char *schema_json)
> +                                              const QLitObject *qlit)
>  {
>      SchemaInfoList *schema = NULL;
> +    QObject *obj = qobject_from_qlit(qlit);
>      Visitor *v;
>  
> -    v = visitor_input_test_init_raw(data, schema_json);
> +    v = qobject_input_visitor_new(obj);
>  
>      visit_type_SchemaInfoList(v, NULL, &schema, &error_abort);
>      g_assert(schema);
>  
>      qapi_free_SchemaInfoList(schema);
> +    qobject_decref(obj);
>  }
>  
>  static void test_visitor_in_qmp_introspect(TestInputVisitorData *data,
>                                             const void *unused)
>  {
> -    do_test_visitor_in_qmp_introspect(data, test_qmp_schema_json);
> -    do_test_visitor_in_qmp_introspect(data, qmp_schema_json);
> +    do_test_visitor_in_qmp_introspect(data, &test_qmp_schema_qlit);
> +    do_test_visitor_in_qmp_introspect(data, &qmp_schema_qlit);
>  }
>  

These tests are only marginally useful now.  Before, they ensured that a
qapi-introspect.py generating invalid JSON fails at "make check" compile
time.  Such bugs should now fail when we compile the generated
qapi-introspect.c.

>  int main(int argc, char **argv)
> diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt
> index 9903ac4c19..885c61b52f 100644
> --- a/docs/devel/qapi-code-gen.txt
> +++ b/docs/devel/qapi-code-gen.txt
> @@ -1295,18 +1295,27 @@ Example:
>      #ifndef EXAMPLE_QMP_INTROSPECT_H
>      #define EXAMPLE_QMP_INTROSPECT_H
>  
> -    extern const char example_qmp_schema_json[];
> +    extern const QLitObject qmp_schema_qlit;
>  
>      #endif
>      $ cat qapi-generated/example-qmp-introspect.c
>  [Uninteresting stuff omitted...]
>  
> -    const char example_qmp_schema_json[] = "["
> -        "{\"arg-type\": \"0\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
> -        "{\"arg-type\": \"1\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"2\"}, "
> -        "{\"members\": [], \"meta-type\": \"object\", \"name\": \"0\"}, "
> -        "{\"members\": [{\"name\": \"arg1\", \"type\": \"[2]\"}], \"meta-type\": \"object\", \"name\": \"1\"}, "
> -        "{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"default\": null, \"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, "
> -        "{\"element-type\": \"2\", \"meta-type\": \"array\", \"name\": \"[2]\"}, "
> -        "{\"json-type\": \"int\", \"meta-type\": \"builtin\", \"name\": \"int\"}, "
> -        "{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}]";
> +    const QLitObject example_qmp_schema_qlit = QLIT_QLIST(((QLitObject[]) {
> +        QLIT_QDICT(((QLitDictEntry[]) {
> +            { "arg-type", QLIT_QSTR("0") },
> +            { "meta-type", QLIT_QSTR("event") },
> +            { "name", QLIT_QSTR("Event") },
> +            { }
> +        })),
> +        QLIT_QDICT(((QLitDictEntry[]) {
> +            { "members", QLIT_QLIST(((QLitObject[]) {
> +                { }
> +            })) },
> +            { "meta-type", QLIT_QSTR("object") },
> +            { "name", QLIT_QSTR("0") },
> +            { }
> +        })),
> +        ....

Ellipsis is three dots, not four :)

Output is no longer complete (less file boilerplate).  Not an issue now,
but it might become one when we make the examples testable.  We can
restore the deleted output then.

> +        { }
> +    }));

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 05/26] visitor: pass size of strings array to enum visitor
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 05/26] visitor: pass size of strings array to enum visitor Marc-André Lureau
@ 2017-08-16 12:54   ` Markus Armbruster
  2017-08-22 11:17     ` Marc-André Lureau
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2017-08-16 12:54 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Eduardo Habkost, Jason Wang, Michael Roth,
	Igor Mammedov, Andreas Färber

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> The size is known at compile time, this avoids having to compute to
> check array boundaries.
>
> Additionally, the following conditional enum entry change will create
> "hole" in the generated _lookup tables, that should be skipped.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  include/qapi/visitor.h             |  3 ++-
>  scripts/qapi-visit.py              | 10 +++++-----
>  include/hw/qdev-core.h             |  1 +
>  include/qom/object.h               |  4 ++++
>  qapi/qapi-visit-core.c             | 23 ++++++++++++-----------
>  backends/hostmem.c                 |  1 +
>  crypto/secret.c                    |  1 +
>  crypto/tlscreds.c                  |  1 +
>  hw/core/qdev-properties.c          | 11 +++++++++--
>  net/filter.c                       |  1 +
>  qom/object.c                       | 11 ++++++++---
>  tests/check-qom-proplist.c         |  1 +
>  tests/test-qobject-input-visitor.c |  2 +-
>  13 files changed, 47 insertions(+), 23 deletions(-)

No change to scripts/qapi-types.c.  The generated lookup tables continue
to contain the NULL sentinel.  Possibly intentional, because it saves
you the trouble of searching for uses of FOO_lookup[FOO__MAX].

> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index fe9faf469f..a2d9786c52 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -469,7 +469,8 @@ bool visit_optional(Visitor *v, const char *name, bool *present);
>   * that visit_type_str() must have no unwelcome side effects.
>   */
>  void visit_type_enum(Visitor *v, const char *name, int *obj,
> -                     const char *const strings[], Error **errp);
> +                     const char *const strings[], int nstrings,
> +                     Error **errp);
>  
>  /*
>   * Check if visitor is an input visitor.
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index bd0b742236..60850a6cdd 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -147,17 +147,17 @@ out:
>                   c_name=c_name(name), c_elt_type=element_type.c_name())
>  
>  
> -def gen_visit_enum(name):
> +def gen_visit_enum(name, prefix):
>      return mcgen('''
>  
>  void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error **errp)
>  {
>      int value = *obj;
> -    visit_type_enum(v, name, &value, %(c_name)s_lookup, errp);
> +    visit_type_enum(v, name, &value, %(c_name)s_lookup, %(c_max)s, errp);
>      *obj = value;
>  }
>  ''',
> -                 c_name=c_name(name))
> +                 c_name=c_name(name), c_max=c_enum_const(name, '_MAX', prefix))
>  
>  
>  def gen_visit_alternate(name, variants):
> @@ -288,10 +288,10 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
>          if not info:
>              self._btin += gen_visit_decl(name, scalar=True)
>              if do_builtins:
> -                self.defn += gen_visit_enum(name)
> +                self.defn += gen_visit_enum(name, prefix)
>          else:
>              self.decl += gen_visit_decl(name, scalar=True)
> -            self.defn += gen_visit_enum(name)
> +            self.defn += gen_visit_enum(name, prefix)
>  
>      def visit_array_type(self, name, info, element_type):
>          decl = gen_visit_decl(name)
> diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
> index ae317286a4..f86a0e1a75 100644
> --- a/include/hw/qdev-core.h
> +++ b/include/hw/qdev-core.h
> @@ -250,6 +250,7 @@ struct PropertyInfo {
>      const char *name;
>      const char *description;
>      const char * const *enum_table;
> +    int enum_table_size;
>      int (*print)(DeviceState *dev, Property *prop, char *dest, size_t len);
>      void (*set_default_value)(Object *obj, const Property *prop);
>      void (*create)(Object *obj, Property *prop, Error **errp);
> diff --git a/include/qom/object.h b/include/qom/object.h
> index 1b828994fa..53d807e1e6 100644
> --- a/include/qom/object.h
> +++ b/include/qom/object.h
> @@ -1406,6 +1406,8 @@ void object_class_property_add_bool(ObjectClass *klass, const char *name,
>   * @obj: the object to add a property to
>   * @name: the name of the property
>   * @typename: the name of the enum data type
> + * @strings: an array of strings for the enum

Fixes a preexisting doc buglet.

> + * @nstrings: the size of @strings
>   * @get: the getter or %NULL if the property is write-only.
>   * @set: the setter or %NULL if the property is read-only
>   * @errp: if an error occurs, a pointer to an area to store the error
> @@ -1416,6 +1418,7 @@ void object_class_property_add_bool(ObjectClass *klass, const char *name,
>  void object_property_add_enum(Object *obj, const char *name,
>                                const char *typename,
>                                const char * const *strings,
> +                              int nstrings,
>                                int (*get)(Object *, Error **),
>                                void (*set)(Object *, int, Error **),
>                                Error **errp);
> @@ -1423,6 +1426,7 @@ void object_property_add_enum(Object *obj, const char *name,
>  void object_class_property_add_enum(ObjectClass *klass, const char *name,
>                                      const char *typename,
>                                      const char * const *strings,
> +                                    int nstrings,
>                                      int (*get)(Object *, Error **),
>                                      void (*set)(Object *, int, Error **),
>                                      Error **errp);
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index ed6d2af462..dc0b9f2cee 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -333,14 +333,13 @@ void visit_type_null(Visitor *v, const char *name, QNull **obj,
>  }
>  
>  static void output_type_enum(Visitor *v, const char *name, int *obj,
> -                             const char *const strings[], Error **errp)
> +                             const char *const strings[],
> +                             int nstrings, Error **errp)
>  {
> -    int i = 0;
>      int value = *obj;
>      char *enum_str;
>  
> -    while (strings[i++] != NULL);

This is the computation we save.

> -    if (value < 0 || value >= i - 1) {
> +    if (value < 0 || value >= nstrings) {
>          error_setg(errp, QERR_INVALID_PARAMETER, name ? name : "null");
>          return;
>      }
> @@ -350,7 +349,8 @@ static void output_type_enum(Visitor *v, const char *name, int *obj,
>  }
>  
>  static void input_type_enum(Visitor *v, const char *name, int *obj,
> -                            const char *const strings[], Error **errp)
> +                            const char *const strings[],
> +                            int nstrings, Error **errp)
>  {
>      Error *local_err = NULL;
>      int64_t value = 0;
> @@ -362,14 +362,14 @@ static void input_type_enum(Visitor *v, const char *name, int *obj,
>          return;
>      }
>  
> -    while (strings[value] != NULL) {

This is the predicate that becomes invalid when we put holes into
strings[].

> -        if (strcmp(strings[value], enum_str) == 0) {
> +    while (value < nstrings) {
> +        if (strings[value] && strcmp(strings[value], enum_str) == 0) {

I'd prefer !strcmp().

>              break;
>          }
>          value++;
>      }
>  
> -    if (strings[value] == NULL) {
> +    if (value >= nstrings || strings[value] == NULL) {

Make that

       if (value == nstrings) {

to match the loop above.

>          error_setg(errp, QERR_INVALID_PARAMETER, enum_str);
>          g_free(enum_str);
>          return;
> @@ -380,16 +380,17 @@ static void input_type_enum(Visitor *v, const char *name, int *obj,
>  }
>  
>  void visit_type_enum(Visitor *v, const char *name, int *obj,
> -                     const char *const strings[], Error **errp)
> +                     const char *const strings[], int nstrings,
> +                     Error **errp)
>  {
>      assert(obj && strings);
>      trace_visit_type_enum(v, name, obj);
>      switch (v->type) {
>      case VISITOR_INPUT:
> -        input_type_enum(v, name, obj, strings, errp);
> +        input_type_enum(v, name, obj, strings, nstrings, errp);
>          break;
>      case VISITOR_OUTPUT:
> -        output_type_enum(v, name, obj, strings, errp);
> +        output_type_enum(v, name, obj, strings, nstrings, errp);
>          break;
>      case VISITOR_CLONE:
>          /* nothing further to do, scalar value was already copied by
> diff --git a/backends/hostmem.c b/backends/hostmem.c
> index 4606b73849..fc475a5387 100644
> --- a/backends/hostmem.c
> +++ b/backends/hostmem.c
> @@ -396,6 +396,7 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
>          NULL, NULL, &error_abort);
>      object_class_property_add_enum(oc, "policy", "HostMemPolicy",
>          HostMemPolicy_lookup,
> +        HOST_MEM_POLICY__MAX,
>          host_memory_backend_get_policy,
>          host_memory_backend_set_policy, &error_abort);
>      object_class_property_add_str(oc, "id", get_id, set_id, &error_abort);
> diff --git a/crypto/secret.c b/crypto/secret.c
> index 285ab7a63c..b5382cb7e3 100644
> --- a/crypto/secret.c
> +++ b/crypto/secret.c
> @@ -379,6 +379,7 @@ qcrypto_secret_class_init(ObjectClass *oc, void *data)
>      object_class_property_add_enum(oc, "format",
>                                     "QCryptoSecretFormat",
>                                     QCryptoSecretFormat_lookup,
> +                                   QCRYPTO_SECRET_FORMAT__MAX,
>                                     qcrypto_secret_prop_get_format,
>                                     qcrypto_secret_prop_set_format,
>                                     NULL);
> diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
> index a8965531b6..8c060127ea 100644
> --- a/crypto/tlscreds.c
> +++ b/crypto/tlscreds.c
> @@ -234,6 +234,7 @@ qcrypto_tls_creds_class_init(ObjectClass *oc, void *data)
>      object_class_property_add_enum(oc, "endpoint",
>                                     "QCryptoTLSCredsEndpoint",
>                                     QCryptoTLSCredsEndpoint_lookup,
> +                                   QCRYPTO_TLS_CREDS_ENDPOINT__MAX,
>                                     qcrypto_tls_creds_prop_get_endpoint,
>                                     qcrypto_tls_creds_prop_set_endpoint,
>                                     NULL);
> diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
> index 078fc5d239..696fed5b5b 100644
> --- a/hw/core/qdev-properties.c
> +++ b/hw/core/qdev-properties.c
> @@ -52,7 +52,8 @@ static void get_enum(Object *obj, Visitor *v, const char *name, void *opaque,
>      Property *prop = opaque;
>      int *ptr = qdev_get_prop_ptr(dev, prop);
>  
> -    visit_type_enum(v, prop->name, ptr, prop->info->enum_table, errp);
> +    visit_type_enum(v, prop->name, ptr, prop->info->enum_table,
> +                    prop->info->enum_table_size, errp);
>  }
>  
>  static void set_enum(Object *obj, Visitor *v, const char *name, void *opaque,
> @@ -67,7 +68,8 @@ static void set_enum(Object *obj, Visitor *v, const char *name, void *opaque,
>          return;
>      }
>  
> -    visit_type_enum(v, prop->name, ptr, prop->info->enum_table, errp);
> +    visit_type_enum(v, prop->name, ptr, prop->info->enum_table,
> +                    prop->info->enum_table_size, errp);
>  }
>  
>  static void set_default_value_enum(Object *obj, const Property *prop)
> @@ -586,6 +588,7 @@ const PropertyInfo qdev_prop_on_off_auto = {
>      .name = "OnOffAuto",
>      .description = "on/off/auto",
>      .enum_table = OnOffAuto_lookup,
> +    .enum_table_size = ON_OFF_AUTO__MAX,
>      .get = get_enum,
>      .set = set_enum,
>      .set_default_value = set_default_value_enum,
> @@ -598,6 +601,7 @@ QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int));
>  const PropertyInfo qdev_prop_losttickpolicy = {
>      .name  = "LostTickPolicy",
>      .enum_table  = LostTickPolicy_lookup,
> +    .enum_table_size = LOST_TICK_POLICY__MAX,
>      .get   = get_enum,
>      .set   = set_enum,
>      .set_default_value = set_default_value_enum,
> @@ -612,6 +616,7 @@ const PropertyInfo qdev_prop_blockdev_on_error = {
>      .description = "Error handling policy, "
>                     "report/ignore/enospc/stop/auto",
>      .enum_table = BlockdevOnError_lookup,
> +    .enum_table_size = BLOCKDEV_ON_ERROR__MAX,
>      .get = get_enum,
>      .set = set_enum,
>      .set_default_value = set_default_value_enum,
> @@ -626,6 +631,7 @@ const PropertyInfo qdev_prop_bios_chs_trans = {
>      .description = "Logical CHS translation algorithm, "
>                     "auto/none/lba/large/rechs",
>      .enum_table = BiosAtaTranslation_lookup,
> +    .enum_table_size = BIOS_ATA_TRANSLATION__MAX,
>      .get = get_enum,
>      .set = set_enum,
>      .set_default_value = set_default_value_enum,
> @@ -638,6 +644,7 @@ const PropertyInfo qdev_prop_fdc_drive_type = {
>      .description = "FDC drive type, "
>                     "144/288/120/none/auto",
>      .enum_table = FloppyDriveType_lookup,
> +    .enum_table_size = FLOPPY_DRIVE_TYPE__MAX,
>      .get = get_enum,
>      .set = set_enum,
>      .set_default_value = set_default_value_enum,
> diff --git a/net/filter.c b/net/filter.c
> index 1dfd2caa23..cf62851344 100644
> --- a/net/filter.c
> +++ b/net/filter.c
> @@ -180,6 +180,7 @@ static void netfilter_init(Object *obj)
>                              NULL);
>      object_property_add_enum(obj, "queue", "NetFilterDirection",
>                               NetFilterDirection_lookup,
> +                             NET_FILTER_DIRECTION__MAX,
>                               netfilter_get_direction, netfilter_set_direction,
>                               NULL);
>      object_property_add_str(obj, "status",
> diff --git a/qom/object.c b/qom/object.c
> index fe6e744b4d..425bae3a2a 100644
> --- a/qom/object.c
> +++ b/qom/object.c
> @@ -1247,6 +1247,7 @@ uint64_t object_property_get_uint(Object *obj, const char *name,
>  
>  typedef struct EnumProperty {
>      const char * const *strings;
> +    int nstrings;
>      int (*get)(Object *, Error **);
>      void (*set)(Object *, int, Error **);
>  } EnumProperty;
> @@ -1284,7 +1285,7 @@ int object_property_get_enum(Object *obj, const char *name,
>      visit_complete(v, &str);
>      visit_free(v);
>      v = string_input_visitor_new(str);
> -    visit_type_enum(v, name, &ret, enumprop->strings, errp);
> +    visit_type_enum(v, name, &ret, enumprop->strings, enumprop->nstrings, errp);

Long line.

>  
>      g_free(str);
>      visit_free(v);
> @@ -1950,7 +1951,7 @@ static void property_get_enum(Object *obj, Visitor *v, const char *name,
>          return;
>      }
>  
> -    visit_type_enum(v, name, &value, prop->strings, errp);
> +    visit_type_enum(v, name, &value, prop->strings, prop->nstrings, errp);
>  }
>  
>  static void property_set_enum(Object *obj, Visitor *v, const char *name,
> @@ -1960,7 +1961,7 @@ static void property_set_enum(Object *obj, Visitor *v, const char *name,
>      int value;
>      Error *err = NULL;
>  
> -    visit_type_enum(v, name, &value, prop->strings, &err);
> +    visit_type_enum(v, name, &value, prop->strings, prop->nstrings, &err);
>      if (err) {
>          error_propagate(errp, err);
>          return;
> @@ -1978,6 +1979,7 @@ static void property_release_enum(Object *obj, const char *name,
>  void object_property_add_enum(Object *obj, const char *name,
>                                const char *typename,
>                                const char * const *strings,
> +                              int nstrings,
>                                int (*get)(Object *, Error **),
>                                void (*set)(Object *, int, Error **),
>                                Error **errp)
> @@ -1986,6 +1988,7 @@ void object_property_add_enum(Object *obj, const char *name,
>      EnumProperty *prop = g_malloc(sizeof(*prop));
>  
>      prop->strings = strings;
> +    prop->nstrings = nstrings;
>      prop->get = get;
>      prop->set = set;
>  
> @@ -2003,6 +2006,7 @@ void object_property_add_enum(Object *obj, const char *name,
>  void object_class_property_add_enum(ObjectClass *klass, const char *name,
>                                      const char *typename,
>                                      const char * const *strings,
> +                                    int nstrings,
>                                      int (*get)(Object *, Error **),
>                                      void (*set)(Object *, int, Error **),
>                                      Error **errp)
> @@ -2011,6 +2015,7 @@ void object_class_property_add_enum(ObjectClass *klass, const char *name,
>      EnumProperty *prop = g_malloc(sizeof(*prop));
>  
>      prop->strings = strings;
> +    prop->nstrings = nstrings;
>      prop->get = get;
>      prop->set = set;
>  
> diff --git a/tests/check-qom-proplist.c b/tests/check-qom-proplist.c
> index 432b66585f..1179030248 100644
> --- a/tests/check-qom-proplist.c
> +++ b/tests/check-qom-proplist.c
> @@ -143,6 +143,7 @@ static void dummy_class_init(ObjectClass *cls, void *data)
>      object_class_property_add_enum(cls, "av",
>                                     "DummyAnimal",
>                                     dummy_animal_map,
> +                                   DUMMY_LAST,
>                                     dummy_get_av,
>                                     dummy_set_av,
>                                     NULL);
> diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c
> index 1969733971..4da5d02c35 100644
> --- a/tests/test-qobject-input-visitor.c
> +++ b/tests/test-qobject-input-visitor.c
> @@ -1110,7 +1110,7 @@ static void test_visitor_in_fail_struct_missing(TestInputVisitorData *data,
>      error_free_or_abort(&err);
>      visit_optional(v, "optional", &present);
>      g_assert(!present);
> -    visit_type_enum(v, "enum", &en, EnumOne_lookup, &err);
> +    visit_type_enum(v, "enum", &en, EnumOne_lookup, ENUM_ONE__MAX, &err);
>      error_free_or_abort(&err);
>      visit_type_int(v, "i64", &i64, &err);
>      error_free_or_abort(&err);

Missing: a review of FOO_lookup[] uses outside these two, to make sure
none of them fall into holes like input_type_enum() would.  From the top
of my head: qapi_enum_parse().  A quick grep for loops counting up to
FOO__MAX additionally finds get_event_by_name() in blkdebug.c,
parse_read_pattern() in quorum.c, hmp_migrate_set_capability() in hmp.c,
and then I stopped looking.  Most (or all?) of them should use
qapi_enum_parse().

There's one patch hunk to make input_type_enum() cope with holes, one
patch hunk to opportunistically simplify output_type_enum(), and all the
others are for plumbing the table size to these two.  That's a lot of
plumbing.  Can't say I like it.

Alternatives:

(1) Use a value other than NULL for holes, say ""

(2) Use a value other than NULL for the sentinel, say ""

(3) Store the length in the lookup table, i.e. change it from
    const char *const[] to struct { int n, const char *const s[] }.

The least work is probably (1).  Slightly ugly.

If you do (3), please consider getting rid of the sentinel.

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 06/26] qapi2texi: minor python code simplification
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 06/26] qapi2texi: minor python code simplification Marc-André Lureau
@ 2017-08-16 12:55   ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2017-08-16 12:55 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  scripts/qapi2texi.py | 5 ++---
>  1 file changed, 2 insertions(+), 3 deletions(-)
>
> diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py
> index 9e015002ef..639eb1d042 100755
> --- a/scripts/qapi2texi.py
> +++ b/scripts/qapi2texi.py
> @@ -136,10 +136,9 @@ def texi_enum_value(value):
>  def texi_member(member, suffix=''):
>      """Format a table of members item for an object type member"""
>      typ = member.type.doc_type()
> -    return '@item @code{%s%s%s}%s%s\n' % (
> +    return '@item @code{%s%s}%s%s\n' % (
>          member.name,
> -        ': ' if typ else '',
> -        typ if typ else '',
> +        ': %s' % typ if typ else '',
>          ' (optional)' if member.optional else '',
>          suffix)

Is nested interpolation really simpler?

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 07/26] qapi: add 'if' condition on top-level schema elements
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 07/26] qapi: add 'if' condition on top-level schema elements Marc-André Lureau
@ 2017-08-16 15:43   ` Markus Armbruster
  2017-08-17  5:50     ` Markus Armbruster
  2017-08-22 11:17     ` Marc-André Lureau
  2017-08-17 11:51   ` Markus Armbruster
  1 sibling, 2 replies; 69+ messages in thread
From: Markus Armbruster @ 2017-08-16 15:43 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Add 'if' c-preprocessor condition on top-level schema elements:
> struct, enum, union, alternate, command, event.

An example would be useful here.  Your cover letter has nice ones, would
be a shame not to preserve them for posterity in the commit log.

> Variants objects types are created outside of #if blocks, since they

What are "variants objects types"?

> may be shared by various types. Even if unused, this shouldn't be an
> issue, since those are internal types.
>
> Note that there is no checks in qapi scripts to verify that elements

there are

> have compatible conditions (ex: if-struct used by a if-foo
> command). This may thus fail at C build time if they don't share the
> same subset of conditions.

Fair enough.

> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  scripts/qapi.py                         | 153 +++++++++++++++++++++++++-------
>  scripts/qapi-commands.py                |   4 +-
>  scripts/qapi-event.py                   |   4 +-
>  scripts/qapi-introspect.py              |  46 ++++++----
>  scripts/qapi-types.py                   |  51 +++++++----
>  scripts/qapi-visit.py                   |  13 ++-
>  scripts/qapi2texi.py                    |  10 +--
>  tests/Makefile.include                  |   1 +
>  tests/qapi-schema/bad-if.err            |   1 +
>  tests/qapi-schema/bad-if.exit           |   1 +
>  tests/qapi-schema/bad-if.json           |   3 +
>  tests/qapi-schema/bad-if.out            |   0
>  tests/qapi-schema/qapi-schema-test.json |  20 +++++
>  tests/qapi-schema/qapi-schema-test.out  |  31 +++++++
>  tests/qapi-schema/test-qapi.py          |  21 +++--
>  15 files changed, 272 insertions(+), 87 deletions(-)
>  create mode 100644 tests/qapi-schema/bad-if.err
>  create mode 100644 tests/qapi-schema/bad-if.exit
>  create mode 100644 tests/qapi-schema/bad-if.json
>  create mode 100644 tests/qapi-schema/bad-if.out
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 4ecc19e944..79ba1e93da 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -639,6 +639,16 @@ def add_name(name, info, meta, implicit=False):
>      all_names[name] = meta
>  
>  
> +def check_if(expr, info):
> +    ifcond = expr.get('if')
> +    if not ifcond or isinstance(ifcond, str):
> +        return
> +    if (not isinstance(ifcond, list) or
> +        any([not isinstance(elt, str) for elt in ifcond])):
> +        raise QAPISemError(info, "'if' condition requires a string or "
> +                           "a list of string")

Wait a second!  What's this list business?  The commit message doesn't
say.

Also, pep8 gripes:

    scripts/qapi.py:647:9: E129 visually indented line with same indent as next logical line

> +
> +
>  def check_type(info, source, value, allow_array=False,
>                 allow_dict=False, allow_optional=False,
>                 allow_metas=[]):
> @@ -865,6 +875,7 @@ def check_keys(expr_elem, meta, required, optional=[]):
>      expr = expr_elem['expr']
>      info = expr_elem['info']
>      name = expr[meta]
> +    optional.append('if')

Caution!

    $ python
    Python 2.7.13 (default, May 10 2017, 20:04:36) 
    >>> def surprise(arg=[]):
    ...     arg.append('if')
    ...     return arg
    ... 
    >>> surprise()
    ['if']
    >>> surprise()
    ['if', 'if']
    >>> surprise()
    ['if', 'if', 'if']

Never modify an argument that has list or dictionary default value.  To
avoid the temptation, never use such defaul values.

>      if not isinstance(name, str):
>          raise QAPISemError(info, "'%s' key must have a string value" % meta)
>      required = required + [meta]
> @@ -880,6 +891,8 @@ def check_keys(expr_elem, meta, required, optional=[]):
>              raise QAPISemError(info,
>                                 "'%s' of %s '%s' should only use true value"
>                                 % (key, meta, name))
> +        if key == 'if':
> +            check_if(expr, info)
>      for key in required:
>          if key not in expr:
>              raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
> @@ -989,6 +1002,10 @@ class QAPISchemaEntity(object):
>          # such place).
>          self.info = info
>          self.doc = doc
> +        self.ifcond = None
> +
> +    def set_ifcond(self, ifcond):
> +        self.ifcond = ifcond

@ifcond is an awkward name, but I don't have better ideas.

>  
>      def c_name(self):
>          return c_name(self.name)
> @@ -1017,26 +1034,26 @@ class QAPISchemaVisitor(object):
>      def visit_builtin_type(self, name, info, json_type):
>          pass
>  
> -    def visit_enum_type(self, name, info, values, prefix):
> +    def visit_enum_type(self, name, info, values, prefix, ifcond):
>          pass
>  
> -    def visit_array_type(self, name, info, element_type):
> +    def visit_array_type(self, name, info, element_type, ifcond):
>          pass
>  
> -    def visit_object_type(self, name, info, base, members, variants):
> +    def visit_object_type(self, name, info, base, members, variants, ifcond):
>          pass
>  
> -    def visit_object_type_flat(self, name, info, members, variants):
> +    def visit_object_type_flat(self, name, info, members, variants, ifcond):
>          pass
>  
> -    def visit_alternate_type(self, name, info, variants):
> +    def visit_alternate_type(self, name, info, variants, ifcond):
>          pass
>  
>      def visit_command(self, name, info, arg_type, ret_type,
> -                      gen, success_response, boxed):
> +                      gen, success_response, boxed, ifcond):
>          pass
>  
> -    def visit_event(self, name, info, arg_type, boxed):
> +    def visit_event(self, name, info, arg_type, boxed, ifcond):
>          pass
>  
>  
> @@ -1136,7 +1153,7 @@ class QAPISchemaEnumType(QAPISchemaType):
>  
>      def visit(self, visitor):
>          visitor.visit_enum_type(self.name, self.info,
> -                                self.member_names(), self.prefix)
> +                                self.member_names(), self.prefix, self.ifcond)
>  
>  
>  class QAPISchemaArrayType(QAPISchemaType):
> @@ -1149,6 +1166,7 @@ class QAPISchemaArrayType(QAPISchemaType):
>      def check(self, schema):
>          self.element_type = schema.lookup_type(self._element_type_name)
>          assert self.element_type
> +        self.ifcond = self.element_type.ifcond
>  
>      def is_implicit(self):
>          return True
> @@ -1166,7 +1184,8 @@ class QAPISchemaArrayType(QAPISchemaType):
>          return 'array of ' + elt_doc_type
>  
>      def visit(self, visitor):
> -        visitor.visit_array_type(self.name, self.info, self.element_type)
> +        visitor.visit_array_type(self.name, self.info, self.element_type,
> +                                 self.ifcond)
>  
>  
>  class QAPISchemaObjectType(QAPISchemaType):
> @@ -1247,9 +1266,10 @@ class QAPISchemaObjectType(QAPISchemaType):
>  
>      def visit(self, visitor):
>          visitor.visit_object_type(self.name, self.info,
> -                                  self.base, self.local_members, self.variants)
> +                                  self.base, self.local_members, self.variants,
> +                                  self.ifcond)
>          visitor.visit_object_type_flat(self.name, self.info,
> -                                       self.members, self.variants)
> +                                       self.members, self.variants, self.ifcond)

You break the line before self.ifcond almost everywhere, and when you
don't, the line gets long.  This one goes over the limit:

    scripts/qapi.py:1285:80: E501 line too long (80 > 79 characters)

Suggest to break it before self.ifcond everywhere.

>  
>  
>  class QAPISchemaMember(object):
> @@ -1392,7 +1412,8 @@ class QAPISchemaAlternateType(QAPISchemaType):
>          return 'value'
>  
>      def visit(self, visitor):
> -        visitor.visit_alternate_type(self.name, self.info, self.variants)
> +        visitor.visit_alternate_type(self.name, self.info,
> +                                     self.variants, self.ifcond)
>  
>      def is_empty(self):
>          return False
> @@ -1434,7 +1455,8 @@ class QAPISchemaCommand(QAPISchemaEntity):
>      def visit(self, visitor):
>          visitor.visit_command(self.name, self.info,
>                                self.arg_type, self.ret_type,
> -                              self.gen, self.success_response, self.boxed)
> +                              self.gen, self.success_response, self.boxed,
> +                              self.ifcond)
>  
>  
>  class QAPISchemaEvent(QAPISchemaEntity):
> @@ -1462,7 +1484,8 @@ class QAPISchemaEvent(QAPISchemaEntity):
>              raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
>  
>      def visit(self, visitor):
> -        visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
> +        visitor.visit_event(self.name, self.info, self.arg_type, self.boxed,
> +                            self.ifcond)
>  
>  
>  class QAPISchema(object):
> @@ -1481,11 +1504,12 @@ class QAPISchema(object):
>              print >>sys.stderr, err
>              exit(1)
>  
> -    def _def_entity(self, ent):
> +    def _def_entity(self, ent, ifcond=None):
>          # Only the predefined types are allowed to not have info
>          assert ent.info or self._predefining
>          assert ent.name not in self._entity_dict
>          self._entity_dict[ent.name] = ent
> +        ent.set_ifcond(ifcond)

.set_ifcond(None) does the right thing.

However:

>  
>      def lookup_entity(self, name, typ=None):
>          ent = self._entity_dict.get(name)
> @@ -1534,11 +1558,11 @@ class QAPISchema(object):
>      def _make_enum_members(self, values):
>          return [QAPISchemaMember(v) for v in values]
>  
> -    def _make_implicit_enum_type(self, name, info, values):
> +    def _make_implicit_enum_type(self, name, info, values, ifcond):
>          # See also QAPISchemaObjectTypeMember._pretty_owner()
>          name = name + 'Kind'   # Use namespace reserved by add_name()
>          self._def_entity(QAPISchemaEnumType(
> -            name, info, None, self._make_enum_members(values), None))
> +            name, info, None, self._make_enum_members(values), None), ifcond)
>          return name

Why is ifcond not a constructor argument like name, info, and so forth?
What makes it special?

>  
>      def _make_array_type(self, element_type, info):
> @@ -1547,22 +1571,26 @@ class QAPISchema(object):
>              self._def_entity(QAPISchemaArrayType(name, info, element_type))
>          return name
>  
> -    def _make_implicit_object_type(self, name, info, doc, role, members):
> +    def _make_implicit_object_type(self, name, info, doc, role, members,
> +                                   ifcond=None):
>          if not members:
>              return None
>          # See also QAPISchemaObjectTypeMember._pretty_owner()
>          name = 'q_obj_%s-%s' % (name, role)
> -        if not self.lookup_entity(name, QAPISchemaObjectType):
> +        if self.lookup_entity(name, QAPISchemaObjectType):
> +            assert ifcond is None
> +        else:
>              self._def_entity(QAPISchemaObjectType(name, info, doc, None,
> -                                                  members, None))
> +                                                  members, None), ifcond)
>          return name

Hmm, this smells like it might be the "variants objects types" mentioned
in the commit message.

Types made with _make_implicit_object_type():

* The wrapper around "simple" union members, by _make_simple_variant()

* A flat union's base type when it's implicit, by _def_union_type()

* A command's argument type when it's implicit, by _def_command()

* An event's argument type when it's implicit, by _def_event()

Only the first one can be used more than once, namely when a type occurs
in more than one simple union.  The "correct" ifcond is the disjunction
of all its user's ifconds.  You make it use the *first* ifcond instead.
Wrong: breaks when one of the other simple unions has a condition that
is true when the first one's is false.

Your commit message suggests you intended to make it unconditional
instead.  That would work: the worst that can happen is compiling a few
q_obj_FOO_wrapper typedefs and visit_type_q_FOO_wrapper() functions that
aren't actually used.  Tolerable, in particular since I hope to get rid
of "simple" unions some day.

Sadly, it would prevent us from making the visit functions for implicit
types static, because unused static functions trigger warnings.  Let's
not worry about that now.

Generating the disjunction of all conditions wouldn't be terribly hard.
I'm not asking for it, though.

You assert that implicit types are unconditional from the second use on.
I guess you mean to assert the ones used more than once are
unconditional:

           typ = self.lookup_entity(name, QAPISchemaObjectType)
           if typ:
               assert ifcond is None and typ.ifcond is None

But what you *should* assert is that the conditions are the same:

           typ = self.lookup_entity(name, QAPISchemaObjectType)
           if typ:
               assert ifcond == typ.ifcond

>  
>      def _def_enum_type(self, expr, info, doc):
>          name = expr['enum']
>          data = expr['data']
>          prefix = expr.get('prefix')
> -        self._def_entity(QAPISchemaEnumType(
> -            name, info, doc, self._make_enum_members(data), prefix))
> +        return self._def_entity(QAPISchemaEnumType(
> +            name, info, doc, self._make_enum_members(data), prefix),
> +                                expr.get('if'))

Covers enumeration types.

Why return?  The (only) caller throws the value away...

>  
>      def _make_member(self, name, typ, info):
>          optional = False
> @@ -1584,7 +1612,8 @@ class QAPISchema(object):
>          data = expr['data']
>          self._def_entity(QAPISchemaObjectType(name, info, doc, base,
>                                                self._make_members(data, info),
> -                                              None))
> +                                              None),
> +                         expr.get('if'))

Covers struct types.

>  
>      def _make_variant(self, case, typ):
>          return QAPISchemaObjectTypeVariant(case, typ)
> @@ -1593,8 +1622,10 @@ class QAPISchema(object):
>          if isinstance(typ, list):
>              assert len(typ) == 1
>              typ = self._make_array_type(typ[0], info)
> +        type_entity = self.lookup_type(typ)
>          typ = self._make_implicit_object_type(
> -            typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
> +            typ, info, None, 'wrapper',
> +            [self._make_member('data', typ, info)], type_entity.ifcond)
>          return QAPISchemaObjectTypeVariant(case, typ)

A simple union member's wrapper type inherits its condition from the
member type.

I think you need to pass None instead of type_entity.ifcond here.

>  
>      def _def_union_type(self, expr, info, doc):
> @@ -1604,8 +1635,9 @@ class QAPISchema(object):
>          tag_name = expr.get('discriminator')
>          tag_member = None
>          if isinstance(base, dict):
> -            base = (self._make_implicit_object_type(
> -                name, info, doc, 'base', self._make_members(base, info)))
> +            base = self._make_implicit_object_type(
> +                name, info, doc, 'base', self._make_members(base, info),
> +                expr.get('if'))

A flat union's implicit base type inherits its condition from the flat
union.  Good.

>          if tag_name:
>              variants = [self._make_variant(key, value)
>                          for (key, value) in data.iteritems()]
> @@ -1614,14 +1646,16 @@ class QAPISchema(object):
>              variants = [self._make_simple_variant(key, value, info)
>                          for (key, value) in data.iteritems()]
>              typ = self._make_implicit_enum_type(name, info,
> -                                                [v.name for v in variants])
> +                                                [v.name for v in variants],
> +                                                expr.get('if'))

A flat union's implicit enumeration type inherits its condition from the
flat union.  Good.

>              tag_member = QAPISchemaObjectTypeMember('type', typ, False)
>              members = [tag_member]
>          self._def_entity(
>              QAPISchemaObjectType(name, info, doc, base, members,
>                                   QAPISchemaObjectTypeVariants(tag_name,
>                                                                tag_member,
> -                                                              variants)))
> +                                                              variants)),
> +            expr.get('if'))

Covers union types.

Third use of expr.get('if') in this function.  Please put it in a
variable.

Actually, do that for *all* uses of expr[X] and expr.get(X) in class
QAPISchema, because that's how the existing code works.

>  
>      def _def_alternate_type(self, expr, info, doc):
>          name = expr['alternate']
> @@ -1633,7 +1667,8 @@ class QAPISchema(object):
>              QAPISchemaAlternateType(name, info, doc,
>                                      QAPISchemaObjectTypeVariants(None,
>                                                                   tag_member,
> -                                                                 variants)))
> +                                                                 variants)),
> +            expr.get('if'))

Covers alternate types.

>  
>      def _def_command(self, expr, info, doc):
>          name = expr['command']
> @@ -1644,12 +1679,14 @@ class QAPISchema(object):
>          boxed = expr.get('boxed', False)
>          if isinstance(data, OrderedDict):
>              data = self._make_implicit_object_type(
> -                name, info, doc, 'arg', self._make_members(data, info))
> +                name, info, doc, 'arg', self._make_members(data, info),
> +                expr.get('if'))

A command's implicit argument type inherits its condition from the
command.  Good.

>          if isinstance(rets, list):
>              assert len(rets) == 1
>              rets = self._make_array_type(rets[0], info)
>          self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
> -                                           gen, success_response, boxed))
> +                                           gen, success_response, boxed),
> +                         expr.get('if'))

Covers commands.

>  
>      def _def_event(self, expr, info, doc):
>          name = expr['event']
> @@ -1657,8 +1694,10 @@ class QAPISchema(object):
>          boxed = expr.get('boxed', False)
>          if isinstance(data, OrderedDict):
>              data = self._make_implicit_object_type(
> -                name, info, doc, 'arg', self._make_members(data, info))
> -        self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
> +                name, info, doc, 'arg', self._make_members(data, info),
> +                expr.get('if'))

An event's implicit argument type inherits its condition from the
command.  Good.

> +        self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed),
> +                         expr.get('if'))

Covers events.  You got them all.  Good.

>  
>      def _def_exprs(self):
>          for expr_elem in self.exprs:
> @@ -1848,6 +1887,54 @@ def guardend(name):
>                   name=guardname(name))
>  
>  
> +def gen_if(ifcond, func=''):
> +    if not ifcond:
> +        return ''
> +    if isinstance(ifcond, str):
> +        ifcond = [ifcond]
> +    ret = '\n'
> +    for ifc in ifcond:
> +        ret += mcgen('#if %(ifcond)s /* %(func)s */\n', ifcond=ifc, func=func)
> +    ret += '\n'
> +    return ret

Please use mcgen() like the existing code:

           ret += mcgen('''
   #if %(ifcond)s /* %(func)s */
   ''', ifcond=ifc, func=func)

With the default value of @func, we get a useless, ugly comment /* */.
If this can happen, please suppress the comment.  Else, drop @func's
default value.

Lists appear to be conjunctions.  What for?

> +
> +
> +def gen_endif(ifcond, func=''):
> +    if not ifcond:
> +        return ''
> +    if isinstance(ifcond, str):
> +        ifcond = [ifcond]
> +    ret = '\n'
> +    for ifc in reversed(ifcond):
> +        ret += mcgen('#endif /* %(ifcond)s %(func)s */\n',
> +                     ifcond=ifc, func=func)
> +    ret += '\n'
> +    return ret

Likewise.

> +
> +
> +# wrap a method to add #if / #endif to generated code
> +# self must have 'if_members' listing the attributes to wrap
> +# the last argument of the wrapped function must be the 'ifcond'

Start your sentences with a capital letter, and end them with a period,
please.

> +def if_wrap(func):

Blank line, please.

> +    def func_wrapper(self, *args, **kwargs):
> +        funcname = self.__class__.__name__ + '.' + func.__name__
> +        ifcond = args[-1]
> +        save = {}
> +        for mem in self.if_members:
> +            save[mem] = getattr(self, mem)
> +        func(self, *args, **kwargs)
> +        for mem, val in save.items():
> +            newval = getattr(self, mem)
> +            if newval != val:
> +                assert newval.startswith(val)
> +                newval = newval[len(val):]
> +                val += gen_if(ifcond, funcname)

Emitting comments pointing to the QAPI schema into the generated code is
often helpful.  But this one points to QAPI generator code.  Why is that
useful?

> +                val += newval
> +                val += gen_endif(ifcond, funcname)
> +            setattr(self, mem, val)
> +
> +    return func_wrapper
> +

pep8 again:

    scripts/qapi.py:1955:1: E302 expected 2 blank lines, found 1

Peeking ahead: this function is used as a decorator.  Let's help the
reader and mention that in the function comment, or by naming the
function suitably.  ifcond_decorator?

Messing with the wrapped method's class's attributes is naughty.  Worse,
it's hard to understand.  What alternatives have you considered?

>  def gen_enum_lookup(name, values, prefix=None):
>      ret = mcgen('''
>  
> diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
> index 974d0a4a80..19b1bb9b88 100644
> --- a/scripts/qapi-commands.py
> +++ b/scripts/qapi-commands.py
> @@ -228,6 +228,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
>          self.defn = None
>          self._regy = None
>          self._visited_ret_types = None
> +        self.if_members = ['decl', 'defn', '_regy']
>  
>      def visit_begin(self, schema):
>          self.decl = ''
> @@ -240,8 +241,9 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
>          self._regy = None
>          self._visited_ret_types = None
>  
> +    @if_wrap
>      def visit_command(self, name, info, arg_type, ret_type,
> -                      gen, success_response, boxed):
> +                      gen, success_response, boxed, ifcond):
>          if not gen:
>              return
>          self.decl += gen_command_decl(name, arg_type, boxed, ret_type)
> diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
> index bcbef1035f..cad9ece790 100644
> --- a/scripts/qapi-event.py
> +++ b/scripts/qapi-event.py
> @@ -152,6 +152,7 @@ class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
>          self.decl = None
>          self.defn = None
>          self._event_names = None
> +        self.if_members = ['decl', 'defn']
>  
>      def visit_begin(self, schema):
>          self.decl = ''
> @@ -163,7 +164,8 @@ class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
>          self.defn += gen_enum_lookup(event_enum_name, self._event_names)
>          self._event_names = None
>  
> -    def visit_event(self, name, info, arg_type, boxed):
> +    @if_wrap
> +    def visit_event(self, name, info, arg_type, boxed, ifcond):
>          self.decl += gen_event_send_decl(name, arg_type, boxed)
>          self.defn += gen_event_send(name, arg_type, boxed)
>          self._event_names.append(name)
> diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
> index fc72cdb66d..ecfb0f2752 100644
> --- a/scripts/qapi-introspect.py
> +++ b/scripts/qapi-introspect.py
> @@ -12,7 +12,7 @@
>  from qapi import *
>  
>  
> -def to_qlit(obj, level=0, first_indent=True):
> +def to_qlit(obj, level=0, first_indent=True, suffix=''):
>      def indent(level):
>          return level * 4 * ' '
>      ret = ''
> @@ -20,14 +20,20 @@ def to_qlit(obj, level=0, first_indent=True):
>          ret += indent(level)
>      if obj is None:
>          ret += 'QLIT_QNULL'
> +    elif isinstance(obj, tuple):
> +        obj, ifcond =  obj
> +        ret += gen_if(ifcond)
> +        ret += to_qlit(obj, level, False) + suffix

Keyword argument instead of bare False, please.

> +        ret += gen_endif(ifcond)
> +        suffix = ''

New case tuple, for generating conditionals.  Okay.

>      elif isinstance(obj, str):
>          ret += 'QLIT_QSTR(' + '"' + obj.replace('"', r'\"') + '"' + ')'
>      elif isinstance(obj, list):
> -        elts = [to_qlit(elt, level + 1)
> +        elts = [to_qlit(elt, level + 1, True, ",")

Make that

           elts = [to_qlit(elt, level + 1, suffix=",")

>                  for elt in obj]
>          elts.append(indent(level + 1) + "{ }")
>          ret += 'QLIT_QLIST(((QLitObject[]) {\n'
> -        ret += ',\n'.join(elts) + '\n'
> +        ret += '\n'.join(elts) + '\n'
>          ret += indent(level) + '}))'
>      elif isinstance(obj, dict):
>          elts = [ indent(level + 1) + '{ "%s", %s }' %
> @@ -39,7 +45,7 @@ def to_qlit(obj, level=0, first_indent=True):
>          ret += indent(level) + '}))'
>      else:
>          assert False                # not implemented
> -    return ret
> +    return ret + suffix

This is getting really hard to review --- my brain is about to overflow
and shut down for the day.  Can you split off some preparatory work?
The introduction of suffix here, perhaps?

>  
>  
>  class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor):
> @@ -113,12 +119,12 @@ const QLitObject %(c_name)s = %(c_string)s;
>              return '[' + self._use_type(typ.element_type) + ']'
>          return self._name(typ.name)
>  
> -    def _gen_qlit(self, name, mtype, obj):
> +    def _gen_qlit(self, name, mtype, obj, ifcond):
>          if mtype not in ('command', 'event', 'builtin', 'array'):
>              name = self._name(name)
>          obj['name'] = name
>          obj['meta-type'] = mtype
> -        self._qlits.append(obj)
> +        self._qlits.append((obj, ifcond))
>  
>      def _gen_member(self, member):
>          ret = {'name': member.name, 'type': self._use_type(member.type)}
> @@ -134,38 +140,40 @@ const QLitObject %(c_name)s = %(c_string)s;
>          return {'case': variant.name, 'type': self._use_type(variant.type)}
>  
>      def visit_builtin_type(self, name, info, json_type):
> -        self._gen_qlit(name, 'builtin', {'json-type': json_type})
> +        self._gen_qlit(name, 'builtin', {'json-type': json_type}, None)
>  
> -    def visit_enum_type(self, name, info, values, prefix):
> -        self._gen_qlit(name, 'enum', {'values': values})
> +    def visit_enum_type(self, name, info, values, prefix, ifcond):
> +        self._gen_qlit(name, 'enum', {'values': values}, ifcond)
>  
> -    def visit_array_type(self, name, info, element_type):
> +    def visit_array_type(self, name, info, element_type, ifcond):
>          element = self._use_type(element_type)
> -        self._gen_qlit('[' + element + ']', 'array', {'element-type': element})
> +        self._gen_qlit('[' + element + ']', 'array', {'element-type': element},
> +                       ifcond)
>  
> -    def visit_object_type_flat(self, name, info, members, variants):
> +    def visit_object_type_flat(self, name, info, members, variants, ifcond):
>          obj = {'members': [self._gen_member(m) for m in members]}
>          if variants:
>              obj.update(self._gen_variants(variants.tag_member.name,
>                                            variants.variants))
> -        self._gen_qlit(name, 'object', obj)
> +        self._gen_qlit(name, 'object', obj, ifcond)
>  
> -    def visit_alternate_type(self, name, info, variants):
> +    def visit_alternate_type(self, name, info, variants, ifcond):
>          self._gen_qlit(name, 'alternate',
>                         {'members': [{'type': self._use_type(m.type)}
> -                                    for m in variants.variants]})
> +                                    for m in variants.variants]}, ifcond)
>  
>      def visit_command(self, name, info, arg_type, ret_type,
> -                      gen, success_response, boxed):
> +                      gen, success_response, boxed, ifcond):
>          arg_type = arg_type or self._schema.the_empty_object_type
>          ret_type = ret_type or self._schema.the_empty_object_type
>          self._gen_qlit(name, 'command',
>                         {'arg-type': self._use_type(arg_type),
> -                        'ret-type': self._use_type(ret_type)})
> +                        'ret-type': self._use_type(ret_type)}, ifcond)
>  
> -    def visit_event(self, name, info, arg_type, boxed):
> +    def visit_event(self, name, info, arg_type, boxed, ifcond):
>          arg_type = arg_type or self._schema.the_empty_object_type
> -        self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)})
> +        self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)},
> +                       ifcond)
>  
>  # Debugging aid: unmask QAPI schema's type names
>  # We normally mask them, because they're not QMP wire ABI

Out of review brainpower for today.  Hope to resume tomorrow.

[...]

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 07/26] qapi: add 'if' condition on top-level schema elements
  2017-08-16 15:43   ` Markus Armbruster
@ 2017-08-17  5:50     ` Markus Armbruster
  2017-08-22 11:17     ` Marc-André Lureau
  1 sibling, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2017-08-17  5:50 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

Markus Armbruster <armbru@redhat.com> writes:

[...]
> Out of review brainpower for today.  Hope to resume tomorrow.
>
> [...]

Nope, I'm giving up on this one.  Please split it for reviewability.
Suggested split:

1. Preparatory refactoring for step 2, step by step

2. Frontend part: accept and check 'if', step by step

   The accepted conditions should be visible in
   tests/qapi-schema/qapi-schema-test.out.

3. Preparatory refactoring for step 4, step by step

4. Backend part: generate the ifdeffery, step by step

   You already split off qapi2texi steps [PATCH 12-15].  Good.  Perhaps
   that's all that can be split off, perhaps not.

PATCH 08-11 may well profit from the same treatment.

Moving on to PATCH 16.

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 16/26] qapi: add conditions to VNC type/commands/events on the schema
  2017-07-28 19:00   ` Dr. David Alan Gilbert
@ 2017-08-17  6:32     ` Markus Armbruster
  2017-08-17  9:33       ` Dr. David Alan Gilbert
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2017-08-17  6:32 UTC (permalink / raw)
  To: Dr. David Alan Gilbert; +Cc: Marc-André Lureau, qemu-devel

"Dr. David Alan Gilbert" <dgilbert@redhat.com> writes:

> * Marc-André Lureau (marcandre.lureau@redhat.com) wrote:
>> Add #if defined(CONFIG_VNC) in generated code, and adjust the
>> qmp/hmp code accordingly.
>> 
>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
>> diff --git a/hmp.c b/hmp.c
>> index fd80dce758..9454c634bd 100644
>> --- a/hmp.c
>> +++ b/hmp.c
>> @@ -605,6 +605,7 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict)
>>      qapi_free_BlockStatsList(stats_list);
>>  }
>>  
>> +#ifdef CONFIG_VNC
>>  /* Helper for hmp_info_vnc_clients, _servers */
>>  static void hmp_info_VncBasicInfo(Monitor *mon, VncBasicInfo *info,
>>                                    const char *name)
>> @@ -692,6 +693,12 @@ void hmp_info_vnc(Monitor *mon, const QDict *qdict)
>>      qapi_free_VncInfo2List(info2l);
>>  
>>  }
>> +#else
>> +void hmp_info_vnc(Monitor *mon, const QDict *qdict)
>> +{
>> +    warn_report("VNC support is disabled");

error_report(), please (see below).

>> +}
>> +#endif
>
> I'm OK with this, so
>
> Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
>
> although you might just be able to add a #ifdef in hmp-commands-info.hx
> and avoid the is disabled function, or you might find that with the QMP
> returning an error the HMP just passes that error on.

Let's compare failures when !CONFIG_VNC:

(a) Marc-André's patch as is:

        (qemu) info vnc
        warning: VNC support is disabled

    Drop the "warning: " (because it ain't; the command failed), and this
    is fine.

(b) Compiling them out completely (#ifdef in hmp-commands*.hx):

        unknown command: 'vnc'

    HMP bug; should be something like

        Unknown command: 'info vnc'

    but that's not this series' problem.

    Good enough for me.

(c) Forwarding the QMP error verbatim

        The command query-vnc has not been found

    No good.

(d) Handling CommandNotFound

    More work than (a) for the same result.

As far as I'm concerned, feel free to do (a) or (b).

[...]

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 16/26] qapi: add conditions to VNC type/commands/events on the schema
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 16/26] qapi: add conditions to VNC type/commands/events on the schema Marc-André Lureau
  2017-07-28 19:00   ` Dr. David Alan Gilbert
@ 2017-08-17  7:04   ` Markus Armbruster
  2017-08-17  8:56     ` Markus Armbruster
                       ` (2 more replies)
  1 sibling, 3 replies; 69+ messages in thread
From: Markus Armbruster @ 2017-08-17  7:04 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Dr. David Alan Gilbert, Gerd Hoffmann, Daniel P. Berrange

Copying our resident VNC maintainer^Wodd fixer Gerd.

Also copying Dan for QCryptoCipherAlgorithm.

Gerd, Dan, this patch is about making VNC support visible in
query-qmp-schema, by having the QAPI generators generate suitable
ifdeffery.  Bonus: no need for QMP command stubs for
!defined(CONFIG_VNC).

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Add #if defined(CONFIG_VNC) in generated code, and adjust the
> qmp/hmp code accordingly.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  qapi-schema.json | 34 ++++++++++++++++++++++------------
>  qapi/event.json  |  9 ++++++---
>  hmp.c            | 14 +++++++++++++-
>  qmp.c            | 30 ++++--------------------------
>  4 files changed, 45 insertions(+), 42 deletions(-)
>
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 9c6c3e1a53..829c66f9eb 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -1660,7 +1660,8 @@
>    'data': { 'host': 'str',
>              'service': 'str',
>              'family': 'NetworkAddressFamily',
> -            'websocket': 'bool' } }
> +            'websocket': 'bool' },
> +  'if': 'defined(CONFIG_VNC)' }
>  
>  ##
>  # @VncServerInfo:
> @@ -1674,7 +1675,8 @@
>  ##
>  { 'struct': 'VncServerInfo',
>    'base': 'VncBasicInfo',
> -  'data': { '*auth': 'str' } }
> +  'data': { '*auth': 'str' },
> +  'if': 'defined(CONFIG_VNC)' }
>  
>  ##
>  # @VncClientInfo:
> @@ -1691,7 +1693,8 @@
>  ##
>  { 'struct': 'VncClientInfo',
>    'base': 'VncBasicInfo',
> -  'data': { '*x509_dname': 'str', '*sasl_username': 'str' } }
> +  'data': { '*x509_dname': 'str', '*sasl_username': 'str' },
> +  'if': 'defined(CONFIG_VNC)' }
>  
>  ##
>  # @VncInfo:
> @@ -1732,7 +1735,8 @@
>  { 'struct': 'VncInfo',
>    'data': {'enabled': 'bool', '*host': 'str',
>             '*family': 'NetworkAddressFamily',
> -           '*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']} }
> +           '*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']},
> +  'if': 'defined(CONFIG_VNC)' }
>  
>  ##
>  # @VncPrimaryAuth:
> @@ -1743,7 +1747,8 @@
>  ##
>  { 'enum': 'VncPrimaryAuth',
>    'data': [ 'none', 'vnc', 'ra2', 'ra2ne', 'tight', 'ultra',
> -            'tls', 'vencrypt', 'sasl' ] }
> +            'tls', 'vencrypt', 'sasl' ],
> +  'if': 'defined(CONFIG_VNC)' }
>  
>  ##
>  # @VncVencryptSubAuth:
> @@ -1757,7 +1762,8 @@
>              'tls-none',  'x509-none',
>              'tls-vnc',   'x509-vnc',
>              'tls-plain', 'x509-plain',
> -            'tls-sasl',  'x509-sasl' ] }
> +            'tls-sasl',  'x509-sasl' ],
> +  'if': 'defined(CONFIG_VNC)' }
>  
>  
>  ##
> @@ -1775,7 +1781,8 @@
>  { 'struct': 'VncServerInfo2',
>    'base': 'VncBasicInfo',
>    'data': { 'auth'      : 'VncPrimaryAuth',
> -            '*vencrypt' : 'VncVencryptSubAuth' } }
> +            '*vencrypt' : 'VncVencryptSubAuth' },
> +  'if': 'defined(CONFIG_VNC)' }
>  
>  
>  ##
> @@ -1808,7 +1815,8 @@
>              'clients'   : ['VncClientInfo'],
>              'auth'      : 'VncPrimaryAuth',
>              '*vencrypt' : 'VncVencryptSubAuth',
> -            '*display'  : 'str' } }
> +            '*display'  : 'str' },
> +  'if': 'defined(CONFIG_VNC)' }
>  
>  ##
>  # @query-vnc:
> @@ -1839,7 +1847,8 @@
>  #    }
>  #
>  ##
> -{ 'command': 'query-vnc', 'returns': 'VncInfo' }
> +{ 'command': 'query-vnc', 'returns': 'VncInfo',
> +  'if': 'defined(CONFIG_VNC)' }
>  
>  ##
>  # @query-vnc-servers:
> @@ -1850,7 +1859,8 @@
>  #
>  # Since: 2.3
>  ##
> -{ 'command': 'query-vnc-servers', 'returns': ['VncInfo2'] }
> +{ 'command': 'query-vnc-servers', 'returns': ['VncInfo2'],
> +  'if': 'defined(CONFIG_VNC)' }
>  
>  ##
>  # @SpiceBasicInfo:
> @@ -3077,8 +3087,8 @@
>  # Notes:  An empty password in this command will set the password to the empty
>  #         string.  Existing clients are unaffected by executing this command.
>  ##
> -{ 'command': 'change-vnc-password', 'data': {'password': 'str'} }
> -
> +{ 'command': 'change-vnc-password', 'data': {'password': 'str'},
> +  'if': 'defined(CONFIG_VNC)' }
>  ##
>  # @change:
>  #
> diff --git a/qapi/event.json b/qapi/event.json
> index 6d22b025cc..c8b8e9f384 100644
> --- a/qapi/event.json
> +++ b/qapi/event.json
> @@ -263,7 +263,8 @@
>  ##
>  { 'event': 'VNC_CONNECTED',
>    'data': { 'server': 'VncServerInfo',
> -            'client': 'VncBasicInfo' } }
> +            'client': 'VncBasicInfo' },
> +  'if': 'defined(CONFIG_VNC)' }
>  
>  ##
>  # @VNC_INITIALIZED:
> @@ -290,7 +291,8 @@
>  ##
>  { 'event': 'VNC_INITIALIZED',
>    'data': { 'server': 'VncServerInfo',
> -            'client': 'VncClientInfo' } }
> +            'client': 'VncClientInfo' },
> +  'if': 'defined(CONFIG_VNC)' }
>  
>  ##
>  # @VNC_DISCONNECTED:
> @@ -316,7 +318,8 @@
>  ##
>  { 'event': 'VNC_DISCONNECTED',
>    'data': { 'server': 'VncServerInfo',
> -            'client': 'VncClientInfo' } }
> +            'client': 'VncClientInfo' },
> +  'if': 'defined(CONFIG_VNC)' }
>  
>  ##
>  # @SPICE_CONNECTED:
> diff --git a/hmp.c b/hmp.c
> index fd80dce758..9454c634bd 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -605,6 +605,7 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict)
>      qapi_free_BlockStatsList(stats_list);
>  }
>  
> +#ifdef CONFIG_VNC
>  /* Helper for hmp_info_vnc_clients, _servers */
>  static void hmp_info_VncBasicInfo(Monitor *mon, VncBasicInfo *info,
>                                    const char *name)
> @@ -692,6 +693,12 @@ void hmp_info_vnc(Monitor *mon, const QDict *qdict)
>      qapi_free_VncInfo2List(info2l);
>  
>  }
> +#else
> +void hmp_info_vnc(Monitor *mon, const QDict *qdict)
> +{
> +    warn_report("VNC support is disabled");
> +}
> +#endif
>  
>  #ifdef CONFIG_SPICE
>  void hmp_info_spice(Monitor *mon, const QDict *qdict)
> @@ -1708,12 +1715,14 @@ void hmp_eject(Monitor *mon, const QDict *qdict)
>      hmp_handle_error(mon, &err);
>  }
>  
> +#ifdef CONFIG_VNC
>  static void hmp_change_read_arg(void *opaque, const char *password,
>                                  void *readline_opaque)
>  {
>      qmp_change_vnc_password(password, NULL);
>      monitor_read_command(opaque, 1);
>  }
> +#endif
>  
>  void hmp_change(Monitor *mon, const QDict *qdict)
>  {
> @@ -1724,6 +1733,7 @@ void hmp_change(Monitor *mon, const QDict *qdict)
>      BlockdevChangeReadOnlyMode read_only_mode = 0;
>      Error *err = NULL;
>  
> +#ifdef CONFIG_VNC
>      if (strcmp(device, "vnc") == 0) {
>          if (read_only) {
>              monitor_printf(mon,
> @@ -1738,7 +1748,9 @@ void hmp_change(Monitor *mon, const QDict *qdict)
>              }
>          }
>          qmp_change("vnc", target, !!arg, arg, &err);
> -    } else {
> +    } else
> +#endif
> +    {
>          if (read_only) {
>              read_only_mode =
>                  qapi_enum_parse(BlockdevChangeReadOnlyMode_lookup,

On HMP, see my reply to Dave's review.

> diff --git a/qmp.c b/qmp.c
> index b86201e349..2c90dacb56 100644
> --- a/qmp.c
> +++ b/qmp.c
> @@ -130,22 +130,6 @@ void qmp_cpu_add(int64_t id, Error **errp)
>      }
>  }
>  
> -#ifndef CONFIG_VNC
> -/* If VNC support is enabled, the "true" query-vnc command is
> -   defined in the VNC subsystem */
> -VncInfo *qmp_query_vnc(Error **errp)
> -{
> -    error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
> -    return NULL;
> -};
> -
> -VncInfo2List *qmp_query_vnc_servers(Error **errp)
> -{
> -    error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
> -    return NULL;
> -};
> -#endif
> -
>  #ifndef CONFIG_SPICE
>  /*
>   * qmp-commands.hx ensures that QMP command query-spice exists only
> @@ -403,23 +387,17 @@ static void qmp_change_vnc(const char *target, bool has_arg, const char *arg,
>          qmp_change_vnc_listen(target, errp);
>      }
>  }
> -#else
> -void qmp_change_vnc_password(const char *password, Error **errp)
> -{
> -    error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
> -}
> -static void qmp_change_vnc(const char *target, bool has_arg, const char *arg,
> -                           Error **errp)
> -{
> -    error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
> -}
>  #endif /* !CONFIG_VNC */
>  
>  void qmp_change(const char *device, const char *target,
>                  bool has_arg, const char *arg, Error **errp)
>  {
>      if (strcmp(device, "vnc") == 0) {
> +#ifdef CONFIG_VNC
>          qmp_change_vnc(target, has_arg, arg, errp);
> +#else
> +        error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
> +#endif
>      } else {
>          qmp_blockdev_change_medium(true, device, false, NULL, target,
>                                     has_arg, arg, false, 0, errp);

Commands you make conditional:

* query-vnc, query-vnc-servers, change-vnc-password

  Before the patch, the commands for !CONFIG_VNC are stubs that fail
  like this:

    {"error": {"class": "GenericError",
               "desc": "The feature 'vnc' is not enabled"}}

  Afterwards, they fail like this:

    {"error": {"class": "CommandNotFound",
               "desc": "The command FOO has not been found"}}

  I call that an improvement, because it lets clients distinguish
  between command unavailable (class CommandNotFound) and command failed
  (class GenericError).

Events you make conditional:

* VNC_CONNECTED, VNC_INITIALIZED, VNC_DISCONNECTED

Now let me check for completeness.  Occurrences of VNC (case
insensitive) in the schema that aren't covered by your changes:

* add_client

  Command has other uses, including "socket bases character devices".
  These are unconditional as far as I can tell.  Good.

* set_password, expire_password

  In theory, these commands could be used for managing any service's
  password.  In practice, they're used for VNC and SPICE services.
  They're documented for "remote display session" / "remote display
  server".

  The service is selected by argument @protocol.  The code special-cases
  protocol-specific argument checking, then calls a protocol-specific
  function to do the work.  If it fails, the command fails with "Could
  not set password".  It does when the service isn't compiled in (it's a
  stub then).

  We could make these commands conditional on the conjunction of all
  services [currently: defined(CONFIG_VNC) || defined(CONFIG_SPICE)],
  but I doubt it's worthwhile.

  Okay.

* change

  Command has other uses, namely changing media.

  Your patch inlines a stub; no functional change.  Good.

* QCryptoCipherAlgorithm

  This:

    # @des-rfb: RFB specific variant of single DES. Do not use except in VNC.

  I guess we could compile this out if we wanted to.  I doubt we do, but
  Dan might have other ideas.

Some of this analysis should perhaps be worked into the commit message.

Overall, the schema syntax works nicely for me.  A bit on the verbose
side perhaps, but I like that the conditions are locally obvious.

Observation: we got >250 lines of VNC stuff in qapi-schema.json.  Moving
them into qapi/vnc.json would permit proper MAINTAINERS coverage.  Gerd,
what do you think?

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 17/26] qapi: add conditions to SPICE type/commands/events on the schema
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 17/26] qapi: add conditions to SPICE " Marc-André Lureau
@ 2017-08-17  8:10   ` Markus Armbruster
  2017-08-17  8:43     ` Markus Armbruster
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2017-08-17  8:10 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Dr. David Alan Gilbert, Gerd Hoffmann

Copying our resident SPICE maintainer Gerd.

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Add #if defined(CONFIG_SPICE) in generated code, and adjust the
> qmp/hmp code accordingly.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  qapi-schema.json | 28 ++++++++++++++++++----------
>  qapi/event.json  | 12 ++++++++----
>  monitor.c        |  3 ---
>  qmp.c            | 16 ----------------
>  4 files changed, 26 insertions(+), 33 deletions(-)
>
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 829c66f9eb..bcee3157b0 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -1878,7 +1878,8 @@
>  { 'struct': 'SpiceBasicInfo',
>    'data': { 'host': 'str',
>              'port': 'str',
> -            'family': 'NetworkAddressFamily' } }
> +            'family': 'NetworkAddressFamily' },
> +  'if': 'defined(CONFIG_SPICE)' }
>  
>  ##
>  # @SpiceServerInfo:
> @@ -1891,7 +1892,8 @@
>  ##
>  { 'struct': 'SpiceServerInfo',
>    'base': 'SpiceBasicInfo',
> -  'data': { '*auth': 'str' } }
> +  'data': { '*auth': 'str' },
> +  'if': 'defined(CONFIG_SPICE)' }
>  
>  ##
>  # @SpiceChannel:
> @@ -1916,7 +1918,8 @@
>  { 'struct': 'SpiceChannel',
>    'base': 'SpiceBasicInfo',
>    'data': {'connection-id': 'int', 'channel-type': 'int', 'channel-id': 'int',
> -           'tls': 'bool'} }
> +           'tls': 'bool'},
> +  'if': 'defined(CONFIG_SPICE)' }
>  
>  ##
>  # @SpiceQueryMouseMode:
> @@ -1935,7 +1938,8 @@
>  # Since: 1.1
>  ##
>  { 'enum': 'SpiceQueryMouseMode',
> -  'data': [ 'client', 'server', 'unknown' ] }
> +  'data': [ 'client', 'server', 'unknown' ],
> +  'if': 'defined(CONFIG_SPICE)' }
>  
>  ##
>  # @SpiceInfo:
> @@ -1972,7 +1976,8 @@
>  { 'struct': 'SpiceInfo',
>    'data': {'enabled': 'bool', 'migrated': 'bool', '*host': 'str', '*port': 'int',
>             '*tls-port': 'int', '*auth': 'str', '*compiled-version': 'str',
> -           'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']} }
> +           'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']},
> +  'if': 'defined(CONFIG_SPICE)' }
>  
>  ##
>  # @query-spice:
> @@ -2017,7 +2022,8 @@
>  #    }
>  #
>  ##
> -{ 'command': 'query-spice', 'returns': 'SpiceInfo' }
> +{ 'command': 'query-spice', 'returns': 'SpiceInfo',
> +  'if': 'defined(CONFIG_SPICE)' }
>  
>  ##
>  # @BalloonInfo:
> @@ -5067,7 +5073,8 @@
>  # Since: 1.5
>  ##
>  { 'struct': 'ChardevSpiceChannel', 'data': { 'type'  : 'str' },
> -  'base': 'ChardevCommon' }
> +  'base': 'ChardevCommon',
> +  'if': 'defined(CONFIG_SPICE)' }
>  
>  ##
>  # @ChardevSpicePort:
> @@ -5079,7 +5086,8 @@
>  # Since: 1.5
>  ##
>  { 'struct': 'ChardevSpicePort', 'data': { 'fqdn'  : 'str' },
> -  'base': 'ChardevCommon' }
> +  'base': 'ChardevCommon',
> +  'if': 'defined(CONFIG_SPICE)' }
>  
>  ##
>  # @ChardevVC:
> @@ -5133,8 +5141,8 @@
>                                         'testdev': 'ChardevCommon',
>                                         'stdio'  : 'ChardevStdio',
>                                         'console': 'ChardevCommon',
> -                                       'spicevmc' : 'ChardevSpiceChannel',
> -                                       'spiceport' : 'ChardevSpicePort',
> +                                       'spicevmc' : { 'type': 'ChardevSpiceChannel', 'if': 'defined(CONFIG_SPICE)' },
> +                                       'spiceport' : { 'type': 'ChardevSpicePort', 'if': 'defined(CONFIG_SPICE)' },
>                                         'vc'     : 'ChardevVC',
>                                         'ringbuf': 'ChardevRingbuf',
>                                         # next one is just for compatibility
> diff --git a/qapi/event.json b/qapi/event.json
> index c8b8e9f384..ff59551914 100644
> --- a/qapi/event.json
> +++ b/qapi/event.json
> @@ -344,7 +344,8 @@
>  ##
>  { 'event': 'SPICE_CONNECTED',
>    'data': { 'server': 'SpiceBasicInfo',
> -            'client': 'SpiceBasicInfo' } }
> +            'client': 'SpiceBasicInfo' },
> +  'if': 'defined(CONFIG_SPICE)' }
>  
>  ##
>  # @SPICE_INITIALIZED:
> @@ -372,7 +373,8 @@
>  ##
>  { 'event': 'SPICE_INITIALIZED',
>    'data': { 'server': 'SpiceServerInfo',
> -            'client': 'SpiceChannel' } }
> +            'client': 'SpiceChannel' },
> +  'if': 'defined(CONFIG_SPICE)' }
>  
>  ##
>  # @SPICE_DISCONNECTED:
> @@ -397,7 +399,8 @@
>  ##
>  { 'event': 'SPICE_DISCONNECTED',
>    'data': { 'server': 'SpiceBasicInfo',
> -            'client': 'SpiceBasicInfo' } }
> +            'client': 'SpiceBasicInfo' },
> +  'if': 'defined(CONFIG_SPICE)' }
>  
>  ##
>  # @SPICE_MIGRATE_COMPLETED:
> @@ -412,7 +415,8 @@
>  #      "event": "SPICE_MIGRATE_COMPLETED" }
>  #
>  ##
> -{ 'event': 'SPICE_MIGRATE_COMPLETED' }
> +{ 'event': 'SPICE_MIGRATE_COMPLETED',
> +  'if': 'defined(CONFIG_SPICE)' }
>  
>  ##
>  # @MIGRATION:
> diff --git a/monitor.c b/monitor.c
> index a1773d5bc7..4bf6a3ea2e 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -970,9 +970,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
>   */
>  static void qmp_unregister_commands_hack(void)
>  {
> -#ifndef CONFIG_SPICE
> -    qmp_unregister_command(&qmp_commands, "query-spice");
> -#endif
>  #ifndef CONFIG_REPLICATION
>      qmp_unregister_command(&qmp_commands, "xen-set-replication");
>      qmp_unregister_command(&qmp_commands, "query-xen-replication-status");
> diff --git a/qmp.c b/qmp.c
> index 2c90dacb56..90816ba283 100644
> --- a/qmp.c
> +++ b/qmp.c
> @@ -130,22 +130,6 @@ void qmp_cpu_add(int64_t id, Error **errp)
>      }
>  }
>  
> -#ifndef CONFIG_SPICE
> -/*
> - * qmp-commands.hx ensures that QMP command query-spice exists only
> - * #ifdef CONFIG_SPICE.  Necessary for an accurate query-commands
> - * result.  However, the QAPI schema is blissfully unaware of that,
> - * and the QAPI code generator happily generates a dead
> - * qmp_marshal_query_spice() that calls qmp_query_spice().  Provide it
> - * one, or else linking fails.  FIXME Educate the QAPI schema on
> - * CONFIG_SPICE.
> - */
> -SpiceInfo *qmp_query_spice(Error **errp)
> -{
> -    abort();
> -};
> -#endif
> -
>  void qmp_cont(Error **errp)
>  {
>      BlockBackend *blk;

Same drill as for PATCH 16.

Commands you make conditional:

* query-spice

  Before the patch, we first register the unconditionally command in
  generated code (requires a stub), then conditionally unregister in
  qmp_unregister_commands_hack().

  Afterwards, we register only when CONFIG_SPICE.  The command fails
  exactly the same, with CommandNotFound.

  Improvement, because now query-qmp-schema is accurate, and we're one
  step close to killing qmp_unregister_commands_hack().

Command arguments you make conditional:

* chardev-add, chardev-change variants spicevmc, spiceport

  Before the patch, "spicevmc" and "spiceport" are valid values of
  "type", but char_get_class() can't map them to a QOM type name, and
  fails with "'FOO' is not a valid char driver name".

  Afterwards, they aren't valid values, and input_type_enum() rejects
  them with "Invalid parameter 'FOO'".  That error message could use
  improvement, but that's not this patch's problem.

Events you make conditional:

* SPICE_CONNECTED, SPICE_INITIALIZED, SPICE_DISCONNECTED,
  SPICE_MIGRATE_COMPLETED

Check for completeness by reviewing the case insensitive occurrences of
SPICE in the schema not covered by your patch:

* add_client

  Same rationale as for VNC (see my review of PATCH 16).  Good.

* query-chardev-backends

  No conditional, because ChardevBackendInfo member @name is 'str'.  I
  think it should be an enum instead, and then we'd need to make some
  values conditional.  Not this patch's problem.

* client_migrate_info

  Similar to set_password and expire_password, this is generic in
  theory, documented for just "remote display", and implemented just for
  SPICE, with a stub for !CONFIG_SPICE.  Okay for the same reasons
  set_password and expire_password are okay.

* set_password, expire_password

  Same rationale as for VNC (see my review of PATCH 16).  Okay.

Some of this analysis should perhaps be worked into the commit message.

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 17/26] qapi: add conditions to SPICE type/commands/events on the schema
  2017-08-17  8:10   ` Markus Armbruster
@ 2017-08-17  8:43     ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2017-08-17  8:43 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Dr. David Alan Gilbert, Gerd Hoffmann

Markus Armbruster <armbru@redhat.com> writes:

[...]
> Check for completeness by reviewing the case insensitive occurrences of
> SPICE in the schema not covered by your patch:
>
> * add_client
>
>   Same rationale as for VNC (see my review of PATCH 16).  Good.

Hmm, there's a difference to VNC, in qmp_add_client():

    if (strcmp(protocol, "spice") == 0) {
        if (!qemu_using_spice(errp)) {
            close(fd);
            return;
        }
        skipauth = has_skipauth ? skipauth : false;
        tls = has_tls ? tls : false;
        if (qemu_spice_display_add_client(fd, skipauth, tls) < 0) {
            error_setg(errp, "spice failed to add client");
            close(fd);
        }
        return;
#ifdef CONFIG_VNC
    } else if (strcmp(protocol, "vnc") == 0) {
        skipauth = has_skipauth ? skipauth : false;
        vnc_display_add_client(NULL, fd, skipauth);
        return;
#endif
    } else if ((s = qemu_chr_find(protocol)) != NULL) {
        if (qemu_chr_add_client(s, fd) < 0) {
            error_setg(errp, "failed to add client");
            close(fd);
            return;
        }
        return;
    }

    error_setg(errp, "protocol '%s' is invalid", protocol);
    close(fd);

Observe:

* Protocol "spice" is always recognized.  Without CONFIG_SPICE,
  using_spice is always false, and the command fails with
  qemu_using_spice()'s error "SPICE is not in use".

* Protocol "vnc" is only recognized with CONFIG_VNC.  Without, it's
  rejected with "protocol 'vnc' is invalid".

* If you name your character device "spice" or "vnc", you can't use it
  with add_client.  This is a design flaw.

  Except you can use one named "vnc" when !CONFIG_VNC.

  I'm afraid the command needs to be replaced.  Not in this series, of
  course.

[...]

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 16/26] qapi: add conditions to VNC type/commands/events on the schema
  2017-08-17  7:04   ` Markus Armbruster
@ 2017-08-17  8:56     ` Markus Armbruster
  2017-08-23 15:07       ` Gerd Hoffmann
  2017-08-23 15:09     ` Gerd Hoffmann
  2017-08-29 10:42     ` Daniel P. Berrange
  2 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2017-08-17  8:56 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel, Marc-André Lureau

Gerd, can we delete the vnc_init_func() stub?  Things still compile for
me when I do.

diff --git a/include/ui/console.h b/include/ui/console.h
index 7262bef..2c3b2cd 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -484,11 +484,6 @@ static inline QemuOpts *vnc_parse(const char *str, Error **errp)
     error_setg(errp, "VNC support is disabled");
     return NULL;
 }
-static inline int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp)
-{
-    error_setg(errp, "VNC support is disabled");
-    return -1;
-}
 #endif
 
 /* curses.c */

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 18/26] qapi: add conditions to REPLICATION type/commands on the schema
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 18/26] qapi: add conditions to REPLICATION type/commands " Marc-André Lureau
@ 2017-08-17  9:16   ` Markus Armbruster
  2017-08-22 11:18     ` Marc-André Lureau
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2017-08-17  9:16 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, zhanghailiang, Juan Quintela, Dr. David Alan Gilbert

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Add #if defined(CONFIG_REPLICATION) in generated code, and adjust the
> code accordingly.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  qapi-schema.json | 12 ++++++++----
>  migration/colo.c | 14 ++------------
>  monitor.c        |  5 -----
>  3 files changed, 10 insertions(+), 21 deletions(-)
>
> diff --git a/qapi-schema.json b/qapi-schema.json
> index bcee3157b0..2f4528c769 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -6337,7 +6337,8 @@
>  # Since: 2.9
>  ##
>  { 'command': 'xen-set-replication',
> -  'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' } }
> +  'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' },
> +  'if': 'defined(CONFIG_REPLICATION)' }
>  
>  ##
>  # @ReplicationStatus:
> @@ -6352,7 +6353,8 @@
>  # Since: 2.9
>  ##
>  { 'struct': 'ReplicationStatus',
> -  'data': { 'error': 'bool', '*desc': 'str' } }
> +  'data': { 'error': 'bool', '*desc': 'str' },
> +  'if': 'defined(CONFIG_REPLICATION)' }
>  
>  ##
>  # @query-xen-replication-status:
> @@ -6369,7 +6371,8 @@
>  # Since: 2.9
>  ##
>  { 'command': 'query-xen-replication-status',
> -  'returns': 'ReplicationStatus' }
> +  'returns': 'ReplicationStatus',
> +  'if': 'defined(CONFIG_REPLICATION)' }
>  
>  ##
>  # @xen-colo-do-checkpoint:
> @@ -6385,7 +6388,8 @@
>  #
>  # Since: 2.9
>  ##
> -{ 'command': 'xen-colo-do-checkpoint' }
> +{ 'command': 'xen-colo-do-checkpoint',
> +  'if': 'defined(CONFIG_REPLICATION)' }
>  
>  ##
>  # @GICCapability:
> diff --git a/migration/colo.c b/migration/colo.c
> index a4255432ac..3bff9fc9a4 100644
> --- a/migration/colo.c
> +++ b/migration/colo.c
> @@ -147,11 +147,11 @@ void colo_do_failover(MigrationState *s)
>      }
>  }
>  
> +#ifdef CONFIG_REPLICATION
>  void qmp_xen_set_replication(bool enable, bool primary,
>                               bool has_failover, bool failover,
>                               Error **errp)
>  {
> -#ifdef CONFIG_REPLICATION
>      ReplicationMode mode = primary ?
>                             REPLICATION_MODE_PRIMARY :
>                             REPLICATION_MODE_SECONDARY;
> @@ -170,14 +170,10 @@ void qmp_xen_set_replication(bool enable, bool primary,
>          }
>          replication_stop_all(failover, failover ? NULL : errp);
>      }
> -#else
> -    abort();
> -#endif
>  }
>  
>  ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
>  {
> -#ifdef CONFIG_REPLICATION
>      Error *err = NULL;
>      ReplicationStatus *s = g_new0(ReplicationStatus, 1);
>  
> @@ -192,19 +188,13 @@ ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
>  
>      error_free(err);
>      return s;
> -#else
> -    abort();
> -#endif
>  }
>  
>  void qmp_xen_colo_do_checkpoint(Error **errp)
>  {
> -#ifdef CONFIG_REPLICATION
>      replication_do_checkpoint_all(errp);
> -#else
> -    abort();
> -#endif
>  }
> +#endif
>  
>  static void colo_send_message(QEMUFile *f, COLOMessage msg,
>                                Error **errp)
> diff --git a/monitor.c b/monitor.c
> index 4bf6a3ea2e..383c84d162 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -970,11 +970,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
>   */
>  static void qmp_unregister_commands_hack(void)
>  {
> -#ifndef CONFIG_REPLICATION
> -    qmp_unregister_command(&qmp_commands, "xen-set-replication");
> -    qmp_unregister_command(&qmp_commands, "query-xen-replication-status");
> -    qmp_unregister_command(&qmp_commands, "xen-colo-do-checkpoint");
> -#endif
>  #ifndef TARGET_I386
>      qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection");
>  #endif

Same drill as for PATCH 16.

Commands you make conditional:

* xen-set-replication, query-xen-replication-status,
  xen-colo-do-checkpoint

  Before the patch, we first register the commands unconditionally in
  generated code (requires a stub), then conditionally unregister in
  qmp_unregister_commands_hack().

  Afterwards, we register only when CONFIG_REPLICATION.  The command
  fails exactly the same, with CommandNotFound.

  Improvement, because now query-qmp-schema is accurate, and we're one
  step close to killing qmp_unregister_commands_hack().

Check for completeness by reviewing the case insensitive occurrences of
replication in the schema not covered by your patch, and review uses of
CONFIG_REPLICATION for possible schema connections (I did that for
CONFIG_VNC [PATCH 16] and CONFIG_SPICE [PATCH 17], too):

* enum BlockdevDriver value "replication" in command blockdev-add

* Makefile.objs:block-obj-$(CONFIG_REPLICATION) += replication.o
  block/Makefile.objs:block-obj-$(CONFIG_REPLICATION) += replication.o

I think the following should be ifdef CONFIG_REPLICATION: enum
BlockdevDriver value @replication BlockdevOptions variant @replication,
BlockdevOptionsReplication, BlockdevOptionsReplicationMode.

Some of this analysis should perhaps be worked into the commit message.

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 19/26] build-sys: move qapi variables in qapi.mak
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 19/26] build-sys: move qapi variables in qapi.mak Marc-André Lureau
@ 2017-08-17  9:19   ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2017-08-17  9:19 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

We don't move in qapi.mak we move *into* qapi.mak.

While we're talking: use a more common tag, and start the phrase with a
capital letter: "Makefile: Move qapi variables into qapi.mak".

Commit message then still misses the most important part: why?

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  Makefile | 43 +++++++++++++++++--------------------------
>  qapi.mak | 14 ++++++++++++++
>  2 files changed, 31 insertions(+), 26 deletions(-)
>  create mode 100644 qapi.mak
>
> diff --git a/Makefile b/Makefile
> index ef721480eb..8cd30fd88e 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -50,6 +50,7 @@ endif
>  endif
>  
>  include $(SRC_PATH)/rules.mak
> +include $(SRC_PATH)/qapi.mak
>  
>  GENERATED_FILES = qemu-version.h config-host.h qemu-options.def
>  GENERATED_FILES += qmp-commands.h qapi-types.h qapi-visit.h qapi-event.h
> @@ -390,56 +391,46 @@ qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
>  qemu-ga$(EXESUF): LIBS = $(LIBS_QGA)
>  qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated
>  
> -gen-out-type = $(subst .,-,$(suffix $@))
> -
> -qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py
> -
>  qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h :\
> -$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
> +$(SRC_PATH)/qga/qapi-schema.json $(qapi-types-py)
>  	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py \
> -		$(gen-out-type) -o qga/qapi-generated -p "qga-" $<, \
> +		$(qapi-gen-type) -o qga/qapi-generated -p "qga-" $<, \
>  		"GEN","$@")
>  qga/qapi-generated/qga-qapi-visit.c qga/qapi-generated/qga-qapi-visit.h :\
> -$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
> +$(SRC_PATH)/qga/qapi-schema.json $(qapi-visit-py)
>  	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py \
> -		$(gen-out-type) -o qga/qapi-generated -p "qga-" $<, \
> +		$(qapi-gen-type) -o qga/qapi-generated -p "qga-" $<, \
>  		"GEN","$@")
>  qga/qapi-generated/qga-qmp-commands.h qga/qapi-generated/qga-qmp-marshal.c :\
> -$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
> +$(SRC_PATH)/qga/qapi-schema.json $(qapi-commands-py)
>  	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \
> -		$(gen-out-type) -o qga/qapi-generated -p "qga-" $<, \
> +		$(qapi-gen-type) -o qga/qapi-generated -p "qga-" $<, \
>  		"GEN","$@")
>  
> -qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
> -               $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
> -               $(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json \
> -               $(SRC_PATH)/qapi/crypto.json $(SRC_PATH)/qapi/rocker.json \
> -               $(SRC_PATH)/qapi/trace.json
> -
>  qapi-types.c qapi-types.h :\
> -$(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
> +$(qapi-modules) $(qapi-types-py)
>  	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py \
> -		$(gen-out-type) -o "." -b $<, \
> +		$(qapi-gen-type) -o "." -b $<, \
>  		"GEN","$@")
>  qapi-visit.c qapi-visit.h :\
> -$(qapi-modules) $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
> +$(qapi-modules) $(qapi-visit-py)
>  	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py \
> -		$(gen-out-type) -o "." -b $<, \
> +		$(qapi-gen-type) -o "." -b $<, \
>  		"GEN","$@")
>  qapi-event.c qapi-event.h :\
> -$(qapi-modules) $(SRC_PATH)/scripts/qapi-event.py $(qapi-py)
> +$(qapi-modules) $(qapi-event-py)
>  	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py \
> -		$(gen-out-type) -o "." $<, \
> +		$(qapi-gen-type) -o "." $<, \
>  		"GEN","$@")
>  qmp-commands.h qmp-marshal.c :\
> -$(qapi-modules) $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
> +$(qapi-modules) $(qapi-commands-py)
>  	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \
> -		$(gen-out-type) -o "." $<, \
> +		$(qapi-gen-type) -o "." $<, \
>  		"GEN","$@")
>  qmp-introspect.h qmp-introspect.c :\
> -$(qapi-modules) $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py)
> +$(qapi-modules) $(qapi-introspect-py)
>  	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-introspect.py \
> -		$(gen-out-type) -o "." $<, \
> +		$(qapi-gen-type) -o "." $<, \
>  		"GEN","$@")
>  
>  QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h)
> diff --git a/qapi.mak b/qapi.mak
> new file mode 100644
> index 0000000000..70196127d9
> --- /dev/null
> +++ b/qapi.mak
> @@ -0,0 +1,14 @@
> +qapi-gen-type = $(subst .,-,$(suffix $@))
> +
> +qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
> +       $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
> +       $(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json \
> +       $(SRC_PATH)/qapi/crypto.json $(SRC_PATH)/qapi/rocker.json \
> +       $(SRC_PATH)/qapi/trace.json
> +
> +qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py
> +qapi-types-py = $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
> +qapi-visit-py = $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
> +qapi-commands-py = $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
> +qapi-introspect-py = $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py)
> +

.git/rebase-apply/patch:111: new blank line at EOF.

I'm not sure separating the variables from the rules using them is a
good idea.  Perhaps I'll understand once you explain *why* you're doing
this.

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 16/26] qapi: add conditions to VNC type/commands/events on the schema
  2017-08-17  6:32     ` Markus Armbruster
@ 2017-08-17  9:33       ` Dr. David Alan Gilbert
  0 siblings, 0 replies; 69+ messages in thread
From: Dr. David Alan Gilbert @ 2017-08-17  9:33 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: Marc-André Lureau, qemu-devel

* Markus Armbruster (armbru@redhat.com) wrote:
> "Dr. David Alan Gilbert" <dgilbert@redhat.com> writes:
> 
> > * Marc-André Lureau (marcandre.lureau@redhat.com) wrote:
> >> Add #if defined(CONFIG_VNC) in generated code, and adjust the
> >> qmp/hmp code accordingly.
> >> 
> >> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> >
> >> diff --git a/hmp.c b/hmp.c
> >> index fd80dce758..9454c634bd 100644
> >> --- a/hmp.c
> >> +++ b/hmp.c
> >> @@ -605,6 +605,7 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict)
> >>      qapi_free_BlockStatsList(stats_list);
> >>  }
> >>  
> >> +#ifdef CONFIG_VNC
> >>  /* Helper for hmp_info_vnc_clients, _servers */
> >>  static void hmp_info_VncBasicInfo(Monitor *mon, VncBasicInfo *info,
> >>                                    const char *name)
> >> @@ -692,6 +693,12 @@ void hmp_info_vnc(Monitor *mon, const QDict *qdict)
> >>      qapi_free_VncInfo2List(info2l);
> >>  
> >>  }
> >> +#else
> >> +void hmp_info_vnc(Monitor *mon, const QDict *qdict)
> >> +{
> >> +    warn_report("VNC support is disabled");
> 
> error_report(), please (see below).
> 
> >> +}
> >> +#endif
> >
> > I'm OK with this, so
> >
> > Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> >
> > although you might just be able to add a #ifdef in hmp-commands-info.hx
> > and avoid the is disabled function, or you might find that with the QMP
> > returning an error the HMP just passes that error on.
> 
> Let's compare failures when !CONFIG_VNC:
> 
> (a) Marc-André's patch as is:
> 
>         (qemu) info vnc
>         warning: VNC support is disabled
> 
>     Drop the "warning: " (because it ain't; the command failed), and this
>     is fine.
> 
> (b) Compiling them out completely (#ifdef in hmp-commands*.hx):
> 
>         unknown command: 'vnc'
> 
>     HMP bug; should be something like
> 
>         Unknown command: 'info vnc'
> 
>     but that's not this series' problem.

I'll fix that missing 'info'

Dave

>     Good enough for me.
> 
> (c) Forwarding the QMP error verbatim
> 
>         The command query-vnc has not been found
> 
>     No good.
> 
> (d) Handling CommandNotFound
> 
>     More work than (a) for the same result.
> 
> As far as I'm concerned, feel free to do (a) or (b).
> 
> [...]
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 21/26] build-sys: make qemu qapi objects per-target
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 21/26] build-sys: make qemu qapi objects per-target Marc-André Lureau
@ 2017-08-17 11:44   ` Markus Armbruster
  2017-08-22 11:18     ` Marc-André Lureau
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2017-08-17 11:44 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Michael Roth, Stefan Hajnoczi, Paolo Bonzini

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> The qapi schema has per-target definitions. Move qapi objects in the
> per-target build, so they can be configured at compile time.

Suggest something like:

    QAPI can't do target-specific conditionals (the symbols are
    poisoned), and the work-around is to pretend the target-specific
    stuff is target-independent, with stubs for the other targets.
    Makes the target-specifity invisible in introspection.

    To unpoison the symbols, we need to move the generated QAPI code to
    the per-target build.

> Keep qapi-types.o qapi-visit.o in util-obj as they are necessary for
> common code, but they will be overwritten during the target link.

--verbose: how are they supposed to even compile in the
target-independent build once we generate #if defined(TARGET_FOO) into
them?

>                                                                   Add
> some stubs for block events, in code shared by tools & qemu.

Sounds awkward.

> The following patch will configure the schema to conditionally remove
> per-target disabled features.

"The following patches", right?

> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  stubs/qapi-event.c                 | 74 ++++++++++++++++++++++++++++++++++++++
>  tests/test-qobject-input-visitor.c |  1 -
>  Makefile.objs                      |  9 +----
>  Makefile.target                    |  4 +++
>  stubs/Makefile.objs                |  1 +
>  trace/Makefile.objs                |  2 +-
>  6 files changed, 81 insertions(+), 10 deletions(-)
>  create mode 100644 stubs/qapi-event.c
>
> diff --git a/stubs/qapi-event.c b/stubs/qapi-event.c
> new file mode 100644
> index 0000000000..9415299f3a
> --- /dev/null
> +++ b/stubs/qapi-event.c
> @@ -0,0 +1,74 @@
> +#include "qemu/osdep.h"
> +#include "qapi-event.h"
> +
> +void qapi_event_send_device_tray_moved(const char *device, const char *id,
> +                                       bool tray_open, Error **errp)
> +{
> +}
> +
> +void qapi_event_send_quorum_report_bad(QuorumOpType type, bool has_error,
> +                                       const char *error, const char *node_name,
> +                                       int64_t sector_num,
> +                                       int64_t sectors_count, Error **errp)
> +{
> +}
> +
> +void qapi_event_send_quorum_failure(const char *reference, int64_t sector_num,
> +                                    int64_t sectors_count, Error **errp)
> +{
> +}
> +
> +void qapi_event_send_block_job_cancelled(BlockJobType type, const char *device,
> +                                         int64_t len, int64_t offset,
> +                                         int64_t speed, Error **errp)
> +{
> +}
> +
> +void qapi_event_send_block_job_completed(BlockJobType type, const char *device,
> +                                         int64_t len, int64_t offset,
> +                                         int64_t speed, bool has_error,
> +                                         const char *error, Error **errp)
> +{
> +}
> +
> +void qapi_event_send_block_job_error(const char *device,
> +                                     IoOperationType operation,
> +                                     BlockErrorAction action, Error **errp)
> +{
> +}
> +
> +void qapi_event_send_block_job_ready(BlockJobType type, const char *device,
> +                                     int64_t len, int64_t offset, int64_t speed,
> +                                     Error **errp)
> +{
> +}
> +
> +void qapi_event_send_block_io_error(const char *device, const char *node_name,
> +                                    IoOperationType operation,
> +                                    BlockErrorAction action, bool has_nospace,
> +                                    bool nospace, const char *reason,
> +                                    Error **errp)
> +{
> +}
> +
> +void qapi_event_send_block_image_corrupted(const char *device,
> +                                           bool has_node_name,
> +                                           const char *node_name,
> +                                           const char *msg, bool has_offset,
> +                                           int64_t offset, bool has_size,
> +                                           int64_t size, bool fatal,
> +                                           Error **errp)
> +{
> +}
> +
> +void qapi_event_send_block_write_threshold(const char *node_name,
> +                                           uint64_t amount_exceeded,
> +                                           uint64_t write_threshold,
> +                                           Error **errp)
> +{
> +}
> +
> +void qapi_event_send_device_deleted(bool has_device, const char *device,
> +                                    const char *path, Error **errp)
> +{
> +}

Yup, awkward.

> diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c
> index 4da5d02c35..0a9352c5c1 100644
> --- a/tests/test-qobject-input-visitor.c
> +++ b/tests/test-qobject-input-visitor.c
> @@ -1266,7 +1266,6 @@ static void test_visitor_in_qmp_introspect(TestInputVisitorData *data,
>                                             const void *unused)
>  {
>      do_test_visitor_in_qmp_introspect(data, &test_qmp_schema_qlit);
> -    do_test_visitor_in_qmp_introspect(data, &qmp_schema_qlit);
>  }
>  
>  int main(int argc, char **argv)

Either squash this change into PATCH 20, or mention it in the commit
message.

See also my review of PATCH 04.

> diff --git a/Makefile.objs b/Makefile.objs
> index 24a4ea08b8..2664720f9b 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -2,7 +2,7 @@
>  # Common libraries for tools and emulators
>  stub-obj-y = stubs/ crypto/
>  util-obj-y = util/ qobject/ qapi/
> -util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
> +util-obj-y += qapi-types.o qapi-visit.o
>  
>  chardev-obj-y = chardev/
>  
> @@ -72,13 +72,6 @@ common-obj-y += chardev/
>  common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o
>  
>  common-obj-$(CONFIG_FDT) += device_tree.o
> -
> -######################################################################
> -# qapi
> -
> -common-obj-y += qmp-marshal.o
> -common-obj-y += qmp-introspect.o
> -common-obj-y += qmp.o hmp.o
>  endif
>  
>  #######################################################################
> diff --git a/Makefile.target b/Makefile.target
> index 2baec9252f..a97dd056ad 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -154,6 +154,10 @@ endif
>  
>  GENERATED_FILES += hmp-commands.h hmp-commands-info.h
>  
> +obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
> +obj-y += qmp-marshal.o qmp-introspect.o
> +obj-y += qmp.o hmp.o
> +

Moving so much code from "compile once" to "compile per target" is kind
of sad.  With the full series applied, I see

    $ wc qapi*c qmp*c
       1528    3089   34576 qapi-event.c
       5097    9126  107004 qapi-types.c
      18848   44862  469514 qapi-visit.c
      12407   32395  404704 qmp-introspect.c
       6883   14997  182063 qmp-marshal.c
      44763  104469 1197861 total

Is there any way to split stuff so we recompile less?  Note that this is
a valid question even without your patches: changing one little thing in
the QAPI schema commonly triggers a lengthy recompile.  In large part
because our undisciplined use of #include.  But also because the
generator's output is monolithic.

I'm not expecting you to answer this question now, I just want to toss
it out :)

>  endif # CONFIG_SOFTMMU
>  
>  # Workaround for http://gcc.gnu.org/PR55489, see configure.
> diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
> index f5b47bfd74..1b2bef99c9 100644
> --- a/stubs/Makefile.objs
> +++ b/stubs/Makefile.objs
> @@ -21,6 +21,7 @@ stub-obj-y += machine-init-done.o
>  stub-obj-y += migr-blocker.o
>  stub-obj-y += monitor.o
>  stub-obj-y += notify-event.o
> +stub-obj-y += qapi-event.o
>  stub-obj-y += qtest.o
>  stub-obj-y += replay.o
>  stub-obj-y += runstate-check.o
> diff --git a/trace/Makefile.objs b/trace/Makefile.objs
> index afd571c3ec..6447729d60 100644
> --- a/trace/Makefile.objs
> +++ b/trace/Makefile.objs
> @@ -56,4 +56,4 @@ util-obj-$(CONFIG_TRACE_SIMPLE) += simple.o
>  util-obj-$(CONFIG_TRACE_FTRACE) += ftrace.o
>  util-obj-y += control.o
>  target-obj-y += control-target.o
> -util-obj-y += qmp.o
> +target-obj-y += qmp.o

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 04/26] qapi: generate a literal qobject for introspection
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 04/26] qapi: generate a literal qobject for introspection Marc-André Lureau
  2017-08-16 10:21   ` Markus Armbruster
@ 2017-08-17 11:48   ` Markus Armbruster
  1 sibling, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2017-08-17 11:48 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth, Dr. David Alan Gilbert

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Replace the generated json string with a literal qobject. The later is
> easier to deal with, at run time, as well as compile time: #if blocks
> can be more easily added than in a json string.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
[...]
> diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c
> index bcf02617dc..1969733971 100644
> --- a/tests/test-qobject-input-visitor.c
> +++ b/tests/test-qobject-input-visitor.c
> @@ -1247,24 +1247,26 @@ static void test_visitor_in_fail_alternate(TestInputVisitorData *data,
>  }
>  
>  static void do_test_visitor_in_qmp_introspect(TestInputVisitorData *data,
> -                                              const char *schema_json)
> +                                              const QLitObject *qlit)
>  {
>      SchemaInfoList *schema = NULL;
> +    QObject *obj = qobject_from_qlit(qlit);
>      Visitor *v;
>  
> -    v = visitor_input_test_init_raw(data, schema_json);
> +    v = qobject_input_visitor_new(obj);
>  
>      visit_type_SchemaInfoList(v, NULL, &schema, &error_abort);
>      g_assert(schema);
>  
>      qapi_free_SchemaInfoList(schema);
> +    qobject_decref(obj);
>  }

Are you leaking @v?

>  
>  static void test_visitor_in_qmp_introspect(TestInputVisitorData *data,
>                                             const void *unused)
>  {
> -    do_test_visitor_in_qmp_introspect(data, test_qmp_schema_json);
> -    do_test_visitor_in_qmp_introspect(data, qmp_schema_json);
> +    do_test_visitor_in_qmp_introspect(data, &test_qmp_schema_qlit);
> +    do_test_visitor_in_qmp_introspect(data, &qmp_schema_qlit);
>  }
>  
>  int main(int argc, char **argv)
[...]

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 07/26] qapi: add 'if' condition on top-level schema elements
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 07/26] qapi: add 'if' condition on top-level schema elements Marc-André Lureau
  2017-08-16 15:43   ` Markus Armbruster
@ 2017-08-17 11:51   ` Markus Armbruster
  1 sibling, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2017-08-17 11:51 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Add 'if' c-preprocessor condition on top-level schema elements:
> struct, enum, union, alternate, command, event.
>
> Variants objects types are created outside of #if blocks, since they
> may be shared by various types. Even if unused, this shouldn't be an
> issue, since those are internal types.
>
> Note that there is no checks in qapi scripts to verify that elements
> have compatible conditions (ex: if-struct used by a if-foo
> command). This may thus fail at C build time if they don't share the
> same subset of conditions.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  scripts/qapi.py                         | 153 +++++++++++++++++++++++++-------
>  scripts/qapi-commands.py                |   4 +-
>  scripts/qapi-event.py                   |   4 +-
>  scripts/qapi-introspect.py              |  46 ++++++----
>  scripts/qapi-types.py                   |  51 +++++++----
>  scripts/qapi-visit.py                   |  13 ++-
>  scripts/qapi2texi.py                    |  10 +--
>  tests/Makefile.include                  |   1 +
>  tests/qapi-schema/bad-if.err            |   1 +
>  tests/qapi-schema/bad-if.exit           |   1 +
>  tests/qapi-schema/bad-if.json           |   3 +
>  tests/qapi-schema/bad-if.out            |   0
>  tests/qapi-schema/qapi-schema-test.json |  20 +++++
>  tests/qapi-schema/qapi-schema-test.out  |  31 +++++++
>  tests/qapi-schema/test-qapi.py          |  21 +++--
>  15 files changed, 272 insertions(+), 87 deletions(-)
>  create mode 100644 tests/qapi-schema/bad-if.err
>  create mode 100644 tests/qapi-schema/bad-if.exit
>  create mode 100644 tests/qapi-schema/bad-if.json
>  create mode 100644 tests/qapi-schema/bad-if.out

Missing: docs/devel/qapi-code-gen.txt update.

[...]

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 22/26] qapi: make rtc-reset-reinjection depend on TARGET_I386
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 22/26] qapi: make rtc-reset-reinjection depend on TARGET_I386 Marc-André Lureau
@ 2017-08-17 11:57   ` Markus Armbruster
  2017-08-22 11:18     ` Marc-André Lureau
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2017-08-17 11:57 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Dr. David Alan Gilbert

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  qapi-schema.json |  3 ++-
>  monitor.c        | 10 ----------
>  2 files changed, 2 insertions(+), 11 deletions(-)
>
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 2f4528c769..2361c13fc8 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -6270,7 +6270,8 @@
>  # <- { "return": {} }
>  #
>  ##
> -{ 'command': 'rtc-reset-reinjection' }
> +{ 'command': 'rtc-reset-reinjection',
> +  'if': ['defined(NEED_CPU_H)', 'defined(TARGET_I386)'] }

Aha, here' you use the list syntax.

And your strategy to keep things compiling also becomes clear: you wrap
uses of poisoned symbols like TARGET_I386 in #if defined(NEED_CPU_H).

Not exactly elegant, but looks workable.  But you need to explain this
solution in commit messages [PATCH 21, I guess] and document it in
qapi-code-gen.txt.  *Unless* we can find a better one.

>  
>  # Rocker ethernet network switch
>  { 'include': 'qapi/rocker.json' }
> diff --git a/monitor.c b/monitor.c
> index 383c84d162..f3dafafa22 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -970,9 +970,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
>   */
>  static void qmp_unregister_commands_hack(void)
>  {
> -#ifndef TARGET_I386
> -    qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection");
> -#endif
>  #ifndef TARGET_S390X
>      qmp_unregister_command(&qmp_commands, "dump-skeys");
>  #endif
> @@ -4151,13 +4148,6 @@ QemuOptsList qemu_mon_opts = {
>      },
>  };
>  
> -#ifndef TARGET_I386
> -void qmp_rtc_reset_reinjection(Error **errp)
> -{
> -    error_setg(errp, QERR_FEATURE_DISABLED, "rtc-reset-reinjection");
> -}
> -#endif
> -
>  #ifndef TARGET_S390X
>  void qmp_dump_skeys(const char *filename, Error **errp)
>  {

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 23/26] qapi: make s390 commands depend on TARGET_S390X
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 23/26] qapi: make s390 commands depend on TARGET_S390X Marc-André Lureau
@ 2017-08-17 12:13   ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2017-08-17 12:13 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Alexander Graf, Dr. David Alan Gilbert,
	Paolo Bonzini, Richard Henderson, David Hildenbrand,
	Cornelia Huck, Eduardo Habkost

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  qapi-schema.json                        | 10 +++++++---
>  include/sysemu/arch_init.h              |  6 ------
>  monitor.c                               | 14 --------------
>  qmp.c                                   | 14 --------------
>  stubs/arch-query-cpu-model-baseline.c   | 12 ------------
>  stubs/arch-query-cpu-model-comparison.c | 12 ------------
>  target/s390x/cpu_models.c               |  4 ++--
>  stubs/Makefile.objs                     |  2 --
>  8 files changed, 9 insertions(+), 65 deletions(-)
>  delete mode 100644 stubs/arch-query-cpu-model-baseline.c
>  delete mode 100644 stubs/arch-query-cpu-model-comparison.c
>
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 2361c13fc8..278d7e2aa3 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -3577,7 +3577,8 @@
>  #
>  ##
>  { 'command': 'dump-skeys',
> -  'data': { 'filename': 'str' } }
> +  'data': { 'filename': 'str' },
> +  'if': ['defined(NEED_CPU_H)', 'defined(TARGET_S390X)']}

Same technique as for TARGET_I386 in PATCH 22.  See my review of it for
how it works.

>  
>  ##
>  # @netdev_add:
> @@ -4621,7 +4622,9 @@
>  ##
>  { 'command': 'query-cpu-model-comparison',
>    'data': { 'modela': 'CpuModelInfo', 'modelb': 'CpuModelInfo' },

Not your patch's fault: 'modela' sounds like the name of some high end
escort service or something.  The QAPI naming conventions want proper
words connected with dashes: 'model-a' and 'model-b'.  Although I
wouldn't mind just 'a' and 'b'.  This should've been caught in review.
Too late to fix now, I'm afraid.


> -  'returns': 'CpuModelCompareInfo' }
> +  'returns': 'CpuModelCompareInfo',
> +  'if': ['defined(NEED_CPU_H)', 'defined(TARGET_S390X)']}
> +
>  
>  ##
>  # @CpuModelBaselineInfo:
> @@ -4673,7 +4676,8 @@
>  { 'command': 'query-cpu-model-baseline',
>    'data': { 'modela': 'CpuModelInfo',
>              'modelb': 'CpuModelInfo' },
> -  'returns': 'CpuModelBaselineInfo' }
> +  'returns': 'CpuModelBaselineInfo',
> +  'if': ['defined(NEED_CPU_H)', 'defined(TARGET_S390X)']}
>  
>  ##
>  # @AddfdInfo:
> diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h
> index 8751c468ed..e9f1ea0cca 100644
> --- a/include/sysemu/arch_init.h
> +++ b/include/sysemu/arch_init.h
> @@ -35,11 +35,5 @@ CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp);
>  CpuModelExpansionInfo *arch_query_cpu_model_expansion(CpuModelExpansionType type,
>                                                        CpuModelInfo *mode,
>                                                        Error **errp);
> -CpuModelCompareInfo *arch_query_cpu_model_comparison(CpuModelInfo *modela,
> -                                                     CpuModelInfo *modelb,
> -                                                     Error **errp);
> -CpuModelBaselineInfo *arch_query_cpu_model_baseline(CpuModelInfo *modela,
> -                                                    CpuModelInfo *modelb,
> -                                                    Error **errp);
>  
>  #endif
> diff --git a/monitor.c b/monitor.c
> index f3dafafa22..505ee5c58d 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -970,19 +970,12 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
>   */
>  static void qmp_unregister_commands_hack(void)
>  {
> -#ifndef TARGET_S390X
> -    qmp_unregister_command(&qmp_commands, "dump-skeys");
> -#endif
>  #ifndef TARGET_ARM
>      qmp_unregister_command(&qmp_commands, "query-gic-capabilities");
>  #endif
>  #if !defined(TARGET_S390X) && !defined(TARGET_I386)
>      qmp_unregister_command(&qmp_commands, "query-cpu-model-expansion");
>  #endif
> -#if !defined(TARGET_S390X)
> -    qmp_unregister_command(&qmp_commands, "query-cpu-model-baseline");
> -    qmp_unregister_command(&qmp_commands, "query-cpu-model-comparison");
> -#endif
>  #if !defined(TARGET_PPC) && !defined(TARGET_ARM) && !defined(TARGET_I386) \
>      && !defined(TARGET_S390X)
>      qmp_unregister_command(&qmp_commands, "query-cpu-definitions");
> @@ -4148,13 +4141,6 @@ QemuOptsList qemu_mon_opts = {
>      },
>  };
>  
> -#ifndef TARGET_S390X
> -void qmp_dump_skeys(const char *filename, Error **errp)
> -{
> -    error_setg(errp, QERR_FEATURE_DISABLED, "dump-skeys");
> -}
> -#endif
> -
>  #ifndef TARGET_ARM
>  GICCapabilityList *qmp_query_gic_capabilities(Error **errp)
>  {
> diff --git a/qmp.c b/qmp.c
> index 90816ba283..7b6861846f 100644
> --- a/qmp.c
> +++ b/qmp.c
> @@ -553,20 +553,6 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type,
>      return arch_query_cpu_model_expansion(type, model, errp);
>  }
>  
> -CpuModelCompareInfo *qmp_query_cpu_model_comparison(CpuModelInfo *modela,
> -                                                    CpuModelInfo *modelb,
> -                                                    Error **errp)
> -{
> -    return arch_query_cpu_model_comparison(modela, modelb, errp);
> -}
> -
> -CpuModelBaselineInfo *qmp_query_cpu_model_baseline(CpuModelInfo *modela,
> -                                                   CpuModelInfo *modelb,
> -                                                   Error **errp)
> -{
> -    return arch_query_cpu_model_baseline(modela, modelb, errp);
> -}
> -
>  void qmp_add_client(const char *protocol, const char *fdname,
>                      bool has_skipauth, bool skipauth, bool has_tls, bool tls,
>                      Error **errp)
> diff --git a/stubs/arch-query-cpu-model-baseline.c b/stubs/arch-query-cpu-model-baseline.c
> deleted file mode 100644
> index 094ec13c2c..0000000000
> --- a/stubs/arch-query-cpu-model-baseline.c
> +++ /dev/null
> @@ -1,12 +0,0 @@
> -#include "qemu/osdep.h"
> -#include "qemu-common.h"
> -#include "sysemu/arch_init.h"
> -#include "qapi/qmp/qerror.h"
> -
> -CpuModelBaselineInfo *arch_query_cpu_model_baseline(CpuModelInfo *modela,
> -                                                    CpuModelInfo *modelb,
> -                                                    Error **errp)
> -{
> -    error_setg(errp, QERR_UNSUPPORTED);
> -    return NULL;
> -}
> diff --git a/stubs/arch-query-cpu-model-comparison.c b/stubs/arch-query-cpu-model-comparison.c
> deleted file mode 100644
> index d5486ae980..0000000000
> --- a/stubs/arch-query-cpu-model-comparison.c
> +++ /dev/null
> @@ -1,12 +0,0 @@
> -#include "qemu/osdep.h"
> -#include "qemu-common.h"
> -#include "sysemu/arch_init.h"
> -#include "qapi/qmp/qerror.h"
> -
> -CpuModelCompareInfo *arch_query_cpu_model_comparison(CpuModelInfo *modela,
> -                                                     CpuModelInfo *modelb,
> -                                                     Error **errp)
> -{
> -    error_setg(errp, QERR_UNSUPPORTED);
> -    return NULL;
> -}
> diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
> index fa1338fc72..cdff9cdd3b 100644
> --- a/target/s390x/cpu_models.c
> +++ b/target/s390x/cpu_models.c
> @@ -559,7 +559,7 @@ static void list_add_feat(const char *name, void *opaque)
>      *last = entry;
>  }
>  
> -CpuModelCompareInfo *arch_query_cpu_model_comparison(CpuModelInfo *infoa,
> +CpuModelCompareInfo *qmp_query_cpu_model_comparison(CpuModelInfo *infoa,
>                                                       CpuModelInfo *infob,
>                                                       Error **errp)
>  {
> @@ -632,7 +632,7 @@ CpuModelCompareInfo *arch_query_cpu_model_comparison(CpuModelInfo *infoa,
>      return compare_info;
>  }
>  
> -CpuModelBaselineInfo *arch_query_cpu_model_baseline(CpuModelInfo *infoa,
> +CpuModelBaselineInfo *qmp_query_cpu_model_baseline(CpuModelInfo *infoa,
>                                                      CpuModelInfo *infob,
>                                                      Error **errp)
>  {
> diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
> index 1b2bef99c9..049d389966 100644
> --- a/stubs/Makefile.objs
> +++ b/stubs/Makefile.objs
> @@ -1,7 +1,5 @@
>  stub-obj-y += arch-query-cpu-def.o
>  stub-obj-y += arch-query-cpu-model-expansion.o
> -stub-obj-y += arch-query-cpu-model-comparison.o
> -stub-obj-y += arch-query-cpu-model-baseline.o
>  stub-obj-y += bdrv-next-monitor-owned.o
>  stub-obj-y += blk-commit-all.o
>  stub-obj-y += blockdev-close-all-bdrv-states.o

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 26/26] qapi: make query-cpu-definitions depend on specific targets
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 26/26] qapi: make query-cpu-definitions depend on specific targets Marc-André Lureau
@ 2017-08-17 12:30   ` Markus Armbruster
  2017-08-17 12:43     ` Marc-André Lureau
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2017-08-17 12:30 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Peter Maydell, Eduardo Habkost,
	Dr. David Alan Gilbert, Alexander Graf, open list:ARM,
	open list:PowerPC, Paolo Bonzini, David Gibson,
	Richard Henderson

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> It depends on TARGET_PPC || TARGET_ARM || TARGET_I386 || TARGET_S390X.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  qapi-schema.json            |  4 +++-
>  include/sysemu/arch_init.h  |  2 --
>  monitor.c                   | 22 ----------------------
>  qmp.c                       |  5 -----
>  stubs/arch-query-cpu-def.c  | 10 ----------
>  target/arm/helper.c         |  3 ++-
>  target/i386/cpu.c           |  3 ++-
>  target/ppc/translate_init.c |  3 ++-
>  target/s390x/cpu_models.c   |  2 +-
>  stubs/Makefile.objs         |  1 -
>  10 files changed, 10 insertions(+), 45 deletions(-)
>  delete mode 100644 stubs/arch-query-cpu-def.c
>
> diff --git a/qapi-schema.json b/qapi-schema.json
> index f5e1acff83..8e3949bca8 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -4433,7 +4433,9 @@
>  #
>  # Since: 1.2.0
>  ##
> -{ 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'] }
> +{ 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'],
> +  'if': ['defined(NEED_CPU_H)',
> +         'defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_I386) || defined(TARGET_S390X)'] }
>  
>  ##
>  # @CpuModelInfo:
> diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h
> index fb3d20a1b8..e9721b9ce8 100644
> --- a/include/sysemu/arch_init.h
> +++ b/include/sysemu/arch_init.h
> @@ -31,6 +31,4 @@ extern const uint32_t arch_type;
>  int kvm_available(void);
>  int xen_available(void);
>  
> -CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp);
> -
>  #endif
> diff --git a/monitor.c b/monitor.c
> index b134c39144..6600819599 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -956,26 +956,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
>      *ret_data = qobject_from_qlit(&qmp_schema_qlit);
>  }
>  
> -/*
> - * We used to define commands in qmp-commands.hx in addition to the
> - * QAPI schema.  This permitted defining some of them only in certain
> - * configurations.  query-commands has always reflected that (good,
> - * because it lets QMP clients figure out what's actually available),
> - * while query-qmp-schema never did (not so good).  This function is a
> - * hack to keep the configuration-specific commands defined exactly as
> - * before, even though qmp-commands.hx is gone.
> - *
> - * FIXME Educate the QAPI schema on configuration-specific commands,
> - * and drop this hack.
> - */
> -static void qmp_unregister_commands_hack(void)
> -{
> -#if !defined(TARGET_PPC) && !defined(TARGET_ARM) && !defined(TARGET_I386) \
> -    && !defined(TARGET_S390X)
> -    qmp_unregister_command(&qmp_commands, "query-cpu-definitions");
> -#endif
> -}
> -

Very nice to see this gone.  Its removal could be made a separate commit
to highlight the achievement :)

There are a few more candidates:

* QERR_FEATURE_DISABLED leads me to
  - query-hotpluggable-cpus via monitor.c
  - x-colo-lost-heartbeat via colo-failover.c
  - query-rocker, query-rocker-ports, query-rocker-of-dpa-flows,
    query-rocker-of-dpa-groups via qmp-norocker.c

* QERR_UNSUPPORTED leads me to
  - dump-guest-memory via dump_init() and stubs/dump.c
  - query-vm-generation-id via stubs/vmgenid.c
  - inject-nmi via nmi_monitor_handle() and s390_nmi()
  - query-pci via pci-stub.c

* grep error_set stubs/* doesn't find more

>  void monitor_init_qmp_commands(void)
>  {
>      /*
> @@ -995,8 +975,6 @@ void monitor_init_qmp_commands(void)
>      qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add,
>                           QCO_NO_OPTIONS);
>  
> -    qmp_unregister_commands_hack();
> -
>      QTAILQ_INIT(&qmp_cap_negotiation_commands);
>      qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities",
>                           qmp_marshal_qmp_capabilities, QCO_NO_OPTIONS);
> diff --git a/qmp.c b/qmp.c
> index afa266ec1e..d57ccf1251 100644
> --- a/qmp.c
> +++ b/qmp.c
> @@ -541,11 +541,6 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
>      return prop_list;
>  }
>  
> -CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
> -{
> -    return arch_query_cpu_definitions(errp);
> -}
> -
>  void qmp_add_client(const char *protocol, const char *fdname,
>                      bool has_skipauth, bool skipauth, bool has_tls, bool tls,
>                      Error **errp)
> diff --git a/stubs/arch-query-cpu-def.c b/stubs/arch-query-cpu-def.c
> deleted file mode 100644
> index cefe4beb82..0000000000
> --- a/stubs/arch-query-cpu-def.c
> +++ /dev/null
> @@ -1,10 +0,0 @@
> -#include "qemu/osdep.h"
> -#include "qemu-common.h"
> -#include "sysemu/arch_init.h"
> -#include "qapi/qmp/qerror.h"
> -
> -CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
> -{
> -    error_setg(errp, QERR_UNSUPPORTED);
> -    return NULL;
> -}
> diff --git a/target/arm/helper.c b/target/arm/helper.c
> index 4ed32c56b8..ec644f3930 100644
> --- a/target/arm/helper.c
> +++ b/target/arm/helper.c
> @@ -15,6 +15,7 @@
>  #include <zlib.h> /* For crc32 */
>  #include "exec/semihost.h"
>  #include "sysemu/kvm.h"
> +#include "qmp-commands.h"
>  
>  #define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */
>  
> @@ -5336,7 +5337,7 @@ static void arm_cpu_add_definition(gpointer data, gpointer user_data)
>      *cpu_list = entry;
>  }
>  
> -CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
> +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
>  {
>      CpuDefinitionInfoList *cpu_list = NULL;
>      GSList *list;
> diff --git a/target/i386/cpu.c b/target/i386/cpu.c
> index d683e70a13..e5f61f6bff 100644
> --- a/target/i386/cpu.c
> +++ b/target/i386/cpu.c
> @@ -36,6 +36,7 @@
>  #include "qapi/visitor.h"
>  #include "qom/qom-qobject.h"
>  #include "sysemu/arch_init.h"
> +#include "qmp-commands.h"
>  
>  #if defined(CONFIG_KVM)
>  #include <linux/kvm_para.h>
> @@ -2318,7 +2319,7 @@ static void x86_cpu_definition_entry(gpointer data, gpointer user_data)
>      *cpu_list = entry;
>  }
>  
> -CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
> +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
>  {
>      CpuDefinitionInfoList *cpu_list = NULL;
>      GSList *list = get_sorted_cpu_model_list();
> diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c
> index 01723bdfec..2a2d62e5bb 100644
> --- a/target/ppc/translate_init.c
> +++ b/target/ppc/translate_init.c
> @@ -34,6 +34,7 @@
>  #include "hw/ppc/ppc.h"
>  #include "mmu-book3s-v3.h"
>  #include "sysemu/qtest.h"
> +#include "qmp-commands.h"
>  
>  //#define PPC_DUMP_CPU
>  //#define PPC_DEBUG_SPR
> @@ -10391,7 +10392,7 @@ static void ppc_cpu_defs_entry(gpointer data, gpointer user_data)
>      *first = entry;
>  }
>  
> -CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
> +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
>  {
>      CpuDefinitionInfoList *cpu_list = NULL;
>      GSList *list;
> diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
> index 863dce064f..8021dda341 100644
> --- a/target/s390x/cpu_models.c
> +++ b/target/s390x/cpu_models.c
> @@ -387,7 +387,7 @@ static void create_cpu_model_list(ObjectClass *klass, void *opaque)
>      *cpu_list = entry;
>  }
>  
> -CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
> +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
>  {
>      struct CpuDefinitionInfoListData list_data = {
>          .list = NULL,
> diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
> index dcfe6f49f9..71af433f6b 100644
> --- a/stubs/Makefile.objs
> +++ b/stubs/Makefile.objs
> @@ -1,4 +1,3 @@
> -stub-obj-y += arch-query-cpu-def.o
>  stub-obj-y += bdrv-next-monitor-owned.o
>  stub-obj-y += blk-commit-all.o
>  stub-obj-y += blockdev-close-all-bdrv-states.o

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 26/26] qapi: make query-cpu-definitions depend on specific targets
  2017-08-17 12:30   ` Markus Armbruster
@ 2017-08-17 12:43     ` Marc-André Lureau
  0 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-08-17 12:43 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Peter Maydell, Eduardo Habkost, Alexander Graf, qemu-devel,
	open list:ARM, open list:PowerPC, Paolo Bonzini,
	Richard Henderson, Dr. David Alan Gilbert, David Gibson

Hi

On Thu, Aug 17, 2017 at 2:30 PM Markus Armbruster <armbru@redhat.com> wrote:

> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
> > It depends on TARGET_PPC || TARGET_ARM || TARGET_I386 || TARGET_S390X.
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  qapi-schema.json            |  4 +++-
> >  include/sysemu/arch_init.h  |  2 --
> >  monitor.c                   | 22 ----------------------
> >  qmp.c                       |  5 -----
> >  stubs/arch-query-cpu-def.c  | 10 ----------
> >  target/arm/helper.c         |  3 ++-
> >  target/i386/cpu.c           |  3 ++-
> >  target/ppc/translate_init.c |  3 ++-
> >  target/s390x/cpu_models.c   |  2 +-
> >  stubs/Makefile.objs         |  1 -
> >  10 files changed, 10 insertions(+), 45 deletions(-)
> >  delete mode 100644 stubs/arch-query-cpu-def.c
> >
> > diff --git a/qapi-schema.json b/qapi-schema.json
> > index f5e1acff83..8e3949bca8 100644
> > --- a/qapi-schema.json
> > +++ b/qapi-schema.json
> > @@ -4433,7 +4433,9 @@
> >  #
> >  # Since: 1.2.0
> >  ##
> > -{ 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'] }
> > +{ 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'],
> > +  'if': ['defined(NEED_CPU_H)',
> > +         'defined(TARGET_PPC) || defined(TARGET_ARM) ||
> defined(TARGET_I386) || defined(TARGET_S390X)'] }
> >
> >  ##
> >  # @CpuModelInfo:
> > diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h
> > index fb3d20a1b8..e9721b9ce8 100644
> > --- a/include/sysemu/arch_init.h
> > +++ b/include/sysemu/arch_init.h
> > @@ -31,6 +31,4 @@ extern const uint32_t arch_type;
> >  int kvm_available(void);
> >  int xen_available(void);
> >
> > -CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp);
> > -
> >  #endif
> > diff --git a/monitor.c b/monitor.c
> > index b134c39144..6600819599 100644
> > --- a/monitor.c
> > +++ b/monitor.c
> > @@ -956,26 +956,6 @@ static void qmp_query_qmp_schema(QDict *qdict,
> QObject **ret_data,
> >      *ret_data = qobject_from_qlit(&qmp_schema_qlit);
> >  }
> >
> > -/*
> > - * We used to define commands in qmp-commands.hx in addition to the
> > - * QAPI schema.  This permitted defining some of them only in certain
> > - * configurations.  query-commands has always reflected that (good,
> > - * because it lets QMP clients figure out what's actually available),
> > - * while query-qmp-schema never did (not so good).  This function is a
> > - * hack to keep the configuration-specific commands defined exactly as
> > - * before, even though qmp-commands.hx is gone.
> > - *
> > - * FIXME Educate the QAPI schema on configuration-specific commands,
> > - * and drop this hack.
> > - */
> > -static void qmp_unregister_commands_hack(void)
> > -{
> > -#if !defined(TARGET_PPC) && !defined(TARGET_ARM) &&
> !defined(TARGET_I386) \
> > -    && !defined(TARGET_S390X)
> > -    qmp_unregister_command(&qmp_commands, "query-cpu-definitions");
> > -#endif
> > -}
> > -
>
> Very nice to see this gone.  Its removal could be made a separate commit
> to highlight the achievement :)
>
> There are a few more candidates:
>
> * QERR_FEATURE_DISABLED leads me to
>   - query-hotpluggable-cpus via monitor.c
>   - x-colo-lost-heartbeat via colo-failover.c
>   - query-rocker, query-rocker-ports, query-rocker-of-dpa-flows,
>     query-rocker-of-dpa-groups via qmp-norocker.c
>
> * QERR_UNSUPPORTED leads me to
>   - dump-guest-memory via dump_init() and stubs/dump.c
>   - query-vm-generation-id via stubs/vmgenid.c
>   - inject-nmi via nmi_monitor_handle() and s390_nmi()
>   - query-pci via pci-stub.c
>
> * grep error_set stubs/* doesn't find more
>

I have started to look at other opportunities in
https://github.com/elmarco/qemu/commits/qapi-if-more, but I have to revisit
that once we have that series ready. I guess this could be done case by
case later, and collaboratively.

Thanks for the review so far, I'll get back to it soon.


> >  void monitor_init_qmp_commands(void)
> >  {
> >      /*
> > @@ -995,8 +975,6 @@ void monitor_init_qmp_commands(void)
> >      qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add,
> >                           QCO_NO_OPTIONS);
> >
> > -    qmp_unregister_commands_hack();
> > -
> >      QTAILQ_INIT(&qmp_cap_negotiation_commands);
> >      qmp_register_command(&qmp_cap_negotiation_commands,
> "qmp_capabilities",
> >                           qmp_marshal_qmp_capabilities, QCO_NO_OPTIONS);
> > diff --git a/qmp.c b/qmp.c
> > index afa266ec1e..d57ccf1251 100644
> > --- a/qmp.c
> > +++ b/qmp.c
> > @@ -541,11 +541,6 @@ DevicePropertyInfoList
> *qmp_device_list_properties(const char *typename,
> >      return prop_list;
> >  }
> >
> > -CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
> > -{
> > -    return arch_query_cpu_definitions(errp);
> > -}
> > -
> >  void qmp_add_client(const char *protocol, const char *fdname,
> >                      bool has_skipauth, bool skipauth, bool has_tls,
> bool tls,
> >                      Error **errp)
> > diff --git a/stubs/arch-query-cpu-def.c b/stubs/arch-query-cpu-def.c
> > deleted file mode 100644
> > index cefe4beb82..0000000000
> > --- a/stubs/arch-query-cpu-def.c
> > +++ /dev/null
> > @@ -1,10 +0,0 @@
> > -#include "qemu/osdep.h"
> > -#include "qemu-common.h"
> > -#include "sysemu/arch_init.h"
> > -#include "qapi/qmp/qerror.h"
> > -
> > -CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
> > -{
> > -    error_setg(errp, QERR_UNSUPPORTED);
> > -    return NULL;
> > -}
> > diff --git a/target/arm/helper.c b/target/arm/helper.c
> > index 4ed32c56b8..ec644f3930 100644
> > --- a/target/arm/helper.c
> > +++ b/target/arm/helper.c
> > @@ -15,6 +15,7 @@
> >  #include <zlib.h> /* For crc32 */
> >  #include "exec/semihost.h"
> >  #include "sysemu/kvm.h"
> > +#include "qmp-commands.h"
> >
> >  #define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable
> */
> >
> > @@ -5336,7 +5337,7 @@ static void arm_cpu_add_definition(gpointer data,
> gpointer user_data)
> >      *cpu_list = entry;
> >  }
> >
> > -CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
> > +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
> >  {
> >      CpuDefinitionInfoList *cpu_list = NULL;
> >      GSList *list;
> > diff --git a/target/i386/cpu.c b/target/i386/cpu.c
> > index d683e70a13..e5f61f6bff 100644
> > --- a/target/i386/cpu.c
> > +++ b/target/i386/cpu.c
> > @@ -36,6 +36,7 @@
> >  #include "qapi/visitor.h"
> >  #include "qom/qom-qobject.h"
> >  #include "sysemu/arch_init.h"
> > +#include "qmp-commands.h"
> >
> >  #if defined(CONFIG_KVM)
> >  #include <linux/kvm_para.h>
> > @@ -2318,7 +2319,7 @@ static void x86_cpu_definition_entry(gpointer
> data, gpointer user_data)
> >      *cpu_list = entry;
> >  }
> >
> > -CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
> > +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
> >  {
> >      CpuDefinitionInfoList *cpu_list = NULL;
> >      GSList *list = get_sorted_cpu_model_list();
> > diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c
> > index 01723bdfec..2a2d62e5bb 100644
> > --- a/target/ppc/translate_init.c
> > +++ b/target/ppc/translate_init.c
> > @@ -34,6 +34,7 @@
> >  #include "hw/ppc/ppc.h"
> >  #include "mmu-book3s-v3.h"
> >  #include "sysemu/qtest.h"
> > +#include "qmp-commands.h"
> >
> >  //#define PPC_DUMP_CPU
> >  //#define PPC_DEBUG_SPR
> > @@ -10391,7 +10392,7 @@ static void ppc_cpu_defs_entry(gpointer data,
> gpointer user_data)
> >      *first = entry;
> >  }
> >
> > -CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
> > +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
> >  {
> >      CpuDefinitionInfoList *cpu_list = NULL;
> >      GSList *list;
> > diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
> > index 863dce064f..8021dda341 100644
> > --- a/target/s390x/cpu_models.c
> > +++ b/target/s390x/cpu_models.c
> > @@ -387,7 +387,7 @@ static void create_cpu_model_list(ObjectClass
> *klass, void *opaque)
> >      *cpu_list = entry;
> >  }
> >
> > -CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
> > +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
> >  {
> >      struct CpuDefinitionInfoListData list_data = {
> >          .list = NULL,
> > diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
> > index dcfe6f49f9..71af433f6b 100644
> > --- a/stubs/Makefile.objs
> > +++ b/stubs/Makefile.objs
> > @@ -1,4 +1,3 @@
> > -stub-obj-y += arch-query-cpu-def.o
> >  stub-obj-y += bdrv-next-monitor-owned.o
> >  stub-obj-y += blk-commit-all.o
> >  stub-obj-y += blockdev-close-all-bdrv-states.o
>
> --
Marc-André Lureau

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code
  2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
                   ` (25 preceding siblings ...)
  2017-07-27 15:41 ` [Qemu-devel] [PATCH 26/26] qapi: make query-cpu-definitions depend on specific targets Marc-André Lureau
@ 2017-08-17 13:55 ` Markus Armbruster
  2017-08-22 11:22   ` Marc-André Lureau
  26 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2017-08-17 13:55 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Paolo Bonzini

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Hi,
>
> In order to clean-up some hacks in qapi (having to unregister commands
> at runtime), I proposed a "[PATCH v5 02/20] qapi.py: add a simple #ifdef condition"
>
> (see http://lists.gnu.org/archive/html/qemu-devel/2016-08/msg03106.html).
>
> However, we decided to drop that patch from the series and solve the
> problem later. The main issues were:
> - the syntax was awkward to the JSON schema and documentation
> - the evaluation of the condition was done in the qapi scripts, with
>   very limited capability
> - each target/config would need different generated files.
>
> Instead, it could defer the #if evaluation to the C-preprocessor.
>
> With this series, top-level qapi JSON entity can take 'if' keys:
>
> { 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
>   'if': 'defined(TEST_IF_STRUCT)' }
>
> Members can be exploded as dictionnary with 'type'/'if' keys:
>
> { 'struct': 'TestIfStruct', 'data':
>   { 'foo': 'int',
>     'bar': { 'type': 'int', 'if': 'defined(TEST_IF_STRUCT_BAR)'} } }
>
> Enum values can be exploded as dictionnary with 'type'/'if' keys:
>
> { 'enum': 'TestIfEnum', 'data':
>   [ 'foo',
>     { 'name' : 'bar', 'if': 'defined(TEST_IF_ENUM_BAR)' } ] }
>
> A good benefit from having conditional schema is that introspection
> will reflect more accurately the capability of the server. Another
> benefit is that it may help to remove some dead code when disabling a
> functionality.

This is the main benefit.  Until we realize it, introspection remains
seriously hobbled.

A few closing remarks.

The general approach "generate the #if for the compiler to evaluate"
looks sound.

I haven't been able to fully review how it's integrated into the QAPI
language and how the generators implement it.  I hope a bit of judicious
patch splitting will help me over the hump.

As so often, solving one problem makes other problems more visible.  In
this case, the problem that we generate a monolith, and pay for that
with compile time.  More of it once we compile the monolith per target.

> Starting from patch "qapi: add conditions to VNC type/commands/events
> on the schema", the series demonstrate adding conditions, in order to
> remove qmp_unregister_commands_hack(). However, it feels like I
> cheated a little bit by using per-target NEED_CPU_H in the headers to
> avoid #define poison. The alternative could be to split the headers in
> common/target?

Yup, we could really use a way to modularize the generated code.

If your work leads us to ideas on how to crack the monolith, great.  If
not, we'll have to decide whether we can live with the compile time hit.
I'd rather not block your work on cracking the monolith.

> There are a lot more things we could make conditional in the QAPI
> schema, like pci/kvm/xen/numa/vde/slirp/posix/win32/vsock/lzo etc etc,
> however I am still evaluating the implication of such changes both
> externally and internally, for those interested, I can share my wip
> branch.

You provide the infrastructure and useful examples of use.  Good enough,
no need to hunt down *all* uses right away.

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 01/26] qapi: fix type_seen key error
  2017-08-15 14:40   ` Markus Armbruster
@ 2017-08-17 23:17     ` Marc-André Lureau
  0 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-08-17 23:17 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

On Tue, Aug 15, 2017 at 4:41 PM Markus Armbruster <armbru@redhat.com> wrote:

> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
> > The type_seen member can be of a different type than the 'qtype' being
> > checked, since a string create several conflicts. Lookup the real
> > conflicting type in the conflict set, that one must be present in
> > type_seen.
> >
> > This fixes the following error, reproducible with the modified test:
> >
> > Traceback (most recent call last):
> >   File "/home/elmarco/src/qq/tests/qapi-schema/test-qapi.py", line 56,
> in <module>
> >     schema = QAPISchema(sys.argv[1])
> >   File "/home/elmarco/src/qq/scripts/qapi.py", line 1470, in __init__
> >     self.exprs = check_exprs(parser.exprs)
> >   File "/home/elmarco/src/qq/scripts/qapi.py", line 959, in check_exprs
> >     check_alternate(expr, info)
> >   File "/home/elmarco/src/qq/scripts/qapi.py", line 831, in
> check_alternate
> >     % (name, key, types_seen[qtype]))
> > KeyError: 'QTYPE_QSTRING'
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  scripts/qapi.py                                  | 4 +++-
> >  tests/qapi-schema/alternate-conflict-string.json | 4 ++--
> >  2 files changed, 5 insertions(+), 3 deletions(-)
> >
> > diff --git a/scripts/qapi.py b/scripts/qapi.py
> > index 8aa2775f12..4ecc19e944 100644
> > --- a/scripts/qapi.py
> > +++ b/scripts/qapi.py
> > @@ -825,7 +825,9 @@ def check_alternate(expr, info):
> >              else:
> >                  conflicting.add('QTYPE_QNUM')
> >                  conflicting.add('QTYPE_QBOOL')
> > -        if conflicting & set(types_seen):
> > +        conflict = conflicting & set(types_seen)
> > +        if conflict:
> > +            qtype = list(conflict)[0]
> >              raise QAPISemError(info, "Alternate '%s' member '%s' can't "
> >                                 "be distinguished from member '%s'"
> >                                 % (name, key, types_seen[qtype]))
>
> I see.
>
> The fix is uninvasive, but I'm not thrilled by repurposing @qtype, and
> the meaning of list(dict) is less obvious than dict.keys().  What about
>

it's list(set) here


>
>            conflict = conflicting & set(types_seen)
>            if conflict:
>                conflicting_member_name = conflict.keys()[0]
>

so that doesn't work


>                raise QAPISemError(info, "Alternate '%s' member '%s' can't "
>                                   "be distinguished from member '%s'"
>                                   % (name, key, conflicting_member_name))
>
> Alternatively, list all conflicting names (I wouldn't bother).
>

That would be less accurate than today, and it's just a single qtype
assignment away. Let's call it conflict_qtype if you prefer.


>
> > diff --git a/tests/qapi-schema/alternate-conflict-string.json
> b/tests/qapi-schema/alternate-conflict-string.json
> > index 85adbd4adc..bb2702978e 100644
> > --- a/tests/qapi-schema/alternate-conflict-string.json
> > +++ b/tests/qapi-schema/alternate-conflict-string.json
> > @@ -1,4 +1,4 @@
> >  # alternate branches of 'str' type conflict with all scalar types
> >  { 'alternate': 'Alt',
> > -  'data': { 'one': 'str',
> > -            'two': 'int' } }
> > +  'data': { 'one': 'int',
> > +            'two': 'str' } }
>
> I had to think for several minutes to convince myself that this is a
> better test, not just a test that happens to demonstrate a particular
> bug.  It's hot, I'm slow :)
>
> --
Marc-André Lureau

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 03/26] qboject: add literal qobject type
  2017-08-16  8:59   ` Markus Armbruster
@ 2017-08-22 11:16     ` Marc-André Lureau
  0 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-08-22 11:16 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU

Hi

On Wed, Aug 16, 2017 at 10:59 AM, Markus Armbruster <armbru@redhat.com> wrote:
> In the subject: s/qboject: add/qobject: Add/
>
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
>> Promote LiteralQObject from tests/check-qjson.c to qobject/qlit.c,
>> allowing to statically declare complex qobjects.
>>
>> Add a helper qobject_from_qlit() to instantiate a literal qobject to a
>> real QObject. Add some simple test.
>
> Suggest
>
> * to also move the existing function making use of LiteralQObject
>   compare_litqobj_to_qobj(), so other tests can use it to verify a
>   QObject matches expectations, and
>
> * to split the patch into the move and the addition of
>   qobject_from_qlit().
>

ok

>>
>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> ---
>>  include/qapi/qmp/qlit.h | 53 ++++++++++++++++++++++++++++++++++++
>>  qobject/qlit.c          | 53 ++++++++++++++++++++++++++++++++++++
>>  tests/check-qjson.c     | 72 +++++++++++++++++--------------------------------
>>  tests/check-qlit.c      | 52 +++++++++++++++++++++++++++++++++++
>>  qobject/Makefile.objs   |  2 +-
>>  tests/Makefile.include  |  5 +++-
>>  6 files changed, 187 insertions(+), 50 deletions(-)
>>  create mode 100644 include/qapi/qmp/qlit.h
>>  create mode 100644 qobject/qlit.c
>>  create mode 100644 tests/check-qlit.c
>>
>> diff --git a/include/qapi/qmp/qlit.h b/include/qapi/qmp/qlit.h
>> new file mode 100644
>> index 0000000000..fd6bfc3e69
>> --- /dev/null
>> +++ b/include/qapi/qmp/qlit.h
>> @@ -0,0 +1,53 @@
>> +/*
>> + * Copyright IBM, Corp. 2009
>> + * Copyright (c) 2013, 2015 Red Hat Inc.
>> + *
>> + * Authors:
>> + *  Anthony Liguori   <aliguori@us.ibm.com>
>> + *  Markus Armbruster <armbru@redhat.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 QLIT_H_
>> +#define QLIT_H_
>> +
>> +#include "qapi-types.h"
>> +#include "qobject.h"
>> +
>> +typedef struct QLitDictEntry QLitDictEntry;
>> +typedef struct QLitObject QLitObject;
>> +
>> +struct QLitObject {
>> +    int type;
>> +    union {
>> +        bool qbool;
>> +        int64_t qnum;
>
> Not this patch's fault: QLitObject restricts numbers to signed integers.
>
>> +        const char *qstr;
>> +        QLitDictEntry *qdict;
>> +        QLitObject *qlist;
>> +    } value;
>> +};
>> +
>> +struct QLitDictEntry {
>> +    const char *key;
>> +    QLitObject value;
>> +};
>> +
>> +#define QLIT_QNULL \
>> +    { .type = QTYPE_QNULL }
>> +#define QLIT_QBOOL(val) \
>> +    { .type = QTYPE_QBOOL, .value.qbool = (val) }
>> +#define QLIT_QNUM(val) \
>> +    { .type = QTYPE_QNUM, .value.qnum = (val) }
>> +#define QLIT_QSTR(val) \
>> +    { .type = QTYPE_QSTRING, .value.qstr = (val) }
>> +#define QLIT_QDICT(val) \
>> +    { .type = QTYPE_QDICT, .value.qdict = (val) }
>> +#define QLIT_QLIST(val) \
>> +    { .type = QTYPE_QLIST, .value.qlist = (val) }
>> +
>> +QObject *qobject_from_qlit(const QLitObject *qlit);
>> +
>> +#endif /* QLIT_H_ */
>> diff --git a/qobject/qlit.c b/qobject/qlit.c
>> new file mode 100644
>> index 0000000000..d7407b4b34
>> --- /dev/null
>> +++ b/qobject/qlit.c
>> @@ -0,0 +1,53 @@
>> +/*
>> + * QLit literal qobject
>> + *
>> + * Copyright (C) 2017 Red Hat Inc.
>> + *
>> + * Authors:
>> + *  Marc-André Lureau <marcandre.lureau@redhat.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/osdep.h"
>> +
>> +#include "qapi/qmp/qlit.h"
>> +#include "qapi/qmp/types.h"
>> +
>> +QObject *qobject_from_qlit(const QLitObject *qlit)
>> +{
>> +    int i;
>> +
>> +    switch (qlit->type) {
>> +    case QTYPE_QNULL:
>> +        return QOBJECT(qnull());
>> +    case QTYPE_QNUM:
>> +        return QOBJECT(qnum_from_int(qlit->value.qnum));
>> +    case QTYPE_QSTRING:
>> +        return QOBJECT(qstring_from_str(qlit->value.qstr));
>> +    case QTYPE_QDICT: {
>> +        QDict *qdict = qdict_new();
>> +        for (i = 0; qlit->value.qdict[i].value.type != QTYPE_NONE; i++) {
>> +            QLitDictEntry *e = &qlit->value.qdict[i];
>> +
>> +            qdict_put_obj(qdict, e->key, qobject_from_qlit(&e->value));
>> +        }
>> +        return QOBJECT(qdict);
>> +    }
>> +    case QTYPE_QLIST: {
>> +        QList *qlist = qlist_new();
>> +
>> +        for (i = 0; qlit->value.qlist[i].type != QTYPE_NONE; i++) {
>> +            qlist_append_obj(qlist, qobject_from_qlit(&qlit->value.qlist[i]));
>> +        }
>> +        return QOBJECT(qlist);
>> +    }
>> +    case QTYPE_QBOOL:
>> +        return QOBJECT(qbool_from_bool(qlit->value.qbool));
>> +    case QTYPE_NONE:
>> +        assert(0);
>> +    }
>> +
>> +    return NULL;
>> +}
>> diff --git a/tests/check-qjson.c b/tests/check-qjson.c
>> index 9c42a46b7d..1a95aff2ba 100644
>> --- a/tests/check-qjson.c
>> +++ b/tests/check-qjson.c
>> @@ -16,6 +16,7 @@
>>  #include "qapi/error.h"
>>  #include "qapi/qmp/types.h"
>>  #include "qapi/qmp/qjson.h"
>> +#include "qapi/qmp/qlit.h"
>>  #include "qemu-common.h"
>>
>>  static void escaped_string(void)
>> @@ -1059,39 +1060,14 @@ static void keyword_literal(void)
>>      QDECREF(null);
>>  }
>>
>> -typedef struct LiteralQDictEntry LiteralQDictEntry;
>> -typedef struct LiteralQObject LiteralQObject;
>> -
>> -struct LiteralQObject
>> -{
>> -    int type;
>> -    union {
>> -        int64_t qnum;
>> -        const char *qstr;
>> -        LiteralQDictEntry *qdict;
>> -        LiteralQObject *qlist;
>> -    } value;
>> -};
>> -
>> -struct LiteralQDictEntry
>> -{
>> -    const char *key;
>> -    LiteralQObject value;
>> -};
>> -
>> -#define QLIT_QNUM(val) (LiteralQObject){.type = QTYPE_QNUM, .value.qnum = (val)}
>> -#define QLIT_QSTR(val) (LiteralQObject){.type = QTYPE_QSTRING, .value.qstr = (val)}
>> -#define QLIT_QDICT(val) (LiteralQObject){.type = QTYPE_QDICT, .value.qdict = (val)}
>> -#define QLIT_QLIST(val) (LiteralQObject){.type = QTYPE_QLIST, .value.qlist = (val)}
>> -
>>  typedef struct QListCompareHelper
>>  {
>>      int index;
>> -    LiteralQObject *objs;
>> +    QLitObject *objs;
>>      int result;
>>  } QListCompareHelper;
>>
>> -static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs);
>> +static int compare_litqobj_to_qobj(QLitObject *lhs, QObject *rhs);
>>
>>  static void compare_helper(QObject *obj, void *opaque)
>>  {
>> @@ -1109,7 +1085,7 @@ static void compare_helper(QObject *obj, void *opaque)
>>      helper->result = compare_litqobj_to_qobj(&helper->objs[helper->index++], obj);
>>  }
>>
>> -static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs)
>> +static int compare_litqobj_to_qobj(QLitObject *lhs, QObject *rhs)
>>  {
>>      int64_t val;
>>
>> @@ -1159,23 +1135,23 @@ static void simple_dict(void)
>>      int i;
>>      struct {
>>          const char *encoded;
>> -        LiteralQObject decoded;
>> +        QLitObject decoded;
>>      } test_cases[] = {
>>          {
>>              .encoded = "{\"foo\": 42, \"bar\": \"hello world\"}",
>> -            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
>> +            .decoded = QLIT_QDICT(((QLitDictEntry[]){
>>                          { "foo", QLIT_QNUM(42) },
>>                          { "bar", QLIT_QSTR("hello world") },
>>                          { }
>>                      })),
>>          }, {
>>              .encoded = "{}",
>> -            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
>> +            .decoded = QLIT_QDICT(((QLitDictEntry[]){
>>                          { }
>>                      })),
>>          }, {
>>              .encoded = "{\"foo\": 43}",
>> -            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
>> +            .decoded = QLIT_QDICT(((QLitDictEntry[]){
>>                          { "foo", QLIT_QNUM(43) },
>>                          { }
>>                      })),
>> @@ -1257,11 +1233,11 @@ static void simple_list(void)
>>      int i;
>>      struct {
>>          const char *encoded;
>> -        LiteralQObject decoded;
>> +        QLitObject decoded;
>>      } test_cases[] = {
>>          {
>>              .encoded = "[43,42]",
>> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
>> +            .decoded = QLIT_QLIST(((QLitObject[]){
>>                          QLIT_QNUM(43),
>>                          QLIT_QNUM(42),
>>                          { }
>> @@ -1269,21 +1245,21 @@ static void simple_list(void)
>>          },
>>          {
>>              .encoded = "[43]",
>> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
>> +            .decoded = QLIT_QLIST(((QLitObject[]){
>>                          QLIT_QNUM(43),
>>                          { }
>>                      })),
>>          },
>>          {
>>              .encoded = "[]",
>> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
>> +            .decoded = QLIT_QLIST(((QLitObject[]){
>>                          { }
>>                      })),
>>          },
>>          {
>>              .encoded = "[{}]",
>> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
>> -                        QLIT_QDICT(((LiteralQDictEntry[]){
>> +            .decoded = QLIT_QLIST(((QLitObject[]){
>> +                        QLIT_QDICT(((QLitDictEntry[]){
>>                                      {},
>>                                          })),
>>                          {},
>> @@ -1314,11 +1290,11 @@ static void simple_whitespace(void)
>>      int i;
>>      struct {
>>          const char *encoded;
>> -        LiteralQObject decoded;
>> +        QLitObject decoded;
>>      } test_cases[] = {
>>          {
>>              .encoded = " [ 43 , 42 ]",
>> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
>> +            .decoded = QLIT_QLIST(((QLitObject[]){
>>                          QLIT_QNUM(43),
>>                          QLIT_QNUM(42),
>>                          { }
>> @@ -1326,12 +1302,12 @@ static void simple_whitespace(void)
>>          },
>>          {
>>              .encoded = " [ 43 , { 'h' : 'b' }, [ ], 42 ]",
>> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
>> +            .decoded = QLIT_QLIST(((QLitObject[]){
>>                          QLIT_QNUM(43),
>> -                        QLIT_QDICT(((LiteralQDictEntry[]){
>> +                        QLIT_QDICT(((QLitDictEntry[]){
>>                                      { "h", QLIT_QSTR("b") },
>>                                      { }})),
>> -                        QLIT_QLIST(((LiteralQObject[]){
>> +                        QLIT_QLIST(((QLitObject[]){
>>                                      { }})),
>>                          QLIT_QNUM(42),
>>                          { }
>> @@ -1339,13 +1315,13 @@ static void simple_whitespace(void)
>>          },
>>          {
>>              .encoded = " [ 43 , { 'h' : 'b' , 'a' : 32 }, [ ], 42 ]",
>> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
>> +            .decoded = QLIT_QLIST(((QLitObject[]){
>>                          QLIT_QNUM(43),
>> -                        QLIT_QDICT(((LiteralQDictEntry[]){
>> +                        QLIT_QDICT(((QLitDictEntry[]){
>>                                      { "h", QLIT_QSTR("b") },
>>                                      { "a", QLIT_QNUM(32) },
>>                                      { }})),
>> -                        QLIT_QLIST(((LiteralQObject[]){
>> +                        QLIT_QLIST(((QLitObject[]){
>>                                      { }})),
>>                          QLIT_QNUM(42),
>>                          { }
>> @@ -1393,10 +1369,10 @@ static void simple_varargs(void)
>>  {
>>      QObject *embedded_obj;
>>      QObject *obj;
>> -    LiteralQObject decoded = QLIT_QLIST(((LiteralQObject[]){
>> +    QLitObject decoded = QLIT_QLIST(((QLitObject[]){
>>              QLIT_QNUM(1),
>>              QLIT_QNUM(2),
>> -            QLIT_QLIST(((LiteralQObject[]){
>> +            QLIT_QLIST(((QLitObject[]){
>>                          QLIT_QNUM(32),
>>                          QLIT_QNUM(42),
>>                          {}})),
>> diff --git a/tests/check-qlit.c b/tests/check-qlit.c
>> new file mode 100644
>> index 0000000000..cda4620f92
>> --- /dev/null
>> +++ b/tests/check-qlit.c
>> @@ -0,0 +1,52 @@
>> +/*
>> + * QLit unit-tests.
>> + *
>> + * Copyright (C) 2017 Red Hat Inc.
>> + *
>> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
>> + * See the COPYING.LIB file in the top-level directory.
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +
>> +#include "qapi/qmp/qlit.h"
>> +
>> +static void qobject_from_qlit_test(void)
>> +{
>> +    char *str;
>> +    QObject *qobj = NULL;
>> +    QLitObject qlit = QLIT_QDICT((
>> +        (QLitDictEntry[]) {
>> +            { "foo", QLIT_QNUM(42) },
>> +            { "bar", QLIT_QSTR("hello world") },
>> +            { "baz", QLIT_QNULL },
>> +            { "bee", QLIT_QLIST((
>> +                (QLitObject[]) {
>> +                    QLIT_QNUM(43),
>> +                    QLIT_QNUM(44),
>> +                    QLIT_QBOOL(true),
>> +                    { },
>> +                }))
>> +            },
>> +            { },
>> +        }));
>> +
>> +    qobj = qobject_from_qlit(&qlit);
>> +
>> +    str = qobject_to_string(qobj);
>> +    g_assert_cmpstr(str, ==,
>> +                    "bee:\n    [0]: 43\n    [1]: 44\n    [2]: true\n"   \
>> +                    "baz: null\nbar: hello world\nfoo: 42\n");
>
> I don't like this.  The order of QDict members in @str depends on
> qdict_first()/qdict_next() iteration order, which is unspecified.
>
> Here's how we check elsewhere that a QObject matches expectations:
>
>     static void qobject_from_qlit_test(void)
>     {
>         QLitObject qlit = QLIT_QDICT((
>             (QLitDictEntry[]) {
>                 { "foo", QLIT_QNUM(42) },
>                 { "bar", QLIT_QSTR("hello world") },
>                 { "baz", QLIT_QNULL },
>                 { "bee", QLIT_QLIST((
>                     (QLitObject[]) {
>                         QLIT_QNUM(43),
>                         QLIT_QNUM(44),
>                         QLIT_QBOOL(true),
>                         { },
>                     }))
>                 },
>                 { },
>             }));
>         QObject *qobj = qobject_from_qlit(&qlit);
>         QDict *qdict;
>         QList *baz;
>
>         qdict = qobject_to_qdict(qobj);
>         g_assert_cmpint(qdict_get_int(qdict, "foo"), ==, 42);
>         g_assert_cmpstr(qdict_get_str(qdict, "bar"), ==, "hello world");
>         g_assert(qobject_type(qdict_get(qdict, "baz")) == QTYPE_QNULL);
>         baz = qdict_get_qlist(qdict, "bee");
>         g_assert_cmpint(qnum_get_int(qobject_to_qnum(qlist_pop(baz))), ==, 43);
>         g_assert_cmpint(qnum_get_int(qobject_to_qnum(qlist_pop(baz))), ==, 44);
>         g_assert_cmpint(qbool_get_bool(qobject_to_qbool(qlist_pop(baz))), ==, 1);
>
>         qobject_decref(qobj);
>     }
>
> Robust, just tedious.

done the tedious way

>
> check-qjson.c uses a differently tedious technique: compare expected
> QLitObject to actual QObject with compare_litqobj_to_qobj().  I like
> that better, because I find the QLIT... initializers easier to read, and
> less error prone to write.  You might prefer not to use QLit to test
> QLit, though.
>

ok

>> +
>> +    g_free(str);
>> +    qobject_decref(qobj);
>> +}
>> +
>> +int main(int argc, char **argv)
>> +{
>> +    g_test_init(&argc, &argv, NULL);
>> +
>> +    g_test_add_func("/qlit/qobject_from_qlit", qobject_from_qlit_test);
>> +
>> +    return g_test_run();
>> +}
>> diff --git a/qobject/Makefile.objs b/qobject/Makefile.objs
>> index fc8885c9a4..002d25873a 100644
>> --- a/qobject/Makefile.objs
>> +++ b/qobject/Makefile.objs
>> @@ -1,2 +1,2 @@
>> -util-obj-y = qnull.o qnum.o qstring.o qdict.o qlist.o qbool.o
>> +util-obj-y = qnull.o qnum.o qstring.o qdict.o qlist.o qbool.o qlit.o
>>  util-obj-y += qjson.o qobject.o json-lexer.o json-streamer.o json-parser.o
>> diff --git a/tests/Makefile.include b/tests/Makefile.include
>> index 7af278db55..960ab8c6dd 100644
>> --- a/tests/Makefile.include
>> +++ b/tests/Makefile.include
>> @@ -12,6 +12,8 @@ check-unit-y += tests/test-char$(EXESUF)
>>  gcov-files-check-qdict-y = chardev/char.c
>>  check-unit-y += tests/check-qnum$(EXESUF)
>>  gcov-files-check-qnum-y = qobject/qnum.c
>> +check-unit-y += tests/check-qlit$(EXESUF)
>> +gcov-files-check-qlit-y = qobject/qlit.c
>>  check-unit-y += tests/check-qstring$(EXESUF)
>>  gcov-files-check-qstring-y = qobject/qstring.c
>>  check-unit-y += tests/check-qlist$(EXESUF)
>> @@ -523,7 +525,7 @@ test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \
>>       tests/rcutorture.o tests/test-rcu-list.o \
>>       tests/test-qdist.o tests/test-shift128.o \
>>       tests/test-qht.o tests/qht-bench.o tests/test-qht-par.o \
>> -     tests/atomic_add-bench.o
>> +     tests/atomic_add-bench.o tests/check-qlit.o
>
> Please add it right next to the other qobject-related tests:
>

ok

>    test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \
>         tests/check-qlist.o tests/check-qnull.o \
> -       tests/check-qjson.o \
> +       tests/check-qjson.o tests/check-qlit.o \
>
>>
>>  $(test-obj-y): QEMU_INCLUDES += -Itests
>>  QEMU_CFLAGS += -I$(SRC_PATH)/tests
>> @@ -541,6 +543,7 @@ test-io-obj-y = $(io-obj-y) $(test-crypto-obj-y)
>>  test-block-obj-y = $(block-obj-y) $(test-io-obj-y) tests/iothread.o
>>
>>  tests/check-qnum$(EXESUF): tests/check-qnum.o $(test-util-obj-y)
>> +tests/check-qlit$(EXESUF): tests/check-qlit.o $(test-util-obj-y)
>>  tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y)
>>  tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y)
>>  tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y)
>
> Same order as in test-obj-y, please.
>

ok

-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 04/26] qapi: generate a literal qobject for introspection
  2017-08-16 10:21   ` Markus Armbruster
@ 2017-08-22 11:17     ` Marc-André Lureau
  0 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-08-22 11:17 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU, Dr. David Alan Gilbert, Michael Roth

Hi

On Wed, Aug 16, 2017 at 12:21 PM, Markus Armbruster <armbru@redhat.com> wrote:
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
>> Replace the generated json string with a literal qobject. The later is
>> easier to deal with, at run time, as well as compile time:
>
> at run time as well as compile time
>
>>                                                            #if blocks
>> can be more easily added than in a json string.
>
> Future tense, e.g. "adding #if conditionals will be easier".

ok

>
>>
>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> ---
>>  scripts/qapi-introspect.py         | 83 +++++++++++++++++++++-----------------
>>  monitor.c                          |  2 +-
>>  tests/test-qobject-input-visitor.c | 10 +++--
>>  docs/devel/qapi-code-gen.txt       | 29 ++++++++-----
>>  4 files changed, 72 insertions(+), 52 deletions(-)
>>
>> diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
>> index 032bcea491..fc72cdb66d 100644
>> --- a/scripts/qapi-introspect.py
>> +++ b/scripts/qapi-introspect.py
>> @@ -12,72 +12,74 @@
>>  from qapi import *
>>
>>
>> -# Caveman's json.dumps() replacement (we're stuck at Python 2.4)
>> -# TODO try to use json.dumps() once we get unstuck
>> -def to_json(obj, level=0):
>> +def to_qlit(obj, level=0, first_indent=True):
>
> Suggest suppress_indent=False.  I prefer defaulting to False.

It's not about suppressing whole indent, just the first.

I suggest you rewrite the code in a follow-up patch if you have an
idea how to do it.

I'd like this apporach in general for the whole series, since it is
long and tedious to deal with that many patches, and not always easy
to understand what you want.  If something works, it will be easier to
get it done and improve the code as follow-up. This approach worked
better when we ended qapi-doc for ex, and you followed up soon after
with 50 more patches ;).

>
>> +    def indent(level):
>> +        return level * 4 * ' '
>
> Blank line before and after nested function definition, please.

ok

>
>> +    ret = ''
>> +    if first_indent:
>> +        ret += indent(level)
>>      if obj is None:
>> -        ret = 'null'
>> +        ret += 'QLIT_QNULL'
>>      elif isinstance(obj, str):
>> -        ret = '"' + obj.replace('"', r'\"') + '"'
>> +        ret += 'QLIT_QSTR(' + '"' + obj.replace('"', r'\"') + '"' + ')'
>
> Why not
>
>            ret += 'QLIT_QSTR("' + obj.replace('"', r'\"') + '")'
>
> Hmm, make that
>
>            ret += 'QLIT_QSTR(' + to_c_string(obj) + ')'
>
> because it makes the meaning more obvious, and it's also more robust: it
> doubles backslashes.

I can't find to_c_string(), I added one

>
>>      elif isinstance(obj, list):
>> -        elts = [to_json(elt, level + 1)
>> +        elts = [to_qlit(elt, level + 1)
>>                  for elt in obj]
>> -        ret = '[' + ', '.join(elts) + ']'
>> +        elts.append(indent(level + 1) + "{ }")
>
> I'd prefer "{}".  More of the same below.

ok

>
>> +        ret += 'QLIT_QLIST(((QLitObject[]) {\n'
>> +        ret += ',\n'.join(elts) + '\n'
>> +        ret += indent(level) + '}))'
>
> The extra pair of parenthesis in QLIT_QLIST(( ... )) is slightly ugly.
> Not this patch's fault.  Same for QLIT_QDICT(( ... )) below.
>
>>      elif isinstance(obj, dict):
>> -        elts = ['"%s": %s' % (key.replace('"', r'\"'),
>> -                              to_json(obj[key], level + 1))
>> -                for key in sorted(obj.keys())]
>> -        ret = '{' + ', '.join(elts) + '}'
>> +        elts = [ indent(level + 1) + '{ "%s", %s }' %
>> +                 (key.replace('"', r'\"'), to_qlit(obj[key], level + 1, False))
>
> $ pep8 scripts/qapi-introspect.py
> scripts/qapi-introspect.py:33:17: E201 whitespace after '['
>
> Also, break lines at operators with the least precedence, not in the
> middle of sub-expressions.
>
> However
>
>            elts = [indent(level + 1)
>                    + ('{ "%s", %s }'
>                       % (to_c_string(key),
>                          to_qlit(obj[key], level + 1, suppress_indent=True)))
>                    for key in sorted(obj.keys())]
>
> is still illegible.  I'm afraid this is simply too complex for a list
> comprehension.  Try rewriting as a loop.
>

ok

> Another option would be separating off indentation: generate the C
> initializer unindented, then feed it to a stupid indenter that counts
> parentheses (round, square and curly).

hmm

>
>> +                 for key in sorted(obj.keys())]
>> +        elts.append(indent(level + 1) + '{ }')
>> +        ret += 'QLIT_QDICT(((QLitDictEntry[]) {\n'
>> +        ret += ',\n'.join(elts) + '\n'
>> +        ret += indent(level) + '}))'
>>      else:
>>          assert False                # not implemented
>> -    if level == 1:
>> -        ret = '\n' + ret
>>      return ret
>>
>>
>> -def to_c_string(string):
>> -    return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"'
>> -
>> -
>>  class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor):
>>      def __init__(self, unmask):
>>          self._unmask = unmask
>>          self.defn = None
>>          self.decl = None
>>          self._schema = None
>> -        self._jsons = None
>> +        self._qlits = None
>>          self._used_types = None
>>          self._name_map = None
>>
>>      def visit_begin(self, schema):
>>          self._schema = schema
>> -        self._jsons = []
>> +        self._qlits = []
>>          self._used_types = []
>>          self._name_map = {}
>>
>>      def visit_end(self):
>>          # visit the types that are actually used
>> -        jsons = self._jsons
>> -        self._jsons = []
>> +        qlits = self._qlits
>> +        self._qlits = []
>>          for typ in self._used_types:
>>              typ.visit(self)
>>          # generate C
>>          # TODO can generate awfully long lines
>> -        jsons.extend(self._jsons)
>> -        name = c_name(prefix, protect=False) + 'qmp_schema_json'
>> +        qlits.extend(self._qlits)
>> +        name = c_name(prefix, protect=False) + 'qmp_schema_qlit'
>>          self.decl = mcgen('''
>> -extern const char %(c_name)s[];
>> +extern const QLitObject %(c_name)s;
>>  ''',
>>                            c_name=c_name(name))
>> -        lines = to_json(jsons).split('\n')
>> -        c_string = '\n    '.join([to_c_string(line) for line in lines])
>> +        c_string = to_qlit(qlits)
>
> The value is simple, and it's used just once.  Let's eliminate the
> variable.
>
>>          self.defn = mcgen('''
>> -const char %(c_name)s[] = %(c_string)s;
>> +const QLitObject %(c_name)s = %(c_string)s;
>>  ''',
>>                            c_name=c_name(name),
>>                            c_string=c_string)
>>          self._schema = None
>> -        self._jsons = None
>> +        self._qlits = None
>>          self._used_types = None
>>          self._name_map = None
>>
>> @@ -111,12 +113,12 @@ const char %(c_name)s[] = %(c_string)s;
>>              return '[' + self._use_type(typ.element_type) + ']'
>>          return self._name(typ.name)
>>
>> -    def _gen_json(self, name, mtype, obj):
>> +    def _gen_qlit(self, name, mtype, obj):
>>          if mtype not in ('command', 'event', 'builtin', 'array'):
>>              name = self._name(name)
>>          obj['name'] = name
>>          obj['meta-type'] = mtype
>> -        self._jsons.append(obj)
>> +        self._qlits.append(obj)
>>
>>      def _gen_member(self, member):
>>          ret = {'name': member.name, 'type': self._use_type(member.type)}
>> @@ -132,24 +134,24 @@ const char %(c_name)s[] = %(c_string)s;
>>          return {'case': variant.name, 'type': self._use_type(variant.type)}
>>
>>      def visit_builtin_type(self, name, info, json_type):
>> -        self._gen_json(name, 'builtin', {'json-type': json_type})
>> +        self._gen_qlit(name, 'builtin', {'json-type': json_type})
>>
>>      def visit_enum_type(self, name, info, values, prefix):
>> -        self._gen_json(name, 'enum', {'values': values})
>> +        self._gen_qlit(name, 'enum', {'values': values})
>>
>>      def visit_array_type(self, name, info, element_type):
>>          element = self._use_type(element_type)
>> -        self._gen_json('[' + element + ']', 'array', {'element-type': element})
>> +        self._gen_qlit('[' + element + ']', 'array', {'element-type': element})
>>
>>      def visit_object_type_flat(self, name, info, members, variants):
>>          obj = {'members': [self._gen_member(m) for m in members]}
>>          if variants:
>>              obj.update(self._gen_variants(variants.tag_member.name,
>>                                            variants.variants))
>> -        self._gen_json(name, 'object', obj)
>> +        self._gen_qlit(name, 'object', obj)
>>
>>      def visit_alternate_type(self, name, info, variants):
>> -        self._gen_json(name, 'alternate',
>> +        self._gen_qlit(name, 'alternate',
>>                         {'members': [{'type': self._use_type(m.type)}
>>                                      for m in variants.variants]})
>>
>> @@ -157,13 +159,13 @@ const char %(c_name)s[] = %(c_string)s;
>>                        gen, success_response, boxed):
>>          arg_type = arg_type or self._schema.the_empty_object_type
>>          ret_type = ret_type or self._schema.the_empty_object_type
>> -        self._gen_json(name, 'command',
>> +        self._gen_qlit(name, 'command',
>>                         {'arg-type': self._use_type(arg_type),
>>                          'ret-type': self._use_type(ret_type)})
>>
>>      def visit_event(self, name, info, arg_type, boxed):
>>          arg_type = arg_type or self._schema.the_empty_object_type
>> -        self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)})
>> +        self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)})
>>
>>  # Debugging aid: unmask QAPI schema's type names
>>  # We normally mask them, because they're not QMP wire ABI
>> @@ -205,11 +207,18 @@ h_comment = '''
>>
>>  fdef.write(mcgen('''
>>  #include "qemu/osdep.h"
>> +#include "qapi/qmp/qlit.h"
>>  #include "%(prefix)sqmp-introspect.h"
>>
>>  ''',
>>                   prefix=prefix))
>>
>> +fdecl.write(mcgen('''
>> +#include "qemu/osdep.h"
>> +#include "qapi/qmp/qlit.h"
>> +
>> +'''))
>> +
>>  schema = QAPISchema(input_file)
>>  gen = QAPISchemaGenIntrospectVisitor(opt_unmask)
>>  schema.visit(gen)
>> diff --git a/monitor.c b/monitor.c
>> index 6d040e620f..a1773d5bc7 100644
>> --- a/monitor.c
>> +++ b/monitor.c
>> @@ -953,7 +953,7 @@ EventInfoList *qmp_query_events(Error **errp)
>>  static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
>>                                   Error **errp)
>>  {
>> -    *ret_data = qobject_from_json(qmp_schema_json, &error_abort);
>> +    *ret_data = qobject_from_qlit(&qmp_schema_qlit);
>>  }
>>
>>  /*
>> diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c
>> index bcf02617dc..1969733971 100644
>> --- a/tests/test-qobject-input-visitor.c
>> +++ b/tests/test-qobject-input-visitor.c
>> @@ -1247,24 +1247,26 @@ static void test_visitor_in_fail_alternate(TestInputVisitorData *data,
>>  }
>>
>>  static void do_test_visitor_in_qmp_introspect(TestInputVisitorData *data,
>> -                                              const char *schema_json)
>> +                                              const QLitObject *qlit)
>>  {
>>      SchemaInfoList *schema = NULL;
>> +    QObject *obj = qobject_from_qlit(qlit);
>>      Visitor *v;
>>
>> -    v = visitor_input_test_init_raw(data, schema_json);
>> +    v = qobject_input_visitor_new(obj);
>>
>>      visit_type_SchemaInfoList(v, NULL, &schema, &error_abort);
>>      g_assert(schema);
>>
>>      qapi_free_SchemaInfoList(schema);
>> +    qobject_decref(obj);
>>  }
>>
>>  static void test_visitor_in_qmp_introspect(TestInputVisitorData *data,
>>                                             const void *unused)
>>  {
>> -    do_test_visitor_in_qmp_introspect(data, test_qmp_schema_json);
>> -    do_test_visitor_in_qmp_introspect(data, qmp_schema_json);
>> +    do_test_visitor_in_qmp_introspect(data, &test_qmp_schema_qlit);
>> +    do_test_visitor_in_qmp_introspect(data, &qmp_schema_qlit);
>>  }
>>
>
> These tests are only marginally useful now.  Before, they ensured that a
> qapi-introspect.py generating invalid JSON fails at "make check" compile
> time.  Such bugs should now fail when we compile the generated
> qapi-introspect.c.
>
>>  int main(int argc, char **argv)
>> diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt
>> index 9903ac4c19..885c61b52f 100644
>> --- a/docs/devel/qapi-code-gen.txt
>> +++ b/docs/devel/qapi-code-gen.txt
>> @@ -1295,18 +1295,27 @@ Example:
>>      #ifndef EXAMPLE_QMP_INTROSPECT_H
>>      #define EXAMPLE_QMP_INTROSPECT_H
>>
>> -    extern const char example_qmp_schema_json[];
>> +    extern const QLitObject qmp_schema_qlit;
>>
>>      #endif
>>      $ cat qapi-generated/example-qmp-introspect.c
>>  [Uninteresting stuff omitted...]
>>
>> -    const char example_qmp_schema_json[] = "["
>> -        "{\"arg-type\": \"0\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
>> -        "{\"arg-type\": \"1\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"2\"}, "
>> -        "{\"members\": [], \"meta-type\": \"object\", \"name\": \"0\"}, "
>> -        "{\"members\": [{\"name\": \"arg1\", \"type\": \"[2]\"}], \"meta-type\": \"object\", \"name\": \"1\"}, "
>> -        "{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"default\": null, \"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, "
>> -        "{\"element-type\": \"2\", \"meta-type\": \"array\", \"name\": \"[2]\"}, "
>> -        "{\"json-type\": \"int\", \"meta-type\": \"builtin\", \"name\": \"int\"}, "
>> -        "{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}]";
>> +    const QLitObject example_qmp_schema_qlit = QLIT_QLIST(((QLitObject[]) {
>> +        QLIT_QDICT(((QLitDictEntry[]) {
>> +            { "arg-type", QLIT_QSTR("0") },
>> +            { "meta-type", QLIT_QSTR("event") },
>> +            { "name", QLIT_QSTR("Event") },
>> +            { }
>> +        })),
>> +        QLIT_QDICT(((QLitDictEntry[]) {
>> +            { "members", QLIT_QLIST(((QLitObject[]) {
>> +                { }
>> +            })) },
>> +            { "meta-type", QLIT_QSTR("object") },
>> +            { "name", QLIT_QSTR("0") },
>> +            { }
>> +        })),
>> +        ....
>
> Ellipsis is three dots, not four :)
>
> Output is no longer complete (less file boilerplate).  Not an issue now,
> but it might become one when we make the examples testable.  We can
> restore the deleted output then.
>

ok



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 05/26] visitor: pass size of strings array to enum visitor
  2017-08-16 12:54   ` Markus Armbruster
@ 2017-08-22 11:17     ` Marc-André Lureau
  0 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-08-22 11:17 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Eduardo Habkost, Jason Wang, Michael Roth, QEMU, Igor Mammedov,
	Andreas Färber

hi

On Wed, Aug 16, 2017 at 2:54 PM, Markus Armbruster <armbru@redhat.com> wrote:
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
>> The size is known at compile time, this avoids having to compute to
>> check array boundaries.
>>
>> Additionally, the following conditional enum entry change will create
>> "hole" in the generated _lookup tables, that should be skipped.
>>
>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> ---
>>  include/qapi/visitor.h             |  3 ++-
>>  scripts/qapi-visit.py              | 10 +++++-----
>>  include/hw/qdev-core.h             |  1 +
>>  include/qom/object.h               |  4 ++++
>>  qapi/qapi-visit-core.c             | 23 ++++++++++++-----------
>>  backends/hostmem.c                 |  1 +
>>  crypto/secret.c                    |  1 +
>>  crypto/tlscreds.c                  |  1 +
>>  hw/core/qdev-properties.c          | 11 +++++++++--
>>  net/filter.c                       |  1 +
>>  qom/object.c                       | 11 ++++++++---
>>  tests/check-qom-proplist.c         |  1 +
>>  tests/test-qobject-input-visitor.c |  2 +-
>>  13 files changed, 47 insertions(+), 23 deletions(-)
>
> No change to scripts/qapi-types.c.  The generated lookup tables continue
> to contain the NULL sentinel.  Possibly intentional, because it saves
> you the trouble of searching for uses of FOO_lookup[FOO__MAX].
>
>> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
>> index fe9faf469f..a2d9786c52 100644
>> --- a/include/qapi/visitor.h
>> +++ b/include/qapi/visitor.h
>> @@ -469,7 +469,8 @@ bool visit_optional(Visitor *v, const char *name, bool *present);
>>   * that visit_type_str() must have no unwelcome side effects.
>>   */
>>  void visit_type_enum(Visitor *v, const char *name, int *obj,
>> -                     const char *const strings[], Error **errp);
>> +                     const char *const strings[], int nstrings,
>> +                     Error **errp);
>>
>>  /*
>>   * Check if visitor is an input visitor.
>> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
>> index bd0b742236..60850a6cdd 100644
>> --- a/scripts/qapi-visit.py
>> +++ b/scripts/qapi-visit.py
>> @@ -147,17 +147,17 @@ out:
>>                   c_name=c_name(name), c_elt_type=element_type.c_name())
>>
>>
>> -def gen_visit_enum(name):
>> +def gen_visit_enum(name, prefix):
>>      return mcgen('''
>>
>>  void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error **errp)
>>  {
>>      int value = *obj;
>> -    visit_type_enum(v, name, &value, %(c_name)s_lookup, errp);
>> +    visit_type_enum(v, name, &value, %(c_name)s_lookup, %(c_max)s, errp);
>>      *obj = value;
>>  }
>>  ''',
>> -                 c_name=c_name(name))
>> +                 c_name=c_name(name), c_max=c_enum_const(name, '_MAX', prefix))
>>
>>
>>  def gen_visit_alternate(name, variants):
>> @@ -288,10 +288,10 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
>>          if not info:
>>              self._btin += gen_visit_decl(name, scalar=True)
>>              if do_builtins:
>> -                self.defn += gen_visit_enum(name)
>> +                self.defn += gen_visit_enum(name, prefix)
>>          else:
>>              self.decl += gen_visit_decl(name, scalar=True)
>> -            self.defn += gen_visit_enum(name)
>> +            self.defn += gen_visit_enum(name, prefix)
>>
>>      def visit_array_type(self, name, info, element_type):
>>          decl = gen_visit_decl(name)
>> diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
>> index ae317286a4..f86a0e1a75 100644
>> --- a/include/hw/qdev-core.h
>> +++ b/include/hw/qdev-core.h
>> @@ -250,6 +250,7 @@ struct PropertyInfo {
>>      const char *name;
>>      const char *description;
>>      const char * const *enum_table;
>> +    int enum_table_size;
>>      int (*print)(DeviceState *dev, Property *prop, char *dest, size_t len);
>>      void (*set_default_value)(Object *obj, const Property *prop);
>>      void (*create)(Object *obj, Property *prop, Error **errp);
>> diff --git a/include/qom/object.h b/include/qom/object.h
>> index 1b828994fa..53d807e1e6 100644
>> --- a/include/qom/object.h
>> +++ b/include/qom/object.h
>> @@ -1406,6 +1406,8 @@ void object_class_property_add_bool(ObjectClass *klass, const char *name,
>>   * @obj: the object to add a property to
>>   * @name: the name of the property
>>   * @typename: the name of the enum data type
>> + * @strings: an array of strings for the enum
>
> Fixes a preexisting doc buglet.
>
>> + * @nstrings: the size of @strings
>>   * @get: the getter or %NULL if the property is write-only.
>>   * @set: the setter or %NULL if the property is read-only
>>   * @errp: if an error occurs, a pointer to an area to store the error
>> @@ -1416,6 +1418,7 @@ void object_class_property_add_bool(ObjectClass *klass, const char *name,
>>  void object_property_add_enum(Object *obj, const char *name,
>>                                const char *typename,
>>                                const char * const *strings,
>> +                              int nstrings,
>>                                int (*get)(Object *, Error **),
>>                                void (*set)(Object *, int, Error **),
>>                                Error **errp);
>> @@ -1423,6 +1426,7 @@ void object_property_add_enum(Object *obj, const char *name,
>>  void object_class_property_add_enum(ObjectClass *klass, const char *name,
>>                                      const char *typename,
>>                                      const char * const *strings,
>> +                                    int nstrings,
>>                                      int (*get)(Object *, Error **),
>>                                      void (*set)(Object *, int, Error **),
>>                                      Error **errp);
>> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
>> index ed6d2af462..dc0b9f2cee 100644
>> --- a/qapi/qapi-visit-core.c
>> +++ b/qapi/qapi-visit-core.c
>> @@ -333,14 +333,13 @@ void visit_type_null(Visitor *v, const char *name, QNull **obj,
>>  }
>>
>>  static void output_type_enum(Visitor *v, const char *name, int *obj,
>> -                             const char *const strings[], Error **errp)
>> +                             const char *const strings[],
>> +                             int nstrings, Error **errp)
>>  {
>> -    int i = 0;
>>      int value = *obj;
>>      char *enum_str;
>>
>> -    while (strings[i++] != NULL);
>
> This is the computation we save.
>
>> -    if (value < 0 || value >= i - 1) {
>> +    if (value < 0 || value >= nstrings) {
>>          error_setg(errp, QERR_INVALID_PARAMETER, name ? name : "null");
>>          return;
>>      }
>> @@ -350,7 +349,8 @@ static void output_type_enum(Visitor *v, const char *name, int *obj,
>>  }
>>
>>  static void input_type_enum(Visitor *v, const char *name, int *obj,
>> -                            const char *const strings[], Error **errp)
>> +                            const char *const strings[],
>> +                            int nstrings, Error **errp)
>>  {
>>      Error *local_err = NULL;
>>      int64_t value = 0;
>> @@ -362,14 +362,14 @@ static void input_type_enum(Visitor *v, const char *name, int *obj,
>>          return;
>>      }
>>
>> -    while (strings[value] != NULL) {
>
> This is the predicate that becomes invalid when we put holes into
> strings[].
>
>> -        if (strcmp(strings[value], enum_str) == 0) {
>> +    while (value < nstrings) {
>> +        if (strings[value] && strcmp(strings[value], enum_str) == 0) {
>
> I'd prefer !strcmp().
>
>>              break;
>>          }
>>          value++;
>>      }
>>
>> -    if (strings[value] == NULL) {
>> +    if (value >= nstrings || strings[value] == NULL) {
>
> Make that
>
>        if (value == nstrings) {
>
> to match the loop above.
>
>>          error_setg(errp, QERR_INVALID_PARAMETER, enum_str);
>>          g_free(enum_str);
>>          return;
>> @@ -380,16 +380,17 @@ static void input_type_enum(Visitor *v, const char *name, int *obj,
>>  }
>>
>>  void visit_type_enum(Visitor *v, const char *name, int *obj,
>> -                     const char *const strings[], Error **errp)
>> +                     const char *const strings[], int nstrings,
>> +                     Error **errp)
>>  {
>>      assert(obj && strings);
>>      trace_visit_type_enum(v, name, obj);
>>      switch (v->type) {
>>      case VISITOR_INPUT:
>> -        input_type_enum(v, name, obj, strings, errp);
>> +        input_type_enum(v, name, obj, strings, nstrings, errp);
>>          break;
>>      case VISITOR_OUTPUT:
>> -        output_type_enum(v, name, obj, strings, errp);
>> +        output_type_enum(v, name, obj, strings, nstrings, errp);
>>          break;
>>      case VISITOR_CLONE:
>>          /* nothing further to do, scalar value was already copied by
>> diff --git a/backends/hostmem.c b/backends/hostmem.c
>> index 4606b73849..fc475a5387 100644
>> --- a/backends/hostmem.c
>> +++ b/backends/hostmem.c
>> @@ -396,6 +396,7 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
>>          NULL, NULL, &error_abort);
>>      object_class_property_add_enum(oc, "policy", "HostMemPolicy",
>>          HostMemPolicy_lookup,
>> +        HOST_MEM_POLICY__MAX,
>>          host_memory_backend_get_policy,
>>          host_memory_backend_set_policy, &error_abort);
>>      object_class_property_add_str(oc, "id", get_id, set_id, &error_abort);
>> diff --git a/crypto/secret.c b/crypto/secret.c
>> index 285ab7a63c..b5382cb7e3 100644
>> --- a/crypto/secret.c
>> +++ b/crypto/secret.c
>> @@ -379,6 +379,7 @@ qcrypto_secret_class_init(ObjectClass *oc, void *data)
>>      object_class_property_add_enum(oc, "format",
>>                                     "QCryptoSecretFormat",
>>                                     QCryptoSecretFormat_lookup,
>> +                                   QCRYPTO_SECRET_FORMAT__MAX,
>>                                     qcrypto_secret_prop_get_format,
>>                                     qcrypto_secret_prop_set_format,
>>                                     NULL);
>> diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
>> index a8965531b6..8c060127ea 100644
>> --- a/crypto/tlscreds.c
>> +++ b/crypto/tlscreds.c
>> @@ -234,6 +234,7 @@ qcrypto_tls_creds_class_init(ObjectClass *oc, void *data)
>>      object_class_property_add_enum(oc, "endpoint",
>>                                     "QCryptoTLSCredsEndpoint",
>>                                     QCryptoTLSCredsEndpoint_lookup,
>> +                                   QCRYPTO_TLS_CREDS_ENDPOINT__MAX,
>>                                     qcrypto_tls_creds_prop_get_endpoint,
>>                                     qcrypto_tls_creds_prop_set_endpoint,
>>                                     NULL);
>> diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
>> index 078fc5d239..696fed5b5b 100644
>> --- a/hw/core/qdev-properties.c
>> +++ b/hw/core/qdev-properties.c
>> @@ -52,7 +52,8 @@ static void get_enum(Object *obj, Visitor *v, const char *name, void *opaque,
>>      Property *prop = opaque;
>>      int *ptr = qdev_get_prop_ptr(dev, prop);
>>
>> -    visit_type_enum(v, prop->name, ptr, prop->info->enum_table, errp);
>> +    visit_type_enum(v, prop->name, ptr, prop->info->enum_table,
>> +                    prop->info->enum_table_size, errp);
>>  }
>>
>>  static void set_enum(Object *obj, Visitor *v, const char *name, void *opaque,
>> @@ -67,7 +68,8 @@ static void set_enum(Object *obj, Visitor *v, const char *name, void *opaque,
>>          return;
>>      }
>>
>> -    visit_type_enum(v, prop->name, ptr, prop->info->enum_table, errp);
>> +    visit_type_enum(v, prop->name, ptr, prop->info->enum_table,
>> +                    prop->info->enum_table_size, errp);
>>  }
>>
>>  static void set_default_value_enum(Object *obj, const Property *prop)
>> @@ -586,6 +588,7 @@ const PropertyInfo qdev_prop_on_off_auto = {
>>      .name = "OnOffAuto",
>>      .description = "on/off/auto",
>>      .enum_table = OnOffAuto_lookup,
>> +    .enum_table_size = ON_OFF_AUTO__MAX,
>>      .get = get_enum,
>>      .set = set_enum,
>>      .set_default_value = set_default_value_enum,
>> @@ -598,6 +601,7 @@ QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int));
>>  const PropertyInfo qdev_prop_losttickpolicy = {
>>      .name  = "LostTickPolicy",
>>      .enum_table  = LostTickPolicy_lookup,
>> +    .enum_table_size = LOST_TICK_POLICY__MAX,
>>      .get   = get_enum,
>>      .set   = set_enum,
>>      .set_default_value = set_default_value_enum,
>> @@ -612,6 +616,7 @@ const PropertyInfo qdev_prop_blockdev_on_error = {
>>      .description = "Error handling policy, "
>>                     "report/ignore/enospc/stop/auto",
>>      .enum_table = BlockdevOnError_lookup,
>> +    .enum_table_size = BLOCKDEV_ON_ERROR__MAX,
>>      .get = get_enum,
>>      .set = set_enum,
>>      .set_default_value = set_default_value_enum,
>> @@ -626,6 +631,7 @@ const PropertyInfo qdev_prop_bios_chs_trans = {
>>      .description = "Logical CHS translation algorithm, "
>>                     "auto/none/lba/large/rechs",
>>      .enum_table = BiosAtaTranslation_lookup,
>> +    .enum_table_size = BIOS_ATA_TRANSLATION__MAX,
>>      .get = get_enum,
>>      .set = set_enum,
>>      .set_default_value = set_default_value_enum,
>> @@ -638,6 +644,7 @@ const PropertyInfo qdev_prop_fdc_drive_type = {
>>      .description = "FDC drive type, "
>>                     "144/288/120/none/auto",
>>      .enum_table = FloppyDriveType_lookup,
>> +    .enum_table_size = FLOPPY_DRIVE_TYPE__MAX,
>>      .get = get_enum,
>>      .set = set_enum,
>>      .set_default_value = set_default_value_enum,
>> diff --git a/net/filter.c b/net/filter.c
>> index 1dfd2caa23..cf62851344 100644
>> --- a/net/filter.c
>> +++ b/net/filter.c
>> @@ -180,6 +180,7 @@ static void netfilter_init(Object *obj)
>>                              NULL);
>>      object_property_add_enum(obj, "queue", "NetFilterDirection",
>>                               NetFilterDirection_lookup,
>> +                             NET_FILTER_DIRECTION__MAX,
>>                               netfilter_get_direction, netfilter_set_direction,
>>                               NULL);
>>      object_property_add_str(obj, "status",
>> diff --git a/qom/object.c b/qom/object.c
>> index fe6e744b4d..425bae3a2a 100644
>> --- a/qom/object.c
>> +++ b/qom/object.c
>> @@ -1247,6 +1247,7 @@ uint64_t object_property_get_uint(Object *obj, const char *name,
>>
>>  typedef struct EnumProperty {
>>      const char * const *strings;
>> +    int nstrings;
>>      int (*get)(Object *, Error **);
>>      void (*set)(Object *, int, Error **);
>>  } EnumProperty;
>> @@ -1284,7 +1285,7 @@ int object_property_get_enum(Object *obj, const char *name,
>>      visit_complete(v, &str);
>>      visit_free(v);
>>      v = string_input_visitor_new(str);
>> -    visit_type_enum(v, name, &ret, enumprop->strings, errp);
>> +    visit_type_enum(v, name, &ret, enumprop->strings, enumprop->nstrings, errp);
>
> Long line.
>
>>
>>      g_free(str);
>>      visit_free(v);
>> @@ -1950,7 +1951,7 @@ static void property_get_enum(Object *obj, Visitor *v, const char *name,
>>          return;
>>      }
>>
>> -    visit_type_enum(v, name, &value, prop->strings, errp);
>> +    visit_type_enum(v, name, &value, prop->strings, prop->nstrings, errp);
>>  }
>>
>>  static void property_set_enum(Object *obj, Visitor *v, const char *name,
>> @@ -1960,7 +1961,7 @@ static void property_set_enum(Object *obj, Visitor *v, const char *name,
>>      int value;
>>      Error *err = NULL;
>>
>> -    visit_type_enum(v, name, &value, prop->strings, &err);
>> +    visit_type_enum(v, name, &value, prop->strings, prop->nstrings, &err);
>>      if (err) {
>>          error_propagate(errp, err);
>>          return;
>> @@ -1978,6 +1979,7 @@ static void property_release_enum(Object *obj, const char *name,
>>  void object_property_add_enum(Object *obj, const char *name,
>>                                const char *typename,
>>                                const char * const *strings,
>> +                              int nstrings,
>>                                int (*get)(Object *, Error **),
>>                                void (*set)(Object *, int, Error **),
>>                                Error **errp)
>> @@ -1986,6 +1988,7 @@ void object_property_add_enum(Object *obj, const char *name,
>>      EnumProperty *prop = g_malloc(sizeof(*prop));
>>
>>      prop->strings = strings;
>> +    prop->nstrings = nstrings;
>>      prop->get = get;
>>      prop->set = set;
>>
>> @@ -2003,6 +2006,7 @@ void object_property_add_enum(Object *obj, const char *name,
>>  void object_class_property_add_enum(ObjectClass *klass, const char *name,
>>                                      const char *typename,
>>                                      const char * const *strings,
>> +                                    int nstrings,
>>                                      int (*get)(Object *, Error **),
>>                                      void (*set)(Object *, int, Error **),
>>                                      Error **errp)
>> @@ -2011,6 +2015,7 @@ void object_class_property_add_enum(ObjectClass *klass, const char *name,
>>      EnumProperty *prop = g_malloc(sizeof(*prop));
>>
>>      prop->strings = strings;
>> +    prop->nstrings = nstrings;
>>      prop->get = get;
>>      prop->set = set;
>>
>> diff --git a/tests/check-qom-proplist.c b/tests/check-qom-proplist.c
>> index 432b66585f..1179030248 100644
>> --- a/tests/check-qom-proplist.c
>> +++ b/tests/check-qom-proplist.c
>> @@ -143,6 +143,7 @@ static void dummy_class_init(ObjectClass *cls, void *data)
>>      object_class_property_add_enum(cls, "av",
>>                                     "DummyAnimal",
>>                                     dummy_animal_map,
>> +                                   DUMMY_LAST,
>>                                     dummy_get_av,
>>                                     dummy_set_av,
>>                                     NULL);
>> diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c
>> index 1969733971..4da5d02c35 100644
>> --- a/tests/test-qobject-input-visitor.c
>> +++ b/tests/test-qobject-input-visitor.c
>> @@ -1110,7 +1110,7 @@ static void test_visitor_in_fail_struct_missing(TestInputVisitorData *data,
>>      error_free_or_abort(&err);
>>      visit_optional(v, "optional", &present);
>>      g_assert(!present);
>> -    visit_type_enum(v, "enum", &en, EnumOne_lookup, &err);
>> +    visit_type_enum(v, "enum", &en, EnumOne_lookup, ENUM_ONE__MAX, &err);
>>      error_free_or_abort(&err);
>>      visit_type_int(v, "i64", &i64, &err);
>>      error_free_or_abort(&err);
>
> Missing: a review of FOO_lookup[] uses outside these two, to make sure
> none of them fall into holes like input_type_enum() would.  From the top
> of my head: qapi_enum_parse().  A quick grep for loops counting up to
> FOO__MAX additionally finds get_event_by_name() in blkdebug.c,
> parse_read_pattern() in quorum.c, hmp_migrate_set_capability() in hmp.c,
> and then I stopped looking.  Most (or all?) of them should use
> qapi_enum_parse().
>
> There's one patch hunk to make input_type_enum() cope with holes, one
> patch hunk to opportunistically simplify output_type_enum(), and all the
> others are for plumbing the table size to these two.  That's a lot of
> plumbing.  Can't say I like it.
>
> Alternatives:
>
> (1) Use a value other than NULL for holes, say ""
>
> (2) Use a value other than NULL for the sentinel, say ""
>
> (3) Store the length in the lookup table, i.e. change it from
>     const char *const[] to struct { int n, const char *const s[] }.
>
> The least work is probably (1).  Slightly ugly.
>
> If you do (3), please consider getting rid of the sentinel.
>

I have done (3)

-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 07/26] qapi: add 'if' condition on top-level schema elements
  2017-08-16 15:43   ` Markus Armbruster
  2017-08-17  5:50     ` Markus Armbruster
@ 2017-08-22 11:17     ` Marc-André Lureau
  2017-08-22 16:52       ` Markus Armbruster
  1 sibling, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2017-08-22 11:17 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU, Michael Roth

Hi

On Wed, Aug 16, 2017 at 5:43 PM, Markus Armbruster <armbru@redhat.com> wrote:
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
>> Add 'if' c-preprocessor condition on top-level schema elements:
>> struct, enum, union, alternate, command, event.
>
> An example would be useful here.  Your cover letter has nice ones, would
> be a shame not to preserve them for posterity in the commit log.
>

ok

>> Variants objects types are created outside of #if blocks, since they
>
> What are "variants objects types"?
>

I think I meant implicit objects types, that may be created by variant.

>> may be shared by various types. Even if unused, this shouldn't be an
>> issue, since those are internal types.
>>
>> Note that there is no checks in qapi scripts to verify that elements
>
> there are
>
>> have compatible conditions (ex: if-struct used by a if-foo
>> command). This may thus fail at C build time if they don't share the
>> same subset of conditions.
>
> Fair enough.
>
>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> ---
>>  scripts/qapi.py                         | 153 +++++++++++++++++++++++++-------
>>  scripts/qapi-commands.py                |   4 +-
>>  scripts/qapi-event.py                   |   4 +-
>>  scripts/qapi-introspect.py              |  46 ++++++----
>>  scripts/qapi-types.py                   |  51 +++++++----
>>  scripts/qapi-visit.py                   |  13 ++-
>>  scripts/qapi2texi.py                    |  10 +--
>>  tests/Makefile.include                  |   1 +
>>  tests/qapi-schema/bad-if.err            |   1 +
>>  tests/qapi-schema/bad-if.exit           |   1 +
>>  tests/qapi-schema/bad-if.json           |   3 +
>>  tests/qapi-schema/bad-if.out            |   0
>>  tests/qapi-schema/qapi-schema-test.json |  20 +++++
>>  tests/qapi-schema/qapi-schema-test.out  |  31 +++++++
>>  tests/qapi-schema/test-qapi.py          |  21 +++--
>>  15 files changed, 272 insertions(+), 87 deletions(-)
>>  create mode 100644 tests/qapi-schema/bad-if.err
>>  create mode 100644 tests/qapi-schema/bad-if.exit
>>  create mode 100644 tests/qapi-schema/bad-if.json
>>  create mode 100644 tests/qapi-schema/bad-if.out
>>
>> diff --git a/scripts/qapi.py b/scripts/qapi.py
>> index 4ecc19e944..79ba1e93da 100644
>> --- a/scripts/qapi.py
>> +++ b/scripts/qapi.py
>> @@ -639,6 +639,16 @@ def add_name(name, info, meta, implicit=False):
>>      all_names[name] = meta
>>
>>
>> +def check_if(expr, info):
>> +    ifcond = expr.get('if')
>> +    if not ifcond or isinstance(ifcond, str):
>> +        return
>> +    if (not isinstance(ifcond, list) or
>> +        any([not isinstance(elt, str) for elt in ifcond])):
>> +        raise QAPISemError(info, "'if' condition requires a string or "
>> +                           "a list of string")
>
> Wait a second!  What's this list business?  The commit message doesn't
> say.

Updated commit message, and documented in docs/devel/qapi-code-gen.txt

>
> Also, pep8 gripes:
>
>     scripts/qapi.py:647:9: E129 visually indented line with same indent as next logical line
>

fixed

>> +
>> +
>>  def check_type(info, source, value, allow_array=False,
>>                 allow_dict=False, allow_optional=False,
>>                 allow_metas=[]):
>> @@ -865,6 +875,7 @@ def check_keys(expr_elem, meta, required, optional=[]):
>>      expr = expr_elem['expr']
>>      info = expr_elem['info']
>>      name = expr[meta]
>> +    optional.append('if')
>
> Caution!
>
>     $ python
>     Python 2.7.13 (default, May 10 2017, 20:04:36)
>     >>> def surprise(arg=[]):
>     ...     arg.append('if')
>     ...     return arg
>     ...
>     >>> surprise()
>     ['if']
>     >>> surprise()
>     ['if', 'if']
>     >>> surprise()
>     ['if', 'if', 'if']
>
> Never modify an argument that has list or dictionary default value.  To
> avoid the temptation, never use such defaul values.

Oops! Without bad consequences here, but fixed anyway.

>
>>      if not isinstance(name, str):
>>          raise QAPISemError(info, "'%s' key must have a string value" % meta)
>>      required = required + [meta]
>> @@ -880,6 +891,8 @@ def check_keys(expr_elem, meta, required, optional=[]):
>>              raise QAPISemError(info,
>>                                 "'%s' of %s '%s' should only use true value"
>>                                 % (key, meta, name))
>> +        if key == 'if':
>> +            check_if(expr, info)
>>      for key in required:
>>          if key not in expr:
>>              raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
>> @@ -989,6 +1002,10 @@ class QAPISchemaEntity(object):
>>          # such place).
>>          self.info = info
>>          self.doc = doc
>> +        self.ifcond = None
>> +
>> +    def set_ifcond(self, ifcond):
>> +        self.ifcond = ifcond
>
> @ifcond is an awkward name, but I don't have better ideas.

Yeah, I got used to it now ;)

>
>>
>>      def c_name(self):
>>          return c_name(self.name)
>> @@ -1017,26 +1034,26 @@ class QAPISchemaVisitor(object):
>>      def visit_builtin_type(self, name, info, json_type):
>>          pass
>>
>> -    def visit_enum_type(self, name, info, values, prefix):
>> +    def visit_enum_type(self, name, info, values, prefix, ifcond):
>>          pass
>>
>> -    def visit_array_type(self, name, info, element_type):
>> +    def visit_array_type(self, name, info, element_type, ifcond):
>>          pass
>>
>> -    def visit_object_type(self, name, info, base, members, variants):
>> +    def visit_object_type(self, name, info, base, members, variants, ifcond):
>>          pass
>>
>> -    def visit_object_type_flat(self, name, info, members, variants):
>> +    def visit_object_type_flat(self, name, info, members, variants, ifcond):
>>          pass
>>
>> -    def visit_alternate_type(self, name, info, variants):
>> +    def visit_alternate_type(self, name, info, variants, ifcond):
>>          pass
>>
>>      def visit_command(self, name, info, arg_type, ret_type,
>> -                      gen, success_response, boxed):
>> +                      gen, success_response, boxed, ifcond):
>>          pass
>>
>> -    def visit_event(self, name, info, arg_type, boxed):
>> +    def visit_event(self, name, info, arg_type, boxed, ifcond):
>>          pass
>>
>>
>> @@ -1136,7 +1153,7 @@ class QAPISchemaEnumType(QAPISchemaType):
>>
>>      def visit(self, visitor):
>>          visitor.visit_enum_type(self.name, self.info,
>> -                                self.member_names(), self.prefix)
>> +                                self.member_names(), self.prefix, self.ifcond)
>>
>>
>>  class QAPISchemaArrayType(QAPISchemaType):
>> @@ -1149,6 +1166,7 @@ class QAPISchemaArrayType(QAPISchemaType):
>>      def check(self, schema):
>>          self.element_type = schema.lookup_type(self._element_type_name)
>>          assert self.element_type
>> +        self.ifcond = self.element_type.ifcond
>>
>>      def is_implicit(self):
>>          return True
>> @@ -1166,7 +1184,8 @@ class QAPISchemaArrayType(QAPISchemaType):
>>          return 'array of ' + elt_doc_type
>>
>>      def visit(self, visitor):
>> -        visitor.visit_array_type(self.name, self.info, self.element_type)
>> +        visitor.visit_array_type(self.name, self.info, self.element_type,
>> +                                 self.ifcond)
>>
>>
>>  class QAPISchemaObjectType(QAPISchemaType):
>> @@ -1247,9 +1266,10 @@ class QAPISchemaObjectType(QAPISchemaType):
>>
>>      def visit(self, visitor):
>>          visitor.visit_object_type(self.name, self.info,
>> -                                  self.base, self.local_members, self.variants)
>> +                                  self.base, self.local_members, self.variants,
>> +                                  self.ifcond)
>>          visitor.visit_object_type_flat(self.name, self.info,
>> -                                       self.members, self.variants)
>> +                                       self.members, self.variants, self.ifcond)
>
> You break the line before self.ifcond almost everywhere, and when you
> don't, the line gets long.  This one goes over the limit:
>
>     scripts/qapi.py:1285:80: E501 line too long (80 > 79 characters)
>
> Suggest to break it before self.ifcond everywhere.
>

ok

>>
>>
>>  class QAPISchemaMember(object):
>> @@ -1392,7 +1412,8 @@ class QAPISchemaAlternateType(QAPISchemaType):
>>          return 'value'
>>
>>      def visit(self, visitor):
>> -        visitor.visit_alternate_type(self.name, self.info, self.variants)
>> +        visitor.visit_alternate_type(self.name, self.info,
>> +                                     self.variants, self.ifcond)
>>
>>      def is_empty(self):
>>          return False
>> @@ -1434,7 +1455,8 @@ class QAPISchemaCommand(QAPISchemaEntity):
>>      def visit(self, visitor):
>>          visitor.visit_command(self.name, self.info,
>>                                self.arg_type, self.ret_type,
>> -                              self.gen, self.success_response, self.boxed)
>> +                              self.gen, self.success_response, self.boxed,
>> +                              self.ifcond)
>>
>>
>>  class QAPISchemaEvent(QAPISchemaEntity):
>> @@ -1462,7 +1484,8 @@ class QAPISchemaEvent(QAPISchemaEntity):
>>              raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
>>
>>      def visit(self, visitor):
>> -        visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
>> +        visitor.visit_event(self.name, self.info, self.arg_type, self.boxed,
>> +                            self.ifcond)
>>
>>
>>  class QAPISchema(object):
>> @@ -1481,11 +1504,12 @@ class QAPISchema(object):
>>              print >>sys.stderr, err
>>              exit(1)
>>
>> -    def _def_entity(self, ent):
>> +    def _def_entity(self, ent, ifcond=None):
>>          # Only the predefined types are allowed to not have info
>>          assert ent.info or self._predefining
>>          assert ent.name not in self._entity_dict
>>          self._entity_dict[ent.name] = ent
>> +        ent.set_ifcond(ifcond)
>
> .set_ifcond(None) does the right thing.
>
> However:
>
>>
>>      def lookup_entity(self, name, typ=None):
>>          ent = self._entity_dict.get(name)
>> @@ -1534,11 +1558,11 @@ class QAPISchema(object):
>>      def _make_enum_members(self, values):
>>          return [QAPISchemaMember(v) for v in values]
>>
>> -    def _make_implicit_enum_type(self, name, info, values):
>> +    def _make_implicit_enum_type(self, name, info, values, ifcond):
>>          # See also QAPISchemaObjectTypeMember._pretty_owner()
>>          name = name + 'Kind'   # Use namespace reserved by add_name()
>>          self._def_entity(QAPISchemaEnumType(
>> -            name, info, None, self._make_enum_members(values), None))
>> +            name, info, None, self._make_enum_members(values), None), ifcond)
>>          return name
>
> Why is ifcond not a constructor argument like name, info, and so forth?
> What makes it special?
>

I think it was easier that way (builder pattern), but it make sense as
constructor too. Changed

>>
>>      def _make_array_type(self, element_type, info):
>> @@ -1547,22 +1571,26 @@ class QAPISchema(object):
>>              self._def_entity(QAPISchemaArrayType(name, info, element_type))
>>          return name
>>
>> -    def _make_implicit_object_type(self, name, info, doc, role, members):
>> +    def _make_implicit_object_type(self, name, info, doc, role, members,
>> +                                   ifcond=None):
>>          if not members:
>>              return None
>>          # See also QAPISchemaObjectTypeMember._pretty_owner()
>>          name = 'q_obj_%s-%s' % (name, role)
>> -        if not self.lookup_entity(name, QAPISchemaObjectType):
>> +        if self.lookup_entity(name, QAPISchemaObjectType):
>> +            assert ifcond is None
>> +        else:
>>              self._def_entity(QAPISchemaObjectType(name, info, doc, None,
>> -                                                  members, None))
>> +                                                  members, None), ifcond)
>>          return name
>
> Hmm, this smells like it might be the "variants objects types" mentioned
> in the commit message.
>
> Types made with _make_implicit_object_type():
>
> * The wrapper around "simple" union members, by _make_simple_variant()
>
> * A flat union's base type when it's implicit, by _def_union_type()
>
> * A command's argument type when it's implicit, by _def_command()
>
> * An event's argument type when it's implicit, by _def_event()
>
> Only the first one can be used more than once, namely when a type occurs
> in more than one simple union.  The "correct" ifcond is the disjunction
> of all its user's ifconds.  You make it use the *first* ifcond instead.
> Wrong: breaks when one of the other simple unions has a condition that
> is true when the first one's is false.
>
> Your commit message suggests you intended to make it unconditional
> instead.  That would work: the worst that can happen is compiling a few
> q_obj_FOO_wrapper typedefs and visit_type_q_FOO_wrapper() functions that
> aren't actually used.  Tolerable, in particular since I hope to get rid
> of "simple" unions some day.
>
> Sadly, it would prevent us from making the visit functions for implicit
> types static, because unused static functions trigger warnings.  Let's
> not worry about that now.
>
> Generating the disjunction of all conditions wouldn't be terribly hard.
> I'm not asking for it, though.

I suggest to tackle it as follow-up then. Added a FIXME


>
> You assert that implicit types are unconditional from the second use on.
> I guess you mean to assert the ones used more than once are
> unconditional:
>
>            typ = self.lookup_entity(name, QAPISchemaObjectType)
>            if typ:
>                assert ifcond is None and typ.ifcond is None
>
> But what you *should* assert is that the conditions are the same:
>
>            typ = self.lookup_entity(name, QAPISchemaObjectType)
>            if typ:
>                assert ifcond == typ.ifcond
>
>>

ok

>>      def _def_enum_type(self, expr, info, doc):
>>          name = expr['enum']
>>          data = expr['data']
>>          prefix = expr.get('prefix')
>> -        self._def_entity(QAPISchemaEnumType(
>> -            name, info, doc, self._make_enum_members(data), prefix))
>> +        return self._def_entity(QAPISchemaEnumType(
>> +            name, info, doc, self._make_enum_members(data), prefix),
>> +                                expr.get('if'))
>
> Covers enumeration types.
>
> Why return?  The (only) caller throws the value away...

left-over, fixed

>
>>
>>      def _make_member(self, name, typ, info):
>>          optional = False
>> @@ -1584,7 +1612,8 @@ class QAPISchema(object):
>>          data = expr['data']
>>          self._def_entity(QAPISchemaObjectType(name, info, doc, base,
>>                                                self._make_members(data, info),
>> -                                              None))
>> +                                              None),
>> +                         expr.get('if'))
>
> Covers struct types.
>
>>
>>      def _make_variant(self, case, typ):
>>          return QAPISchemaObjectTypeVariant(case, typ)
>> @@ -1593,8 +1622,10 @@ class QAPISchema(object):
>>          if isinstance(typ, list):
>>              assert len(typ) == 1
>>              typ = self._make_array_type(typ[0], info)
>> +        type_entity = self.lookup_type(typ)
>>          typ = self._make_implicit_object_type(
>> -            typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
>> +            typ, info, None, 'wrapper',
>> +            [self._make_member('data', typ, info)], type_entity.ifcond)
>>          return QAPISchemaObjectTypeVariant(case, typ)
>
> A simple union member's wrapper type inherits its condition from the
> member type.
>
> I think you need to pass None instead of type_entity.ifcond here.

That doesn't work, visitor symbols are missing in generated code. Why
shouldn't the wrapper share the same condition as the underlying type?

>
>>
>>      def _def_union_type(self, expr, info, doc):
>> @@ -1604,8 +1635,9 @@ class QAPISchema(object):
>>          tag_name = expr.get('discriminator')
>>          tag_member = None
>>          if isinstance(base, dict):
>> -            base = (self._make_implicit_object_type(
>> -                name, info, doc, 'base', self._make_members(base, info)))
>> +            base = self._make_implicit_object_type(
>> +                name, info, doc, 'base', self._make_members(base, info),
>> +                expr.get('if'))
>
> A flat union's implicit base type inherits its condition from the flat
> union.  Good.
>
>>          if tag_name:
>>              variants = [self._make_variant(key, value)
>>                          for (key, value) in data.iteritems()]
>> @@ -1614,14 +1646,16 @@ class QAPISchema(object):
>>              variants = [self._make_simple_variant(key, value, info)
>>                          for (key, value) in data.iteritems()]
>>              typ = self._make_implicit_enum_type(name, info,
>> -                                                [v.name for v in variants])
>> +                                                [v.name for v in variants],
>> +                                                expr.get('if'))
>
> A flat union's implicit enumeration type inherits its condition from the
> flat union.  Good.
>
>>              tag_member = QAPISchemaObjectTypeMember('type', typ, False)
>>              members = [tag_member]
>>          self._def_entity(
>>              QAPISchemaObjectType(name, info, doc, base, members,
>>                                   QAPISchemaObjectTypeVariants(tag_name,
>>                                                                tag_member,
>> -                                                              variants)))
>> +                                                              variants)),
>> +            expr.get('if'))
>
> Covers union types.
>
> Third use of expr.get('if') in this function.  Please put it in a
> variable.
>
> Actually, do that for *all* uses of expr[X] and expr.get(X) in class
> QAPISchema, because that's how the existing code works.

done

>
>>
>>      def _def_alternate_type(self, expr, info, doc):
>>          name = expr['alternate']
>> @@ -1633,7 +1667,8 @@ class QAPISchema(object):
>>              QAPISchemaAlternateType(name, info, doc,
>>                                      QAPISchemaObjectTypeVariants(None,
>>                                                                   tag_member,
>> -                                                                 variants)))
>> +                                                                 variants)),
>> +            expr.get('if'))
>
> Covers alternate types.
>
>>
>>      def _def_command(self, expr, info, doc):
>>          name = expr['command']
>> @@ -1644,12 +1679,14 @@ class QAPISchema(object):
>>          boxed = expr.get('boxed', False)
>>          if isinstance(data, OrderedDict):
>>              data = self._make_implicit_object_type(
>> -                name, info, doc, 'arg', self._make_members(data, info))
>> +                name, info, doc, 'arg', self._make_members(data, info),
>> +                expr.get('if'))
>
> A command's implicit argument type inherits its condition from the
> command.  Good.
>
>>          if isinstance(rets, list):
>>              assert len(rets) == 1
>>              rets = self._make_array_type(rets[0], info)
>>          self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
>> -                                           gen, success_response, boxed))
>> +                                           gen, success_response, boxed),
>> +                         expr.get('if'))
>
> Covers commands.
>
>>
>>      def _def_event(self, expr, info, doc):
>>          name = expr['event']
>> @@ -1657,8 +1694,10 @@ class QAPISchema(object):
>>          boxed = expr.get('boxed', False)
>>          if isinstance(data, OrderedDict):
>>              data = self._make_implicit_object_type(
>> -                name, info, doc, 'arg', self._make_members(data, info))
>> -        self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
>> +                name, info, doc, 'arg', self._make_members(data, info),
>> +                expr.get('if'))
>
> An event's implicit argument type inherits its condition from the
> command.  Good.
>
>> +        self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed),
>> +                         expr.get('if'))
>
> Covers events.  You got them all.  Good.
>
>>
>>      def _def_exprs(self):
>>          for expr_elem in self.exprs:
>> @@ -1848,6 +1887,54 @@ def guardend(name):
>>                   name=guardname(name))
>>
>>
>> +def gen_if(ifcond, func=''):
>> +    if not ifcond:
>> +        return ''
>> +    if isinstance(ifcond, str):
>> +        ifcond = [ifcond]
>> +    ret = '\n'
>> +    for ifc in ifcond:
>> +        ret += mcgen('#if %(ifcond)s /* %(func)s */\n', ifcond=ifc, func=func)
>> +    ret += '\n'
>> +    return ret
>
> Please use mcgen() like the existing code:
>

mcgen() does indent and fails with pre-processor lines. I added a comment

>            ret += mcgen('''
>    #if %(ifcond)s /* %(func)s */
>    ''', ifcond=ifc, func=func)
>
> With the default value of @func, we get a useless, ugly comment /* */.
> If this can happen, please suppress the comment.  Else, drop @func's
> default value.
>
> Lists appear to be conjunctions.  What for?

I added some doc in qapi-code-gen.txt

>
>> +
>> +
>> +def gen_endif(ifcond, func=''):
>> +    if not ifcond:
>> +        return ''
>> +    if isinstance(ifcond, str):
>> +        ifcond = [ifcond]
>> +    ret = '\n'
>> +    for ifc in reversed(ifcond):
>> +        ret += mcgen('#endif /* %(ifcond)s %(func)s */\n',
>> +                     ifcond=ifc, func=func)
>> +    ret += '\n'
>> +    return ret
>
> Likewise.
>
>> +
>> +
>> +# wrap a method to add #if / #endif to generated code
>> +# self must have 'if_members' listing the attributes to wrap
>> +# the last argument of the wrapped function must be the 'ifcond'
>
> Start your sentences with a capital letter, and end them with a period,
> please.

ok

>
>> +def if_wrap(func):
>
> Blank line, please.

ok

>
>> +    def func_wrapper(self, *args, **kwargs):
>> +        funcname = self.__class__.__name__ + '.' + func.__name__
>> +        ifcond = args[-1]
>> +        save = {}
>> +        for mem in self.if_members:
>> +            save[mem] = getattr(self, mem)
>> +        func(self, *args, **kwargs)
>> +        for mem, val in save.items():
>> +            newval = getattr(self, mem)
>> +            if newval != val:
>> +                assert newval.startswith(val)
>> +                newval = newval[len(val):]
>> +                val += gen_if(ifcond, funcname)
>
> Emitting comments pointing to the QAPI schema into the generated code is
> often helpful.  But this one points to QAPI generator code.  Why is that
> useful?

That was mostly useful during development, removed

>
>> +                val += newval
>> +                val += gen_endif(ifcond, funcname)
>> +            setattr(self, mem, val)
>> +
>> +    return func_wrapper
>> +
>
> pep8 again:
>
>     scripts/qapi.py:1955:1: E302 expected 2 blank lines, found 1
>
> Peeking ahead: this function is used as a decorator.  Let's help the
> reader and mention that in the function comment, or by naming the
> function suitably.  ifcond_decorator?

done

>
> Messing with the wrapped method's class's attributes is naughty.  Worse,
> it's hard to understand.  What alternatives have you considered?

Well, I started writing the code that checked if members got code
added, I had to put some enter()/leave() code everywhere. Then I
realize this could easily be the job for a decorator. I think the end
result is pretty neat. If you have a better idea, can you do it in a
follow-up?

>
>>  def gen_enum_lookup(name, values, prefix=None):
>>      ret = mcgen('''
>>
>> diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
>> index 974d0a4a80..19b1bb9b88 100644
>> --- a/scripts/qapi-commands.py
>> +++ b/scripts/qapi-commands.py
>> @@ -228,6 +228,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
>>          self.defn = None
>>          self._regy = None
>>          self._visited_ret_types = None
>> +        self.if_members = ['decl', 'defn', '_regy']
>>
>>      def visit_begin(self, schema):
>>          self.decl = ''
>> @@ -240,8 +241,9 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
>>          self._regy = None
>>          self._visited_ret_types = None
>>
>> +    @if_wrap
>>      def visit_command(self, name, info, arg_type, ret_type,
>> -                      gen, success_response, boxed):
>> +                      gen, success_response, boxed, ifcond):
>>          if not gen:
>>              return
>>          self.decl += gen_command_decl(name, arg_type, boxed, ret_type)
>> diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
>> index bcbef1035f..cad9ece790 100644
>> --- a/scripts/qapi-event.py
>> +++ b/scripts/qapi-event.py
>> @@ -152,6 +152,7 @@ class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
>>          self.decl = None
>>          self.defn = None
>>          self._event_names = None
>> +        self.if_members = ['decl', 'defn']
>>
>>      def visit_begin(self, schema):
>>          self.decl = ''
>> @@ -163,7 +164,8 @@ class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
>>          self.defn += gen_enum_lookup(event_enum_name, self._event_names)
>>          self._event_names = None
>>
>> -    def visit_event(self, name, info, arg_type, boxed):
>> +    @if_wrap
>> +    def visit_event(self, name, info, arg_type, boxed, ifcond):
>>          self.decl += gen_event_send_decl(name, arg_type, boxed)
>>          self.defn += gen_event_send(name, arg_type, boxed)
>>          self._event_names.append(name)
>> diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
>> index fc72cdb66d..ecfb0f2752 100644
>> --- a/scripts/qapi-introspect.py
>> +++ b/scripts/qapi-introspect.py
>> @@ -12,7 +12,7 @@
>>  from qapi import *
>>
>>
>> -def to_qlit(obj, level=0, first_indent=True):
>> +def to_qlit(obj, level=0, first_indent=True, suffix=''):
>>      def indent(level):
>>          return level * 4 * ' '
>>      ret = ''
>> @@ -20,14 +20,20 @@ def to_qlit(obj, level=0, first_indent=True):
>>          ret += indent(level)
>>      if obj is None:
>>          ret += 'QLIT_QNULL'
>> +    elif isinstance(obj, tuple):
>> +        obj, ifcond =  obj
>> +        ret += gen_if(ifcond)
>> +        ret += to_qlit(obj, level, False) + suffix
>
> Keyword argument instead of bare False, please.

ok

>
>> +        ret += gen_endif(ifcond)
>> +        suffix = ''
>
> New case tuple, for generating conditionals.  Okay.
>
>>      elif isinstance(obj, str):
>>          ret += 'QLIT_QSTR(' + '"' + obj.replace('"', r'\"') + '"' + ')'
>>      elif isinstance(obj, list):
>> -        elts = [to_qlit(elt, level + 1)
>> +        elts = [to_qlit(elt, level + 1, True, ",")
>
> Make that
>
>            elts = [to_qlit(elt, level + 1, suffix=",")

ok

>
>>                  for elt in obj]
>>          elts.append(indent(level + 1) + "{ }")
>>          ret += 'QLIT_QLIST(((QLitObject[]) {\n'
>> -        ret += ',\n'.join(elts) + '\n'
>> +        ret += '\n'.join(elts) + '\n'
>>          ret += indent(level) + '}))'
>>      elif isinstance(obj, dict):
>>          elts = [ indent(level + 1) + '{ "%s", %s }' %
>> @@ -39,7 +45,7 @@ def to_qlit(obj, level=0, first_indent=True):
>>          ret += indent(level) + '}))'
>>      else:
>>          assert False                # not implemented
>> -    return ret
>> +    return ret + suffix
>
> This is getting really hard to review --- my brain is about to overflow
> and shut down for the day.  Can you split off some preparatory work?
> The introduction of suffix here, perhaps?

ok

>
>>
>>
>>  class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor):
>> @@ -113,12 +119,12 @@ const QLitObject %(c_name)s = %(c_string)s;
>>              return '[' + self._use_type(typ.element_type) + ']'
>>          return self._name(typ.name)
>>
>> -    def _gen_qlit(self, name, mtype, obj):
>> +    def _gen_qlit(self, name, mtype, obj, ifcond):
>>          if mtype not in ('command', 'event', 'builtin', 'array'):
>>              name = self._name(name)
>>          obj['name'] = name
>>          obj['meta-type'] = mtype
>> -        self._qlits.append(obj)
>> +        self._qlits.append((obj, ifcond))
>>
>>      def _gen_member(self, member):
>>          ret = {'name': member.name, 'type': self._use_type(member.type)}
>> @@ -134,38 +140,40 @@ const QLitObject %(c_name)s = %(c_string)s;
>>          return {'case': variant.name, 'type': self._use_type(variant.type)}
>>
>>      def visit_builtin_type(self, name, info, json_type):
>> -        self._gen_qlit(name, 'builtin', {'json-type': json_type})
>> +        self._gen_qlit(name, 'builtin', {'json-type': json_type}, None)
>>
>> -    def visit_enum_type(self, name, info, values, prefix):
>> -        self._gen_qlit(name, 'enum', {'values': values})
>> +    def visit_enum_type(self, name, info, values, prefix, ifcond):
>> +        self._gen_qlit(name, 'enum', {'values': values}, ifcond)
>>
>> -    def visit_array_type(self, name, info, element_type):
>> +    def visit_array_type(self, name, info, element_type, ifcond):
>>          element = self._use_type(element_type)
>> -        self._gen_qlit('[' + element + ']', 'array', {'element-type': element})
>> +        self._gen_qlit('[' + element + ']', 'array', {'element-type': element},
>> +                       ifcond)
>>
>> -    def visit_object_type_flat(self, name, info, members, variants):
>> +    def visit_object_type_flat(self, name, info, members, variants, ifcond):
>>          obj = {'members': [self._gen_member(m) for m in members]}
>>          if variants:
>>              obj.update(self._gen_variants(variants.tag_member.name,
>>                                            variants.variants))
>> -        self._gen_qlit(name, 'object', obj)
>> +        self._gen_qlit(name, 'object', obj, ifcond)
>>
>> -    def visit_alternate_type(self, name, info, variants):
>> +    def visit_alternate_type(self, name, info, variants, ifcond):
>>          self._gen_qlit(name, 'alternate',
>>                         {'members': [{'type': self._use_type(m.type)}
>> -                                    for m in variants.variants]})
>> +                                    for m in variants.variants]}, ifcond)
>>
>>      def visit_command(self, name, info, arg_type, ret_type,
>> -                      gen, success_response, boxed):
>> +                      gen, success_response, boxed, ifcond):
>>          arg_type = arg_type or self._schema.the_empty_object_type
>>          ret_type = ret_type or self._schema.the_empty_object_type
>>          self._gen_qlit(name, 'command',
>>                         {'arg-type': self._use_type(arg_type),
>> -                        'ret-type': self._use_type(ret_type)})
>> +                        'ret-type': self._use_type(ret_type)}, ifcond)
>>
>> -    def visit_event(self, name, info, arg_type, boxed):
>> +    def visit_event(self, name, info, arg_type, boxed, ifcond):
>>          arg_type = arg_type or self._schema.the_empty_object_type
>> -        self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)})
>> +        self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)},
>> +                       ifcond)
>>
>>  # Debugging aid: unmask QAPI schema's type names
>>  # We normally mask them, because they're not QMP wire ABI
>
> Out of review brainpower for today.  Hope to resume tomorrow.
>
> [...]
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 18/26] qapi: add conditions to REPLICATION type/commands on the schema
  2017-08-17  9:16   ` Markus Armbruster
@ 2017-08-22 11:18     ` Marc-André Lureau
  0 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-08-22 11:18 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Juan Quintela, QEMU, Dr. David Alan Gilbert, zhanghailiang

Hi

On Thu, Aug 17, 2017 at 11:16 AM, Markus Armbruster <armbru@redhat.com> wrote:
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
>> Add #if defined(CONFIG_REPLICATION) in generated code, and adjust the
>> code accordingly.
>>
>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> ---
>>  qapi-schema.json | 12 ++++++++----
>>  migration/colo.c | 14 ++------------
>>  monitor.c        |  5 -----
>>  3 files changed, 10 insertions(+), 21 deletions(-)
>>
>> diff --git a/qapi-schema.json b/qapi-schema.json
>> index bcee3157b0..2f4528c769 100644
>> --- a/qapi-schema.json
>> +++ b/qapi-schema.json
>> @@ -6337,7 +6337,8 @@
>>  # Since: 2.9
>>  ##
>>  { 'command': 'xen-set-replication',
>> -  'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' } }
>> +  'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' },
>> +  'if': 'defined(CONFIG_REPLICATION)' }
>>
>>  ##
>>  # @ReplicationStatus:
>> @@ -6352,7 +6353,8 @@
>>  # Since: 2.9
>>  ##
>>  { 'struct': 'ReplicationStatus',
>> -  'data': { 'error': 'bool', '*desc': 'str' } }
>> +  'data': { 'error': 'bool', '*desc': 'str' },
>> +  'if': 'defined(CONFIG_REPLICATION)' }
>>
>>  ##
>>  # @query-xen-replication-status:
>> @@ -6369,7 +6371,8 @@
>>  # Since: 2.9
>>  ##
>>  { 'command': 'query-xen-replication-status',
>> -  'returns': 'ReplicationStatus' }
>> +  'returns': 'ReplicationStatus',
>> +  'if': 'defined(CONFIG_REPLICATION)' }
>>
>>  ##
>>  # @xen-colo-do-checkpoint:
>> @@ -6385,7 +6388,8 @@
>>  #
>>  # Since: 2.9
>>  ##
>> -{ 'command': 'xen-colo-do-checkpoint' }
>> +{ 'command': 'xen-colo-do-checkpoint',
>> +  'if': 'defined(CONFIG_REPLICATION)' }
>>
>>  ##
>>  # @GICCapability:
>> diff --git a/migration/colo.c b/migration/colo.c
>> index a4255432ac..3bff9fc9a4 100644
>> --- a/migration/colo.c
>> +++ b/migration/colo.c
>> @@ -147,11 +147,11 @@ void colo_do_failover(MigrationState *s)
>>      }
>>  }
>>
>> +#ifdef CONFIG_REPLICATION
>>  void qmp_xen_set_replication(bool enable, bool primary,
>>                               bool has_failover, bool failover,
>>                               Error **errp)
>>  {
>> -#ifdef CONFIG_REPLICATION
>>      ReplicationMode mode = primary ?
>>                             REPLICATION_MODE_PRIMARY :
>>                             REPLICATION_MODE_SECONDARY;
>> @@ -170,14 +170,10 @@ void qmp_xen_set_replication(bool enable, bool primary,
>>          }
>>          replication_stop_all(failover, failover ? NULL : errp);
>>      }
>> -#else
>> -    abort();
>> -#endif
>>  }
>>
>>  ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
>>  {
>> -#ifdef CONFIG_REPLICATION
>>      Error *err = NULL;
>>      ReplicationStatus *s = g_new0(ReplicationStatus, 1);
>>
>> @@ -192,19 +188,13 @@ ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
>>
>>      error_free(err);
>>      return s;
>> -#else
>> -    abort();
>> -#endif
>>  }
>>
>>  void qmp_xen_colo_do_checkpoint(Error **errp)
>>  {
>> -#ifdef CONFIG_REPLICATION
>>      replication_do_checkpoint_all(errp);
>> -#else
>> -    abort();
>> -#endif
>>  }
>> +#endif
>>
>>  static void colo_send_message(QEMUFile *f, COLOMessage msg,
>>                                Error **errp)
>> diff --git a/monitor.c b/monitor.c
>> index 4bf6a3ea2e..383c84d162 100644
>> --- a/monitor.c
>> +++ b/monitor.c
>> @@ -970,11 +970,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
>>   */
>>  static void qmp_unregister_commands_hack(void)
>>  {
>> -#ifndef CONFIG_REPLICATION
>> -    qmp_unregister_command(&qmp_commands, "xen-set-replication");
>> -    qmp_unregister_command(&qmp_commands, "query-xen-replication-status");
>> -    qmp_unregister_command(&qmp_commands, "xen-colo-do-checkpoint");
>> -#endif
>>  #ifndef TARGET_I386
>>      qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection");
>>  #endif
>
> Same drill as for PATCH 16.
>
> Commands you make conditional:
>
> * xen-set-replication, query-xen-replication-status,
>   xen-colo-do-checkpoint
>
>   Before the patch, we first register the commands unconditionally in
>   generated code (requires a stub), then conditionally unregister in
>   qmp_unregister_commands_hack().
>
>   Afterwards, we register only when CONFIG_REPLICATION.  The command
>   fails exactly the same, with CommandNotFound.
>
>   Improvement, because now query-qmp-schema is accurate, and we're one
>   step close to killing qmp_unregister_commands_hack().
>
> Check for completeness by reviewing the case insensitive occurrences of
> replication in the schema not covered by your patch, and review uses of
> CONFIG_REPLICATION for possible schema connections (I did that for
> CONFIG_VNC [PATCH 16] and CONFIG_SPICE [PATCH 17], too):
>
> * enum BlockdevDriver value "replication" in command blockdev-add
>
> * Makefile.objs:block-obj-$(CONFIG_REPLICATION) += replication.o
>   block/Makefile.objs:block-obj-$(CONFIG_REPLICATION) += replication.o
>
> I think the following should be ifdef CONFIG_REPLICATION: enum
> BlockdevDriver value @replication BlockdevOptions variant @replication,
> BlockdevOptionsReplication, BlockdevOptionsReplicationMode.

Added

>
> Some of this analysis should perhaps be worked into the commit message.
>

Done

thanks


-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 21/26] build-sys: make qemu qapi objects per-target
  2017-08-17 11:44   ` Markus Armbruster
@ 2017-08-22 11:18     ` Marc-André Lureau
  0 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-08-22 11:18 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: Paolo Bonzini, QEMU, Stefan Hajnoczi, Michael Roth

Hi

On Thu, Aug 17, 2017 at 1:44 PM, Markus Armbruster <armbru@redhat.com> wrote:
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
>> The qapi schema has per-target definitions. Move qapi objects in the
>> per-target build, so they can be configured at compile time.
>
> Suggest something like:
>
>     QAPI can't do target-specific conditionals (the symbols are
>     poisoned), and the work-around is to pretend the target-specific
>     stuff is target-independent, with stubs for the other targets.
>     Makes the target-specifity invisible in introspection.
>
>     To unpoison the symbols, we need to move the generated QAPI code to
>     the per-target build.
>
>> Keep qapi-types.o qapi-visit.o in util-obj as they are necessary for
>> common code, but they will be overwritten during the target link.
>
> --verbose: how are they supposed to even compile in the
> target-independent build once we generate #if defined(TARGET_FOO) into
> them?
>

For common code, they will compile without TARGET_FOO.


>>                                                                   Add
>> some stubs for block events, in code shared by tools & qemu.
>
> Sounds awkward.

I guess the alternative is to make the objects that depend on it per-target.

>
>> The following patch will configure the schema to conditionally remove
>> per-target disabled features.
>
> "The following patches", right?

yes, fixed

>
>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> ---
>>  stubs/qapi-event.c                 | 74 ++++++++++++++++++++++++++++++++++++++
>>  tests/test-qobject-input-visitor.c |  1 -
>>  Makefile.objs                      |  9 +----
>>  Makefile.target                    |  4 +++
>>  stubs/Makefile.objs                |  1 +
>>  trace/Makefile.objs                |  2 +-
>>  6 files changed, 81 insertions(+), 10 deletions(-)
>>  create mode 100644 stubs/qapi-event.c
>>
>> diff --git a/stubs/qapi-event.c b/stubs/qapi-event.c
>> new file mode 100644
>> index 0000000000..9415299f3a
>> --- /dev/null
>> +++ b/stubs/qapi-event.c
>> @@ -0,0 +1,74 @@
>> +#include "qemu/osdep.h"
>> +#include "qapi-event.h"
>> +
>> +void qapi_event_send_device_tray_moved(const char *device, const char *id,
>> +                                       bool tray_open, Error **errp)
>> +{
>> +}
>> +
>> +void qapi_event_send_quorum_report_bad(QuorumOpType type, bool has_error,
>> +                                       const char *error, const char *node_name,
>> +                                       int64_t sector_num,
>> +                                       int64_t sectors_count, Error **errp)
>> +{
>> +}
>> +
>> +void qapi_event_send_quorum_failure(const char *reference, int64_t sector_num,
>> +                                    int64_t sectors_count, Error **errp)
>> +{
>> +}
>> +
>> +void qapi_event_send_block_job_cancelled(BlockJobType type, const char *device,
>> +                                         int64_t len, int64_t offset,
>> +                                         int64_t speed, Error **errp)
>> +{
>> +}
>> +
>> +void qapi_event_send_block_job_completed(BlockJobType type, const char *device,
>> +                                         int64_t len, int64_t offset,
>> +                                         int64_t speed, bool has_error,
>> +                                         const char *error, Error **errp)
>> +{
>> +}
>> +
>> +void qapi_event_send_block_job_error(const char *device,
>> +                                     IoOperationType operation,
>> +                                     BlockErrorAction action, Error **errp)
>> +{
>> +}
>> +
>> +void qapi_event_send_block_job_ready(BlockJobType type, const char *device,
>> +                                     int64_t len, int64_t offset, int64_t speed,
>> +                                     Error **errp)
>> +{
>> +}
>> +
>> +void qapi_event_send_block_io_error(const char *device, const char *node_name,
>> +                                    IoOperationType operation,
>> +                                    BlockErrorAction action, bool has_nospace,
>> +                                    bool nospace, const char *reason,
>> +                                    Error **errp)
>> +{
>> +}
>> +
>> +void qapi_event_send_block_image_corrupted(const char *device,
>> +                                           bool has_node_name,
>> +                                           const char *node_name,
>> +                                           const char *msg, bool has_offset,
>> +                                           int64_t offset, bool has_size,
>> +                                           int64_t size, bool fatal,
>> +                                           Error **errp)
>> +{
>> +}
>> +
>> +void qapi_event_send_block_write_threshold(const char *node_name,
>> +                                           uint64_t amount_exceeded,
>> +                                           uint64_t write_threshold,
>> +                                           Error **errp)
>> +{
>> +}
>> +
>> +void qapi_event_send_device_deleted(bool has_device, const char *device,
>> +                                    const char *path, Error **errp)
>> +{
>> +}
>
> Yup, awkward.
>
>> diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c
>> index 4da5d02c35..0a9352c5c1 100644
>> --- a/tests/test-qobject-input-visitor.c
>> +++ b/tests/test-qobject-input-visitor.c
>> @@ -1266,7 +1266,6 @@ static void test_visitor_in_qmp_introspect(TestInputVisitorData *data,
>>                                             const void *unused)
>>  {
>>      do_test_visitor_in_qmp_introspect(data, &test_qmp_schema_qlit);
>> -    do_test_visitor_in_qmp_introspect(data, &qmp_schema_qlit);
>>  }
>>
>>  int main(int argc, char **argv)
>
> Either squash this change into PATCH 20, or mention it in the commit
> message.

done

>
> See also my review of PATCH 04.
>
>> diff --git a/Makefile.objs b/Makefile.objs
>> index 24a4ea08b8..2664720f9b 100644
>> --- a/Makefile.objs
>> +++ b/Makefile.objs
>> @@ -2,7 +2,7 @@
>>  # Common libraries for tools and emulators
>>  stub-obj-y = stubs/ crypto/
>>  util-obj-y = util/ qobject/ qapi/
>> -util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
>> +util-obj-y += qapi-types.o qapi-visit.o
>>
>>  chardev-obj-y = chardev/
>>
>> @@ -72,13 +72,6 @@ common-obj-y += chardev/
>>  common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o
>>
>>  common-obj-$(CONFIG_FDT) += device_tree.o
>> -
>> -######################################################################
>> -# qapi
>> -
>> -common-obj-y += qmp-marshal.o
>> -common-obj-y += qmp-introspect.o
>> -common-obj-y += qmp.o hmp.o
>>  endif
>>
>>  #######################################################################
>> diff --git a/Makefile.target b/Makefile.target
>> index 2baec9252f..a97dd056ad 100644
>> --- a/Makefile.target
>> +++ b/Makefile.target
>> @@ -154,6 +154,10 @@ endif
>>
>>  GENERATED_FILES += hmp-commands.h hmp-commands-info.h
>>
>> +obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
>> +obj-y += qmp-marshal.o qmp-introspect.o
>> +obj-y += qmp.o hmp.o
>> +
>
> Moving so much code from "compile once" to "compile per target" is kind
> of sad.  With the full series applied, I see
>
>     $ wc qapi*c qmp*c
>        1528    3089   34576 qapi-event.c
>        5097    9126  107004 qapi-types.c
>       18848   44862  469514 qapi-visit.c
>       12407   32395  404704 qmp-introspect.c
>        6883   14997  182063 qmp-marshal.c
>       44763  104469 1197861 total
>
> Is there any way to split stuff so we recompile less?  Note that this is
> a valid question even without your patches: changing one little thing in
> the QAPI schema commonly triggers a lengthy recompile.  In large part
> because our undisciplined use of #include.  But also because the
> generator's output is monolithic.
>
> I'm not expecting you to answer this question now, I just want to toss
> it out :)
>

Yes, I also think it's the next logical thing to do. We would benefit
from a modular schema, especially common/target split.

>>  endif # CONFIG_SOFTMMU
>>
>>  # Workaround for http://gcc.gnu.org/PR55489, see configure.
>> diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
>> index f5b47bfd74..1b2bef99c9 100644
>> --- a/stubs/Makefile.objs
>> +++ b/stubs/Makefile.objs
>> @@ -21,6 +21,7 @@ stub-obj-y += machine-init-done.o
>>  stub-obj-y += migr-blocker.o
>>  stub-obj-y += monitor.o
>>  stub-obj-y += notify-event.o
>> +stub-obj-y += qapi-event.o
>>  stub-obj-y += qtest.o
>>  stub-obj-y += replay.o
>>  stub-obj-y += runstate-check.o
>> diff --git a/trace/Makefile.objs b/trace/Makefile.objs
>> index afd571c3ec..6447729d60 100644
>> --- a/trace/Makefile.objs
>> +++ b/trace/Makefile.objs
>> @@ -56,4 +56,4 @@ util-obj-$(CONFIG_TRACE_SIMPLE) += simple.o
>>  util-obj-$(CONFIG_TRACE_FTRACE) += ftrace.o
>>  util-obj-y += control.o
>>  target-obj-y += control-target.o
>> -util-obj-y += qmp.o
>> +target-obj-y += qmp.o
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 22/26] qapi: make rtc-reset-reinjection depend on TARGET_I386
  2017-08-17 11:57   ` Markus Armbruster
@ 2017-08-22 11:18     ` Marc-André Lureau
  0 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-08-22 11:18 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU, Dr. David Alan Gilbert

Hi

On Thu, Aug 17, 2017 at 1:57 PM, Markus Armbruster <armbru@redhat.com> wrote:
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> ---
>>  qapi-schema.json |  3 ++-
>>  monitor.c        | 10 ----------
>>  2 files changed, 2 insertions(+), 11 deletions(-)
>>
>> diff --git a/qapi-schema.json b/qapi-schema.json
>> index 2f4528c769..2361c13fc8 100644
>> --- a/qapi-schema.json
>> +++ b/qapi-schema.json
>> @@ -6270,7 +6270,8 @@
>>  # <- { "return": {} }
>>  #
>>  ##
>> -{ 'command': 'rtc-reset-reinjection' }
>> +{ 'command': 'rtc-reset-reinjection',
>> +  'if': ['defined(NEED_CPU_H)', 'defined(TARGET_I386)'] }
>
> Aha, here' you use the list syntax.
>
> And your strategy to keep things compiling also becomes clear: you wrap
> uses of poisoned symbols like TARGET_I386 in #if defined(NEED_CPU_H).
>
> Not exactly elegant, but looks workable.  But you need to explain this
> solution in commit messages [PATCH 21, I guess] and document it in
> qapi-code-gen.txt.  *Unless* we can find a better one.

Updated doc

>
>>
>>  # Rocker ethernet network switch
>>  { 'include': 'qapi/rocker.json' }
>> diff --git a/monitor.c b/monitor.c
>> index 383c84d162..f3dafafa22 100644
>> --- a/monitor.c
>> +++ b/monitor.c
>> @@ -970,9 +970,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
>>   */
>>  static void qmp_unregister_commands_hack(void)
>>  {
>> -#ifndef TARGET_I386
>> -    qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection");
>> -#endif
>>  #ifndef TARGET_S390X
>>      qmp_unregister_command(&qmp_commands, "dump-skeys");
>>  #endif
>> @@ -4151,13 +4148,6 @@ QemuOptsList qemu_mon_opts = {
>>      },
>>  };
>>
>> -#ifndef TARGET_I386
>> -void qmp_rtc_reset_reinjection(Error **errp)
>> -{
>> -    error_setg(errp, QERR_FEATURE_DISABLED, "rtc-reset-reinjection");
>> -}
>> -#endif
>> -
>>  #ifndef TARGET_S390X
>>  void qmp_dump_skeys(const char *filename, Error **errp)
>>  {
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code
  2017-08-17 13:55 ` [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Markus Armbruster
@ 2017-08-22 11:22   ` Marc-André Lureau
  2017-08-22 16:58     ` Markus Armbruster
  0 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2017-08-22 11:22 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: Paolo Bonzini, QEMU

Hi

On Thu, Aug 17, 2017 at 3:55 PM, Markus Armbruster <armbru@redhat.com> wrote:
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
>> Hi,
>>
>> In order to clean-up some hacks in qapi (having to unregister commands
>> at runtime), I proposed a "[PATCH v5 02/20] qapi.py: add a simple #ifdef condition"
>>
>> (see http://lists.gnu.org/archive/html/qemu-devel/2016-08/msg03106.html).
>>
>> However, we decided to drop that patch from the series and solve the
>> problem later. The main issues were:
>> - the syntax was awkward to the JSON schema and documentation
>> - the evaluation of the condition was done in the qapi scripts, with
>>   very limited capability
>> - each target/config would need different generated files.
>>
>> Instead, it could defer the #if evaluation to the C-preprocessor.
>>
>> With this series, top-level qapi JSON entity can take 'if' keys:
>>
>> { 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
>>   'if': 'defined(TEST_IF_STRUCT)' }
>>
>> Members can be exploded as dictionnary with 'type'/'if' keys:
>>
>> { 'struct': 'TestIfStruct', 'data':
>>   { 'foo': 'int',
>>     'bar': { 'type': 'int', 'if': 'defined(TEST_IF_STRUCT_BAR)'} } }
>>
>> Enum values can be exploded as dictionnary with 'type'/'if' keys:
>>
>> { 'enum': 'TestIfEnum', 'data':
>>   [ 'foo',
>>     { 'name' : 'bar', 'if': 'defined(TEST_IF_ENUM_BAR)' } ] }
>>
>> A good benefit from having conditional schema is that introspection
>> will reflect more accurately the capability of the server. Another
>> benefit is that it may help to remove some dead code when disabling a
>> functionality.
>
> This is the main benefit.  Until we realize it, introspection remains
> seriously hobbled.
>
> A few closing remarks.
>
> The general approach "generate the #if for the compiler to evaluate"
> looks sound.
>
> I haven't been able to fully review how it's integrated into the QAPI
> language and how the generators implement it.  I hope a bit of judicious
> patch splitting will help me over the hump.

I have done some patch splitting, that doubles the number of patches though ;)

>
> As so often, solving one problem makes other problems more visible.  In
> this case, the problem that we generate a monolith, and pay for that
> with compile time.  More of it once we compile the monolith per target.

Indeed, it would be nice to improve that soon after.

>
>> Starting from patch "qapi: add conditions to VNC type/commands/events
>> on the schema", the series demonstrate adding conditions, in order to
>> remove qmp_unregister_commands_hack(). However, it feels like I
>> cheated a little bit by using per-target NEED_CPU_H in the headers to
>> avoid #define poison. The alternative could be to split the headers in
>> common/target?
>
> Yup, we could really use a way to modularize the generated code.
>
> If your work leads us to ideas on how to crack the monolith, great.  If
> not, we'll have to decide whether we can live with the compile time hit.
> I'd rather not block your work on cracking the monolith.

I think we could start by splitting the json schemas.

>
>> There are a lot more things we could make conditional in the QAPI
>> schema, like pci/kvm/xen/numa/vde/slirp/posix/win32/vsock/lzo etc etc,
>> however I am still evaluating the implication of such changes both
>> externally and internally, for those interested, I can share my wip
>> branch.
>
> You provide the infrastructure and useful examples of use.  Good enough,
> no need to hunt down *all* uses right away.
>

I am sending a new version,

thanks

-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 07/26] qapi: add 'if' condition on top-level schema elements
  2017-08-22 11:17     ` Marc-André Lureau
@ 2017-08-22 16:52       ` Markus Armbruster
  2017-08-23 12:45         ` Eduardo Habkost
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2017-08-22 16:52 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: QEMU, Michael Roth

Marc-André Lureau <marcandre.lureau@gmail.com> writes:

> Hi
>
> On Wed, Aug 16, 2017 at 5:43 PM, Markus Armbruster <armbru@redhat.com> wrote:
>> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>>
>>> Add 'if' c-preprocessor condition on top-level schema elements:
>>> struct, enum, union, alternate, command, event.
>>
>> An example would be useful here.  Your cover letter has nice ones, would
>> be a shame not to preserve them for posterity in the commit log.
>>
>
> ok
>
>>> Variants objects types are created outside of #if blocks, since they
>>
>> What are "variants objects types"?
>>
>
> I think I meant implicit objects types, that may be created by variant.
>
>>> may be shared by various types. Even if unused, this shouldn't be an
>>> issue, since those are internal types.
>>>
>>> Note that there is no checks in qapi scripts to verify that elements
>>
>> there are
>>
>>> have compatible conditions (ex: if-struct used by a if-foo
>>> command). This may thus fail at C build time if they don't share the
>>> same subset of conditions.
>>
>> Fair enough.
>>
>>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>>> ---
>>>  scripts/qapi.py                         | 153 +++++++++++++++++++++++++-------
>>>  scripts/qapi-commands.py                |   4 +-
>>>  scripts/qapi-event.py                   |   4 +-
>>>  scripts/qapi-introspect.py              |  46 ++++++----
>>>  scripts/qapi-types.py                   |  51 +++++++----
>>>  scripts/qapi-visit.py                   |  13 ++-
>>>  scripts/qapi2texi.py                    |  10 +--
>>>  tests/Makefile.include                  |   1 +
>>>  tests/qapi-schema/bad-if.err            |   1 +
>>>  tests/qapi-schema/bad-if.exit           |   1 +
>>>  tests/qapi-schema/bad-if.json           |   3 +
>>>  tests/qapi-schema/bad-if.out            |   0
>>>  tests/qapi-schema/qapi-schema-test.json |  20 +++++
>>>  tests/qapi-schema/qapi-schema-test.out  |  31 +++++++
>>>  tests/qapi-schema/test-qapi.py          |  21 +++--
>>>  15 files changed, 272 insertions(+), 87 deletions(-)
>>>  create mode 100644 tests/qapi-schema/bad-if.err
>>>  create mode 100644 tests/qapi-schema/bad-if.exit
>>>  create mode 100644 tests/qapi-schema/bad-if.json
>>>  create mode 100644 tests/qapi-schema/bad-if.out
>>>
>>> diff --git a/scripts/qapi.py b/scripts/qapi.py
>>> index 4ecc19e944..79ba1e93da 100644
>>> --- a/scripts/qapi.py
>>> +++ b/scripts/qapi.py
>>> @@ -639,6 +639,16 @@ def add_name(name, info, meta, implicit=False):
>>>      all_names[name] = meta
>>>
>>>
>>> +def check_if(expr, info):
>>> +    ifcond = expr.get('if')
>>> +    if not ifcond or isinstance(ifcond, str):
>>> +        return
>>> +    if (not isinstance(ifcond, list) or
>>> +        any([not isinstance(elt, str) for elt in ifcond])):
>>> +        raise QAPISemError(info, "'if' condition requires a string or "
>>> +                           "a list of string")
>>
>> Wait a second!  What's this list business?  The commit message doesn't
>> say.
>
> Updated commit message, and documented in docs/devel/qapi-code-gen.txt
>
>>
>> Also, pep8 gripes:
>>
>>     scripts/qapi.py:647:9: E129 visually indented line with same indent as next logical line
>>
>
> fixed
>
>>> +
>>> +
>>>  def check_type(info, source, value, allow_array=False,
>>>                 allow_dict=False, allow_optional=False,
>>>                 allow_metas=[]):
>>> @@ -865,6 +875,7 @@ def check_keys(expr_elem, meta, required, optional=[]):
>>>      expr = expr_elem['expr']
>>>      info = expr_elem['info']
>>>      name = expr[meta]
>>> +    optional.append('if')
>>
>> Caution!
>>
>>     $ python
>>     Python 2.7.13 (default, May 10 2017, 20:04:36)
>>     >>> def surprise(arg=[]):
>>     ...     arg.append('if')
>>     ...     return arg
>>     ...
>>     >>> surprise()
>>     ['if']
>>     >>> surprise()
>>     ['if', 'if']
>>     >>> surprise()
>>     ['if', 'if', 'if']
>>
>> Never modify an argument that has list or dictionary default value.  To
>> avoid the temptation, never use such defaul values.
>
> Oops! Without bad consequences here, but fixed anyway.
>
>>
>>>      if not isinstance(name, str):
>>>          raise QAPISemError(info, "'%s' key must have a string value" % meta)
>>>      required = required + [meta]
>>> @@ -880,6 +891,8 @@ def check_keys(expr_elem, meta, required, optional=[]):
>>>              raise QAPISemError(info,
>>>                                 "'%s' of %s '%s' should only use true value"
>>>                                 % (key, meta, name))
>>> +        if key == 'if':
>>> +            check_if(expr, info)
>>>      for key in required:
>>>          if key not in expr:
>>>              raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
>>> @@ -989,6 +1002,10 @@ class QAPISchemaEntity(object):
>>>          # such place).
>>>          self.info = info
>>>          self.doc = doc
>>> +        self.ifcond = None
>>> +
>>> +    def set_ifcond(self, ifcond):
>>> +        self.ifcond = ifcond
>>
>> @ifcond is an awkward name, but I don't have better ideas.
>
> Yeah, I got used to it now ;)
>
>>
>>>
>>>      def c_name(self):
>>>          return c_name(self.name)
>>> @@ -1017,26 +1034,26 @@ class QAPISchemaVisitor(object):
>>>      def visit_builtin_type(self, name, info, json_type):
>>>          pass
>>>
>>> -    def visit_enum_type(self, name, info, values, prefix):
>>> +    def visit_enum_type(self, name, info, values, prefix, ifcond):
>>>          pass
>>>
>>> -    def visit_array_type(self, name, info, element_type):
>>> +    def visit_array_type(self, name, info, element_type, ifcond):
>>>          pass
>>>
>>> -    def visit_object_type(self, name, info, base, members, variants):
>>> +    def visit_object_type(self, name, info, base, members, variants, ifcond):
>>>          pass
>>>
>>> -    def visit_object_type_flat(self, name, info, members, variants):
>>> +    def visit_object_type_flat(self, name, info, members, variants, ifcond):
>>>          pass
>>>
>>> -    def visit_alternate_type(self, name, info, variants):
>>> +    def visit_alternate_type(self, name, info, variants, ifcond):
>>>          pass
>>>
>>>      def visit_command(self, name, info, arg_type, ret_type,
>>> -                      gen, success_response, boxed):
>>> +                      gen, success_response, boxed, ifcond):
>>>          pass
>>>
>>> -    def visit_event(self, name, info, arg_type, boxed):
>>> +    def visit_event(self, name, info, arg_type, boxed, ifcond):
>>>          pass
>>>
>>>
>>> @@ -1136,7 +1153,7 @@ class QAPISchemaEnumType(QAPISchemaType):
>>>
>>>      def visit(self, visitor):
>>>          visitor.visit_enum_type(self.name, self.info,
>>> -                                self.member_names(), self.prefix)
>>> +                                self.member_names(), self.prefix, self.ifcond)
>>>
>>>
>>>  class QAPISchemaArrayType(QAPISchemaType):
>>> @@ -1149,6 +1166,7 @@ class QAPISchemaArrayType(QAPISchemaType):
>>>      def check(self, schema):
>>>          self.element_type = schema.lookup_type(self._element_type_name)
>>>          assert self.element_type
>>> +        self.ifcond = self.element_type.ifcond
>>>
>>>      def is_implicit(self):
>>>          return True
>>> @@ -1166,7 +1184,8 @@ class QAPISchemaArrayType(QAPISchemaType):
>>>          return 'array of ' + elt_doc_type
>>>
>>>      def visit(self, visitor):
>>> -        visitor.visit_array_type(self.name, self.info, self.element_type)
>>> +        visitor.visit_array_type(self.name, self.info, self.element_type,
>>> +                                 self.ifcond)
>>>
>>>
>>>  class QAPISchemaObjectType(QAPISchemaType):
>>> @@ -1247,9 +1266,10 @@ class QAPISchemaObjectType(QAPISchemaType):
>>>
>>>      def visit(self, visitor):
>>>          visitor.visit_object_type(self.name, self.info,
>>> -                                  self.base, self.local_members, self.variants)
>>> +                                  self.base, self.local_members, self.variants,
>>> +                                  self.ifcond)
>>>          visitor.visit_object_type_flat(self.name, self.info,
>>> -                                       self.members, self.variants)
>>> +                                       self.members, self.variants, self.ifcond)
>>
>> You break the line before self.ifcond almost everywhere, and when you
>> don't, the line gets long.  This one goes over the limit:
>>
>>     scripts/qapi.py:1285:80: E501 line too long (80 > 79 characters)
>>
>> Suggest to break it before self.ifcond everywhere.
>>
>
> ok
>
>>>
>>>
>>>  class QAPISchemaMember(object):
>>> @@ -1392,7 +1412,8 @@ class QAPISchemaAlternateType(QAPISchemaType):
>>>          return 'value'
>>>
>>>      def visit(self, visitor):
>>> -        visitor.visit_alternate_type(self.name, self.info, self.variants)
>>> +        visitor.visit_alternate_type(self.name, self.info,
>>> +                                     self.variants, self.ifcond)
>>>
>>>      def is_empty(self):
>>>          return False
>>> @@ -1434,7 +1455,8 @@ class QAPISchemaCommand(QAPISchemaEntity):
>>>      def visit(self, visitor):
>>>          visitor.visit_command(self.name, self.info,
>>>                                self.arg_type, self.ret_type,
>>> -                              self.gen, self.success_response, self.boxed)
>>> +                              self.gen, self.success_response, self.boxed,
>>> +                              self.ifcond)
>>>
>>>
>>>  class QAPISchemaEvent(QAPISchemaEntity):
>>> @@ -1462,7 +1484,8 @@ class QAPISchemaEvent(QAPISchemaEntity):
>>>              raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
>>>
>>>      def visit(self, visitor):
>>> -        visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
>>> +        visitor.visit_event(self.name, self.info, self.arg_type, self.boxed,
>>> +                            self.ifcond)
>>>
>>>
>>>  class QAPISchema(object):
>>> @@ -1481,11 +1504,12 @@ class QAPISchema(object):
>>>              print >>sys.stderr, err
>>>              exit(1)
>>>
>>> -    def _def_entity(self, ent):
>>> +    def _def_entity(self, ent, ifcond=None):
>>>          # Only the predefined types are allowed to not have info
>>>          assert ent.info or self._predefining
>>>          assert ent.name not in self._entity_dict
>>>          self._entity_dict[ent.name] = ent
>>> +        ent.set_ifcond(ifcond)
>>
>> .set_ifcond(None) does the right thing.
>>
>> However:
>>
>>>
>>>      def lookup_entity(self, name, typ=None):
>>>          ent = self._entity_dict.get(name)
>>> @@ -1534,11 +1558,11 @@ class QAPISchema(object):
>>>      def _make_enum_members(self, values):
>>>          return [QAPISchemaMember(v) for v in values]
>>>
>>> -    def _make_implicit_enum_type(self, name, info, values):
>>> +    def _make_implicit_enum_type(self, name, info, values, ifcond):
>>>          # See also QAPISchemaObjectTypeMember._pretty_owner()
>>>          name = name + 'Kind'   # Use namespace reserved by add_name()
>>>          self._def_entity(QAPISchemaEnumType(
>>> -            name, info, None, self._make_enum_members(values), None))
>>> +            name, info, None, self._make_enum_members(values), None), ifcond)
>>>          return name
>>
>> Why is ifcond not a constructor argument like name, info, and so forth?
>> What makes it special?
>>
>
> I think it was easier that way (builder pattern), but it make sense as
> constructor too. Changed
>
>>>
>>>      def _make_array_type(self, element_type, info):
>>> @@ -1547,22 +1571,26 @@ class QAPISchema(object):
>>>              self._def_entity(QAPISchemaArrayType(name, info, element_type))
>>>          return name
>>>
>>> -    def _make_implicit_object_type(self, name, info, doc, role, members):
>>> +    def _make_implicit_object_type(self, name, info, doc, role, members,
>>> +                                   ifcond=None):
>>>          if not members:
>>>              return None
>>>          # See also QAPISchemaObjectTypeMember._pretty_owner()
>>>          name = 'q_obj_%s-%s' % (name, role)
>>> -        if not self.lookup_entity(name, QAPISchemaObjectType):
>>> +        if self.lookup_entity(name, QAPISchemaObjectType):
>>> +            assert ifcond is None
>>> +        else:
>>>              self._def_entity(QAPISchemaObjectType(name, info, doc, None,
>>> -                                                  members, None))
>>> +                                                  members, None), ifcond)
>>>          return name
>>
>> Hmm, this smells like it might be the "variants objects types" mentioned
>> in the commit message.
>>
>> Types made with _make_implicit_object_type():
>>
>> * The wrapper around "simple" union members, by _make_simple_variant()
>>
>> * A flat union's base type when it's implicit, by _def_union_type()
>>
>> * A command's argument type when it's implicit, by _def_command()
>>
>> * An event's argument type when it's implicit, by _def_event()
>>
>> Only the first one can be used more than once, namely when a type occurs
>> in more than one simple union.  The "correct" ifcond is the disjunction
>> of all its user's ifconds.  You make it use the *first* ifcond instead.
>> Wrong: breaks when one of the other simple unions has a condition that
>> is true when the first one's is false.
>>
>> Your commit message suggests you intended to make it unconditional
>> instead.  That would work: the worst that can happen is compiling a few
>> q_obj_FOO_wrapper typedefs and visit_type_q_FOO_wrapper() functions that
>> aren't actually used.  Tolerable, in particular since I hope to get rid
>> of "simple" unions some day.
>>
>> Sadly, it would prevent us from making the visit functions for implicit
>> types static, because unused static functions trigger warnings.  Let's
>> not worry about that now.
>>
>> Generating the disjunction of all conditions wouldn't be terribly hard.
>> I'm not asking for it, though.
>
> I suggest to tackle it as follow-up then. Added a FIXME
>
>
>>
>> You assert that implicit types are unconditional from the second use on.
>> I guess you mean to assert the ones used more than once are
>> unconditional:
>>
>>            typ = self.lookup_entity(name, QAPISchemaObjectType)
>>            if typ:
>>                assert ifcond is None and typ.ifcond is None
>>
>> But what you *should* assert is that the conditions are the same:
>>
>>            typ = self.lookup_entity(name, QAPISchemaObjectType)
>>            if typ:
>>                assert ifcond == typ.ifcond
>>
>>>
>
> ok
>
>>>      def _def_enum_type(self, expr, info, doc):
>>>          name = expr['enum']
>>>          data = expr['data']
>>>          prefix = expr.get('prefix')
>>> -        self._def_entity(QAPISchemaEnumType(
>>> -            name, info, doc, self._make_enum_members(data), prefix))
>>> +        return self._def_entity(QAPISchemaEnumType(
>>> +            name, info, doc, self._make_enum_members(data), prefix),
>>> +                                expr.get('if'))
>>
>> Covers enumeration types.
>>
>> Why return?  The (only) caller throws the value away...
>
> left-over, fixed
>
>>
>>>
>>>      def _make_member(self, name, typ, info):
>>>          optional = False
>>> @@ -1584,7 +1612,8 @@ class QAPISchema(object):
>>>          data = expr['data']
>>>          self._def_entity(QAPISchemaObjectType(name, info, doc, base,
>>>                                                self._make_members(data, info),
>>> -                                              None))
>>> +                                              None),
>>> +                         expr.get('if'))
>>
>> Covers struct types.
>>
>>>
>>>      def _make_variant(self, case, typ):
>>>          return QAPISchemaObjectTypeVariant(case, typ)
>>> @@ -1593,8 +1622,10 @@ class QAPISchema(object):
>>>          if isinstance(typ, list):
>>>              assert len(typ) == 1
>>>              typ = self._make_array_type(typ[0], info)
>>> +        type_entity = self.lookup_type(typ)
>>>          typ = self._make_implicit_object_type(
>>> -            typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
>>> +            typ, info, None, 'wrapper',
>>> +            [self._make_member('data', typ, info)], type_entity.ifcond)
>>>          return QAPISchemaObjectTypeVariant(case, typ)
>>
>> A simple union member's wrapper type inherits its condition from the
>> member type.
>>
>> I think you need to pass None instead of type_entity.ifcond here.
>
> That doesn't work, visitor symbols are missing in generated code. Why
> shouldn't the wrapper share the same condition as the underlying type?

Because it's shared with other simple unions that have the same variant?

>>>
>>>      def _def_union_type(self, expr, info, doc):
>>> @@ -1604,8 +1635,9 @@ class QAPISchema(object):
>>>          tag_name = expr.get('discriminator')
>>>          tag_member = None
>>>          if isinstance(base, dict):
>>> -            base = (self._make_implicit_object_type(
>>> -                name, info, doc, 'base', self._make_members(base, info)))
>>> +            base = self._make_implicit_object_type(
>>> +                name, info, doc, 'base', self._make_members(base, info),
>>> +                expr.get('if'))
>>
>> A flat union's implicit base type inherits its condition from the flat
>> union.  Good.
>>
>>>          if tag_name:
>>>              variants = [self._make_variant(key, value)
>>>                          for (key, value) in data.iteritems()]
>>> @@ -1614,14 +1646,16 @@ class QAPISchema(object):
>>>              variants = [self._make_simple_variant(key, value, info)
>>>                          for (key, value) in data.iteritems()]
>>>              typ = self._make_implicit_enum_type(name, info,
>>> -                                                [v.name for v in variants])
>>> +                                                [v.name for v in variants],
>>> +                                                expr.get('if'))
>>
>> A flat union's implicit enumeration type inherits its condition from the
>> flat union.  Good.
>>
>>>              tag_member = QAPISchemaObjectTypeMember('type', typ, False)
>>>              members = [tag_member]
>>>          self._def_entity(
>>>              QAPISchemaObjectType(name, info, doc, base, members,
>>>                                   QAPISchemaObjectTypeVariants(tag_name,
>>>                                                                tag_member,
>>> -                                                              variants)))
>>> +                                                              variants)),
>>> +            expr.get('if'))
>>
>> Covers union types.
>>
>> Third use of expr.get('if') in this function.  Please put it in a
>> variable.
>>
>> Actually, do that for *all* uses of expr[X] and expr.get(X) in class
>> QAPISchema, because that's how the existing code works.
>
> done
>
>>
>>>
>>>      def _def_alternate_type(self, expr, info, doc):
>>>          name = expr['alternate']
>>> @@ -1633,7 +1667,8 @@ class QAPISchema(object):
>>>              QAPISchemaAlternateType(name, info, doc,
>>>                                      QAPISchemaObjectTypeVariants(None,
>>>                                                                   tag_member,
>>> -                                                                 variants)))
>>> +                                                                 variants)),
>>> +            expr.get('if'))
>>
>> Covers alternate types.
>>
>>>
>>>      def _def_command(self, expr, info, doc):
>>>          name = expr['command']
>>> @@ -1644,12 +1679,14 @@ class QAPISchema(object):
>>>          boxed = expr.get('boxed', False)
>>>          if isinstance(data, OrderedDict):
>>>              data = self._make_implicit_object_type(
>>> -                name, info, doc, 'arg', self._make_members(data, info))
>>> +                name, info, doc, 'arg', self._make_members(data, info),
>>> +                expr.get('if'))
>>
>> A command's implicit argument type inherits its condition from the
>> command.  Good.
>>
>>>          if isinstance(rets, list):
>>>              assert len(rets) == 1
>>>              rets = self._make_array_type(rets[0], info)
>>>          self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
>>> -                                           gen, success_response, boxed))
>>> +                                           gen, success_response, boxed),
>>> +                         expr.get('if'))
>>
>> Covers commands.
>>
>>>
>>>      def _def_event(self, expr, info, doc):
>>>          name = expr['event']
>>> @@ -1657,8 +1694,10 @@ class QAPISchema(object):
>>>          boxed = expr.get('boxed', False)
>>>          if isinstance(data, OrderedDict):
>>>              data = self._make_implicit_object_type(
>>> -                name, info, doc, 'arg', self._make_members(data, info))
>>> -        self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
>>> +                name, info, doc, 'arg', self._make_members(data, info),
>>> +                expr.get('if'))
>>
>> An event's implicit argument type inherits its condition from the
>> command.  Good.
>>
>>> +        self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed),
>>> +                         expr.get('if'))
>>
>> Covers events.  You got them all.  Good.
>>
>>>
>>>      def _def_exprs(self):
>>>          for expr_elem in self.exprs:
>>> @@ -1848,6 +1887,54 @@ def guardend(name):
>>>                   name=guardname(name))
>>>
>>>
>>> +def gen_if(ifcond, func=''):
>>> +    if not ifcond:
>>> +        return ''
>>> +    if isinstance(ifcond, str):
>>> +        ifcond = [ifcond]
>>> +    ret = '\n'
>>> +    for ifc in ifcond:
>>> +        ret += mcgen('#if %(ifcond)s /* %(func)s */\n', ifcond=ifc, func=func)
>>> +    ret += '\n'
>>> +    return ret
>>
>> Please use mcgen() like the existing code:
>>
>
> mcgen() does indent and fails with pre-processor lines. I added a comment

I see.  Comment is good enough for now.

>>            ret += mcgen('''
>>    #if %(ifcond)s /* %(func)s */
>>    ''', ifcond=ifc, func=func)
>>
>> With the default value of @func, we get a useless, ugly comment /* */.
>> If this can happen, please suppress the comment.  Else, drop @func's
>> default value.
>>
>> Lists appear to be conjunctions.  What for?
>
> I added some doc in qapi-code-gen.txt
>
>>
>>> +
>>> +
>>> +def gen_endif(ifcond, func=''):
>>> +    if not ifcond:
>>> +        return ''
>>> +    if isinstance(ifcond, str):
>>> +        ifcond = [ifcond]
>>> +    ret = '\n'
>>> +    for ifc in reversed(ifcond):
>>> +        ret += mcgen('#endif /* %(ifcond)s %(func)s */\n',
>>> +                     ifcond=ifc, func=func)
>>> +    ret += '\n'
>>> +    return ret
>>
>> Likewise.
>>
>>> +
>>> +
>>> +# wrap a method to add #if / #endif to generated code
>>> +# self must have 'if_members' listing the attributes to wrap
>>> +# the last argument of the wrapped function must be the 'ifcond'
>>
>> Start your sentences with a capital letter, and end them with a period,
>> please.
>
> ok
>
>>
>>> +def if_wrap(func):
>>
>> Blank line, please.
>
> ok
>
>>
>>> +    def func_wrapper(self, *args, **kwargs):
>>> +        funcname = self.__class__.__name__ + '.' + func.__name__
>>> +        ifcond = args[-1]
>>> +        save = {}
>>> +        for mem in self.if_members:
>>> +            save[mem] = getattr(self, mem)
>>> +        func(self, *args, **kwargs)
>>> +        for mem, val in save.items():
>>> +            newval = getattr(self, mem)
>>> +            if newval != val:
>>> +                assert newval.startswith(val)
>>> +                newval = newval[len(val):]
>>> +                val += gen_if(ifcond, funcname)
>>
>> Emitting comments pointing to the QAPI schema into the generated code is
>> often helpful.  But this one points to QAPI generator code.  Why is that
>> useful?
>
> That was mostly useful during development, removed
>
>>
>>> +                val += newval
>>> +                val += gen_endif(ifcond, funcname)
>>> +            setattr(self, mem, val)
>>> +
>>> +    return func_wrapper
>>> +
>>
>> pep8 again:
>>
>>     scripts/qapi.py:1955:1: E302 expected 2 blank lines, found 1
>>
>> Peeking ahead: this function is used as a decorator.  Let's help the
>> reader and mention that in the function comment, or by naming the
>> function suitably.  ifcond_decorator?
>
> done
>
>>
>> Messing with the wrapped method's class's attributes is naughty.  Worse,
>> it's hard to understand.  What alternatives have you considered?
>
> Well, I started writing the code that checked if members got code
> added, I had to put some enter()/leave() code everywhere. Then I
> realize this could easily be the job for a decorator. I think the end
> result is pretty neat.

I think "clever" would describe it better than "neat".  Possibly "too
clever".

>                        If you have a better idea, can you do it in a
> follow-up?

I need to complete review before I can tell.

>>
>>>  def gen_enum_lookup(name, values, prefix=None):
>>>      ret = mcgen('''
>>>
[...]

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code
  2017-08-22 11:22   ` Marc-André Lureau
@ 2017-08-22 16:58     ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2017-08-22 16:58 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: Paolo Bonzini, QEMU

Marc-André Lureau <marcandre.lureau@gmail.com> writes:

> Hi
>
> On Thu, Aug 17, 2017 at 3:55 PM, Markus Armbruster <armbru@redhat.com> wrote:
>> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>>
>>> Hi,
>>>
>>> In order to clean-up some hacks in qapi (having to unregister commands
>>> at runtime), I proposed a "[PATCH v5 02/20] qapi.py: add a simple #ifdef condition"
>>>
>>> (see http://lists.gnu.org/archive/html/qemu-devel/2016-08/msg03106.html).
>>>
>>> However, we decided to drop that patch from the series and solve the
>>> problem later. The main issues were:
>>> - the syntax was awkward to the JSON schema and documentation
>>> - the evaluation of the condition was done in the qapi scripts, with
>>>   very limited capability
>>> - each target/config would need different generated files.
>>>
>>> Instead, it could defer the #if evaluation to the C-preprocessor.
>>>
>>> With this series, top-level qapi JSON entity can take 'if' keys:
>>>
>>> { 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
>>>   'if': 'defined(TEST_IF_STRUCT)' }
>>>
>>> Members can be exploded as dictionnary with 'type'/'if' keys:
>>>
>>> { 'struct': 'TestIfStruct', 'data':
>>>   { 'foo': 'int',
>>>     'bar': { 'type': 'int', 'if': 'defined(TEST_IF_STRUCT_BAR)'} } }
>>>
>>> Enum values can be exploded as dictionnary with 'type'/'if' keys:
>>>
>>> { 'enum': 'TestIfEnum', 'data':
>>>   [ 'foo',
>>>     { 'name' : 'bar', 'if': 'defined(TEST_IF_ENUM_BAR)' } ] }
>>>
>>> A good benefit from having conditional schema is that introspection
>>> will reflect more accurately the capability of the server. Another
>>> benefit is that it may help to remove some dead code when disabling a
>>> functionality.
>>
>> This is the main benefit.  Until we realize it, introspection remains
>> seriously hobbled.
>>
>> A few closing remarks.
>>
>> The general approach "generate the #if for the compiler to evaluate"
>> looks sound.
>>
>> I haven't been able to fully review how it's integrated into the QAPI
>> language and how the generators implement it.  I hope a bit of judicious
>> patch splitting will help me over the hump.
>
> I have done some patch splitting, that doubles the number of patches though ;)

A big pile of patches can look scary, but what really drags out review
is oversized non-trivial patches like PATCH 07.  I can take quick,
refreshing breaks much more easily between patches than within a big &
hairy one.

>> As so often, solving one problem makes other problems more visible.  In
>> this case, the problem that we generate a monolith, and pay for that
>> with compile time.  More of it once we compile the monolith per target.
>
> Indeed, it would be nice to improve that soon after.
>
>>
>>> Starting from patch "qapi: add conditions to VNC type/commands/events
>>> on the schema", the series demonstrate adding conditions, in order to
>>> remove qmp_unregister_commands_hack(). However, it feels like I
>>> cheated a little bit by using per-target NEED_CPU_H in the headers to
>>> avoid #define poison. The alternative could be to split the headers in
>>> common/target?
>>
>> Yup, we could really use a way to modularize the generated code.
>>
>> If your work leads us to ideas on how to crack the monolith, great.  If
>> not, we'll have to decide whether we can live with the compile time hit.
>> I'd rather not block your work on cracking the monolith.
>
> I think we could start by splitting the json schemas.

The way things work, QMP needs to be defined in a single schema.
Doesn't mean we have to generate monoliths from it.

>>> There are a lot more things we could make conditional in the QAPI
>>> schema, like pci/kvm/xen/numa/vde/slirp/posix/win32/vsock/lzo etc etc,
>>> however I am still evaluating the implication of such changes both
>>> externally and internally, for those interested, I can share my wip
>>> branch.
>>
>> You provide the infrastructure and useful examples of use.  Good enough,
>> no need to hunt down *all* uses right away.
>>
>
> I am sending a new version,
>
> thanks

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 07/26] qapi: add 'if' condition on top-level schema elements
  2017-08-22 16:52       ` Markus Armbruster
@ 2017-08-23 12:45         ` Eduardo Habkost
  0 siblings, 0 replies; 69+ messages in thread
From: Eduardo Habkost @ 2017-08-23 12:45 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: Marc-André Lureau, QEMU, Michael Roth

On Tue, Aug 22, 2017 at 06:52:19PM +0200, Markus Armbruster wrote:
> Marc-André Lureau <marcandre.lureau@gmail.com> writes:
[...]
> >>> +    def func_wrapper(self, *args, **kwargs):
> >>> +        funcname = self.__class__.__name__ + '.' + func.__name__
> >>> +        ifcond = args[-1]
> >>> +        save = {}
> >>> +        for mem in self.if_members:
> >>> +            save[mem] = getattr(self, mem)
> >>> +        func(self, *args, **kwargs)
> >>> +        for mem, val in save.items():
> >>> +            newval = getattr(self, mem)
> >>> +            if newval != val:
> >>> +                assert newval.startswith(val)
> >>> +                newval = newval[len(val):]
> >>> +                val += gen_if(ifcond, funcname)
> >>
> >> Emitting comments pointing to the QAPI schema into the generated code is
> >> often helpful.  But this one points to QAPI generator code.  Why is that
> >> useful?
> >
> > That was mostly useful during development, removed
> >
> >>
> >>> +                val += newval
> >>> +                val += gen_endif(ifcond, funcname)
> >>> +            setattr(self, mem, val)
> >>> +
> >>> +    return func_wrapper
> >>> +
> >>
> >> pep8 again:
> >>
> >>     scripts/qapi.py:1955:1: E302 expected 2 blank lines, found 1
> >>
> >> Peeking ahead: this function is used as a decorator.  Let's help the
> >> reader and mention that in the function comment, or by naming the
> >> function suitably.  ifcond_decorator?
> >
> > done
> >
> >>
> >> Messing with the wrapped method's class's attributes is naughty.  Worse,
> >> it's hard to understand.  What alternatives have you considered?
> >
> > Well, I started writing the code that checked if members got code
> > added, I had to put some enter()/leave() code everywhere. Then I
> > realize this could easily be the job for a decorator. I think the end
> > result is pretty neat.
> 
> I think "clever" would describe it better than "neat".  Possibly "too
> clever".

FWIW, I was investigating something else in the series and took a
while to understand how the #if lines were magically appearing on
self.decl and self.defn.

I'd prefer something simpler like:

    def cond(ifcond, s):
        if s:
            return gen_if(ifcond) + s + gen_endif(ifcond)
        return s

    def visit_command(self, name, info, arg_type, ret_type,
                      gen, success_response, boxed, ifcond):
        if not gen:
            return
        self.decl += cond(ifcond, 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)
            self.defn += cond(ifcond,gen_marshal_output(ret_type))
        self.decl += cond(ifcond, gen_marshal_decl(name))
        self.defn += cond(ifcond, gen_marshal(name, arg_type, boxed, ret_type))
        self._regy += cond(ifcond, gen_register_command(name, success_response))

If all callers of some of the gen_*() functions above are wrapped
by ifcond(), then a decorator around them could still be useful.
But preferably if the decorator affects only the function
arguments and return value, not messing with the object
attributes or other state outside the function.

-- 
Eduardo

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 16/26] qapi: add conditions to VNC type/commands/events on the schema
  2017-08-17  8:56     ` Markus Armbruster
@ 2017-08-23 15:07       ` Gerd Hoffmann
  2017-08-23 17:35         ` Eduardo Habkost
  0 siblings, 1 reply; 69+ messages in thread
From: Gerd Hoffmann @ 2017-08-23 15:07 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Marc-André Lureau, Habkost, Eduardo

On Thu, 2017-08-17 at 10:56 +0200, Markus Armbruster wrote:
> Gerd, can we delete the vnc_init_func() stub?  Things still compile
> for
> me when I do.
> 
> diff --git a/include/ui/console.h b/include/ui/console.h
> index 7262bef..2c3b2cd 100644
> --- a/include/ui/console.h
> +++ b/include/ui/console.h
> @@ -484,11 +484,6 @@ static inline QemuOpts *vnc_parse(const char
> *str, Error **errp)
>      error_setg(errp, "VNC support is disabled");
>      return NULL;
>  }
> -static inline int vnc_init_func(void *opaque, QemuOpts *opts, Error
> **errp)
> -{
> -    error_setg(errp, "VNC support is disabled");
> -    return -1;
> -}
>  #endif

Looking at a663fbd9e2f65fae81018d81f231ad79510cf9fb.  Guess we want fix
qemu_opts_foreach to not barf on NULL, then revert that commit, so
f8c75b2486 (adding Eduardo to Cc) works as intended.

Beside that:  The inline for vnc_init_func looks pointless (it's used
via function pointer).  Also it can be empty as it will never be
actually called.  Which is fine, vnc_parse will throw an error if
someone tries to use vnc with a binary without vnc support.

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 16/26] qapi: add conditions to VNC type/commands/events on the schema
  2017-08-17  7:04   ` Markus Armbruster
  2017-08-17  8:56     ` Markus Armbruster
@ 2017-08-23 15:09     ` Gerd Hoffmann
  2017-08-29 10:42     ` Daniel P. Berrange
  2 siblings, 0 replies; 69+ messages in thread
From: Gerd Hoffmann @ 2017-08-23 15:09 UTC (permalink / raw)
  To: Markus Armbruster, Marc-André Lureau
  Cc: qemu-devel, Dr. David Alan Gilbert, Daniel P. Berrange

  Hi,

> Observation: we got >250 lines of VNC stuff in qapi-
> schema.json.  Moving
> them into qapi/vnc.json would permit proper MAINTAINERS
> coverage.  Gerd,
> what do you think?

Yes, looks useful to me.

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 16/26] qapi: add conditions to VNC type/commands/events on the schema
  2017-08-23 15:07       ` Gerd Hoffmann
@ 2017-08-23 17:35         ` Eduardo Habkost
  0 siblings, 0 replies; 69+ messages in thread
From: Eduardo Habkost @ 2017-08-23 17:35 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Markus Armbruster, qemu-devel, Marc-André Lureau

On Wed, Aug 23, 2017 at 05:07:12PM +0200, Gerd Hoffmann wrote:
> On Thu, 2017-08-17 at 10:56 +0200, Markus Armbruster wrote:
> > Gerd, can we delete the vnc_init_func() stub?  Things still compile
> > for
> > me when I do.
> > 
> > diff --git a/include/ui/console.h b/include/ui/console.h
> > index 7262bef..2c3b2cd 100644
> > --- a/include/ui/console.h
> > +++ b/include/ui/console.h
> > @@ -484,11 +484,6 @@ static inline QemuOpts *vnc_parse(const char
> > *str, Error **errp)
> >      error_setg(errp, "VNC support is disabled");
> >      return NULL;
> >  }
> > -static inline int vnc_init_func(void *opaque, QemuOpts *opts, Error
> > **errp)
> > -{
> > -    error_setg(errp, "VNC support is disabled");
> > -    return -1;
> > -}
> >  #endif
> 
> Looking at a663fbd9e2f65fae81018d81f231ad79510cf9fb.  Guess we want fix
> qemu_opts_foreach to not barf on NULL, then revert that commit, so
> f8c75b2486 (adding Eduardo to Cc) works as intended.

I agree with that plan, but I wouldn't mind having the stub
removed in the meantime.

> 
> Beside that:  The inline for vnc_init_func looks pointless (it's used
> via function pointer).  Also it can be empty as it will never be
> actually called.  Which is fine, vnc_parse will throw an error if
> someone tries to use vnc with a binary without vnc support.

True.

-- 
Eduardo

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 16/26] qapi: add conditions to VNC type/commands/events on the schema
  2017-08-17  7:04   ` Markus Armbruster
  2017-08-17  8:56     ` Markus Armbruster
  2017-08-23 15:09     ` Gerd Hoffmann
@ 2017-08-29 10:42     ` Daniel P. Berrange
  2017-08-29 10:46       ` Marc-André Lureau
  2 siblings, 1 reply; 69+ messages in thread
From: Daniel P. Berrange @ 2017-08-29 10:42 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Marc-André Lureau, qemu-devel, Dr. David Alan Gilbert,
	Gerd Hoffmann

On Thu, Aug 17, 2017 at 09:04:38AM +0200, Markus Armbruster wrote:
> Copying our resident VNC maintainer^Wodd fixer Gerd.
> 
> Also copying Dan for QCryptoCipherAlgorithm.
> 
> Gerd, Dan, this patch is about making VNC support visible in
> query-qmp-schema, by having the QAPI generators generate suitable
> ifdeffery.  Bonus: no need for QMP command stubs for
> !defined(CONFIG_VNC).

[snip]

> * QCryptoCipherAlgorithm
> 
>   This:
> 
>     # @des-rfb: RFB specific variant of single DES. Do not use except in VNC.
> 
>   I guess we could compile this out if we wanted to.  I doubt we do, but
>   Dan might have other ideas.

It isn't worth the effort to make that conditionally compiled. We getting
DES from nettle/gcrypt these days, instead of our custom in-tree desrfb.c
file, so its no burden to have it.

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [Qemu-devel] [PATCH 16/26] qapi: add conditions to VNC type/commands/events on the schema
  2017-08-29 10:42     ` Daniel P. Berrange
@ 2017-08-29 10:46       ` Marc-André Lureau
  0 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2017-08-29 10:46 UTC (permalink / raw)
  To: Daniel P. Berrange
  Cc: Markus Armbruster, qemu-devel, Dr. David Alan Gilbert, Gerd Hoffmann

Hi

----- Original Message -----
> On Thu, Aug 17, 2017 at 09:04:38AM +0200, Markus Armbruster wrote:
> > Copying our resident VNC maintainer^Wodd fixer Gerd.
> > 
> > Also copying Dan for QCryptoCipherAlgorithm.
> > 
> > Gerd, Dan, this patch is about making VNC support visible in
> > query-qmp-schema, by having the QAPI generators generate suitable
> > ifdeffery.  Bonus: no need for QMP command stubs for
> > !defined(CONFIG_VNC).
> 
> [snip]
> 
> > * QCryptoCipherAlgorithm
> > 
> >   This:
> >  
> >     # @des-rfb: RFB specific variant of single DES. Do not use except in
> >     VNC.
> > 
> >   I guess we could compile this out if we wanted to.  I doubt we do, but
> >   Dan might have other ideas.
> 
> It isn't worth the effort to make that conditionally compiled. We getting
> DES from nettle/gcrypt these days, instead of our custom in-tree desrfb.c
> file, so its no burden to have it.

fyi, the last iteration of the patch made it conditional too.

^ permalink raw reply	[flat|nested] 69+ messages in thread

end of thread, other threads:[~2017-08-29 10:46 UTC | newest]

Thread overview: 69+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-27 15:41 [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 01/26] qapi: fix type_seen key error Marc-André Lureau
2017-08-15 14:40   ` Markus Armbruster
2017-08-17 23:17     ` Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 02/26] qobject: replace dump_qobject() by qobject_to_string() Marc-André Lureau
2017-08-16  9:02   ` Markus Armbruster
2017-07-27 15:41 ` [Qemu-devel] [PATCH 03/26] qboject: add literal qobject type Marc-André Lureau
2017-08-16  8:59   ` Markus Armbruster
2017-08-22 11:16     ` Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 04/26] qapi: generate a literal qobject for introspection Marc-André Lureau
2017-08-16 10:21   ` Markus Armbruster
2017-08-22 11:17     ` Marc-André Lureau
2017-08-17 11:48   ` Markus Armbruster
2017-07-27 15:41 ` [Qemu-devel] [PATCH 05/26] visitor: pass size of strings array to enum visitor Marc-André Lureau
2017-08-16 12:54   ` Markus Armbruster
2017-08-22 11:17     ` Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 06/26] qapi2texi: minor python code simplification Marc-André Lureau
2017-08-16 12:55   ` Markus Armbruster
2017-07-27 15:41 ` [Qemu-devel] [PATCH 07/26] qapi: add 'if' condition on top-level schema elements Marc-André Lureau
2017-08-16 15:43   ` Markus Armbruster
2017-08-17  5:50     ` Markus Armbruster
2017-08-22 11:17     ` Marc-André Lureau
2017-08-22 16:52       ` Markus Armbruster
2017-08-23 12:45         ` Eduardo Habkost
2017-08-17 11:51   ` Markus Armbruster
2017-07-27 15:41 ` [Qemu-devel] [PATCH 08/26] qapi: add 'if' condition on enum member values Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 09/26] qapi: add 'if' condition on struct member Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 10/26] qapi: add 'if' condition on union variant Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 11/26] qapi: add 'if' condition on alternate variant Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 12/26] qapi2texi: add 'If:' section to generated documentation Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 13/26] qapi2texi: add 'If:' condition to enum values Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 14/26] qapi2texi: add 'If:' condition to struct members Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 15/26] qapi2texi: add condition to variants Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 16/26] qapi: add conditions to VNC type/commands/events on the schema Marc-André Lureau
2017-07-28 19:00   ` Dr. David Alan Gilbert
2017-08-17  6:32     ` Markus Armbruster
2017-08-17  9:33       ` Dr. David Alan Gilbert
2017-08-17  7:04   ` Markus Armbruster
2017-08-17  8:56     ` Markus Armbruster
2017-08-23 15:07       ` Gerd Hoffmann
2017-08-23 17:35         ` Eduardo Habkost
2017-08-23 15:09     ` Gerd Hoffmann
2017-08-29 10:42     ` Daniel P. Berrange
2017-08-29 10:46       ` Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 17/26] qapi: add conditions to SPICE " Marc-André Lureau
2017-08-17  8:10   ` Markus Armbruster
2017-08-17  8:43     ` Markus Armbruster
2017-07-27 15:41 ` [Qemu-devel] [PATCH 18/26] qapi: add conditions to REPLICATION type/commands " Marc-André Lureau
2017-08-17  9:16   ` Markus Armbruster
2017-08-22 11:18     ` Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 19/26] build-sys: move qapi variables in qapi.mak Marc-André Lureau
2017-08-17  9:19   ` Markus Armbruster
2017-07-27 15:41 ` [Qemu-devel] [PATCH 20/26] tests/qmp-test: add query-qmp-schema test Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 21/26] build-sys: make qemu qapi objects per-target Marc-André Lureau
2017-08-17 11:44   ` Markus Armbruster
2017-08-22 11:18     ` Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 22/26] qapi: make rtc-reset-reinjection depend on TARGET_I386 Marc-André Lureau
2017-08-17 11:57   ` Markus Armbruster
2017-08-22 11:18     ` Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 23/26] qapi: make s390 commands depend on TARGET_S390X Marc-André Lureau
2017-08-17 12:13   ` Markus Armbruster
2017-07-27 15:41 ` [Qemu-devel] [PATCH 24/26] qapi: make query-gic-capabilities depend on TARGET_ARM Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 25/26] qapi: make query-cpu-model-expansion depend on s390 or x86 Marc-André Lureau
2017-07-27 15:41 ` [Qemu-devel] [PATCH 26/26] qapi: make query-cpu-definitions depend on specific targets Marc-André Lureau
2017-08-17 12:30   ` Markus Armbruster
2017-08-17 12:43     ` Marc-André Lureau
2017-08-17 13:55 ` [Qemu-devel] [PATCH 00/26] qapi: add #if pre-processor conditions to generated code Markus Armbruster
2017-08-22 11:22   ` Marc-André Lureau
2017-08-22 16:58     ` Markus Armbruster

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.