Skip to content

Commit 1c0a057

Browse files
committed
doc: update genrest script for latest kconfiglib
The genrest.py script (used to generate config documentation from the Kconfig files) broke when upgrading to the latest kconfiglib (10.9.1). Copied the latest genrest.py script from the Zephyr project (where this script and processing was developed) and things work again. Also update requirements.txt to match the kconfiglib version verified to work. tracked-on: #1387 Signed-off-by: David B. Kinder <david.b.kinder@intel.com>
1 parent 1657544 commit 1c0a057

File tree

2 files changed

+142
-78
lines changed

2 files changed

+142
-78
lines changed

doc/scripts/genrest.py

Lines changed: 141 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -9,38 +9,41 @@
99

1010
import kconfiglib
1111

12-
# "Extend" the standard kconfiglib.expr_str() to turn references to defined
13-
# Kconfig symbols into RST links. Symbol.__str__() will then use the extended
14-
# version.
15-
#
16-
# This is a bit hacky, but better than reimplementing Symbol.__str__() and/or
17-
# kconfiglib.expr_str().
18-
19-
def expr_str_rst(expr):
20-
# Skip constant and undefined symbols by checking if expr.nodes is empty
21-
if isinstance(expr, kconfiglib.Symbol) and expr.nodes:
22-
# The "\ " avoids RST issues for !CONFIG_FOO -- see
23-
# http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#character-level-inline-markup
24-
return r"\ :option:`{0} <CONFIG_{0}>`".format(expr.name)
25-
26-
# Choices appear as dependencies of choice symbols.
27-
#
28-
# Use a :ref: instead of an :option:. With an :option:, we'd have to have
29-
# an '.. option::' in the choice reference page as well. That would make
30-
# the internal choice ID show up in the documentation.
31-
#
32-
# Note that the first pair of <...> is non-syntactic here. We just display
33-
# choices links within <> in the documentation.
34-
if isinstance(expr, kconfiglib.Choice):
12+
13+
def rst_link(sc):
14+
# Returns an RST link (string) for the symbol/choice 'sc', or the normal
15+
# Kconfig expression format (e.g. just the name) for 'sc' if it can't be
16+
# turned into a link.
17+
18+
if isinstance(sc, kconfiglib.Symbol):
19+
# Skip constant and undefined symbols by checking if expr.nodes is
20+
# empty
21+
if sc.nodes:
22+
# The "\ " avoids RST issues for !CONFIG_FOO -- see
23+
# http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#character-level-inline-markup
24+
return r"\ :option:`{0} <CONFIG_{0}>`".format(sc.name)
25+
26+
elif isinstance(sc, kconfiglib.Choice):
27+
# Choices appear as dependencies of choice symbols.
28+
#
29+
# Use a :ref: instead of an :option:. With an :option:, we'd have to have
30+
# an '.. option::' in the choice reference page as well. That would make
31+
# the internal choice ID show up in the documentation.
32+
#
33+
# Note that the first pair of <...> is non-syntactic here. We just display
34+
# choices links within <> in the documentation.
3535
return r"\ :ref:`<{}> <{}>`" \
36-
.format(choice_desc(expr), choice_id(expr))
36+
.format(choice_desc(sc), choice_id(sc))
37+
38+
# Can't turn 'sc' into a link. Use the standard Kconfig format.
39+
return kconfiglib.standard_sc_expr_str(sc)
3740

38-
# We'll end up back in expr_str_rst() when expr_str_orig() does recursive
39-
# calls for subexpressions
40-
return expr_str_orig(expr)
4141

42-
expr_str_orig = kconfiglib.expr_str
43-
kconfiglib.expr_str = expr_str_rst
42+
def expr_str(expr):
43+
# Returns the Kconfig representation of 'expr', with symbols/choices turned
44+
# into RST links
45+
46+
return kconfiglib.expr_str(expr, rst_link)
4447

4548

