diff options
Diffstat (limited to 'tools/check-version-history.py')
-rw-r--r-- | tools/check-version-history.py | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/tools/check-version-history.py b/tools/check-version-history.py new file mode 100644 index 0000000..c4d4b3e --- /dev/null +++ b/tools/check-version-history.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: LGPL-2.1-or-later + +import os +import sys +import lxml.etree as tree + +_parser = tree.XMLParser(resolve_entities=False) +tree.set_default_parser(_parser) + + +def find_undocumented_functions(pages, ignorelist): + undocumented = [] + for page in pages: + filename = os.path.basename(page) + pagetree = tree.parse(page) + + assert pagetree.getroot().tag == "refentry" + + hist_section = pagetree.find("refsect1[title='History']") + for func in pagetree.findall("//funcprototype/funcdef/function"): + path = f"/refsynopsisdiv/funcsynopsis/funcprototype/funcdef/function[.='{func.text}']" + assert pagetree.findall(path) == [func] + + if ( + hist_section is None + or hist_section.find(f"para/function[.='{func.text}()']") is None + ): + if func.text not in ignorelist: + undocumented.append((filename, func.text)) + return undocumented + + +def construct_path(element): + tag = element.tag + + if tag == "refentry": + return "" + + predicate = "" + if tag == "varlistentry": + text = "".join(element.find("term").itertext()) + predicate = f'[term="{text}"]' + elif tag.startswith("refsect"): + text = "".join(element.find("title").itertext()) + predicate = f'[title="{text}"]' + elif tag == "variablelist": + varlists = element.getparent().findall(tag) + if len(varlists) > 1: + predicate = f"[{varlists.index(element)+1}]" + + return construct_path(element.getparent()) + "/" + tag + predicate + + +def find_undocumented_commands(pages, ignorelist): + undocumented = [] + for page in pages: + filename = os.path.basename(page) + + pagetree = tree.parse(page) + if pagetree.getroot().tag != "refentry": + continue + + for varlistentry in pagetree.findall("*//variablelist/varlistentry"): + path = construct_path(varlistentry) + + assert pagetree.findall(path) == [varlistentry] + + listitem = varlistentry.find("listitem") + parent = listitem if listitem is not None else varlistentry + + rev = parent.getchildren()[-1] + if rev.get("href") != "version-info.xml": + if (filename, path) not in ignorelist: + undocumented.append((filename, path)) + return undocumented + + +def process_pages(pages): + command_pages = [] + function_pages = [] + + for page in pages: + filename = os.path.basename(page) + if filename.startswith("org.freedesktop."): # dbus + continue + + if ( + filename.startswith("sd_") + or filename.startswith("sd-") + or filename.startswith("udev_") + ): + function_pages.append(page) + continue + + command_pages.append(page) + + undocumented_commands = find_undocumented_commands( + command_pages, command_ignorelist + ) + undocumented_functions = find_undocumented_functions( + function_pages, function_ignorelist + ) + + return undocumented_commands, undocumented_functions + + +if __name__ == "__main__": + with open(os.path.join(os.path.dirname(__file__), "command_ignorelist")) as f: + command_ignorelist = [] + for l in f.read().splitlines(): + if l.startswith("#"): + continue + fname, path = l.split(" ", 1) + path = path.replace("\\n", "\n") + command_ignorelist.append((fname, path)) + with open(os.path.join(os.path.dirname(__file__), "function_ignorelist")) as f: + function_ignorelist = f.read().splitlines() + + undocumented_commands, undocumented_functions = process_pages(sys.argv[1:]) + + if undocumented_commands or undocumented_functions: + for filename, func in undocumented_functions: + print( + f"Function {func}() in {filename} isn't documented in the History section." + ) + for filename, path in undocumented_commands: + print(filename, path, "is undocumented") + if undocumented_commands: + print( + "Hint: if you reorganized this part of the documentation, " + "please update tools/commands_ignorelist." + ) + + sys.exit(1) |