]> git.ipfire.org Git - thirdparty/systemd.git/blame - tools/check-version-history.py
Merge pull request #32336 from teknoraver/foreach_element
[thirdparty/systemd.git] / tools / check-version-history.py
CommitLineData
3691e7fc
AK
1#!/usr/bin/env python3
2# SPDX-License-Identifier: LGPL-2.1-or-later
3
4import os
5import sys
2ad44c22
AAF
6
7try:
8 import lxml.etree as tree
9except ImportError as e:
10 print(str(e), file=sys.stderr)
11 sys.exit(77)
3691e7fc
AK
12
13_parser = tree.XMLParser(resolve_entities=False)
14tree.set_default_parser(_parser)
15
16
17def find_undocumented_functions(pages, ignorelist):
18 undocumented = []
19 for page in pages:
20 filename = os.path.basename(page)
21 pagetree = tree.parse(page)
22
23 assert pagetree.getroot().tag == "refentry"
24
25 hist_section = pagetree.find("refsect1[title='History']")
26 for func in pagetree.findall("//funcprototype/funcdef/function"):
27 path = f"/refsynopsisdiv/funcsynopsis/funcprototype/funcdef/function[.='{func.text}']"
28 assert pagetree.findall(path) == [func]
29
30 if (
31 hist_section is None
32 or hist_section.find(f"para/function[.='{func.text}()']") is None
33 ):
34 if func.text not in ignorelist:
35 undocumented.append((filename, func.text))
36 return undocumented
37
38
39def construct_path(element):
40 tag = element.tag
41
42 if tag == "refentry":
43 return ""
44
45 predicate = ""
46 if tag == "varlistentry":
47 text = "".join(element.find("term").itertext())
48 predicate = f'[term="{text}"]'
49 elif tag.startswith("refsect"):
50 text = "".join(element.find("title").itertext())
51 predicate = f'[title="{text}"]'
52 elif tag == "variablelist":
53 varlists = element.getparent().findall(tag)
54 if len(varlists) > 1:
55 predicate = f"[{varlists.index(element)+1}]"
56
57 return construct_path(element.getparent()) + "/" + tag + predicate
58
59
60def find_undocumented_commands(pages, ignorelist):
61 undocumented = []
62 for page in pages:
63 filename = os.path.basename(page)
64
65 pagetree = tree.parse(page)
66 if pagetree.getroot().tag != "refentry":
67 continue
68
69 for varlistentry in pagetree.findall("*//variablelist/varlistentry"):
70 path = construct_path(varlistentry)
71
72 assert pagetree.findall(path) == [varlistentry]
73
74 listitem = varlistentry.find("listitem")
75 parent = listitem if listitem is not None else varlistentry
76
77 rev = parent.getchildren()[-1]
78 if rev.get("href") != "version-info.xml":
79 if (filename, path) not in ignorelist:
80 undocumented.append((filename, path))
81 return undocumented
82
83
84def process_pages(pages):
85 command_pages = []
86 function_pages = []
87
88 for page in pages:
89 filename = os.path.basename(page)
90 if filename.startswith("org.freedesktop."): # dbus
91 continue
92
93 if (
94 filename.startswith("sd_")
95 or filename.startswith("sd-")
96 or filename.startswith("udev_")
97 ):
98 function_pages.append(page)
99 continue
100
101 command_pages.append(page)
102
103 undocumented_commands = find_undocumented_commands(
104 command_pages, command_ignorelist
105 )
106 undocumented_functions = find_undocumented_functions(
107 function_pages, function_ignorelist
108 )
109
110 return undocumented_commands, undocumented_functions
111
112
113if __name__ == "__main__":
114 with open(os.path.join(os.path.dirname(__file__), "command_ignorelist")) as f:
115 command_ignorelist = []
116 for l in f.read().splitlines():
117 if l.startswith("#"):
118 continue
119 fname, path = l.split(" ", 1)
120 path = path.replace("\\n", "\n")
121 command_ignorelist.append((fname, path))
122 with open(os.path.join(os.path.dirname(__file__), "function_ignorelist")) as f:
123 function_ignorelist = f.read().splitlines()
124
125 undocumented_commands, undocumented_functions = process_pages(sys.argv[1:])
126
127 if undocumented_commands or undocumented_functions:
128 for filename, func in undocumented_functions:
129 print(
130 f"Function {func}() in {filename} isn't documented in the History section."
131 )
132 for filename, path in undocumented_commands:
133 print(filename, path, "is undocumented")
134 if undocumented_commands:
135 print(
136 "Hint: if you reorganized this part of the documentation, "
137 "please update tools/commands_ignorelist."
138 )
139
140 sys.exit(1)