qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v3 15/18] qapi: Add new clone visitor


From: Markus Armbruster
Subject: Re: [Qemu-devel] [PATCH v3 15/18] qapi: Add new clone visitor
Date: Mon, 02 May 2016 19:54:42 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.5 (gnu/linux)

Eric Blake <address@hidden> writes:

> We have a couple places in the code base that want to deep-clone
> one QAPI object into another, and they were resorting to serializing
> the struct out to QObject then reparsing it.  A much more efficient
> version can be done by adding a new clone visitor.
>
> Note that we can only clone objects (including alternates) and lists,
> not built-ins.

not built-ins or enums.

>                 This is because of things like visit_type_uint8: our
> visitor only implements a 64-bit callback,

"Our visitor implements" suggests it's the clone visitor's fault.  It's
actually the visitor core's fault, and only since commit 04e070d.

I guess we could clone everything except the integers narrower than 64
bits if we really wanted to.  But all that would buy us now is more
generated code we don't use.

Suggest something like:

  Note that we can only clone objects (including alternates) and lists,
  not built-ins or enums.  The visitor core hides integer width from the
  actual visitor (since commit 04e070d), and as long as that's the case,
  we can't clone integers.  Restricting cloning to just objects and
  lists is cleaner than restricting it to non-integers.

>                                            so we have no indication
> what size int to read from the source, and cannot blindly assume that
> it is safe to read a 64-bit int.  As long as a built-in is not the
> root of the visit, scalars copy over just fine by virtue of a
> g_memdup() each time we push another struct onto the stack.
>
> As such, I tried to document that the clone visitor is for direct
> use only by generated code; other code should stick to wrappers.
>
> Add testsuite coverage for several different clone situations, to
> ensure that the code is working.  I also tested that valgrind was
> happy with the test.
>
> Signed-off-by: Eric Blake <address@hidden>

qapi-types.h grows by 492 lines (19KiB, +19%).  Roughly one non-blank
line per non-simple type, including list types.  It's included all over
the place.

qapi-types.c grows by 4212 lines (92KiB, +90%).

Is it a good idea to generate all clone functions?  As far as I can see,
we use only a fraction of them.

A sufficiently smart link-time optimizer can get rid of the ones we
don't use.  But I consider the "sufficiently smart optimizer" argument a
cop out until proven otherwise.

We already generate plenty of code we don't use, but that's not exactly
a reason for generating more code we don't use.

