On 2020-12-05 at 01:41, DanielP. Berrangé wrote:
On Fri, Sep 04, 2020 at 11:34:52AM +0800, Shi Lei wrote:
> V1 here: [
https://www.redhat.com/archives/libvir-list/2020-June/msg00357.html]
> 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:
>
> formatflag:VIR_DOMAIN_DEF_FORMAT_SECURE
>
> 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:
>
> formatflag:%VIR_DOMAIN_DEF_FORMAT_SECURE
>
> 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
>
> int
> virDomainGraphicsListenDefFormatHook(const virDomainGraphicsListenDef *def,
> const void *parent,
> const void *opaque,
> virBufferPtr addressBuf,
> virBufferPtr socketBuf);
>
> bool
> 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'.
It is probably a good idea to put all this doucmentation from
the cover letter into a docs/xmlgenerator.rst file as it'll be
useful reference for developers in future.
Okay. I'll do it.
> 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 +
I think it'd be a good idea to have a test case to validate the
XML generator. For example create a simpe tests/xmlgen/demo.h that
illustrates all the different features we can use.
Then have tests/xmlgen/demo.generated.{c,h}, and the write a
test case that generates a new copy of demo.generated.{c,h}
and compares to what we have in git.
This will help us validate that changes to the xmlgenerator in
future don't result in unexpected changes to the generated
code.
Yes. I agree.
Regards,
Shi Lei