Skip to content

Commit 16740c2

Browse files
mchehabJonathan Corbet
authored andcommitted
scripts/kernel_doc.py: better handle exported symbols
Change the logic which detects internal/external symbols in a way that we can re-use it when calling via Sphinx extension. While here, remove an unused self.config var and let it clearer that self.config variables are read-only. This helps to allow handling multiple times in parallel if ever needed. Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> Signed-off-by: Jonathan Corbet <corbet@lwn.net> Link: https://lore.kernel.org/r/6a69ba8d2b7ee6a6427abb53e60d09bd4d3565ee.1744106242.git.mchehab+huawei@kernel.org
1 parent a566ba5 commit 16740c2

4 files changed

Lines changed: 124 additions & 79 deletions

File tree

scripts/kernel-doc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ def main():
287287

288288
for t in kfiles.msg(enable_lineno=args.enable_lineno, export=args.export,
289289
internal=args.internal, symbol=args.symbol,
290-
nosymbol=args.nosymbol,
290+
nosymbol=args.nosymbol, export_file=args.export_file,
291291
no_doc_sections=args.no_doc_sections):
292292
msg = t[1]
293293
if msg:

scripts/lib/kdoc/kdoc_files.py

Lines changed: 75 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ def parse_files(self, file_list, file_not_found_cb):
6868
handling directories if any
6969
"""
7070

71+
if not file_list:
72+
return
73+
7174
for fname in file_list:
7275
if self.srctree:
7376
f = os.path.join(self.srctree, fname)
@@ -84,40 +87,70 @@ def parse_files(self, file_list, file_not_found_cb):
8487

8588
class KernelFiles():
8689
"""
87-
Parse lernel-doc tags on multiple kernel source files.
90+
Parse kernel-doc tags on multiple kernel source files.
91+
92+
There are two type of parsers defined here:
93+
- self.parse_file(): parses both kernel-doc markups and
94+
EXPORT_SYMBOL* macros;
95+
- self.process_export_file(): parses only EXPORT_SYMBOL* macros.
8896
"""
8997

98+
def warning(self, msg):
99+
"""Ancillary routine to output a warning and increment error count"""
100+
101+
self.config.log.warning(msg)
102+
self.errors += 1
103+
104+
def error(self, msg):
105+
"""Ancillary routine to output an error and increment error count"""
106+
107+
self.config.log.error(msg)
108+
self.errors += 1
109+
90110
def parse_file(self, fname):
91111
"""
92112
Parse a single Kernel source.
93113
"""
94114

115+
# Prevent parsing the same file twice if results are cached
116+
if fname in self.files:
117+
return
118+
95119
doc = KernelDoc(self.config, fname)
96-
doc.run()
120+
export_table, entries = doc.parse_kdoc()
121+
122+
self.export_table[fname] = export_table
123+
124+
self.files.add(fname)
125+
self.export_files.add(fname) # parse_kdoc() already check exports
97126

98-
return doc.entries
127+
self.results[fname] = entries
99128

100129
def process_export_file(self, fname):
101130
"""
102131
Parses EXPORT_SYMBOL* macros from a single Kernel source file.
103132
"""
104-
try:
105-
with open(fname, "r", encoding="utf8",
106-
errors="backslashreplace") as fp:
107-
for line in fp:
108-
KernelDoc.process_export(self.config.function_table, line)
109133

110-
except IOError:
111-
self.config.log.error("Error: Cannot open fname %s", fname)
112-
self.config.errors += 1
134+
# Prevent parsing the same file twice if results are cached
135+
if fname in self.export_files:
136+
return
137+
138+
doc = KernelDoc(self.config, fname)
139+
export_table = doc.parse_export()
140+
141+
if not export_table:
142+
self.error(f"Error: Cannot check EXPORT_SYMBOL* on {fname}")
143+
export_table = set()
144+
145+
self.export_table[fname] = export_table
146+
self.export_files.add(fname)
113147

114148
def file_not_found_cb(self, fname):
115149
"""
116150
Callback to warn if a file was not found.
117151
"""
118152

119-
self.config.log.error("Cannot find file %s", fname)
120-
self.config.errors += 1
153+
self.error(f"Cannot find file {fname}")
121154

122155
def __init__(self, verbose=False, out_style=None,
123156
werror=False, wreturn=False, wshort_desc=False,
@@ -147,7 +180,9 @@ def __init__(self, verbose=False, out_style=None,
147180
if kdoc_werror:
148181
werror = kdoc_werror
149182

150-
# Set global config data used on all files
183+
# Some variables are global to the parser logic as a whole as they are
184+
# used to send control configuration to KernelDoc class. As such,
185+
# those variables are read-only inside the KernelDoc.
151186
self.config = argparse.Namespace
152187

153188
self.config.verbose = verbose
@@ -156,27 +191,25 @@ def __init__(self, verbose=False, out_style=None,
156191
self.config.wshort_desc = wshort_desc
157192
self.config.wcontents_before_sections = wcontents_before_sections
158193

159-
self.config.function_table = set()
160-
self.config.source_map = {}
161-
162194
if not logger:
163195
self.config.log = logging.getLogger("kernel-doc")
164196
else:
165197
self.config.log = logger
166198

167-
self.config.kernel_version = os.environ.get("KERNELVERSION",
168-
"unknown kernel version'")
199+
self.config.warning = self.warning
200+
169201
self.config.src_tree = os.environ.get("SRCTREE", None)
170202

171-
self.out_style = out_style
203+
# Initialize variables that are internal to KernelFiles
172204

173-
# Initialize internal variables
205+
self.out_style = out_style
174206

175-
self.config.errors = 0
207+
self.errors = 0
176208
self.results = {}
177209

178210
self.files = set()
179211
self.export_files = set()
212+
self.export_table = {}
180213

181214
def parse(self, file_list, export_file=None):
182215
"""
@@ -185,28 +218,11 @@ def parse(self, file_list, export_file=None):
185218