>
> ---
> v3: new patch
> ---
>  include/qapi/visitor.h       |  27 ++---
>  include/qapi/visitor-impl.h  |   1 +
>  scripts/qapi-types.py        |  42 ++++++++
>  include/qapi/clone-visitor.h |  28 +++++
>  qapi/qapi-visit-core.c       |   1 +
>  qapi/qapi-clone-visitor.c    | 166 ++++++++++++++++++++++++++++++
>  tests/test-clone-visitor.c   | 239 
> +++++++++++++++++++++++++++++++++++++++++++
>  docs/qapi-code-gen.txt       |  38 +++++++
>  qapi/Makefile.objs           |   2 +-
>  tests/.gitignore             |   1 +
>  tests/Makefile               |   5 +-
>  11 files changed, 536 insertions(+), 14 deletions(-)
>  create mode 100644 include/qapi/clone-visitor.h
>  create mode 100644 qapi/qapi-clone-visitor.c
>  create mode 100644 tests/test-clone-visitor.c
>
> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index e8a4403..4c122cc 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -24,14 +24,15 @@
>   * for doing work at each node of a QAPI graph; it can also be used
>   * for a virtual walk, where there is no actual QAPI C struct.
>   *
> - * There are three kinds of visitor classes: input visitors (QMP,
> + * There are four kinds of visitor classes: input visitors (QMP,
>   * string, and QemuOpts) parse an external representation and build
>   * the corresponding QAPI graph, output visitors (QMP, string, and
>   * JSON) take a completed QAPI graph and generate an external
> - * representation, and the dealloc visitor can take a QAPI graph
> - * (possibly partially constructed) and recursively free its
> - * resources.  While the dealloc and QMP input/output visitors are
> - * general, the string and QemuOpts visitors have some implementation
> + * representation, the dealloc visitor can take a QAPI graph (possibly
> + * partially constructed) and recursively free its resources, and the
> + * clone visitor performs a deep clone of one QAPI object to another.
> + * While the dealloc and QMP input/output visitors are general, the
> + * clone, string and QemuOpts visitors have some implementation
>   * limitations; see the documentation for each visitor for more
>   * details on what it supports.  Also, see visitor-impl.h for the
>   * callback contracts implemented by each visitor, and
> @@ -85,16 +86,18 @@
>   * struct.
>   *
>   * Additionally, in qapi-types.h, all QAPI pointer types (structs,
> - * unions, alternates, and lists) have a generated function compatible
> - * with:
> + * unions, alternates, and lists) have two generated functions
> + * compatible with:
>   *
> + * FOO *qapi_FOO_clone(const FOO *src);
>   * void qapi_free_FOO(FOO *obj);
>   *
> - * which behaves like free() in that @obj may be NULL.  Because of
> - * these functions, the dealloc visitor is seldom used directly
> - * outside of generated code.  QAPI types can also inherit from a base
> - * class; when this happens, a function is generated for easily going
> - * from the derived type to the base type:
> + * where the former does a deep clone, and the latter behaves like
> + * free() in that @obj may be NULL.  Because of these functions, the
> + * clone and dealloc visitor are seldom used directly outside of
> + * generated code.  QAPI types can also inherit from a base class;
> + * when this happens, a function is generated for easily going from
> + * the derived type to the base type:
>   *
>   * BASE *qapi_CHILD_base(CHILD *obj);
>   *
> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index 145afd0..a5a2dd0 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -35,6 +35,7 @@ typedef enum VisitorType {
>      VISITOR_INPUT,
>      VISITOR_OUTPUT,
>      VISITOR_DEALLOC,
> +    VISITOR_CLONE,

It's *two* visitors, running in lockstep: an input visitor visiting
@src, and an output visitor visiting the clone.

To which one does the Visitor's type apply?

Let's review how it's used:

* visit_start_struct()

    if (obj && v->type == VISITOR_INPUT) {
        assert(!err != !*obj);
    }

  The clone visitor behaves like an input visitor here.

* visit_start_alternate()

  Likewise.

* visit_type_str()

  Likewise.

* visit_type_any()

  Likewise.

* visit_type_enum()

    if (v->type == VISITOR_INPUT) {
        input_type_enum(v, name, obj, strings, errp);
    } else if (v->type == VISITOR_OUTPUT) {
        output_type_enum(v, name, obj, strings, errp);
    }

  The clone visitor wants to override this completely.

  Hypothetical input from / output to binary visitors would probably
  want the same.  Perhaps this part of commit da72ab0 wasn't a good
  idea.

Overall, this looks like an input visitor to me so far.  But let's have
a look at its code.

>  } VisitorType;
>
>  struct Visitor
> diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
> index 437cf6c..c5ac493 100644
> --- a/scripts/qapi-types.py
> +++ b/scripts/qapi-types.py
> @@ -116,6 +116,38 @@ static inline %(base)s *qapi_%(c_name)s_base(const 
> %(c_name)s *obj)
>                   c_name=c_name(name), base=base.c_name())
>
>
> +def gen_type_clone_decl(name):
> +    return mcgen('''
> +
> +%(c_name)s *qapi_%(c_name)s_clone(const %(c_name)s *src);

Hmm.  It's qapi_free_FOO(), but qapi_FOO_clone().

> +''',
> +                 c_name=c_name(name))
> +
> +
> +def gen_type_clone(name):
> +    ret = mcgen('''
> +
> +%(c_name)s *qapi_%(c_name)s_clone(const %(c_name)s *src)
> +{
> +    QapiCloneVisitor *qcv;
> +    Visitor *v;
> +    %(c_name)s *dst;
> +
> +    if (!src) {
> +        return NULL;
> +    }
> +
> +    qcv = qapi_clone_visitor_new(src);
> +    v = qapi_clone_get_visitor(qcv);
> +    visit_type_%(c_name)s(v, NULL, &dst, &error_abort);
> +    qapi_clone_visitor_cleanup(qcv);
> +    return dst;
> +}
> +''',
> +                c_name=c_name(name))
> +    return ret
> +
> +
>  def gen_variants(variants):
>      ret = mcgen('''
>      union { /* union tag is @%(c_name)s */
> @@ -212,12 +244,16 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
>          if isinstance(element_type, QAPISchemaBuiltinType):
>              self._btin += gen_fwd_object_or_array(name)
>              self._btin += gen_array(name, element_type)
> +            self._btin += gen_type_clone_decl(name)
>              self._btin += gen_type_cleanup_decl(name)
>              if do_builtins:
> +                self.defn += gen_type_clone(name)
>                  self.defn += gen_type_cleanup(name)
>          else:
>              self._fwdecl += gen_fwd_object_or_array(name)
>              self.decl += gen_array(name, element_type)
> +            self.decl += gen_type_clone_decl(name)
> +            self.defn += gen_type_clone(name)
>              self._gen_type_cleanup(name)
>
>      def visit_object_type(self, name, info, base, members, variants):
> @@ -232,11 +268,15 @@ 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.decl += gen_type_clone_decl(name)
> +            self.defn += gen_type_clone(name)
>              self._gen_type_cleanup(name)
>
>      def visit_alternate_type(self, name, info, variants):
>          self._fwdecl += gen_fwd_object_or_array(name)
>          self.decl += gen_object(name, None, [variants.tag_member], variants)
> +        self.decl += gen_type_clone_decl(name)
> +        self.defn += gen_type_clone(name)
>          self._gen_type_cleanup(name)
>
>  # If you link code generated from multiple schemata, you want only one
> @@ -288,7 +328,9 @@ h_comment = '''
>
>  fdef.write(mcgen('''
>  #include "qemu/osdep.h"
> +#include "qapi/clone-visitor.h"
>  #include "qapi/dealloc-visitor.h"
> +#include "qapi/error.h"
>  #include "%(prefix)sqapi-types.h"
>  #include "%(prefix)sqapi-visit.h"
>  ''',
> diff --git a/include/qapi/clone-visitor.h b/include/qapi/clone-visitor.h
> new file mode 100644
> index 0000000..8da5d0f
> --- /dev/null
> +++ b/include/qapi/clone-visitor.h
> @@ -0,0 +1,28 @@
> +/*
> + * Clone Visitor
> + *
> + * Copyright (C) 2016 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.
> + *
> + */
> +
> +#ifndef QAPI_CLONE_VISITOR_H
> +#define QAPI_CLONE_VISITOR_H
> +
> +#include "qapi/visitor.h"
> +
> +typedef struct QapiCloneVisitor QapiCloneVisitor;
> +
> +/* The clone visitor is for use only by generated qapi_FOO_clone()
> + * functions; it requires that the root visit occur on an object,
> + * list, or alternate, and is directly not usable on built-in QAPI
> + * types.
> + */

Wing your winged comments at both ends, please.

> +QapiCloneVisitor *qapi_clone_visitor_new(const void *src);
> +void qapi_clone_visitor_cleanup(QapiCloneVisitor *v);
> +
> +Visitor *qapi_clone_get_visitor(QapiCloneVisitor *v);
> +
> +#endif
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index f5d4b52..838e5d5 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -325,4 +325,5 @@ void visit_type_enum(Visitor *v, const char *name, int 
> *obj,
>      } else if (v->type == VISITOR_OUTPUT) {
>          output_type_enum(v, name, obj, strings, errp);
>      }
> +    /* dealloc and clone visitors have nothing to do */
>  }
> diff --git a/qapi/qapi-clone-visitor.c b/qapi/qapi-clone-visitor.c
> new file mode 100644
> index 0000000..42384d3
> --- /dev/null
> +++ b/qapi/qapi-clone-visitor.c
> @@ -0,0 +1,166 @@
> +/*
> + * Copy one QAPI object to another

Uh, "copy" suggests it's an assignment.  A clone is more.  "Deep copy"
would be better, but I'd say something like "Create a clone of a QAPI
object".

> + *
> + * Copyright (C) 2016 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/clone-visitor.h"
> +#include "qapi/visitor-impl.h"
> +
> +struct QapiCloneVisitor {
> +    Visitor visitor;
> +    const void *root; /* Must be object, alternate, or list */
> +    size_t depth;
> +};
> +
> +static QapiCloneVisitor *to_qcv(Visitor *v)
> +{
> +    return container_of(v, QapiCloneVisitor, visitor);
> +}
> +
> +static void qapi_clone_start_struct(Visitor *v, const char *name, void **obj,
> +                                    size_t size, Error **errp)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +
> +    if (!obj) {
> +        /* Nothing to allocate on the virtual walk during an
> +         * alternate, but we still have to push depth.

I don't quite get this comment.  I understand why we don't memdup() ---
it's pointless without a place to store the duplicate.  I think I
understand why we want to increment qcv->depth unconditionally, but I
might be wrong.  What exactly is the meaning of member depth?

What's the relation to alternate?

I see this is temporary.  Should I not worry and move on?

> +         * FIXME: passing obj to visit_end_struct would make this easier */
> +        assert(qcv->depth);
> +        qcv->depth++;
> +        return;
> +    }
> +
> +    *obj = g_memdup(qcv->depth ? *obj : qcv->root, size);
> +    qcv->depth++;
> +}
> +
> +static void qapi_clone_end(Visitor *v)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +    assert(qcv->depth);
> +    qcv->depth--;
> +}
> +
> +static void qapi_clone_start_list(Visitor *v, const char *name,
> +                                  GenericList **listp, size_t size,
> +                                  Error **errp)
> +{
> +    qapi_clone_start_struct(v, name, (void **)listp, size, errp);
> +}
> +
> +static GenericList *qapi_clone_next_list(Visitor *v, GenericList *tail,
> +                                         size_t size)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +    assert(qcv->depth);
> +    /* Unshare the tail of the list cloned by g_memdup */

