[PATCH v2 29/34] scripts: apibuild: parse 'Since' for functions

Victor Toso victortoso at redhat.com
Thu Apr 14 20:47:40 UTC 2022


This patch adds 'version' parameter to generated XML API for functions
and functypes.

The 'version' metadata has been added with e0e0bf6628 by parsing .syms
files. This commit does not override that but it will warn if there is
not 'Since' metadata with new additions.

There is not clear benefit for keeping both. For now, I've added a
warning in case there is a mismatch between the version provided by
.syms and docstring.

Signed-off-by: Victor Toso <victortoso at redhat.com>
---
 scripts/apibuild.py | 50 ++++++++++++++++++++++++++++++++++-----------
 1 file changed, 38 insertions(+), 12 deletions(-)

diff --git a/scripts/apibuild.py b/scripts/apibuild.py
index c4ff733550..d16beaa4ae 100755
--- a/scripts/apibuild.py
+++ b/scripts/apibuild.py
@@ -2185,8 +2185,9 @@ class docBuilder:
         self.scanVersions()
 
     # Fetch tags from the comment. Only 'Since' supported at the moment.
-    # Return the tags and the original comment without the tags.
-    def retrieve_comment_tags(self, name: str, comment: str) -> (str, str):
+    # For functions, since tags are on Return comments.
+    # Return the tags and the original comments, but without the tags.
+    def retrieve_comment_tags(self, name: str, comment: str, return_comment="") -> (str, str, str):
         since = ""
         if comment is not None:
             comment_match = re.search(r"\(?Since: v?(\d+\.\d+\.\d+)\)?", comment)
@@ -2198,9 +2199,19 @@ class docBuilder:
                 # Only the version
                 since = comment_match.group(1)
 
+        if since == "" and return_comment is not None:
+            return_match = re.search(r"\(?Since: v?(\d+\.\d+\.\d+)\)?", return_comment)
+            if return_match:
+                # Remove Since tag from the comment
+                (start, end) = return_match.span()
+                return_comment = return_comment[:start] + return_comment[end:]
+                return_comment = return_comment.strip()
+                # Only the version
+                since = return_match.group(1)
+
         if since == "":
             self.warning("Missing 'Since' tag for: " + name)
-        return (since, comment)
+        return (since, comment, return_comment)
 
     def modulename_file(self, file):
         module = os.path.basename(file)
@@ -2236,7 +2247,7 @@ class docBuilder:
                 output.write(" type='%s'" % info[2])
             if info[1] is not None and info[1] != '':
                 # Search for 'Since' version tag
-                (since, comment) = self.retrieve_comment_tags(name, info[1])
+                (since, comment, _) = self.retrieve_comment_tags(name, info[1])
                 if len(since) > 0:
                     output.write(" version='%s'" % escape(since))
                 if len(comment) > 0:
@@ -2266,7 +2277,7 @@ class docBuilder:
         else:
             output.write(" raw='%s'" % escape(rawValue))
 
-        (since, comment) = self.retrieve_comment_tags(name, desc)
+        (since, comment, _) = self.retrieve_comment_tags(name, desc)
         if len(since) > 0:
             output.write(" version='%s'" % escape(since))
         output.write(">\n")
@@ -2300,7 +2311,7 @@ class docBuilder:
 
     def serialize_typedef(self, output, name):
         id = self.idx.typedefs[name]
-        (since, comment) = self.retrieve_comment_tags(name, id.extra)
+        (since, comment, _) = self.retrieve_comment_tags(name, id.extra)
         version_tag = len(since) > 0 and f" version='{since}'" or ""
         if id.info[0:7] == 'struct ':
             output.write("    <struct name='%s' file='%s' type='%s'%s" % (
@@ -2352,6 +2363,12 @@ class docBuilder:
         if name == debugsym and not quiet:
             print("=>", id)
 
+        (ret, params, desc) = id.info
+        return_comment = (ret is not None and ret[1] is not None) and ret[1] or ""
+        (since, comment, return_comment) = self.retrieve_comment_tags(name, desc, return_comment)
+        # Simple way to avoid setting empty version
+        version_tag = len(since) > 0 and f" version='{since}'" or ""
+
         # NB: this is consumed by a regex in 'getAPIFilenames' in hvsupport.pl
         if id.type == "function":
             ver = self.versions[name]
@@ -2361,9 +2378,10 @@ class docBuilder:
                 name, self.modulename_file(id.header),
                 self.modulename_file(id.module), self.versions[name]))
         else:
-            output.write("    <functype name='%s' file='%s' module='%s'>\n" % (
+            output.write("    <functype name='%s' file='%s' module='%s'%s>\n" % (
                 name, self.modulename_file(id.header),
-                self.modulename_file(id.module)))
+                self.modulename_file(id.module),
+                version_tag))
         #
         # Processing of conditionals modified by Bill 1/1/05
         #
@@ -2374,19 +2392,27 @@ class docBuilder:
                     apstr = apstr + " && "
                 apstr = apstr + cond
             output.write("      <cond>%s</cond>\n" % (apstr))
+
         try:
-            (ret, params, desc) = id.info
-            output.write("      <info><![CDATA[%s]]></info>\n" % (desc))
+            # For functions, we get the since from .syms files. This is an extra check to see
+            # that docstrings are correct. Note that docstrings starts with 1.0.0.
+            ver = name in self.versions and self.versions[name] or None
+            if len(since) > 0 and ver is not None and int(ver[0]) != 0 and since != ver:
+                self.warning(f"Function {name} has symversion {ver} but docstring says {since}")
+
+            output.write("      <info><![CDATA[%s]]></info>\n" % (comment))
             self.indexString(name, desc)
+
             if ret[0] is not None:
                 if ret[0] == "void":
                     output.write("      <return type='void'/>\n")
-                elif (ret[1] is None or ret[1] == '') and name not in ignored_functions:
+                elif (return_comment == '') and name not in ignored_functions:
                     self.error("Missing documentation for return of function `%s'" % name)
                 else:
                     output.write("      <return type='%s' info='%s'/>\n" % (
-                        ret[0], escape(ret[1])))
+                        ret[0], escape(return_comment)))
                     self.indexString(name, ret[1])
+
             for param in params:
                 if param[0] == 'void':
                     continue
-- 
2.35.1



More information about the libvir-list mailing list