Skip to content

Commit be712c1

Browse files
nordicjmjukkar
authored andcommitted
[nrf fromlist] doc: extensions: kconfig: Add support for sysbuild
Adds support for outputting details on sysbuild Kconfigs for use with the documentation Kconfig lookup tool Upstream PR #: 93861 Signed-off-by: Jamie McCrae <[email protected]> (cherry picked from commit 6fb16ab)
1 parent 21ebd74 commit be712c1

File tree

1 file changed

+146
-130
lines changed

1 file changed

+146
-130
lines changed

doc/_extensions/zephyr/kconfig/__init__.py

Lines changed: 146 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -70,19 +70,24 @@
7070
ZEPHYR_BASE = Path(__file__).parents[4]
7171

7272

73-
def kconfig_load(app: Sphinx) -> tuple[kconfiglib.Kconfig, dict[str, str]]:
73+
def kconfig_load(app: Sphinx) -> tuple[kconfiglib.Kconfig, kconfiglib.Kconfig, dict[str, str]]:
7474
"""Load Kconfig"""
7575
with TemporaryDirectory() as td:
7676
modules = zephyr_module.parse_modules(ZEPHYR_BASE)
7777

7878
# generate Kconfig.modules file
7979
kconfig = ""
80+
sysbuild_kconfig = ""
8081
for module in modules:
8182
kconfig += zephyr_module.process_kconfig(module.project, module.meta)
83+
sysbuild_kconfig += zephyr_module.process_sysbuildkconfig(module.project, module.meta)
8284

8385
with open(Path(td) / "Kconfig.modules", "w") as f:
8486
f.write(kconfig)
8587

88+
with open(Path(td) / "Kconfig.sysbuild.modules", "w") as f:
89+
f.write(sysbuild_kconfig)
90+
8691
# generate dummy Kconfig.dts file
8792
kconfig = ""
8893

@@ -145,6 +150,13 @@ def kconfig_load(app: Sphinx) -> tuple[kconfiglib.Kconfig, dict[str, str]]:
145150
os.environ["BOARD"] = "boards"
146151
os.environ["KCONFIG_BOARD_DIR"] = str(Path(td) / "boards")
147152

153+
# Sysbuild runs first
154+
os.environ["CONFIG_"] = "SB_CONFIG_"
155+
sysbuild_output = kconfiglib.Kconfig(ZEPHYR_BASE / "share" / "sysbuild" / "Kconfig")
156+
157+
# Normal Kconfig runs second
158+
os.environ["CONFIG_"] = "CONFIG_"
159+
148160
# insert external Kconfigs to the environment
149161
module_paths = dict()
150162
for module in modules:
@@ -172,7 +184,7 @@ def kconfig_load(app: Sphinx) -> tuple[kconfiglib.Kconfig, dict[str, str]]:
172184
if kconfig.exists():
173185
os.environ[f"ZEPHYR_{name_var}_KCONFIG"] = str(kconfig)
174186

175-
return kconfiglib.Kconfig(ZEPHYR_BASE / "Kconfig"), module_paths
187+
return kconfiglib.Kconfig(ZEPHYR_BASE / "Kconfig"), sysbuild_output, module_paths
176188

177189

178190
class KconfigSearchNode(nodes.Element):
@@ -332,13 +344,15 @@ def add_option(self, option):
332344

333345

334346
def sc_fmt(sc):
347+
prefix = os.environ["CONFIG_"]
348+
335349
if isinstance(sc, kconfiglib.Symbol):
336350
if sc.nodes:
337-
return f'<a href="#CONFIG_{sc.name}">CONFIG_{sc.name}</a>'
351+
return f'<a href="#{prefix}{sc.name}">{prefix}{sc.name}</a>'
338352
elif isinstance(sc, kconfiglib.Choice):
339353
if not sc.name:
340354
return "&ltchoice&gt"
341-
return f'&ltchoice <a href="#CONFIG_{sc.name}">CONFIG_{sc.name}</a>&gt'
355+
return f'&ltchoice <a href="#{prefix}{sc.name}">{prefix}{sc.name}</a>&gt'
342356

343357
return kconfiglib.standard_sc_expr_str(sc)
344358

@@ -350,137 +364,139 @@ def kconfig_build_resources(app: Sphinx) -> None:
350364
return
351365

352366
with progress_message("Building Kconfig database..."):
353-
kconfig, module_paths = kconfig_load(app)
367+
kconfig, sysbuild_kconfig, module_paths = kconfig_load(app)
354368
db = list()
355369

356-
for sc in sorted(
357-
chain(kconfig.unique_defined_syms, kconfig.unique_choices),
358-
key=lambda sc: sc.name if sc.name else "",
359-
):
360-
# skip nameless symbols
361-
if not sc.name:
362-
continue
363-
364-
# store alternative defaults (from defconfig files)
365-
alt_defaults = list()
366-
for node in sc.nodes:
367-
if "defconfig" not in node.filename:
370+
for kconfig_obj in [kconfig, sysbuild_kconfig]:
371+
os.environ["CONFIG_"] = kconfig_obj.config_prefix
372+
for sc in sorted(
373+
chain(kconfig_obj.unique_defined_syms, kconfig_obj.unique_choices),
374+
key=lambda sc: sc.name if sc.name else "",
375+
):
376+
# skip nameless symbols
377+
if not sc.name:
368378
continue
369379

