def rst_link(sc): # Returns an RST link (string) for the symbol/choice 'sc', or the normal # Kconfig expression format (e.g. just the name) for 'sc' if it can't be # turned into a link. if isinstance(sc, kconfiglib.Symbol): # Skip constant and undefined symbols by checking if expr.nodes is # empty if sc.nodes: # The "\ " avoids RST issues for !CONFIG_FOO -- see # http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#character-level-inline-markup return r"\ :option:`{0} <CONFIG_{0}>`".format(sc.name) elif isinstance(sc, kconfiglib.Choice): # Choices appear as dependencies of choice symbols. # # Use a :ref: instead of an :option:. With an :option:, we'd have to have # an '.. option::' in the choice reference page as well. That would make # the internal choice ID show up in the documentation. # # Note that the first pair of <...> is non-syntactic here. We just display # choices links within <> in the documentation. return r"\ :ref:`<{}> <{}>`" \ .format(choice_desc(sc), choice_id(sc)) # Can't turn 'sc' into a link. Use the standard Kconfig format. return kconfiglib.standard_sc_expr_str(sc)
def menu_path(node): path = "" while True: # This excludes indented submenus created in the menuconfig # interface when items depend on the preceding symbol. # is_menuconfig means anything that would be shown as a separate # menu (not indented): proper 'menu's, menuconfig symbols, and # choices. node = node.parent while not node.is_menuconfig: node = node.parent if node is node.kconfig.top_node: break # Promptless choices can show up as parents, e.g. when people # define choices in multiple locations to add symbols. Use # standard_sc_expr_str() to show them. That way they show up as # '<choice (name if any)>'. path = arrow + \ (node.prompt[0] if node.prompt else kconfiglib.standard_sc_expr_str(node.item)) + \ path return "(top menu)" + path
def sc_fmt(sc): if isinstance(sc, kconfiglib.Symbol): if sc.nodes: return f'<a href="#CONFIG_{sc.name}">CONFIG_{sc.name}</a>' elif isinstance(sc, kconfiglib.Choice): if not sc.name: return "<choice>" return f'<choice <a href="#CONFIG_{sc.name}">CONFIG_{sc.name}</a>>' return kconfiglib.standard_sc_expr_str(sc)
def _menu_path_info(node): # Returns a string describing the menu path leading up to 'node' path = "" while node.parent is not _kconfig.top_node: node = node.parent # Promptless choices might appear among the parents. Use # standard_sc_expr_str() for them, so that they show up as # '<choice (name if any)>'. path = " -> " + (node.prompt[0] if node.prompt else standard_sc_expr_str(node.item)) + path return "(Top)" + path
def menu_path(node): path = "" while node.parent is not node.kconfig.top_node: node = node.parent # Promptless choices can show up as parents, e.g. when people # define choices in multiple locations to add symbols. Use # standard_sc_expr_str() to show them. That way they show up as # '<choice (name if any)>'. path = arrow + \ (node.prompt[0] if node.prompt else kconfiglib.standard_sc_expr_str(node.item)) + \ path return "(Top)" + path
def _name_and_val_str(sc): # Custom symbol/choice printer that shows symbol values after symbols # Show the values of non-constant (non-quoted) symbols that don't look like # numbers. Things like 123 are actually symbol references, and only work as # expected due to undefined symbols getting their name as their value. # Showing the symbol value for those isn't helpful though. if isinstance(sc, Symbol) and not sc.is_constant and not _is_num(sc.name): if not sc.nodes: # Undefined symbol reference return "{}(undefined/n)".format(sc.name) return '{}(={})'.format(sc.name, sc.str_value) # For other items, use the standard format return standard_sc_expr_str(sc)
def _name_info(sc): # Returns a string with the name of the symbol/choice. Choices are shown as # <choice (name if any)>. return (sc.name if sc.name else standard_sc_expr_str(sc)) + "\n\n"
def kconfig_build_resources(app: Sphinx) -> None: """Build the Kconfig database and install HTML resources.""" if not app.config.kconfig_generate_db: return with progress_message("Building Kconfig database..."): kconfig, module_paths = kconfig_load(app) db = list() for sc in chain(kconfig.unique_defined_syms, kconfig.unique_choices): # 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: 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)}]") 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(): if node.filename.startswith(path): filename = node.filename.replace( path, f"<module:{name}>") 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 outdir = Path(app.outdir) / "kconfig" outdir.mkdir(exist_ok=True) kconfig_db_file = outdir / "kconfig.json" with open(kconfig_db_file, "w") as f: json.dump(db, f) app.config.html_extra_path.append(kconfig_db_file.as_posix()) app.config.html_static_path.append(RESOURCES_DIR.as_posix())