From nobody Mon Feb 9 16:01:57 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) client-ip=216.205.24.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1619076763; cv=none; d=zohomail.com; s=zohoarc; b=mOZ/8c2oNK4Hj6i0KXT1el0ArN56yRHepPJ0BJ+oR65L+mhwKfVz7QuXJUh0VpOjzBL1/K76d2CrZEymU6j9rDCaxRJ++zSIcvBO0EoBrQ8iRz62iEFDuHKDmmKxcAX0xeADrAv+ghQII88E/I79u4oqfJl++rubZuDOs7Epj9E= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1619076763; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=gG4mfHhV0Qnwf3HdzzEv3uUluJren/tlBZuqJoeDi9g=; b=HsV7H3MxTSUcld/xXaIpjttrePBacFLbTNIknBZ9ZqYlcvM5XeGlONFAmgb6u8LWENWbdF59JOi0a6PvRi/EbjY/2OKlNP/kOAbPBKfkFm8ke1F3eCj/f42+7bSaFucY/JQZymwa3gty1PNu8XLpwplzAKyJgEOINhxk9UYZQNU= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by mx.zohomail.com with SMTPS id 1619076763435525.9987081771113; Thu, 22 Apr 2021 00:32:43 -0700 (PDT) Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-223-RkWVR3xpOCOaVSyaiApX2Q-1; Thu, 22 Apr 2021 03:32:39 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 1C7AE18B9F6A; Thu, 22 Apr 2021 07:32:30 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id E877F189B6; Thu, 22 Apr 2021 07:32:29 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id A360A1806D29; Thu, 22 Apr 2021 07:32:29 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 13M7VYbK023819 for ; Thu, 22 Apr 2021 03:31:34 -0400 Received: by smtp.corp.redhat.com (Postfix) id 651A520FE6D3; Thu, 22 Apr 2021 07:31:34 +0000 (UTC) Received: from mimecast-mx02.redhat.com (mimecast06.extmail.prod.ext.rdu2.redhat.com [10.11.55.22]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 5FB4320FE6D2 for ; Thu, 22 Apr 2021 07:31:34 +0000 (UTC) Received: from us-smtp-1.mimecast.com (us-smtp-2.mimecast.com [205.139.110.61]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 402C8185A7B6 for ; Thu, 22 Apr 2021 07:31:34 +0000 (UTC) Received: from m97136.mail.qiye.163.com (m97136.mail.qiye.163.com [220.181.97.136]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-584-lPvt89MPM5q8wu6TuR-iJA-1; Thu, 22 Apr 2021 03:31:30 -0400 Received: from localhost.localdomain (unknown [58.56.27.130]) by smtp2 (Coremail) with SMTP id iOCowAA3HLYJJYFgMwTJAQ--.5876S6; Thu, 22 Apr 2021 15:26:04 +0800 (CST) X-MC-Unique: RkWVR3xpOCOaVSyaiApX2Q-1 X-MC-Unique: lPvt89MPM5q8wu6TuR-iJA-1 From: Shi Lei To: libvir-list@redhat.com Subject: [RFCv3 04/25] docs: Add xmlgen.rst to explain how to use it Date: Thu, 22 Apr 2021 15:25:12 +0800 Message-Id: <20210422072533.312211-5-shi_lei@massclouds.com> In-Reply-To: <20210422072533.312211-1-shi_lei@massclouds.com> References: <20210422072533.312211-1-shi_lei@massclouds.com> MIME-Version: 1.0 X-CM-TRANSID: iOCowAA3HLYJJYFgMwTJAQ--.5876S6 X-Coremail-Antispam: 1Uf129KBjvAXoWfCF13CryDKr15XFyDGr1Dtrb_yoW5JFy7Ao WI9wnIy3WxKr4rCFWkA3WkWFyUua1vgr1IqF4Y9r98Ga4kXF4UCw1jkw48Ga4fWr4YgF15 WFyxJ34Yqr45tF15n29KB7ZKAUJUUUU8529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvjfUlrcSUUUUU X-Originating-IP: [58.56.27.130] X-CM-SenderInfo: pvklsz1hl6ztxvvfz0xxgvhudrp/1tbiDxp8T1nfQ-T5bQAAsD X-Mimecast-Impersonation-Protect: Policy=CLT - Impersonation Protection Definition; Similar Internal Domain=false; Similar Monitored External Domain=false; Custom External Domain=false; Mimecast External Domain=false; Newly Observed Domain=false; Internal User Name=false; Custom Display Name List=false; Reply-to Address Mismatch=false; Targeted Threat Dictionary=false; Mimecast Threat Dictionary=false; Custom Threat Dictionary=false X-Mimecast-Spam-Signature: yes X-Scanned-By: MIMEDefang 2.78 on 10.11.54.4 X-loop: libvir-list@redhat.com Cc: Shi Lei X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=libvir-list-bounces@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Signed-off-by: Shi Lei --- docs/meson.build | 1 + docs/xmlgen.rst | 684 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 685 insertions(+) create mode 100644 docs/xmlgen.rst diff --git a/docs/meson.build b/docs/meson.build index f550629d..a8a58815 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -124,6 +124,7 @@ docs_rst_files =3D [ 'programming-languages', 'styleguide', 'submitting-patches', + 'xmlgen', ] =20 # list of web targets to build for docs/web rule diff --git a/docs/xmlgen.rst b/docs/xmlgen.rst new file mode 100644 index 00000000..caea1f99 --- /dev/null +++ b/docs/xmlgen.rst @@ -0,0 +1,684 @@ +=3D=3D=3D=3D=3D=3D +xmlgen +=3D=3D=3D=3D=3D=3D + +In libvirt, developers usually have to implement and maintain parse/format= functions. This tool xmlgen aims to generate most of these functions autom= atically and help relieve developers' burden. + +A Quick Start +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Take virNetworkDNSDef for example, which is in 'src/conf/network_conf.h'. + +In the past, we have to manually implement virNetworkDNSDefParseXML and vi= rNetworkDNSFormatBuf. +And now, we can just take several steps to have xmlgen generate these func= tions and apply them into libvirt project. + +Step1. Add directives on the struct's declaration. +-------------------------------------------------- + +Directives for xmlgen are used to help direct the generating process. As b= elow: + + :: + + typedef struct _virNetworkDNSDef virNetworkDNSDef; + struct _virNetworkDNSDef { /* genparse, genformat */ + virTristateBool enable; /* xmlattr */ + virTristateBool forwardPlainNames; /* xmlattr */ + size_t nfwds; + virNetworkDNSForwarder *forwarders; /* xmlelem, array:nfwds */ + size_t ntxts; + virNetworkDNSTxtDef *txts; /* xmlelem, array */ + ... ... + }; + +On the line of struct's declaration, we set two directives **genparse** an= d **genformat**, which direct xmlgen to generate parse/format functions for= this struct respectively. In this example, these functions include virNetw= orkDNSDefParseXML, virNetworkDNSDefFormatBuf and some auxilliary functions.= Other directives are for members. They direct xmlgen to generate code bloc= ks in the parse/format functions. Directive **xmlattr** indicates that the = member matches an xml attribute, and **xmlelem** is for an xml element. Add= itional directive **array** indicates that the member matches an array of x= ml node. + +Step2. Preview generated functions. +----------------------------------- + +By the below command line: + + :: + + # ./scripts/xmlgen/go list + +Got a list of structs detected by xmlgen, including *virNetworkDNSDef*. +Then we execute the command line as below: + + :: + + # ./scripts/xmlgen/go show virNetworkDNSDef + +All the generated functions related to virNetworkDNSDef are displayed, the= n we ought to preview them before really use them into libvirt project. +There is a special part **[Tips]** except the generated functions and the = declaration of hooks. + +[**Tips**] provides instructions about how to apply these generated functi= ons into project and how to enable hooks. [**Tips**] will be used in step3. + +Also, we got the declaration of hooks. In step4, we will implement a hook = according to it. + +Step3. Enable hooks and include generated functions. +---------------------------------------------------- + +According to [**Tips**] that we got in step2: + + :: + + [Tips] + + /* Put these lines at the bottom of "conf/network_conf.h" */ + /* Makesure "network_conf.h" to be appended into conf_xmlgen_input in sr= c/conf/meson.build */ + + /* Define macro to enable hook or redefine check when necessary */ + /* #define ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK */ + /* #define ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK_SET_ARGS */ + /* #define ENABLE_VIR_NETWORK_DNSDEF_FORMAT_HOOK */ + + /* #define RESET_VIR_NETWORK_DNSDEF_CHECK */ + + /* Makesure below is the bottom line! */ + #include "network_conf.generated.h" + +Uncomment macros and enable hooks when necessary. In this example, we only= need to define ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK to enable the post-hoo= k for parse-function. + +Include the header-file to apply the generated functions. +In this example, we just include "network_conf.generated.h" into "conf/net= work_conf.h" and modify "src/conf/meson.build" as instructed. + +Step4. Implement hooks when necessary. +-------------------------------------- + +In original implementation of virNetworkDNSDefParseXML, there's a piece of= error-checking code as below: + + :: + + if (def->enable =3D=3D VIR_TRISTATE_BOOL_NO && (nfwds || nhosts || nsrvs= || ntxts)) { + virReportError(VIR_ERR_XML_ERROR, + _("Extra data in disabled network '%s'"), + networkName); + } + +Since this piece of code can't be generated, we need a hook to do the chec= k. +In step2, we have gotten the declaration of virNetworkDNSDefParseHook, as = below: + + :: + + #ifdef ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK + + int + virNetworkDNSDefParseHook(xmlNodePtr node, + virNetworkDNSDef *def, + const char *instname, + void *parent, + void *opaque, + const char *enableStr, + const char *forwardPlainNamesStr, + int nForwarderNodes, + int nTxtNodes, + int nSrvNodes, + int nHostNodes); + + #endif + +The arguments provide all necessary information for us to do some extra th= ings. It's easy for us to transfer the error-checking code into this hook. +Then we put the implementation into a file, which should include the decla= ration of the hook. In this example, we use 'conf/network_conf.c'. + +Directives +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Directives help direct xmlgen to generate parse/format functions. They are= in the form of comment, so they are only valid for xmlgen and ignored by *= *C** compilers. Directives work on either a struct or a member, so they mus= t appear in the same line of struct's or member's declaration. + +On the other hand, some directives are basic, which can be used on their o= wn; and the others are addional, which must be accompanied with baisc ones. + +genparse +-------- + +(only for struct; basic) + +Generate parse-function for the struct. At the meanwhile, clear-function i= s also generated automatically, which is used for cleaning all the members = when parse-function fails. + +E.g. set **genparse** on virNetworkDNSTxtDef, as below: + + :: + + typedef struct _virNetworkDNSTxtDef virNetworkDNSTxtDef; + struct _virNetworkDNSTxtDef { /* genparse */ + char *name; /* xmlattr, required */ + char *value; /* xmlattr */ + }; + +Then we'll get the implementation of virNetworkDNSTxtDefParseXML (parse-fu= nction) and virNetworkDNSTxtDefClear (clear-function). + +We can enable a pre-hook and a post-hook in the parse function when necess= ary. The usage of hooks will be explained in detail in the following chapte= r **Hooks**. + +Note: Specify **xmlattr** or **xmlelem** for at least one member, or we'll= get an empty parse-function. + +genformat[:separate] +-------------------- + +(only for struct; basic) + +Generate format-function for the struct. At the meanwhile, check-function = is also generated automatically, which is used to determine whether an inst= ance of this type is empty. + +E.g. set **genformat** on virNetworkDHCPRangeDef, as below: + + :: + + typedef struct _virNetworkDHCPRangeDef virNetworkDHCPRangeDef; + struct _virNetworkDHCPRangeDef { /* genformat */ + struct { + virSocketAddr start; /* xmlattr, required */ + virSocketAddr end; /* xmlattr, required */ + } addr; + + virNetworkDHCPLeaseTimeDef *lease; /* xmlelem */ + }; + +Then we'll get the implementation of virNetworkDHCPRangeDefFormatBuf (form= at-function) and virNetworkDHCPRangeDefCheck (check-function). + +By default, the format-function outputs xml for all attributes and all ele= ments. + +But in some special cases, we need two separate format-functions: one for = formatting attributes and another for formatting elements. For that, we sho= uld specify **genformat:separate**. + +E.g., for virDomainGraphicsAuthDef + + :: + + struct _virDomainGraphicsAuthDef { /* genformat:separat= e */ + char *passwd; /* xmlattr */ + /* Whether there is an expiry time set */ + bool expires; /* specify:validTo */ + /* seconds since epoch */ + time_t validTo; /* xmlattr:passwdVal= idTo */ + /* action if connected */ + virDomainGraphicsAuthConnectedType connected; /* xmlattr */ + }; + +Then we'll get virDomainGraphicsAuthDefFormatAttr and virDomainGraphicsAut= hDefFormatElem, which are for formatting attributes and elements respective= ly. In the meanwhile, virDomainGraphicsAuthDefCheckAttr and virDomainGraphi= csAuthDefCheckElem are generated to check whether all attributes/elements a= re all empty. + +We can enable a pre-hook for format-function when necessary. Also, the che= ck-function can be redefined if it's not suitable. The usage of hook and ch= eck-function will be explained in detail in the following chapter **Hooks**. + +Note: Specify **xmlattr** or **xmlelem** for at least one member, or we'll= get an empty format-function. + +xmlattr[:[parentname/]thename] +------------------------------ + +(only for member; basic) + +Indicate that the member matches an xml attribute.There're 3 cases: + +1) By default, use the member's name as attribute's name to generate parse= /format code block. + +2) Specify the attribute's name by **thename** when necessary. + +3) Use the form of **xmlattr:parentname/thename** to indicate that this me= mber matches an attribute of an child-element. + +E.g.: + + :: + + struct _virNetworkIPDef { /* genparse, genformat */ + ... ... + char *family; /* xmlattr */ + virTristateBool localPTR; /* xmlattr:localPtr */ + char *tftproot; /* xmlattr:tftp/root */ + ... ... + }; + +This example demonstrates all those three usages: + +The member **family** has the same name with its corresponding xml attribu= te. As in: . + +But for **localPTR**, the name of its corresponding attribute is "localPtr= ", as in: . So we have to specify it explicitly. + +And for **tftproot**, in fact it matches the attribute *root* of the child= -element *tftp*, as in: in xml. So we use the= most complicated form 'xmlattr:tftp/root'. + +xmlelem[:thename] +----------------- + +(only for member; basic) + +Indicate that the member matches an xml element. + +By default, use the member's name as element's name to generate parse/form= at code block. + +Specify the element's name by **thename** when necessary. + +E.g.: + + :: + + struct _virNetworkForwardNatDef { /* genparse, genformat */ + virSocketAddrRange addr; /* xmlelem:address */ + virPortRange port; /* xmlelem */ + ... ... + }; + +For the member **addr**, it matches xml element
in ; +and **port** matches in . + +In special cases, the corresponding element is a *text-node*, like +.... An additional directive **xmltext** should be appended to +handle this situation. + +xmltext +----------------- + +(only for member; additional) + +Indicate that this member matches *text node*. It must be with **xmlelem**. + +E.g.: + + :: + + typedef struct _virNetworkDef virNetworkDef; + struct _virNetworkDef { /* genparse, genformat */ + virUUID uuid; /* xmlelem, xmltext */ + ... ... + }; + +The member **uuid** matches .... + + +array[:countername] +------------------- + +(only for member; additional) + +Indicate that this member matches an array of xml node. It must be with **= xmlattr** or **xmlelem**. + +Each array member is accompanied with a counter member in the same struct,= which name should be in the form of: 'n' + member-name. + +If the counter member's name doesn't follow the common rule, we should spe= cify it. As in: + + :: + + struct _virNetworkDNSDef { /* genparse, genformat */ + ... ... + size_t nfwds; + virNetworkDNSForwarder *forwarders; /* xmlelem, array:nfwds */ + size_t ntxts; + virNetworkDNSTxtDef *txts; /* xmlelem, array */ + ... ... + }; + +For member **forwarders**, its counter member **nfwds** doesn't follow the= common name rule, so we have to specify it explicitly. + +Another note: for array member, use the **singular** form of its name as i= ts corresponding xml node's name. +As the member **txts**, it matches a group of element **. + +required +-------- + +(only for member; additional) + +Indicate that the corresponding xml node of this member must exist and hav= e valid value. It must be with **xmlattr** or **xmlelem**. E.g.: + + :: + + struct _virNetworkDNSTxtDef { /* genparse, genformat */ + char *name; /* xmlattr, required */ + ... ... + }; + +In parse-function, there will be a piece of code to check the existence of= corresponding attribute, or it will report an error. Just like: + + :: + + def->name =3D virXMLPropString(node, "name"); + if (def->name =3D=3D NULL) { + virReportError(VIR_ERR_XML_ERROR, + _("Missing '%s' setting in '%s'"), + "name", instname); + goto error; + } + +specify:thename +--------------- + +(only for member; basic) + +Indicate that this member specify the existence of another member named wi= th **thename**. E.g.: + + :: + + struct _virNetworkDef { /* genparse, genformat */ + ... ... + virMacAddr mac; /* xmlattr:mac/address */ + bool mac_specified; /* specify:mac */ + }; + +The member **mac_specified** specify the existence of another member **mac= **. + +In parse-function, **mac_specified** will be automatically set when **mac*= * exists. + +In format-function, whether to format **mac** depends on whether **mac_spe= cified** is *TRUE*. + +skipparse +--------- + +(only for member; additional) + +Ignore this member in parse-function. E.g.: + + :: + + struct _virNetworkDef { /* genparse, genformat */ + int connections; /* xmlattr, skipparse */ + ... ... + }; + +The member **connections** won't appear in the generated parse-function, b= ut it still appears in the format-function. + +xmlgroup +-------- + +(only for member which type is a struct; basic) + +This member matches an group of xml nodes rather than an xml element. E.g.: + + :: + + struct _virDomainGraphicsVNCDef { /* genparse */ + ... ... + virDomainGraphicsAuthDef auth; /* xmlgroup */ + }; + +For the member **auth**, we can't find an corresponding element in xml. +In fact, it matches an group of attributes, including *passwd*, *passwdVal= idTo*, and *connected*. +Then, parse/format code block will skip over this level and traverse its c= hildren directly. + +xmlswitch:thename +----------------- + +(only for member which type is a union; basic) + +Indicate that this member matches an xml choice and generates *switch* sta= tement to parse/format xml. + +The union member should have a relative enum member which is specified by = **thename**. E.g., + + :: + + /* Implementation of virDomainGraphicsType */ + VIR_ENUM_IMPL(virDomainGraphics, + VIR_DOMAIN_GRAPHICS_TYPE_LAST, + "sdl", + "vnc", + "rdp", + "desktop", + "spice", + "egl-headless", + ); + + struct _virDomainGraphicsDef { /* genparse, g= enformat */ + virDomainGraphicsType type; /* xmlattr */ + ... ... + union { + virDomainGraphicsSDLDef sdl; /* xmlgroup */ + virDomainGraphicsVNCDef vnc; /* xmlgroup */ + virDomainGraphicsRDPDef rdp; /* xmlgroup */ + virDomainGraphicsDesktopDef desktop; /* xmlgroup */ + virDomainGraphicsSpiceDef spice; /* xmlgroup */ + virDomainGraphicsEGLHeadlessDef egl_headless; /* xmlgroup */ + } data; /* xmlswitch:t= ype */ + }; + +For the union member **data**, the enum member **type** is its relative co= unterpart. + +Note: Each child of **data** has the same name with each child of **type**= in order. + +Automatic Features +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Other than directives, xmlgen can automatically detect some features. + +Default item of enum +-------------------- + +For all enums in libvirt, there're two cases: + +- Most of them have an extra default item which value is *ZERO*. + It usually represents that the absence of the corresponding attribute in= xml. E.g. + + :: + + typedef enum { + VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_DEFAULT =3D 0, + VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_KERNEL, + VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_LIBVIRT, + VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_LAST, + } virNetworkBridgeMACTableManagerType; + +In generated parse-function, it's illegal that virNetworkBridgeMACTableMan= agerTypeFromString returns ZERO. + +- The others have no this kind of default item. All items have their actua= l meanings. E.g., + + :: + + typedef enum { + VIR_NETWORK_DHCP_LEASETIME_UNIT_SECONDS =3D 0, + VIR_NETWORK_DHCP_LEASETIME_UNIT_MINUTES, + VIR_NETWORK_DHCP_LEASETIME_UNIT_HOURS, + VIR_NETWORK_DHCP_LEASETIME_UNIT_LAST, + } virNetworkDHCPLeaseTimeUnitType; + +In generated parse-function, the return-value of virNetworkDHCPLeaseTimeUn= itTypeFromString can be ZERO, +which indicates the first item VIR_NETWORK_DHCP_LEASETIME_UNIT_SECONDS. + +The tool xmlgen can distinguish them and generate proper parsing/formattin= g code by checking whether the first item ends with '_DEFAULT', '_NONE' or = '_ABSENT'. + +Namespace +--------- + +For some top structs, such as virNetworkDef, virDomainDef, etc., there're = some code blocks about **namespace**. + +The tool xmlgen generates extra code block to deal with **namespace** in p= arse/format/clear function if it finds that a struct has a member named '**= namespaceData**'. + +Pointer +------- + +Some members' type is a pointer. The tool xmlgen determines it by checking= "*" or "Ptr" so that it can generate proper code. + +Hooks +=3D=3D=3D=3D=3D + +Generated parse/format functions have some hooks which provide flexibility. +By default, hooks are disabled, so we should implement and enable them whe= n necessary. + +Post-hook for parse-function +---------------------------- + +This hook is used to hold error-checking code and even to set/change any m= ember's value in the post process. E.g.: + +:: + + int + virNetworkDNSDefParseXML(xmlNodePtr node, + virNetworkDNSDef *def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) + { + /* Parsing block for each member */ + ... ... + + #ifdef ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK + if (virNetworkDNSDefParseHook(node, def, instname, parent, opaque,= enableStr, forwardPlainNamesStr, nForwarderNodes, nTxtNodes, nSrvNodes, nH= ostNodes) < 0) + goto error; + #endif + + return 0; + + error: + ... ... + return -1; + } + +The hook virNetworkDNSDefParseHook is after all members' parsing blocks. +Now we can take 3 steps to enable this hook to hold some error-checking co= de. + +step1. Look through helpful information. +........................................ + +Execute below command line: + + :: + + # ./scripts/xmlgen/go show virNetworkDNSDef -kp + +Then we got the declaration of virNetworkDNSDefParseHook and tips. +The declaration will be used in step3. And the tips is as below: + + :: + + [Tips] + + /* Put these lines at the bottom of "conf/network_conf.h" */ + /* Makesure "network_conf.h" to be appended into conf_xmlgen_input in = src/conf/meson.build */ + + /* Define macro to enable hook or redefine check when necessary */ + /* #define ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK */ + /* #define ENABLE_VIR_NETWORK_DNSDEF_PARSE_HOOK_SET_ARGS */ + + /* Makesure below is the bottom line! */ + #include "network_conf.generated.h" + +step2. Enable hook and apply generated functions. +................................................. + +According to tips from step1, we define ENABLE_VIR_NETWORK_DNSDEF_PARSE_HO= OK to enable the hook and include the header-file. + +step3. Implement hook. +...................... + +Implement virNetworkDNSDefParseHook according to its delcaration and put t= he error-checking code into it. + +This implementation should be written into a C source file that includes "= network_conf.h". + +In this example, "network_conf.c" is a good choice. + +Pre-hook for parse-function +--------------------------- + +This hook is used to change the common rule of passing some special argume= nts, including **parent** and **opaque**. +The common rule is: + +For each parse-function, the argument **parent** holds the pointer of its = parent struct instance, and the argument **def** will be passed into child = parse-function as their **parent**. + +The argument **opaque** holds an opaque data, and it will be passed from p= arent parse-function to child parse-function recursively. + +But sometimes, we should change this rule for some generated parse-functio= ns. As in: + +:: + + int + virNetworkDHCPDefParseXML(xmlNodePtr node, + virNetworkDHCPDef *def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) + { + ... ... + void *arg_parent G_GNUC_UNUSED =3D def; + void *arg_opaque G_GNUC_UNUSED =3D opaque; + + if (!def) + goto error; + + #ifdef ENABLE_VIR_NETWORK_DHCPDEF_PARSE_HOOK_SET_ARGS + virNetworkDHCPDefParseXMLSetArgs(node, parent, &arg_parent, &arg_o= paque); + #endif + + if (nRangeNodes > 0) { + ... ... + if (virNetworkDHCPRangeDefParseXML(tnode, &def->ranges[i],= instname, arg_parent, arg_opaque) < 0) + goto error; + } + ... ... + } + +The hook virNetworkDHCPDefParseXMLSetArgs has a chance to intercept **pare= nt/opaque** in the chain. In this example, we need pass the pointer of virN= etworkDef rather than that of virNetworkDHCPDef to virNetworkDHCPRangeDefPa= rseXML as its **parent**, so we enable the hook virNetworkDHCPDefParseXMLSe= tArgs to implement it. + +The steps to enable the hook are just similar as mentioned in post-hook fo= r parse-function. + +Pre-hook for format-function +---------------------------- + +The generated format-function has a pre-hook that can override the default= xml output of any member and even the whole output of struct instance. + +E.g., for virNetworkForwardDefFormatBuf, we can implement the hook as belo= w: + +:: + + int + virNetworkForwardDefFormatHook(const virNetworkForwardDef *def, + const void *parent, + const void *opaque G_GNUC_UNUSED, + virTristateBool *empty, + virTristateBool *shortcut, + virBuffer *devBuf, + virBuffer *typeBuf G_GNUC_UNUSED, + virBuffer *managedBuf, + virBuffer *driver_nameBuf G_GNUC_UNUSED, + virBuffer *natBuf G_GNUC_UNUSED, + virBuffer *pfsBuf G_GNUC_UNUSED, + virBuffer *ifsBuf G_GNUC_UNUSED) + { + if (def->type =3D=3D VIR_NETWORK_FORWARD_NONE) { + *empty =3D VIR_TRISTATE_BOOL_YES; + return 0; + } + + if (!def->npfs) { + const char *dev =3D virNetworkDefForwardIf(parent, 0); + virBufferEscapeString(devBuf, " dev=3D'%s'", dev); + } else { + virBufferIgnore(devBuf); + } + + if (def->type !=3D VIR_NETWORK_FORWARD_HOSTDEV) + virBufferIgnore(managedBuf); + + if (!(def->nifs || def->npfs || def->nat.port.start || def->nat.po= rt.end || + VIR_SOCKET_ADDR_VALID(&def->nat.addr.start) || + VIR_SOCKET_ADDR_VALID(&def->nat.addr.end) || + (def->driverName !=3D VIR_NETWORK_FORWARD_DRIVER_NAME_DEFAUL= T) || + def->nat.natIPv6)) + *shortcut =3D VIR_TRISTATE_BOOL_YES; + + return 0; + } + +In the hook, we redefine four things: + +1) Redefine the xml output for the attribute **dev** by resetting *devBuf*. + +2) Indicate that the attribute **managed** should be ignored (by *virBuffe= rIgnore*) under some conditions. + +3) Set **shortcut** to indicate that ... should use the= shortut form like under some conditions. + +4) Set **empty** to indicate that shouldn't appear under some co= nditions. + +The arguments **shortcut** and **empty** are both the type of virTristateB= ool. + +By default, they are VIR_TRISTATE_BOOL_ABSENT which means they don't influ= ence the default behavior of the generated format-function. + +Set them with VIR_TRISTATE_BOOL_YES or VIR_TRISTATE_BOOL_NO to turn on/off= them respectively. + +Check-function +-------------- + +It is an auxiliary function for format-function. + +Before a format-function is called to output an xml node, its auxiliary ch= eck-function should be invoked to determine whether the xml node is empty. +For each struct with **genformat**, their check-functions are generated au= tomatically. + +Sometimes the default implementation is not satisfied, we can redefine it. + +E.g., for virDomainGraphicsListenDef, we look through its check-function b= y calling command line as below: + + :: + + # ./scripts/xmlgen/go show virDomainGraphicsListenDef -kf + +We got the default implementation of virDomainGraphicsListenDefCheck and t= he tips. + +Since it doesn't satisfy our needs, we can define RESET_VIR_DOMAIN_GRAPHIC= S_LISTEN_DEF_CHECK and redefine it. --=20 2.25.1