diff --git a/doc/_extensions/zephyr/kconfig/__init__.py b/doc/_extensions/zephyr/kconfig/__init__.py
index 6bf828a9088..eb1903b1898 100644
--- a/doc/_extensions/zephyr/kconfig/__init__.py
+++ b/doc/_extensions/zephyr/kconfig/__init__.py
@@ -70,19 +70,24 @@
ZEPHYR_BASE = Path(__file__).parents[4]
-def kconfig_load(app: Sphinx) -> tuple[kconfiglib.Kconfig, dict[str, str]]:
+def kconfig_load(app: Sphinx) -> tuple[kconfiglib.Kconfig, kconfiglib.Kconfig, dict[str, str]]:
"""Load Kconfig"""
with TemporaryDirectory() as td:
modules = zephyr_module.parse_modules(ZEPHYR_BASE)
# generate Kconfig.modules file
kconfig = ""
+ sysbuild_kconfig = ""
for module in modules:
kconfig += zephyr_module.process_kconfig(module.project, module.meta)
+ sysbuild_kconfig += zephyr_module.process_sysbuildkconfig(module.project, module.meta)
with open(Path(td) / "Kconfig.modules", "w") as f:
f.write(kconfig)
+ with open(Path(td) / "Kconfig.sysbuild.modules", "w") as f:
+ f.write(sysbuild_kconfig)
+
# generate dummy Kconfig.dts file
kconfig = ""
@@ -145,6 +150,13 @@ def kconfig_load(app: Sphinx) -> tuple[kconfiglib.Kconfig, dict[str, str]]:
os.environ["BOARD"] = "boards"
os.environ["KCONFIG_BOARD_DIR"] = str(Path(td) / "boards")
+ # Sysbuild runs first
+ os.environ["CONFIG_"] = "SB_CONFIG_"
+ sysbuild_output = kconfiglib.Kconfig(ZEPHYR_BASE / "share" / "sysbuild" / "Kconfig")
+
+ # Normal Kconfig runs second
+ os.environ["CONFIG_"] = "CONFIG_"
+
# insert external Kconfigs to the environment
module_paths = dict()
for module in modules:
@@ -172,7 +184,7 @@ def kconfig_load(app: Sphinx) -> tuple[kconfiglib.Kconfig, dict[str, str]]:
if kconfig.exists():
os.environ[f"ZEPHYR_{name_var}_KCONFIG"] = str(kconfig)
- return kconfiglib.Kconfig(ZEPHYR_BASE / "Kconfig"), module_paths
+ return kconfiglib.Kconfig(ZEPHYR_BASE / "Kconfig"), sysbuild_output, module_paths
class KconfigSearchNode(nodes.Element):
@@ -332,13 +344,15 @@ def add_option(self, option):
def sc_fmt(sc):
+ prefix = os.environ["CONFIG_"]
+
if isinstance(sc, kconfiglib.Symbol):
if sc.nodes:
- return f'CONFIG_{sc.name}'
+ return f'{prefix}{sc.name}'
elif isinstance(sc, kconfiglib.Choice):
if not sc.name:
return "<choice>"
- return f'<choice CONFIG_{sc.name}>'
+ return f'<choice {prefix}{sc.name}>'
return kconfiglib.standard_sc_expr_str(sc)
@@ -350,137 +364,139 @@ def kconfig_build_resources(app: Sphinx) -> None:
return
with progress_message("Building Kconfig database..."):
- kconfig, module_paths = kconfig_load(app)
+ kconfig, sysbuild_kconfig, module_paths = kconfig_load(app)
db = list()
- for sc in sorted(
- chain(kconfig.unique_defined_syms, kconfig.unique_choices),
- key=lambda sc: sc.name if sc.name else "",
- ):
- # skip nameless symbols
- if not sc.name:
- continue
-
- # store alternative defaults (from defconfig files)
- alt_defaults = list()
- for node in sc.nodes:
- if "defconfig" not in node.filename:
+ for kconfig_obj in [kconfig, sysbuild_kconfig]:
+ os.environ["CONFIG_"] = kconfig_obj.config_prefix
+ for sc in sorted(
+ chain(kconfig_obj.unique_defined_syms, kconfig_obj.unique_choices),
+ key=lambda sc: sc.name if sc.name else "",
+ ):
+ # skip nameless symbols
+ if not sc.name:
continue
- for value, cond in node.orig_defaults:
- fmt = kconfiglib.expr_str(value, sc_fmt)
- if cond is not sc.kconfig.y:
- fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
- alt_defaults.append([fmt, node.filename])
-
- # build list of symbols that select/imply the current one
- # note: all reverse dependencies are ORed together, and conditionals
- # (e.g. select/imply A if B) turns into A && B. So we first split
- # by OR to include all entries, and we split each one by AND to just
- # take the first entry.
- selected_by = list()
- if isinstance(sc, kconfiglib.Symbol) and sc.rev_dep != sc.kconfig.n:
- for select in kconfiglib.split_expr(sc.rev_dep, kconfiglib.OR):
- sym = kconfiglib.split_expr(select, kconfiglib.AND)[0]
- selected_by.append(f"CONFIG_{sym.name}")
-
- implied_by = list()
- if isinstance(sc, kconfiglib.Symbol) and sc.weak_rev_dep != sc.kconfig.n:
- for select in kconfiglib.split_expr(sc.weak_rev_dep, kconfiglib.OR):
- sym = kconfiglib.split_expr(select, kconfiglib.AND)[0]
- implied_by.append(f"CONFIG_{sym.name}")
-
- # only process nodes with prompt or help
- nodes = [node for node in sc.nodes if node.prompt or node.help]
-
- inserted_paths = list()
- for node in nodes:
- # avoid duplicate symbols by forcing unique paths. this can
- # happen due to dependencies on 0, a trick used by some modules
- path = f"{node.filename}:{node.linenr}"
- if path in inserted_paths:
- continue
- inserted_paths.append(path)
-
- dependencies = None
- if node.dep is not sc.kconfig.y:
- dependencies = kconfiglib.expr_str(node.dep, sc_fmt)
-
- defaults = list()
- for value, cond in node.orig_defaults:
- fmt = kconfiglib.expr_str(value, sc_fmt)
- if cond is not sc.kconfig.y:
- fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
- defaults.append(fmt)
-
- selects = list()
- for value, cond in node.orig_selects:
- fmt = kconfiglib.expr_str(value, sc_fmt)
- if cond is not sc.kconfig.y:
- fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
- selects.append(fmt)
-
- implies = list()
- for value, cond in node.orig_implies:
- fmt = kconfiglib.expr_str(value, sc_fmt)
- if cond is not sc.kconfig.y:
- fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
- implies.append(fmt)
-
- ranges = list()
- for min, max, cond in node.orig_ranges:
- fmt = (
- f"[{kconfiglib.expr_str(min, sc_fmt)}, "
- f"{kconfiglib.expr_str(max, sc_fmt)}]"
+ # store alternative defaults (from defconfig files)
+ alt_defaults = list()
+ for node in sc.nodes:
+ if "defconfig" not in str(node.filename):
+ continue
+
+ for value, cond in node.orig_defaults:
+ fmt = kconfiglib.expr_str(value, sc_fmt)
+ if cond is not sc.kconfig.y:
+ fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
+ alt_defaults.append([fmt, node.filename])
+
+ # build list of symbols that select/imply the current one
+ # note: all reverse dependencies are ORed together, and conditionals
+ # (e.g. select/imply A if B) turns into A && B. So we first split
+ # by OR to include all entries, and we split each one by AND to just
+ # take the first entry.
+ selected_by = list()
+ if isinstance(sc, kconfiglib.Symbol) and sc.rev_dep != sc.kconfig.n:
+ for select in kconfiglib.split_expr(sc.rev_dep, kconfiglib.OR):
+ sym = kconfiglib.split_expr(select, kconfiglib.AND)[0]
+ selected_by.append(f"{kconfig_obj.config_prefix}{sym.name}")
+
+ implied_by = list()
+ if isinstance(sc, kconfiglib.Symbol) and sc.weak_rev_dep != sc.kconfig.n:
+ for select in kconfiglib.split_expr(sc.weak_rev_dep, kconfiglib.OR):
+ sym = kconfiglib.split_expr(select, kconfiglib.AND)[0]
+ implied_by.append(f"{kconfig_obj.config_prefix}{sym.name}")
+
+ # only process nodes with prompt or help
+ nodes = [node for node in sc.nodes if node.prompt or node.help]
+
+ inserted_paths = list()
+ for node in nodes:
+ # avoid duplicate symbols by forcing unique paths. this can
+ # happen due to dependencies on 0, a trick used by some modules
+ path = f"{node.filename}:{node.linenr}"
+ if path in inserted_paths:
+ continue
+ inserted_paths.append(path)
+
+ dependencies = None
+ if node.dep is not sc.kconfig.y:
+ dependencies = kconfiglib.expr_str(node.dep, sc_fmt)
+
+ defaults = list()
+ for value, cond in node.orig_defaults:
+ fmt = kconfiglib.expr_str(value, sc_fmt)
+ if cond is not sc.kconfig.y:
+ fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
+ defaults.append(fmt)
+
+ selects = list()
+ for value, cond in node.orig_selects:
+ fmt = kconfiglib.expr_str(value, sc_fmt)
+ if cond is not sc.kconfig.y:
+ fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
+ selects.append(fmt)
+
+ implies = list()
+ for value, cond in node.orig_implies:
+ fmt = kconfiglib.expr_str(value, sc_fmt)
+ if cond is not sc.kconfig.y:
+ fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
+ implies.append(fmt)
+
+ ranges = list()
+ for min, max, cond in node.orig_ranges:
+ fmt = (
+ f"[{kconfiglib.expr_str(min, sc_fmt)}, "
+ f"{kconfiglib.expr_str(max, sc_fmt)}]"
+ )
+ if cond is not sc.kconfig.y:
+ fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
+ ranges.append(fmt)
+
+ choices = list()
+ if isinstance(sc, kconfiglib.Choice):
+ for sym in sc.syms:
+ choices.append(kconfiglib.expr_str(sym, sc_fmt))
+
+ menupath = ""
+ iternode = node
+ while iternode.parent is not iternode.kconfig.top_node:
+ iternode = iternode.parent
+ if iternode.prompt:
+ title = iternode.prompt[0]
+ else:
+ title = kconfiglib.standard_sc_expr_str(iternode.item)
+ menupath = f" > {title}" + menupath
+
+ menupath = "(Top)" + menupath
+
+ filename = str(node.filename)
+ for name, path in module_paths.items():
+ path += "/"
+ if str(node.filename).startswith(path):
+ filename = str(node.filename).replace(path, f"/")
+ break
+
+ db.append(
+ {
+ "name": f"{kconfig_obj.config_prefix}{sc.name}",
+ "prompt": node.prompt[0] if node.prompt else None,
+ "type": kconfiglib.TYPE_TO_STR[sc.type],
+ "help": node.help,
+ "dependencies": dependencies,
+ "defaults": defaults,
+ "alt_defaults": alt_defaults,
+ "selects": selects,
+ "selected_by": selected_by,
+ "implies": implies,
+ "implied_by": implied_by,
+ "ranges": ranges,
+ "choices": choices,
+ "filename": filename,
+ "linenr": node.linenr,
+ "menupath": menupath,
+ }
)
- if cond is not sc.kconfig.y:
- fmt += f" if {kconfiglib.expr_str(cond, sc_fmt)}"
- ranges.append(fmt)
-
- choices = list()
- if isinstance(sc, kconfiglib.Choice):
- for sym in sc.syms:
- choices.append(kconfiglib.expr_str(sym, sc_fmt))
-
- menupath = ""
- iternode = node
- while iternode.parent is not iternode.kconfig.top_node:
- iternode = iternode.parent
- if iternode.prompt:
- title = iternode.prompt[0]
- else:
- title = kconfiglib.standard_sc_expr_str(iternode.item)
- menupath = f" > {title}" + menupath
-
- menupath = "(Top)" + menupath
-
- filename = node.filename
- for name, path in module_paths.items():
- path += "/"
- if node.filename.startswith(path):
- filename = node.filename.replace(path, f"/")
- break
-
- db.append(
- {
- "name": f"CONFIG_{sc.name}",
- "prompt": node.prompt[0] if node.prompt else None,
- "type": kconfiglib.TYPE_TO_STR[sc.type],
- "help": node.help,
- "dependencies": dependencies,
- "defaults": defaults,
- "alt_defaults": alt_defaults,
- "selects": selects,
- "selected_by": selected_by,
- "implies": implies,
- "implied_by": implied_by,
- "ranges": ranges,
- "choices": choices,
- "filename": filename,
- "linenr": node.linenr,
- "menupath": menupath,
- }
- )
app.env.kconfig_db = db # type: ignore