370-
for value, cond in node.orig_defaults:
371-
fmt = kconfiglib.expr_str(value, sc_fmt)
372-
if cond is not sc.kconfig.y:
373-
fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
374-
alt_defaults.append([fmt, node.filename])
375-
376-
# build list of symbols that select/imply the current one
377-
# note: all reverse dependencies are ORed together, and conditionals
378-
# (e.g. select/imply A if B) turns into A && B. So we first split
379-
# by OR to include all entries, and we split each one by AND to just
380-
# take the first entry.
381-
selected_by = list()
382-
if isinstance(sc, kconfiglib.Symbol) and sc.rev_dep != sc.kconfig.n:
383-
for select in kconfiglib.split_expr(sc.rev_dep, kconfiglib.OR):
384-
sym = kconfiglib.split_expr(select, kconfiglib.AND)[0]
385-
selected_by.append(f"CONFIG_{sym.name}")
386-
387-
implied_by = list()
388-
if isinstance(sc, kconfiglib.Symbol) and sc.weak_rev_dep != sc.kconfig.n:
389-
for select in kconfiglib.split_expr(sc.weak_rev_dep, kconfiglib.OR):
390-
sym = kconfiglib.split_expr(select, kconfiglib.AND)[0]
391-
implied_by.append(f"CONFIG_{sym.name}")
392-
393-
# only process nodes with prompt or help
394-
nodes = [node for node in sc.nodes if node.prompt or node.help]
395-
396-
inserted_paths = list()
397-
for node in nodes:
398-
# avoid duplicate symbols by forcing unique paths. this can
399-
# happen due to dependencies on 0, a trick used by some modules
400-
path = f"{node.filename}:{node.linenr}"
401-
if path in inserted_paths:
402-
continue
403-
inserted_paths.append(path)
404-
405-
dependencies = None
406-
if node.dep is not sc.kconfig.y:
407-
dependencies = kconfiglib.expr_str(node.dep, sc_fmt)
408-
409-
defaults = list()
410-
for value, cond in node.orig_defaults:
411-
fmt = kconfiglib.expr_str(value, sc_fmt)
412-
if cond is not sc.kconfig.y:
413-
fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
414-
defaults.append(fmt)
415-
416-
selects = list()
417-
for value, cond in node.orig_selects:
418-
fmt = kconfiglib.expr_str(value, sc_fmt)
419-
if cond is not sc.kconfig.y:
420-
fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
421-
selects.append(fmt)
422-
423-
implies = list()
424-
for value, cond in node.orig_implies:
425-
fmt = kconfiglib.expr_str(value, sc_fmt)
426-
if cond is not sc.kconfig.y:
427-
fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
428-
implies.append(fmt)
429-
430-
ranges = list()
431-
for min, max, cond in node.orig_ranges:
432-
fmt = (
433-
f"[{kconfiglib.expr_str(min, sc_fmt)}, "
434-
f"{kconfiglib.expr_str(max, sc_fmt)}]"
380+
# store alternative defaults (from defconfig files)
381+
alt_defaults = list()
382+
for node in sc.nodes:
383+
if "defconfig" not in str(node.filename):
384+
continue
385+
386+
for value, cond in node.orig_defaults:
387+
fmt = kconfiglib.expr_str(value, sc_fmt)
388+
if cond is not sc.kconfig.y:
389+
fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
390+
alt_defaults.append([fmt, node.filename])
391+
392+
# build list of symbols that select/imply the current one
393+
# note: all reverse dependencies are ORed together, and conditionals
394+
# (e.g. select/imply A if B) turns into A && B. So we first split
395+
# by OR to include all entries, and we split each one by AND to just
396+
# take the first entry.
397+
selected_by = list()
398+
if isinstance(sc, kconfiglib.Symbol) and sc.rev_dep != sc.kconfig.n:
399+
for select in kconfiglib.split_expr(sc.rev_dep, kconfiglib.OR):
400+
sym = kconfiglib.split_expr(select, kconfiglib.AND)[0]
401+
selected_by.append(f"{kconfig_obj.config_prefix}{sym.name}")
402+
403+
implied_by = list()
404+
if isinstance(sc, kconfiglib.Symbol) and sc.weak_rev_dep != sc.kconfig.n:
405+
for select in kconfiglib.split_expr(sc.weak_rev_dep, kconfiglib.OR):
406+
sym = kconfiglib.split_expr(select, kconfiglib.AND)[0]
407+
implied_by.append(f"{kconfig_obj.config_prefix}{sym.name}")
408+
409+
# only process nodes with prompt or help
410+
nodes = [node for node in sc.nodes if node.prompt or node.help]
411+
412+
inserted_paths = list()
413+
for node in nodes:
414+
# avoid duplicate symbols by forcing unique paths. this can
415+
# happen due to dependencies on 0, a trick used by some modules
416+
path = f"{node.filename}:{node.linenr}"
417+
if path in inserted_paths:
418+
continue
419+
inserted_paths.append(path)
420+
421+
dependencies = None
422+
if node.dep is not sc.kconfig.y:
423+
dependencies = kconfiglib.expr_str(node.dep, sc_fmt)
424+
425+
defaults = list()
426+
for value, cond in node.orig_defaults:
427+
fmt = kconfiglib.expr_str(value, sc_fmt)
428+
if cond is not sc.kconfig.y:
429+
fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
430+
defaults.append(fmt)
431+
432+
selects = list()
433+
for value, cond in node.orig_selects:
434+
fmt = kconfiglib.expr_str(value, sc_fmt)
435+
if cond is not sc.kconfig.y:
436+
fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
437+
selects.append(fmt)
438+
439+
implies = list()
440+
for value, cond in node.orig_implies:
441+
fmt = kconfiglib.expr_str(value, sc_fmt)
442+
if cond is not sc.kconfig.y:
443+
fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
444+
implies.append(fmt)
445+
446+
ranges = list()
447+
for min, max, cond in node.orig_ranges:
448+
fmt = (
449+
f"[{kconfiglib.expr_str(min, sc_fmt)}, "
450+
f"{kconfiglib.expr_str(max, sc_fmt)}]"
451+
)
452+
if cond is not sc.kconfig.y:
453+
fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
454+
ranges.append(fmt)
455+
456+
choices = list()
457+
if isinstance(sc, kconfiglib.Choice):
458+
for sym in sc.syms:
459+
choices.append(kconfiglib.expr_str(sym, sc_fmt))
460+
461+
menupath = ""
462+
iternode = node
463+
while iternode.parent is not iternode.kconfig.top_node:
464+
iternode = iternode.parent
465+
if iternode.prompt:
466+
title = iternode.prompt[0]
467+
else:
468+
title = kconfiglib.standard_sc_expr_str(iternode.item)
469+
menupath = f" > {title}" + menupath
470+
471+
menupath = "(Top)" + menupath
472+
473+
filename = str(node.filename)
474+
for name, path in module_paths.items():
475+
path += "/"
476+
if str(node.filename).startswith(path):
477+
filename = str(node.filename).replace(path, f"<module:{name}>/")
478+
break
479+
480+
db.append(
481+
{
482+
"name": f"{kconfig_obj.config_prefix}{sc.name}",
483+
"prompt": node.prompt[0] if node.prompt else None,
484+
"type": kconfiglib.TYPE_TO_STR[sc.type],
485+
"help": node.help,
486+
"dependencies": dependencies,
487+
"defaults": defaults,
488+
"alt_defaults": alt_defaults,
489+
"selects": selects,
490+
"selected_by": selected_by,
491+
"implies": implies,
492+
"implied_by": implied_by,
493+
"ranges": ranges,
494+
"choices": choices,
495+
"filename": filename,
496+
"linenr": node.linenr,
497+
"menupath": menupath,
498+
}
435499
)
436-
if cond is not sc.kconfig.y:
437-
fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
438-
ranges.append(fmt)
439-
440-
choices = list()
441-
if isinstance(sc, kconfiglib.Choice):
442-
for sym in sc.syms:
443-
choices.append(kconfiglib.expr_str(sym, sc_fmt))
444-
445-
menupath = ""
446-
iternode = node
447-
while iternode.parent is not iternode.kconfig.top_node:
448-
iternode = iternode.parent
449-
if iternode.prompt:
450-
title = iternode.prompt[0]
451-
else:
452-
title = kconfiglib.standard_sc_expr_str(iternode.item)
453-
menupath = f" > {title}" + menupath
454-
455-
menupath = "(Top)" + menupath
456-
457-
filename = node.filename
458-
for name, path in module_paths.items():
459-
path += "/"
460-
if node.filename.startswith(path):
461-
filename = node.filename.replace(path, f"<module:{name}>/")
462-
break
463-
464-
db.append(
465-
{
466-
"name": f"CONFIG_{sc.name}",
467-
"prompt": node.prompt[0] if node.prompt else None,
468-
"type": kconfiglib.TYPE_TO_STR[sc.type],
469-
"help": node.help,
470-
"dependencies": dependencies,
471-
"defaults": defaults,
472-
"alt_defaults": alt_defaults,
473-
"selects": selects,
474-
"selected_by": selected_by,
475-
"implies": implies,
476-
"implied_by": implied_by,
477-
"ranges": ranges,
478-
"choices": choices,
479-
"filename": filename,
480-
"linenr": node.linenr,
481-
"menupath": menupath,
482-
}
483-
)
484500

485501
app.env.kconfig_db = db # type: ignore
486502

0 commit comments

Comments
 (0)