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

View File

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

View File

@@ -427,7 +427,7 @@ class AbiParser:
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"""
part = None
@@ -444,10 +444,6 @@ class AbiParser:
msg = ""
if enable_lineno:
ln = v.get("line_no", 1)
msg += f".. LINENO {file_ref[0][0]}#{ln}\n\n"
if wtype != "File":
cur_part = names[0]
if cur_part.find("/") >= 0:
@@ -508,7 +504,9 @@ class AbiParser:
if users and users.strip(" \t\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):
"""Warn about duplicated ABI entries"""