[libvirt] minor doc generation glitch

Daniel Veillard veillard at redhat.com
Thu Jun 16 07:56:53 UTC 2011


On Thu, Jun 16, 2011 at 01:19:47PM +0800, Daniel Veillard wrote:
> On Wed, Jun 15, 2011 at 03:21:44PM -0600, Eric Blake wrote:
> > On 06/15/2011 01:25 PM, Eric Blake wrote:
> > > Looking at
> > > http://libvirt.org/html/libvirt-libvirt.html#virDomainShutoffReason as
> > > an example, I see several places where there are spurious "*" in the
> > > generated documentation.
> > 
> > Also, http://libvirt.org/html/libvirt-libvirt.html#virTypedParameter is
> > broken, listing 'charfield[length] field' rather than 'char[length]
> > field' or 'char field[length]' for the first member, and completely
> > missing out on the 'union{} value' member.  It's okay if the docs don't
> > list structs in C syntax, but whatever syntax it does use should be
> > close enough to figure out the corresponding C syntax without having to
> > read the source header to check for missing struct members.
> 
>   I'm fixing those, nearly done ...

 The enclosed patch includes quite a number of fixes
   - parsing of "long long int" and similar
   - add parsing of unions within a struct
   - remove spurious " * " fron comments on structure fields and enums
   - fix concatenation of base type and name in arrays
   - extend XSLT to cope with union in structs

this is painful to read (this was apinful to write !), have a look at
the resulting libvirt-api.xml and generated page, that's the nicest way
to validate it :-)

Daniel

-- 
Daniel Veillard      | libxml Gnome XML XSLT toolkit  http://xmlsoft.org/
daniel at veillard.com  | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library  http://libvirt.org/
-------------- next part --------------
diff --git a/docs/apibuild.py b/docs/apibuild.py
index d9e9952..f31a853 100755
--- a/docs/apibuild.py
+++ b/docs/apibuild.py
@@ -179,6 +179,7 @@ class index:
         self.variables = {}
         self.includes = {}
         self.structs = {}
+        self.unions = {}
         self.enums = {}
         self.typedefs = {}
         self.macros = {}
@@ -232,6 +233,10 @@ class index:
                 self.includes[name] = d
             elif type == "struct":
                 self.structs[name] = d
+            elif type == "struct":
+                self.structs[name] = d
+            elif type == "union":
+                self.unions[name] = d
             elif type == "enum":
                 self.enums[name] = d
             elif type == "typedef":
@@ -280,6 +285,13 @@ class index:
              else:
                  self.structs[id] = idx.structs[id]
                  self.identifiers[id] = idx.structs[id]