186219
glob = GlobSourceFiles(srctree=self.config.src_tree)
187220

188-
# Prevent parsing the same file twice to speedup parsing and
189-
# avoid reporting errors multiple times
190-
191221
for fname in glob.parse_files(file_list, self.file_not_found_cb):
192-
if fname not in self.files:
193-
self.results[fname] = self.parse_file(fname)
194-
self.files.add(fname)
195-
196-
# If a list of export files was provided, parse EXPORT_SYMBOL*
197-
# from files that weren't fully parsed
198-
199-
if not export_file:
200-
return
201-
202-
self.export_files |= self.files
203-
204-
glob = GlobSourceFiles(srctree=self.config.src_tree)
222+
self.parse_file(fname)
205223

206224
for fname in glob.parse_files(export_file, self.file_not_found_cb):
207-
if fname not in self.export_files:
208-
self.process_export_file(fname)
209-
self.export_files.add(fname)
225+
self.process_export_file(fname)
210226

211227
def out_msg(self, fname, name, arg):
212228
"""
@@ -223,32 +239,35 @@ def out_msg(self, fname, name, arg):
223239

224240
def msg(self, enable_lineno=False, export=False, internal=False,
225241
symbol=None, nosymbol=None, no_doc_sections=False,
226-
filenames=None):
242+
filenames=None, export_file=None):
227243
"""
228244
Interacts over the kernel-doc results and output messages,
229245
returning kernel-doc markups on each interaction
230246
"""
231247

232-
function_table = self.config.function_table
233-
234-
if symbol:
235-
for s in symbol:
236-
function_table.add(s)
237-
238-
# Output none mode: only warnings will be shown
239-
if not self.out_style:
240-
return
241-
242248
self.out_style.set_config(self.config)
243249

244-
self.out_style.set_filter(export, internal, symbol, nosymbol,
245-
function_table, enable_lineno,
246-
no_doc_sections)
247-
248250
if not filenames:
249251
filenames = sorted(self.results.keys())
250252

251253
for fname in filenames:
254+
function_table = set()
255+
256+
if internal or export:
257+
if not export_file:
258+
export_file = [fname]
259+
260+
for f in export_file:
261+
function_table |= self.export_table[f]
262+
263+
if symbol:
264+
for s in symbol:
265+
function_table.add(s)
266+
267+
self.out_style.set_filter(export, internal, symbol, nosymbol,
268+
function_table, enable_lineno,
269+
no_doc_sections)
270+
252271
msg = ""
253272
for name, arg in self.results[fname]:
254273
msg += self.out_msg(fname, name, arg)
@@ -261,12 +280,3 @@ def msg(self, enable_lineno=False, export=False, internal=False,
261280
fname, ln, dtype)
262281
if msg:
263282
yield fname, msg
264-
265-
@property
266-
def errors(self):
267-
"""
268-
Return a count of the number of warnings found, including
269-
the ones displayed while interacting over self.msg.
270-
"""
271-
272-
return self.config.errors

scripts/lib/kdoc/kdoc_output.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def __init__(self):
6969
self.enable_lineno = None
7070
self.nosymbol = {}
7171
self.symbol = None
72-
self.function_table = set()
72+
self.function_table = None
7373
self.config = None
7474
self.no_doc_sections = False
7575

@@ -94,10 +94,10 @@ def set_filter(self, export, internal, symbol, nosymbol, function_table,
9494

9595
self.enable_lineno = enable_lineno
9696
self.no_doc_sections = no_doc_sections
97+
self.function_table = function_table
9798

9899
if symbol:
99100
self.out_mode = self.OUTPUT_INCLUDE
100-
function_table = symbol
101101
elif export:
102102
self.out_mode = self.OUTPUT_EXPORTED
103103
elif internal:
@@ -108,8 +108,6 @@ def set_filter(self, export, internal, symbol, nosymbol, function_table,
108108
if nosymbol:
109109
self.nosymbol = set(nosymbol)
110110

111-
if function_table:
112-
self.function_table = function_table
113111

114112
def highlight_block(self, block):
115113
"""
@@ -129,8 +127,7 @@ def out_warnings(self, args):
129127
warnings = args.get('warnings', [])
130128