4649
INDEX_RST_HEADER = """.. _configuration:
@@ -87,12 +90,8 @@ def write_kconfig_rst():
8790
# String with the RST for the index page
8891
index_rst = INDEX_RST_HEADER
8992

90-
# - Sort the symbols by name so that they end up in sorted order in
91-
# index.rst
92-
#
93-
# - Use set() to get rid of duplicates for symbols defined in multiple
94-
# locations.
95-
for sym in sorted(set(kconf.defined_syms), key=lambda sym: sym.name):
93+
# Sort the symbols by name so that they end up in sorted order in index.rst
94+
for sym in sorted(kconf.unique_defined_syms, key=lambda sym: sym.name):
9695
# Write an RST file for the symbol
9796
write_sym_rst(sym, out_dir)
9897

@@ -104,7 +103,7 @@ def write_kconfig_rst():
104103
" / ".join(node.prompt[0]
105104
for node in sym.nodes if node.prompt))
106105

107-
for choice in kconf.choices:
106+
for choice in kconf.unique_choices:
108107
# Write an RST file for the choice
109108
write_choice_rst(choice, out_dir)
110109

@@ -120,6 +119,7 @@ def write_sym_rst(sym, out_dir):
120119
direct_deps_rst(sym) +
121120
defaults_rst(sym) +
122121
select_imply_rst(sym) +
122+
selecting_implying_rst(sym) +
123123
kconfig_definition_rst(sym))
124124

125125

@@ -202,24 +202,39 @@ def direct_deps_rst(sc):
202202
"===================\n\n" \
203203
"{}\n\n" \
204204
"*(Includes any dependencies from if's and menus.)*\n\n" \
205-
.format(kconfiglib.expr_str(sc.direct_dep))
205+
.format(expr_str(sc.direct_dep))
206206

207207

208208
def defaults_rst(sc):
209209
# Returns RST that lists the 'default' properties of 'sc' (symbol or
210210
# choice)
211211

212-
if not sc.defaults:
212+
if isinstance(sc, kconfiglib.Symbol) and sc.choice:
213+
# 'default's on choice symbols have no effect (and generate a warning).
214+
# The implicit value hint below would be misleading as well.
213215
return ""
214216

215217
rst = "Defaults\n" \
216218
"========\n\n"
217219

218-
for value, cond in sc.defaults:
219-
default_str = kconfiglib.expr_str(value)
220-
if cond is not sc.kconfig.y:
221-
default_str += " if " + kconfiglib.expr_str(cond)
222-
rst += "- {}\n".format(default_str)
220+
if sc.defaults:
221+
for value, cond in sc.defaults:
222+
rst += "- " + expr_str(value)
223+
if cond is not sc.kconfig.y:
224+
rst += " if " + expr_str(cond)
225+
rst += "\n"
226+
227+
else:
228+
rst += "No defaults. Implicitly defaults to "
229+
230+
if isinstance(sc, kconfiglib.Choice):
231+
rst += "the first (visible) choice option.\n"
232+
elif sc.orig_type in (kconfiglib.BOOL, kconfiglib.TRISTATE):
233+
rst += "``n``.\n"
234+
else:
235+
# This is accurate even for int/hex symbols, though an active
236+
# 'range' might clamp the value (which is then treated as zero)
237+
rst += "the empty string.\n"
223238

224239
return rst + "\n"
225240

@@ -235,53 +250,95 @@ def choice_syms_rst(choice):
235250

236251
for sym in choice.syms:
237252
# Generates a link
238-
rst += "- {}\n".format(kconfiglib.expr_str(sym))
253+
rst += "- {}\n".format(expr_str(sym))
239254

240255
return rst + "\n"
241256

242257

243258
def select_imply_rst(sym):
259+
# Returns RST that lists the symbols 'select'ed or 'imply'd by the symbol
260+
261+
rst = ""
262+
263+
def add_select_imply_rst(type_str, lst):
264+
# Adds RST that lists the selects/implies from 'lst', which holds
265+
# (<symbol>, <condition>) tuples, if any. Also adds a heading derived
266+
# from 'type_str' if there any selects/implies.
267+
268+
nonlocal rst
269+
270+
if lst:
271+
heading = "Symbols {} by this symbol".format(type_str)
272+
rst += "{}\n{}\n\n".format(heading, len(heading)*"=")
273+
274+
for select, cond in lst:
275+
rst += "- " + rst_link(select)
276+
if cond is not sym.kconfig.y:
277+
rst += " if " + expr_str(cond)
278+
rst += "\n"
279+
280+
rst += "\n"
281+
282+
add_select_imply_rst("selected", sym.selects)
283+
add_select_imply_rst("implied", sym.implies)
284+
285+
return rst
286+
287+
288+
def selecting_implying_rst(sym):
244289
# Returns RST that lists the symbols that are 'select'ing or 'imply'ing the
245290
# symbol
246291

247292
rst = ""
248293

249-
def add_select_imply_rst(type_str, expr):
250-
# Writes a link for each selecting symbol (if 'expr' is sym.rev_dep) or
251-
# each implying symbol (if 'expr' is sym.weak_rev_dep). Also adds a
252-
# heading at the top, derived from type_str ("select"/"imply").
294+
def add_selecting_implying_rst(type_str, expr):
295+
# Writes a link for each symbol that selects the symbol (if 'expr' is
296+
# sym.rev_dep) or each symbol that imply's the symbol (if 'expr' is
297+
# sym.weak_rev_dep). Also adds a heading at the top derived from
298+
# type_str ("select"/"imply"), if there are any selecting/implying
299+
# symbols.
253300

254301
nonlocal rst
255302

256-
heading = "Symbols that ``{}`` this symbol".format(type_str)
257-
rst += "{}\n{}\n\n".format(heading, len(heading)*"=")
303+
if expr is not sym.kconfig.n:
304+
heading = "Symbols that {} this symbol".format(type_str)
305+
rst += "{}\n{}\n\n".format(heading, len(heading)*"=")
258306

259-
# The reverse dependencies from each select/imply are ORed together
260-
for select in kconfiglib.split_expr(expr, kconfiglib.OR):
261-
# - 'select/imply A if B' turns into A && B
262-
# - 'select/imply A' just turns into A
263-
#
264-
# In both cases, we can split on AND and pick the first
265-
# operand.
307+
# The reverse dependencies from each select/imply are ORed together
308+
for select in kconfiglib.split_expr(expr, kconfiglib.OR):
309+
# - 'select/imply A if B' turns into A && B
310+
# - 'select/imply A' just turns into A
311+
#
312+
# In both cases, we can split on AND and pick the first
313+
# operand.
266314

267-
# kconfiglib.expr_str() generates a link
268-
rst += "- {}\n".format(kconfiglib.expr_str(
269-
kconfiglib.split_expr(select, kconfiglib.AND)[0]))
315+
rst += "- {}\n".format(rst_link(
316+
kconfiglib.split_expr(select, kconfiglib.AND)[0]))
270317

271-
rst += "\n"
318+
rst += "\n"
272319

273-
if sym.rev_dep is not sym.kconfig.n:
274-
add_select_imply_rst("select", sym.rev_dep)
275-
276-
if sym.weak_rev_dep is not sym.kconfig.n:
277-
add_select_imply_rst("imply", sym.weak_rev_dep)
320+
add_selecting_implying_rst("select", sym.rev_dep)
321+
add_selecting_implying_rst("imply", sym.weak_rev_dep)
278322

279323
return rst
280324

281325

282326
def kconfig_definition_rst(sc):
283-
# Returns RST that lists the Kconfig definition(s) of 'sc' (symbol or
284-
# choice)
327+
# Returns RST that lists the Kconfig definition location, include path,
328+
# menu path, and Kconfig definition for each node (definition location) of
329+
# 'sc' (symbol or choice)
330+
331+
# Fancy Unicode arrow. Added in '93, so ought to be pretty safe.
332+
arrow = " \N{RIGHTWARDS ARROW} "
333+
334+
def include_path(node):
335+
if not node.include_path:
336+
# In the top-level Kconfig file
337+
return ""
338+
339+
return "Included via {}\n\n".format(
340+
arrow.join("``{}:{}``".format(filename, linenr)
341+
for filename, linenr in node.include_path))
285342

286343
def menu_path(node):
287344
path = ""
@@ -299,23 +356,30 @@ def menu_path(node):
299356
if node is node.kconfig.top_node:
300357
break
301358

302-
# Fancy Unicode arrow. Added in '93, so ought to be pretty safe.
303-
path = " → " + node.prompt[0] + path
359+
path = arrow + node.prompt[0] + path
304360

305361
return "(top menu)" + path
306362

307363
heading = "Kconfig definition"
308364
if len(sc.nodes) > 1: heading += "s"
309365
rst = "{}\n{}\n\n".format(heading, len(heading)*"=")
310366

311-
rst += ".. highlight:: kconfig\n\n"
367+
rst += ".. highlight:: kconfig"
312368

313-
rst += "\n\n".join(
314-
"At ``{}:{}``, in menu ``{}``:\n\n"
315-
".. parsed-literal::\n\n"
316-
"{}".format(node.filename, node.linenr, menu_path(node),
317-
textwrap.indent(str(node), " "*4))
318-
for node in sc.nodes)
369+
for node in sc.nodes:
370+
rst += "\n\n" \
371+
"At ``{}:{}``\n\n" \
372+
"{}" \
373+
"Menu path: {}\n\n" \
374+
".. parsed-literal::\n\n{}" \
375+
.format(node.filename, node.linenr,
376+
include_path(node), menu_path(node),
377+
textwrap.indent(node.custom_str(rst_link), 4*" "))
378+
379+
# Not the last node?
380+
if node is not sc.nodes[-1]:
381+
# Add a horizontal line between multiple definitions
382+
rst += "\n\n----"
319383

320384
rst += "\n\n*(Definitions include propagated dependencies, " \
321385
"including from if's and menus.)*"

doc/scripts/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ breathe==4.9.1
22
sphinx==1.7.7
33
docutils==0.14
44
sphinx_rtd_theme==0.4.0
5-
kconfiglib
5+
kconfiglib==10.9.1

0 commit comments

Comments
 (0)