On Fri, Sep 04, 2020 at 11:34:52AM +0800, Shi Lei wrote:
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.
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.
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 :|