Humor me: g_memdup()

> +    tail->next = g_memdup(tail->next, size);
> +    return tail->next;
> +}
> +
> +static void qapi_clone_start_alternate(Visitor *v, const char *name,
> +                                       GenericAlternate **obj, size_t size,
> +                                       bool promote_int, Error **errp)
> +{
> +    qapi_clone_start_struct(v, name, (void **)obj, size, errp);
> +}
> +
> +static void qapi_clone_type_int64(Visitor *v, const char *name, int64_t *obj,
> +                                   Error **errp)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +    assert(qcv->depth);
> +    /* Value was already cloned by g_memdup */
> +}
> +
> +static void qapi_clone_type_uint64(Visitor *v, const char *name,
> +                                    uint64_t *obj, Error **errp)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +    assert(qcv->depth);
> +    /* Value was already cloned by g_memdup */
> +}
> +
> +static void qapi_clone_type_bool(Visitor *v, const char *name, bool *obj,
> +                                  Error **errp)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +    assert(qcv->depth);
> +    /* Value was already cloned by g_memdup */
> +}
> +
> +static void qapi_clone_type_str(Visitor *v, const char *name, char **obj,
> +                                 Error **errp)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +    assert(qcv->depth);
> +    /* Pointer was already cloned by g_memdup; create fresh copy */
> +    *obj = g_strdup(*obj);
> +}
> +
> +static void qapi_clone_type_number(Visitor *v, const char *name, double *obj,
> +                                    Error **errp)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +    assert(qcv->depth);
> +    /* Value was already cloned by g_memdup */
> +}
> +
> +static void qapi_clone_type_any(Visitor *v, const char *name, QObject **obj,
> +                                 Error **errp)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +    assert(qcv->depth);
> +    /* Pointer was already copied by g_memdup; fix the refcount */
> +    qobject_incref(*obj);

