[RFCv2 00/46] RFC: Generate parsexml/formatbuf functions based on directives

Shi Lei shi_lei at massclouds.com
Fri Sep 4 03:34:52 UTC 2020

V1 here: [https://www.redhat.com/archives/libvir-list/2020-June/msg00357.html]

Differ from V1:

  * Move the generator into scripts/xmlgen and rename it 'xmlgen'.

  * Declare virXMLChildNode and virXMLChildNodeSet in libvirt_private.syms.

  * Replace VIR_FREE with g_free and VIR_ALLOC[_N] with g_new0.

  * Adjust virReportError to avoid unnecessary translation.

  * Remove the macro VIR_USED and use G_GNUC_UNUSED to declare arguments.

  * When parsing string member, assign value to it directly instead of
    using middle variable.

  * Don't set libclang_path. Just use python-clang's default setting.

  * Use virEscapeString for escaping xml characters.

  * Enable directive 'genformat' with a parameter to support separation mode.

  * Add directive 'xmlswitch' and 'xmlgroup' to support discriminated unions. 

  * Allow directive 'array' and 'specified' to carry with a parameter,
    which specifies its counterpart explicitly.

  * Enable directive 'xmlattr' with path.

  * Add directive 'formatflag' and 'formathook'.

For those new and changed directives, illustrate them by an example:

struct _virDomainGraphicsAuthDef {  /* genparse, genformat:separate */
    char *passwd;                   /* xmlattr, formatflag:VIR_DOMAIN_DEF_FORMAT_SECURE */
    bool expires;
    time_t validTo;                 /* xmlattr:passwdValidTo, specified:expires */
    virDomainGraphicsAuthConnectedType connected;   /* xmlattr */

struct _virDomainGraphicsListenDef {    /* genparse:withhook, genformat */
    virDomainGraphicsListenType type;   /* xmlattr */
    char *address;                      /* xmlattr, formathook */
    char *network;                      /* xmlattr, formatflag:VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK */
    char *socket;                       /* xmlattr, formathook */
    int fromConfig;                     /* xmlattr, formatflag:%VIR_DOMAIN_DEF_FORMAT_STATUS */
    bool autoGenerated;                 /* xmlattr, formatflag:%VIR_DOMAIN_DEF_FORMAT_STATUS */

struct _virDomainGraphicsSDLDef {   /* genparse, genformat:separate */
    char *display;                  /* xmlattr */
    char *xauth;                    /* xmlattr */
    bool fullscreen;                /* xmlattr */
    virTristateBool gl;             /* xmlattr:gl/enable */

struct _virDomainGraphicsDef {  /* genparse:concisehook, genformat */
    virObjectPtr privateData;
    virDomainGraphicsType type;                         /* xmlattr */

    size_t nListens;
    virDomainGraphicsListenDefPtr listens;  /* xmlelem, array:nListens */

    union {
        virDomainGraphicsSDLDef sdl;                    /* xmlgroup */
        virDomainGraphicsVNCDef vnc;                    /* xmlgroup */
        virDomainGraphicsRDPDef rdp;                    /* xmlgroup */
        virDomainGraphicsDesktopDef desktop;            /* xmlgroup */
        virDomainGraphicsSpiceDef spice;                /* xmlgroup */
        virDomainGraphicsEGLHeadlessDef egl_headless;   /* xmlgroup */
    } data;                                             /* xmlswitch:type */

Explanation for these directives:

    - genformat[:separate|onlyattrs|onlyelems]

      Only work on a struct.
      Generate formatbuf function for this struct only if 'genformat' is specified.
      The function name is based on struct-name and suffixed with 'FormatBuf'.

      When 'genformat:separate' is specified, generate two formatbuf functions
      rather than a single full-mode formatbuf function.
      One for formatting attributes and another for formatting elements.
      These function names are based on struct-name and suffixed with 'FormatAttr'
      and 'FormatElem' respectively.

      The 'onlyattrs' and 'onlyelems' are just like 'separate', but only
      generate one of those two functions according to its denotation.

    - xmlattr[:[parentname/]thename]

      Parse/Format the field as an XML attribute or
      attribute wrapped by an XML element.
      If only 'thename' is specified, use it as the XML attribute name;
      or use the filed name.
      The 'parentname' is the name of the attribute's parent element.
      If 'parentname/thename' is specified, the corresponding form is
      <parentname thename='..' />.

    - xmlgroup

      The field is a struct, but its corresponding form in XML is a group
      rather than an element.

    - xmlswitch:thename

      Only for discriminated union. 'thename' is the name of its relative enum.
      The name of each union member should match a shortname of the enum.

    - array[:countername]

      Parse/Format the field as an array.
      Each array field must have an related counter field, which name is
      specified by 'countername'.
      If 'countername' is omitted, follow the pattern:
      n + 'field_name'.

    - specified[:thename]

      This field has an related field to indicate its existence, and
      'thename' specifies the name of this related field.
      When 'thename' is omitted, follow the pattern:
      'field_name' + '_specified'.

    - formatflag:[!|%]flag

      This field will be formatted and written out to XML only if the 'flag'
      hits a target flagset.
      The target flagset is passed into the formatbuf function through the
      argument 'opaque'.

      Adding a '!' before 'flag' means NOT hitting.

      Adding a '%' before 'flag' means that flag hitting-check is the unique
      condition for formatting this field. For example,
      for 'passwd' in 'virDomainGraphicsAuthDef', the directive is:


      then the generated code:

        if (def->passwd && (virXMLFlag(opaque) & VIR_DOMAIN_DEF_FORMAT_SECURE))
            virBufferEscapeString(buf, " passwd='%s'", def->passwd);

      If '%' is inserted like this:


      then the generated code:

        if ((virXMLFlag(opaque) & VIR_DOMAIN_DEF_FORMAT_SECURE))
            virBufferEscapeString(buf, " passwd='%s'", def->passwd);

    - formathook
      Introduce hooks to handle the field if xmlgen can't deal with it now.

      E.g., virDomainGraphicsListenDef have two fields with 'formathook',
      which are 'address' and 'socket'.
      The xmlgen will generate the declaration of some hooks for formatting
      these fields and developers should implement them.

      1) Check the declaration of hook by a commandline.

        # ./scripts/xmlgen/go show virDomainGraphicsListenDef -kf

        virDomainGraphicsListenDefFormatHook(const virDomainGraphicsListenDef *def,
                                             const void *parent,
                                             const void *opaque,
                                             virBufferPtr addressBuf,
                                             virBufferPtr socketBuf);

        virDomainGraphicsListenDefCheckHook(const virDomainGraphicsListenDef *def,
                                            const void *parent,
                                            void *opaque,
                                            bool result);

      2) Implement these two hooks in src/conf/domain_conf.c.

      2.1) virXXXFormatHook
        It is the hook for formatting field 'address' and 'socket'.
        The 'addressBuf' and 'socketBuf' are used for output destinations respectively.

      2.2) virXXXCheckHook
        For structs, the xmlgen generates virXXXCheck function to come with
        the virXXXFormatBuf. The virXXXCheck reports whether the corresponding
        XML element is null.

        The virXXXCheckHook intercepts the 'result' of virXXXCheck. It changes 'result'
        or just forwards it according to those fields with 'formathook'.


Shi Lei (46):
  scripts: Add a tool to generate xml parse/format functions
  maint: Check python3-clang
  maint: Call xmlgen automatically when c-head-files change
  util: Add some xml-helper-functions to cooperate with xmlgen
  util: Add helper functions for 'bool' and 'time_t' and cooperate with
  util: Add parsexml/formatbuf helper functions for virSocketAddr
  conf: Extract error-checking code from virNetworkDNSTxtDefParseXML
  conf: Replace virNetworkDNSTxtDefParseXML(hardcoded) with
  conf: Generate virNetworkDNSTxtDefFormatBuf
  conf: Extract error-checking code from virNetworkDNSSrvDefParseXML
  conf: Replace virNetworkDNSSrvDefParseXML(hardcoded) with
  conf: Generate virNetworkDNSSrvDefFormatBuf
  conf: Extract error-checking code from virNetworkDNSHostDefParseXML
  conf: Replace virNetworkDNSHostDefParseXML(hardcoded) with
  conf: Generate virNetworkDNSHostDefFormatBuf
  conf: Extract virNetworkDNSForwarderParseXML from
  conf: Replace virNetworkDNSForwarderParseXML(hardcoded) with
  conf: Generate virNetworkDNSForwarderFormatBuf
  conf: Extract error-checking code from virNetworkDNSDefParseXML
  conf: Replace virNetworkDNSDefParseXML(hardcoded) with
  conf: Generate virNetworkDNSDefFormatBuf
  conf: Extract embedded structs from virDomainGraphicsDef as standalone
  conf: Replace virDomainGraphicsDefParseXMLSDL(hardcoded) with
  conf: Generate format functions for virDomainGraphicsSDLDef
  conf: Replace virDomainGraphicsAuthDefParseXML(hardcoded) with
  conf: Generate format functions for virDomainGraphicsAuthDef
  conf: Extract error-checking code from virDomainGraphicsDefParseXMLVNC
  conf: Replace virDomainGraphicsDefParseXMLVNC(hardcoded) with
  conf: Generate virDomainGraphicsVNCDefFormatAttr
  conf: Extract error-checking code from virDomainGraphicsDefParseXMLRDP
  conf: Replace virDomainGraphicsDefParseXMLRDP(hardcoded) with
  conf: Generate virDomainGraphicsRDPDefFormatAttr
  conf: Replace virDomainGraphicsDefParseXMLDesktop(hardcoded) with
  conf: Generate virDomainGraphicsDesktopDefFormatAttr
  conf: Add virSpiceChannelDef to help to parse the member 'channels' of
  conf: Extract error-checking code from
  conf: Replace virDomainGraphicsDefParseXMLSpice(hardcoded) with
  conf: Generate virDomainGraphicsSpiceDefFormatElem and
  conf: Replace virDomainGraphicsDefParseXMLEGLHeadless(hardcoded) with
  conf: Generate virDomainGraphicsEGLHeadlessDefFormatElem
  conf: Extract error-checking code from
  conf: Replace virDomainGraphicsListenDefParseXML(hardcoded) with
  conf: Generate virDomainGraphicsListenDefFormatBuf
  conf: Extract error-checking code from virDomainGraphicsDefParseXML
  conf: Replace virDomainGraphicsDefParseXML(hardcoded) with
  conf: Replace virDomainGraphicsDefFormat(hardcoded) with

 meson.build                      |    5 +
 po/POTFILES.in                   |    3 +
 scripts/meson.build              |    8 +
 scripts/xmlgen/directive.py      | 1115 ++++++++++++++++++++
 scripts/xmlgen/go                |    7 +
 scripts/xmlgen/main.py           |  439 ++++++++
 scripts/xmlgen/utils.py          |  121 +++
 src/conf/domain_conf.c           | 1650 +++++++++---------------------
 src/conf/domain_conf.h           |  179 ++--
 src/conf/meson.build             |   41 +
 src/conf/network_conf.c          |  467 ++-------
 src/conf/network_conf.h          |   54 +-
 src/conf/virconftypes.h          |   18 +
 src/libvirt_private.syms         |    9 +
 src/meson.build                  |    6 +
 src/qemu/qemu_command.c          |    4 +
 src/qemu/qemu_driver.c           |    2 +
 src/qemu/qemu_hotplug.c          |    2 +
 src/qemu/qemu_migration_cookie.c |    1 +
 src/qemu/qemu_process.c          |    5 +
 src/qemu/qemu_validate.c         |    2 +
 src/util/virsocketaddr.c         |   42 +
 src/util/virsocketaddr.h         |   26 +-
 src/util/virstring.c             |   57 ++
 src/util/virstring.h             |    9 +
 src/util/virxml.c                |  105 ++
 src/util/virxml.h                |    6 +
 src/vmx/vmx.c                    |    1 +
 tests/meson.build                |    1 +
 tools/meson.build                |    2 +
 30 files changed, 2738 insertions(+), 1649 deletions(-)
 create mode 100644 scripts/xmlgen/directive.py
 create mode 100755 scripts/xmlgen/go
 create mode 100755 scripts/xmlgen/main.py
 create mode 100644 scripts/xmlgen/utils.py


More information about the libvir-list mailing list