docs: sphinx/kernel_abi: reduce buffer usage for ABI messages

Instead of producing a big message with all ABI contents and then
parse as a whole, simplify the code by handling each ABI symbol
in separate. As an additional benefit, there's no need to place
file/line nubers inlined at the data and use a regex to convert
them.

Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Signed-off-by: Jonathan Corbet <corbet@lwn.net>
Link: https://lore.kernel.org/r/15be22955e3c6df49d7256c8fd24f62b397ad0ff.1739182025.git.mchehab+huawei@kernel.org
This commit is contained in:
Mauro Carvalho Chehab
2025-02-10 11:18:05 +01:00
committed by Jonathan Corbet
parent ee34f8300c
commit aea5e52dce
3 changed files with 48 additions and 45 deletions

View File

@@ -68,6 +68,7 @@ class KernelCmd(Directive):
has_content = False has_content = False
final_argument_whitespace = True final_argument_whitespace = True
logger = logging.getLogger('kernel_abi') logger = logging.getLogger('kernel_abi')
parser = None
option_spec = { option_spec = {
"debug": directives.flag, "debug": directives.flag,
@@ -79,59 +80,60 @@ class KernelCmd(Directive):
raise self.warning("docutils: file insertion disabled") raise self.warning("docutils: file insertion disabled")
path = os.path.join(srctree, "Documentation", self.arguments[0]) path = os.path.join(srctree, "Documentation", self.arguments[0])
parser = AbiParser(path, logger=self.logger) self.parser = AbiParser(path, logger=self.logger)
parser.parse_abi() self.parser.parse_abi()
parser.check_issues() self.parser.check_issues()
msg = "" node = self.nested_parse(None, self.arguments[0])
for m in parser.doc(enable_lineno=True, show_file=True):
msg += m
node = self.nested_parse(msg, self.arguments[0])
return node return node
def nested_parse(self, lines, fname): def nested_parse(self, data, fname):
env = self.state.document.settings.env env = self.state.document.settings.env
content = ViewList() content = ViewList()
node = nodes.section() node = nodes.section()
if "debug" in self.options: if data is not None:
code_block = "\n\n.. code-block:: rst\n :linenos:\n" # Handles the .rst file
for line in lines.split("\n"): for line in data.split("\n"):
code_block += "\n " + line content.append(line, fname, 0)
lines = code_block + "\n\n"
line_regex = re.compile(r"^\.\. LINENO (\S+)\#([0-9]+)$") self.do_parse(content, node)
ln = 0
n = 0
f = fname
for line in lines.split("\n"): else:
n = n + 1 # Handles the ABI parser content, symbol by symbol
match = line_regex.search(line)
if match:
new_f = match.group(1)
# Sphinx parser is lazy: it stops parsing contents in the old_f = fname
# middle, if it is too big. So, handle it per input file n = 0
if new_f != f and content: for msg, f, ln in self.parser.doc():
self.do_parse(content, node) msg_list = msg.split("\n")
content = ViewList() if "debug" in self.options:
lines = [
"", "", ".. code-block:: rst",
" :linenos:", ""
]
for m in msg_list:
lines.append(" " + m)
else:
lines = msg_list
for line in lines:
# sphinx counts lines from 0
content.append(line, f, ln - 1)
n += 1
if f != old_f:
# Add the file to Sphinx build dependencies # Add the file to Sphinx build dependencies
env.note_dependency(os.path.abspath(f)) env.note_dependency(os.path.abspath(f))
f = new_f old_f = f
# sphinx counts lines from 0 # Sphinx doesn't like to parse big messages. So, let's
ln = int(match.group(2)) - 1 # add content symbol by symbol
else: if content:
content.append(line, f, ln) self.do_parse(content, node)
content = ViewList()
self.logger.info("%s: parsed %i lines" % (fname, n)) self.logger.info("%s: parsed %i lines" % (fname, n))
if content:
self.do_parse(content, node)
return node.children return node.children

View File

@@ -63,8 +63,11 @@ class AbiRest:
parser.parse_abi() parser.parse_abi()
parser.check_issues() parser.check_issues()
for msg in parser.doc(args.enable_lineno, args.raw, not args.no_file): for t in parser.doc(args.raw, not args.no_file):
print(msg) if args.enable_lineno:
print (f".. LINENO {t[1]}#{t[2]}\n\n")
print(t[0])
class AbiValidate: class AbiValidate:
"""Initialize an argparse subparser for ABI validation""" """Initialize an argparse subparser for ABI validation"""

View File

@@ -427,7 +427,7 @@ class AbiParser:
return new_desc + "\n\n" return new_desc + "\n\n"
def doc(self, enable_lineno, output_in_txt=False, show_file=False): def doc(self, output_in_txt=False, show_file=True):
"""Print ABI at stdout""" """Print ABI at stdout"""
part = None part = None
@@ -444,10 +444,6 @@ class AbiParser:
msg = "" msg = ""
if enable_lineno:
ln = v.get("line_no", 1)
msg += f".. LINENO {file_ref[0][0]}#{ln}\n\n"
if wtype != "File": if wtype != "File":
cur_part = names[0] cur_part = names[0]
if cur_part.find("/") >= 0: if cur_part.find("/") >= 0:
@@ -508,7 +504,9 @@ class AbiParser:
if users and users.strip(" \t\n"): if users and users.strip(" \t\n"):
msg += f"Users:\n\t{users.strip("\n").replace('\n', '\n\t')}\n\n" msg += f"Users:\n\t{users.strip("\n").replace('\n', '\n\t')}\n\n"
yield msg ln = v.get("line_no", 1)
yield (msg, file_ref[0][0], ln)
def check_issues(self): def check_issues(self):
"""Warn about duplicated ABI entries""" """Warn about duplicated ABI entries"""