'any' values are shared?

> +}
> +
> +static void qapi_clone_type_null(Visitor *v, const char *name, Error **errp)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +    assert(qcv->depth);
> +    /* Nothing to do */
> +}
> +
> +Visitor *qapi_clone_get_visitor(QapiCloneVisitor *v)
> +{
> +    return &v->visitor;
> +}
> +
> +void qapi_clone_visitor_cleanup(QapiCloneVisitor *v)
> +{
> +    g_free(v);
> +}
> +
> +QapiCloneVisitor *qapi_clone_visitor_new(const void *src)
> +{
> +    QapiCloneVisitor *v;
> +
> +    v = g_malloc0(sizeof(*v));
> +    v->root = src;
> +
> +    v->visitor.type = VISITOR_CLONE;
> +    v->visitor.start_struct = qapi_clone_start_struct;
> +    v->visitor.end_struct = qapi_clone_end;
> +    v->visitor.start_list = qapi_clone_start_list;
> +    v->visitor.next_list = qapi_clone_next_list;
> +    v->visitor.end_list = qapi_clone_end;
> +    v->visitor.start_alternate = qapi_clone_start_alternate;
> +    v->visitor.end_alternate = qapi_clone_end;
> +    v->visitor.type_int64 = qapi_clone_type_int64;
> +    v->visitor.type_uint64 = qapi_clone_type_uint64;
> +    v->visitor.type_bool = qapi_clone_type_bool;
> +    v->visitor.type_str = qapi_clone_type_str;
> +    v->visitor.type_number = qapi_clone_type_number;
> +    v->visitor.type_any = qapi_clone_type_any;
> +    v->visitor.type_null = qapi_clone_type_null;
> +
> +    return v;
> +}
[Getting late, skipping the test for now]
> diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
> index d7d6987..92fbb0e 100644
> --- a/docs/qapi-code-gen.txt
> +++ b/docs/qapi-code-gen.txt
> @@ -787,6 +787,8 @@ Example:
>          char *string;
>      };
>
> +    UserDefOne *qapi_UserDefOne_clone(const UserDefOne *src);
> +
>      void qapi_free_UserDefOne(UserDefOne *obj);
>
>      struct UserDefOneList {
> @@ -794,12 +796,31 @@ Example:
>          UserDefOne *value;
>      };
>
> +    UserDefOneList *qapi_UserDefOneList_clone(const UserDefOneList *src);
> +
>      void qapi_free_UserDefOneList(UserDefOneList *obj);
>
>      #endif
>      $ cat qapi-generated/example-qapi-types.c
>  [Uninteresting stuff omitted...]
>
> +    UserDefOne *qapi_UserDefOne_clone(const UserDefOne *src)
> +    {
> +        QapiCloneVisitor *qcv;
> +        Visitor *v;
> +     UserDefOne *dst;

Tab damage.  More of the same below.

> +
> +        if (!src) {
> +            return;
> +        }
> +
> +        qcv = qapi_clone_visitor_new(src);
> +        v = qapi_clone_get_visitor(qcv);
> +        visit_type_UserDefOne(v, NULL, &dst, NULL);
> +        qapi_clone_visitor_cleanup(qcv);
> +     return dst;
> +    }
> +
>      void qapi_free_UserDefOne(UserDefOne *obj)
>      {
>          QapiDeallocVisitor *qdv;
> @@ -815,6 +836,23 @@ Example:
>          qapi_dealloc_visitor_cleanup(qdv);
>      }
>
> +    UserDefOneList *qapi_UserDefOneList_clone(const UserDefOneList *src)
> +    {
> +        QapiCloneVisitor *qcv;
> +        Visitor *v;
> +     UserDefOneList *dst;
> +
> +        if (!dst) {
> +            return;
> +        }
> +
> +        qcv = qapi_clone_visitor_new(src);
> +        v = qapi_clone_get_visitor(qcv);
> +        visit_type_UserDefOneList(v, NULL, &dst, NULL);
> +        qapi_clone_visitor_cleanup(qcv);
> +     return dst;
> +    }
> +
>      void qapi_free_UserDefOneList(UserDefOneList *obj)
>      {
>          QapiDeallocVisitor *qdv;
> diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
> index b60e11b..1406df7 100644
> --- a/qapi/Makefile.objs
> +++ b/qapi/Makefile.objs
> @@ -1,6 +1,6 @@
>  util-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o
>  util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
>  util-obj-y += string-input-visitor.o string-output-visitor.o
> -util-obj-y += opts-visitor.o json-output-visitor.o
> +util-obj-y += opts-visitor.o json-output-visitor.o qapi-clone-visitor.o
>  util-obj-y += qmp-event.o
>  util-obj-y += qapi-util.o
> diff --git a/tests/.gitignore b/tests/.gitignore
> index c2aad79..60ff7cc 100644
> --- a/tests/.gitignore
> +++ b/tests/.gitignore
> @@ -12,6 +12,7 @@ test-aio
>  test-base64
>  test-bitops
>  test-blockjob-txn
> +test-clone-visitor
>  test-coroutine
>  test-crypto-afsplit
>  test-crypto-block
> diff --git a/tests/Makefile b/tests/Makefile
> index 1f8a39d..10ed072 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -24,6 +24,8 @@ check-unit-y += tests/test-qmp-output-visitor$(EXESUF)
>  gcov-files-test-qmp-output-visitor-y = qapi/qmp-output-visitor.c
>  check-unit-y += tests/test-json-output-visitor$(EXESUF)
>  gcov-files-test-json-output-visitor-y = qapi/json-output-visitor.c
> +check-unit-y += tests/test-clone-visitor$(EXESUF)
> +gcov-files-test-clone-visitor-y = qapi/qapi-clone-visitor.c
>  check-unit-y += tests/test-qmp-input-visitor$(EXESUF)
>  gcov-files-test-qmp-input-visitor-y = qapi/qmp-input-visitor.c
>  check-unit-y += tests/test-qmp-input-strict$(EXESUF)
> @@ -390,7 +392,7 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o 
> tests/check-qdict.o \
>       tests/check-qobject-json.o \
>       tests/test-coroutine.o tests/test-string-output-visitor.o \
>       tests/test-string-input-visitor.o tests/test-qmp-output-visitor.o \
> -     tests/test-json-output-visitor.o \
> +     tests/test-clone-visitor.o tests/test-json-output-visitor.o \
>       tests/test-qmp-input-visitor.o tests/test-qmp-input-strict.o \
>       tests/test-qmp-commands.o tests/test-visitor-serialization.o \
>       tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \
> @@ -482,6 +484,7 @@ tests/test-string-input-visitor$(EXESUF): 
> tests/test-string-input-visitor.o $(te
>  tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y)
>  tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o 
> $(test-qapi-obj-y)
>  tests/test-json-output-visitor$(EXESUF): tests/test-json-output-visitor.o 
> $(test-qapi-obj-y)
> +tests/test-clone-visitor$(EXESUF): tests/test-clone-visitor.o 
> $(test-qapi-obj-y)
>  tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o 
> $(test-qapi-obj-y)
>  tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o 
> $(test-qapi-obj-y)
>  tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o 
> tests/test-qmp-marshal.o $(test-qapi-obj-y)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]