forked from systemd/systemd
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
man: add checks for missing version information
This adds a new script tools/check-version-history.py and a corresponding test when building in developer mode. It checks manpages (except dbus documentation which is handled by update-dbus-docs) for missing version history information. It also adds ignore lists based on version 183 (the version that our version annotations go back to). These can be augmented if we want to ignore other elements if it doesn't make sense for them to have version annotations.
- Loading branch information
1 parent
a8b53f4
commit 3691e7f
Showing
5 changed files
with
702 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
Oops, something went wrong.