[RFC v2 19/38] docs: sphinx/kernel_abi: reduce buffer usage for ABI messages

From: Mauro Carvalho Chehab
Date: Mon Jan 27 2025 - 19:11:20 EST


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@xxxxxxxxxx>
---
Documentation/sphinx/kernel_abi.py | 76 +++++++++++++++---------------
scripts/get_abi.py | 17 +++----
2 files changed, 48 insertions(+), 45 deletions(-)

diff --git a/Documentation/sphinx/kernel_abi.py b/Documentation/sphinx/kernel_abi.py
index 93d537d8cb6c..025a4f02e1a4 100644
--- a/Documentation/sphinx/kernel_abi.py
+++ b/Documentation/sphinx/kernel_abi.py
@@ -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
+ self.do_parse(content, node)

- for line in lines.split("\n"):
- n = n + 1
- match = line_regex.search(line)
- if match:
- new_f = match.group(1)
+ else:
+ # Handles the ABI parser content, symbol by symbol

- # 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()
+ 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
+ old_f = f

- # sphinx counts lines from 0
- ln = int(match.group(2)) - 1
- else:
- content.append(line, f, ln)
+ # 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))
-
- if content:
- self.do_parse(content, node)
+ self.logger.info("%s: parsed %i lines" % (fname, n))

return node.children

diff --git a/scripts/get_abi.py b/scripts/get_abi.py
index 3a8dcff85dc2..b57f46b91828 100755
--- a/scripts/get_abi.py
+++ b/scripts/get_abi.py
@@ -441,7 +441,7 @@ class AbiParser:

return new_desc + "\n\n"

- def doc(self, enable_lineno=False, output_in_txt=False, show_file=False):
+ def doc(self, output_in_txt=False, show_file=True):
"""Print ABI at stdout"""

part = None
@@ -458,10 +458,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:
@@ -522,7 +518,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"""
@@ -630,8 +628,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:
--
2.48.1