131129
for log_msg in warnings:
132-
self.config.log.warning(log_msg)
133-
self.config.errors += 1
130+
self.config.warning(log_msg)
134131

135132
def check_doc(self, name, args):
136133
"""Check if DOC should be output"""

scripts/lib/kdoc/kdoc_parser.py

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,21 +1133,25 @@ def dump_typedef(self, ln, proto):
11331133
self.emit_warning(ln, "error: Cannot parse typedef!")
11341134

11351135
@staticmethod
1136-
def process_export(function_table, line):
1136+
def process_export(function_set, line):
11371137
"""
11381138
process EXPORT_SYMBOL* tags
11391139
1140-
This method is called both internally and externally, so, it
1141-
doesn't use self.
1140+
This method doesn't use any variable from the class, so declare it
1141+
with a staticmethod decorator.
11421142
"""
11431143

1144+
# Note: it accepts only one EXPORT_SYMBOL* per line, as having
1145+
# multiple export lines would violate Kernel coding style.
1146+
11441147
if export_symbol.search(line):
11451148
symbol = export_symbol.group(2)
1146-
function_table.add(symbol)
1149+
function_set.add(symbol)
1150+
return
11471151

11481152
if export_symbol_ns.search(line):
11491153
symbol = export_symbol_ns.group(2)
1150-
function_table.add(symbol)
1154+
function_set.add(symbol)
11511155

11521156
def process_normal(self, ln, line):
11531157
"""
@@ -1617,17 +1621,39 @@ def process_docblock(self, ln, line):
16171621
elif doc_content.search(line):
16181622
self.entry.contents += doc_content.group(1) + "\n"
16191623

1620-
def run(self):
1624+
def parse_export(self):
1625+
"""
1626+
Parses EXPORT_SYMBOL* macros from a single Kernel source file.
1627+
"""
1628+
1629+
export_table = set()
1630+
1631+
try:
1632+
with open(self.fname, "r", encoding="utf8",
1633+
errors="backslashreplace") as fp:
1634+
1635+
for line in fp:
1636+
self.process_export(export_table, line)
1637+
1638+
except IOError:
1639+
return None
1640+
1641+
return export_table
1642+
1643+
def parse_kdoc(self):
16211644
"""
16221645
Open and process each line of a C source file.
1623-
he parsing is controlled via a state machine, and the line is passed
1646+
The parsing is controlled via a state machine, and the line is passed
16241647
to a different process function depending on the state. The process
16251648
function may update the state as needed.
1649+
1650+
Besides parsing kernel-doc tags, it also parses export symbols.
16261651
"""
16271652

16281653
cont = False
16291654
prev = ""
16301655
prev_ln = None
1656+
export_table = set()
16311657

16321658
try:
16331659
with open(self.fname, "r", encoding="utf8",
@@ -1659,6 +1685,16 @@ def run(self):
16591685
self.st_inline_name[self.inline_doc_state],
16601686
line)
16611687

1688+
# This is an optimization over the original script.
1689+
# There, when export_file was used for the same file,
1690+
# it was read twice. Here, we use the already-existing
1691+
# loop to parse exported symbols as well.
1692+
#
1693+
# TODO: It should be noticed that not all states are
1694+
# needed here. On a future cleanup, process export only
1695+
# at the states that aren't handling comment markups.
1696+
self.process_export(export_table, line)
1697+
16621698
# Hand this line to the appropriate state handler
16631699
if self.state == self.STATE_NORMAL:
16641700
self.process_normal(ln, line)
@@ -1675,3 +1711,5 @@ def run(self):
16751711
self.process_docblock(ln, line)
16761712
except OSError:
16771713
self.config.log.error(f"Error: Cannot open file {self.fname}")
1714+
1715+
return export_table, self.entries

0 commit comments

Comments
 (0)