+        for id in idx.unions.keys():
+             if self.unions.has_key(id):
+                 print "union %s from %s redeclared in %s" % (
+                    id, self.unions[id].header, idx.unions[id].header)
+             else:
+                 self.unions[id] = idx.unions[id]
+                 self.identifiers[id] = idx.unions[id]
         for id in idx.typedefs.keys():
              if self.typedefs.has_key(id):
                  print "typedef %s from %s redeclared in %s" % (
@@ -347,6 +359,7 @@ class index:
         self.analyze_dict("functions", self.functions)
         self.analyze_dict("variables", self.variables)
         self.analyze_dict("structs", self.structs)
+        self.analyze_dict("unions", self.unions)
         self.analyze_dict("typedefs", self.typedefs)
         self.analyze_dict("macros", self.macros)
 
@@ -656,13 +669,36 @@ class CParser:
                         res[item] = line
         self.index.info = res
 
+    def strip_lead_star(self, line):
+        l = len(line)
+        i = 0
+        while i < l:
+            if line[i] == ' ' or line[i] == '\t':
+                i += 1
+            elif line[i] == '*':
+                return line[:i] + line[i + 1:]
+            else:
+                 return line
+        return line
+
+    def cleanupComment(self):
+        if type(self.comment) != type(""):
+            return
+        # remove the leading * on multi-line comments
+        lines = self.comment.splitlines(True)
+        com = ""
+        for line in lines:
+            com = com + self.strip_lead_star(line)
+        self.comment = com.strip()
+
     def parseComment(self, token):
+        com = token[1]
         if self.top_comment == "":
-            self.top_comment = token[1]
-        if self.comment == None or token[1][0] == '*':
-            self.comment = token[1];
+            self.top_comment = com
+        if self.comment == None or com[0] == '*':
+            self.comment = com;
         else:
-            self.comment = self.comment + token[1]
+            self.comment = self.comment + com
         token = self.lexer.token()
 
         if string.find(self.comment, "DOC_DISABLE") != -1:
@@ -1178,7 +1214,13 @@ class CParser:
                     if token[0] == "sep" and token[1] == ";":
                         self.comment = None
                         token = self.token()
-                        fields.append((self.type, fname, self.comment))
+                        self.cleanupComment()
+                        if self.type == "union":
+                            fields.append((self.type, fname, self.comment,
+                                           self.union_fields))
+                            self.union_fields = []
+                        else:
+                            fields.append((self.type, fname, self.comment))
                         self.comment = None
                     else:
                         self.error("parseStruct: expecting ;", token)
@@ -1201,6 +1243,56 @@ class CParser:
         return token
 
      #
+     # Parse a C union definition till the balancing }
+     #
+    def parseUnion(self, token):
+        fields = []
+        # self.debug("start parseUnion", token)
+        while token != None:
+            if token[0] == "sep" and token[1] == "{":
+                token = self.token()
+                token = self.parseTypeBlock(token)
+            elif token[0] == "sep" and token[1] == "}":
+                self.union_fields = fields
+                # self.debug("end parseUnion", token)
+                # print fields
+                token = self.token()
+                return token
+            else:
+                base_type = self.type
+                # self.debug("before parseType", token)
+                token = self.parseType(token)
+                # self.debug("after parseType", token)
+                if token != None and token[0] == "name":
+                    fname = token[1]
+                    token = self.token()
+                    if token[0] == "sep" and token[1] == ";":
+                        self.comment = None
+                        token = self.token()
+                        self.cleanupComment()
+                        fields.append((self.type, fname, self.comment))
+                        self.comment = None
+                    else:
+                        self.error("parseUnion: expecting ;", token)
+                elif token != None and token[0] == "sep" and token[1] == "{":
+                    token = self.token()
+                    token = self.parseTypeBlock(token)
+                    if token != None and token[0] == "name":
+                        token = self.token()
+                    if token != None and token[0] == "sep" and token[1] == ";":
+                        token = self.token()
+                    else:
+                        self.error("parseUnion: expecting ;", token)
+                else:
+                    self.error("parseUnion: name", token)
+                    token = self.token()
+                self.type = base_type;
+        self.union_fields = fields
+        # self.debug("end parseUnion", token)
+        # print fields
+        return token
+
+     #
      # Parse a C enum block, parse till the balancing }
      #
     def parseEnumBlock(self, token):
@@ -1215,6 +1307,7 @@ class CParser:
                 token = self.parseTypeBlock(token)
             elif token[0] == "sep" and token[1] == "}":
                 if name != None:
+                    self.cleanupComment()
                     if self.comment != None:
                         comment = self.comment
                         self.comment = None
@@ -1222,6 +1315,7 @@ class CParser:
                 token = self.token()
                 return token
             elif token[0] == "name":
+                    self.cleanupComment()
                     if name != None:
                         if self.comment != None:
                             comment = string.strip(self.comment)
@@ -1252,7 +1346,7 @@ class CParser:
         return token
 
      #
-     # Parse a C definition block, used for structs it parse till
+     # Parse a C definition block, used for structs or unions it parse till
      # the balancing }
      #
     def parseTypeBlock(self, token):
@@ -1275,6 +1369,7 @@ class CParser:
     def parseType(self, token):
         self.type = ""
         self.struct_fields = []
+        self.union_fields = []
         self.signature = None
         if token == None:
             return token
@@ -1304,22 +1399,19 @@ class CParser:
                 self.push(token)
                 token = oldtmp
 
+            oldtmp = token
+            token = self.token()
             if token[0] == "name" and token[1] == "int":
-                if self.type == "":
-                    self.type = tmp[1]
-                else:
-                    self.type = self.type + " " + tmp[1]
+                self.type = self.type + " " + token[1]
+            else:
+                self.push(token)
+                token = oldtmp
 
         elif token[0] == "name" and token[1] == "short":
             if self.type == "":
                 self.type = token[1]
             else:
                 self.type = self.type + " " + token[1]
-            if token[0] == "name" and token[1] == "int":
-                if self.type == "":
-                    self.type = tmp[1]
-                else:
-                    self.type = self.type + " " + tmp[1]
 
         elif token[0] == "name" and token[1] == "struct":
             if self.type == "":
@@ -1355,6 +1447,28 @@ class CParser:
                 token = nametok
             return token
 
+        elif token[0] == "name" and token[1] == "union":
+            if self.type == "":
+                self.type = token[1]
+            else:
+                self.type = self.type + " " + token[1]
+            token = self.token()
+            nametok = None
+            if token[0] == "name":
+                nametok = token
+                token = self.token()
+            if token != None and token[0] == "sep" and token[1] == "{":
+                token = self.token()
+                token = self.parseUnion(token)
+            elif token != None and token[0] == "name" and nametok != None:
+                self.type = self.type + " " + nametok[1]
+                return token
+
+            if nametok != None:
+                self.lexer.push(token)
+                token = nametok
+            return token
+
         elif token[0] == "name" and token[1] == "enum":
             if self.type == "":
                 self.type = token[1]
@@ -1434,7 +1548,7 @@ class CParser:
             nametok = token
             token = self.token()
             if token != None and token[0] == "sep" and token[1] == '[':
-                self.type = self.type + nametok[1]
+                self.type = self.type + " " + nametok[1]
                 while token != None and token[0] == "sep" and token[1] == '[':
                     self.type = self.type + token[1]
                     token = self.token()
@@ -1844,6 +1958,20 @@ class docBuilder:
                 pass
         output.write("    </macro>\n")
 
+    def serialize_union(self, output, field, desc):
+        output.write("      <field name='%s' type='union' info='%s'>\n" % (field[1] , desc))
+        output.write("        <union>\n")
+        for f in field[3]:
+            desc = f[2]
+            if desc == None:
+                desc = ''
+            else:
+                desc = escape(desc)
+            output.write("          <field name='%s' type='%s' info='%s'/>\n" % (f[1] , f[0], desc))
+
+        output.write("        </union>\n")
+        output.write("      </field>\n")
+
     def serialize_typedef(self, output, name):
         id = self.idx.typedefs[name]
         if id.info[0:7] == 'struct ':
@@ -1862,7 +1990,10 @@ class docBuilder:
                             desc = ''
                         else:
                             desc = escape(desc)
-                        output.write("      <field name='%s' type='%s' info='%s'/>\n" % (field[1] , field[0], desc))
+                        if field[0] == "union":
+                            self.serialize_union(output, field, desc)
+                        else:
+                            output.write("      <field name='%s' type='%s' info='%s'/>\n" % (field[1] , field[0], desc))
                 except:
                     print "Failed to serialize struct %s" % (name)
                 output.write("    </struct>\n")
@@ -1961,6 +2092,8 @@ class docBuilder:
                 continue
             if dict.structs.has_key(id):
                 continue
+            if dict.unions.has_key(id):
+                continue
             if dict.enums.has_key(id):
                 continue
             output.write("     <exports symbol='%s' type='macro'/>\n" % (id))
diff --git a/docs/newapi.xsl b/docs/newapi.xsl
index b59674a..445a48c 100644
--- a/docs/newapi.xsl
+++ b/docs/newapi.xsl
@@ -174,22 +174,62 @@
       </pre>
       <table>
         <xsl:for-each select="field">
-          <tr>
-            <td>
-              <xsl:call-template name="dumptext">
-                <xsl:with-param name="text" select="@type"/>
-              </xsl:call-template>
-            </td>
-            <td><xsl:value-of select="@name"/></td>
-            <xsl:if test="@info != ''">
-              <td>
-                <xsl:text> : </xsl:text>
-                <xsl:call-template name="dumptext">
-                  <xsl:with-param name="text" select="@info"/>
-                </xsl:call-template>
-              </td>
-            </xsl:if>
-          </tr>
+          <xsl:choose>
+            <xsl:when test='@type = "union"'>
+              <tr><td>union {</td></tr>
+              <tr>
+              <td><table>
+              <xsl:for-each select="union/field">
+                <tr>
+                  <td>
+                    <xsl:call-template name="dumptext">
+                      <xsl:with-param name="text" select="@type"/>
+                    </xsl:call-template>
+                  </td>
+                  <td><xsl:value-of select="@name"/></td>
+                  <xsl:if test="@info != ''">
+                    <td>
+                      <xsl:text> : </xsl:text>
+                      <xsl:call-template name="dumptext">
+                        <xsl:with-param name="text" select="@info"/>
+                      </xsl:call-template>
+                    </td>
+                  </xsl:if>
+                </tr>
+              </xsl:for-each>
+              </table></td>
+              <td></td></tr>
+              <tr><td>}</td>
+              <td><xsl:value-of select="@name"/></td>
+                <xsl:if test="@info != ''">
+                  <td>
+                    <xsl:text> : </xsl:text>
+                    <xsl:call-template name="dumptext">
+                      <xsl:with-param name="text" select="@info"/>
+                    </xsl:call-template>
+                  </td>
+                </xsl:if>
+              <td></td></tr>
+            </xsl:when>
+            <xsl:otherwise>
+              <tr>
+                <td>
+                  <xsl:call-template name="dumptext">
+                    <xsl:with-param name="text" select="@type"/>
+                  </xsl:call-template>
+                </td>
+                <td><xsl:value-of select="@name"/></td>
+                <xsl:if test="@info != ''">
+                  <td>
+                    <xsl:text> : </xsl:text>
+                    <xsl:call-template name="dumptext">
+                      <xsl:with-param name="text" select="@info"/>
+                    </xsl:call-template>
+                  </td>
+                </xsl:if>
+              </tr>
+            </xsl:otherwise>
+          </xsl:choose>
         </xsl:for-each>
         <xsl:if test="not(field)">
           <tr>


More information about the libvir-list mailing list