[Add] 打通Kconfiglib配置操作

This commit is contained in:
gaoyang3513
2024-11-16 17:04:01 +08:00
parent ebd1227d82
commit b2473d60d5
110 changed files with 21987 additions and 4 deletions

View File

@ -0,0 +1,102 @@
mainmenu "Example Kconfig configuration"
config MODULES
bool "Enable loadable module support"
option modules
default y
menu "Bool and tristate symbols"
config BOOL
bool "Bool symbol"
default y
config BOOL_DEP
bool "Dependent bool symbol"
depends on BOOL
# Mix it up a bit with an 'if' instead of a 'depends on'
if BOOL
config TRI_DEP
tristate "Dependent tristate symbol"
select SELECTED_BY_TRI_DEP
imply IMPLIED_BY_TRI_DEP
endif
config TWO_MENU_NODES
bool "First prompt"
depends on BOOL
config TRI
tristate "Tristate symbol"
config TWO_MENU_NODES
bool "Second prompt"
comment "These are selected by TRI_DEP"
config SELECTED_BY_TRI_DEP
tristate "Tristate selected by TRI_DEP"
config IMPLIED_BY_TRI_DEP
tristate "Tristate implied by TRI_DEP"
endmenu
menu "String, int, and hex symbols"
config STRING
string "String symbol"
default "foo"
config INT
int "Int symbol"
default 747
config HEX
hex "Hex symbol"
default 0xABC
endmenu
menu "Various choices"
choice BOOL_CHOICE
bool "Bool choice"
config BOOL_CHOICE_SYM_1
bool "Bool choice sym 1"
config BOOL_CHOICE_SYM_2
bool "Bool choice sym 2"
endchoice
choice TRI_CHOICE
tristate "Tristate choice"
config TRI_CHOICE_SYM_1
tristate "Tristate choice sym 1"
config TRI_CHOICE_SYM_2
tristate "Tristate choice sym 2"
endchoice
choice OPT_BOOL_CHOICE
bool "Optional bool choice"
optional
config OPT_BOOL_CHOICE_SYM_1
bool "Optional bool choice sym 1"
config OPT_BOOL_CHOICE_SYM_2
bool "Optional bool choice sym 2"
endchoice
endmenu

View File

@ -0,0 +1,66 @@
# This is tree-walking version of allnoconfig.py, for demonstration purposes.
# Verified by the test suite to generate identical output to 'make allnoconfig'
# for all ARCHes.
#
# Note: A more practical version would use Kconfig.node_iter(). The manual tree
# walking is for demonstration purposes.
#
# Usage for the Linux kernel:
#
# $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/allnoconfig_walk.py
import sys
from kconfiglib import Kconfig, Symbol
def do_allnoconfig(node):
global changed
# Walk the tree of menu nodes. You can imagine this as going down/into menu
# entries in the menuconfig interface, setting each to n (or the lowest
# assignable value).
while node:
if isinstance(node.item, Symbol):
sym = node.item
# Is the symbol a non-allnoconfig_y symbol that can be set to a
# lower value than its current value?
if (not sym.is_allnoconfig_y and
sym.assignable and
sym.assignable[0] < sym.tri_value):
# Yup, lower it
sym.set_value(sym.assignable[0])
changed = True
# Recursively lower children
if node.list:
do_allnoconfig(node.list)
node = node.next
# Parse the Kconfig files
kconf = Kconfig(sys.argv[1])
# Do an initial pass to set 'option allnoconfig_y' symbols to y
for sym in kconf.unique_defined_syms:
if sym.is_allnoconfig_y:
sym.set_value(2)
while True:
# Changing later symbols in the configuration can sometimes allow earlier
# symbols to be lowered, e.g. if a later symbol 'select's an earlier
# symbol. To handle such situations, we do additional passes over the tree
# until we're no longer able to change the value of any symbol in a pass.
changed = False
do_allnoconfig(kconf.top_node)
# Did the pass change any symbols?
if not changed:
break
print(kconf.write_config())

View File

@ -0,0 +1,39 @@
# Produces exactly the same output as the following script:
#
# make defconfig
# echo CONFIG_ETHERNET=n >> .config
# make oldconfig
# echo CONFIG_ETHERNET=y >> .config
# yes n | make oldconfig
#
# This came up in https://github.com/ulfalizer/Kconfiglib/issues/15.
#
# Usage:
#
# $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/defconfig_oldconfig.py
import sys
import kconfiglib
kconf = kconfiglib.Kconfig(sys.argv[1])
# Mirrors defconfig
kconf.load_config("arch/x86/configs/x86_64_defconfig")
kconf.write_config()
# Mirrors the first oldconfig
kconf.load_config()
kconf.syms["ETHERNET"].set_value(0)
kconf.write_config()
# Mirrors the second oldconfig
kconf.load_config()
kconf.syms["ETHERNET"].set_value(2)
for s in kconf.unique_defined_syms:
if s.user_value is None and 0 in s.assignable:
s.set_value(0)
# Write the final configuration
print(kconf.write_config())

View File

@ -0,0 +1,15 @@
# Prints all (set) environment variables referenced in the Kconfig files
# together with their values, as a list of assignments.
#
# Note: This only works for environment variables referenced via the $(FOO)
# preprocessor syntax. The older $FOO syntax is maintained for backwards
# compatibility.
import os
import sys
import kconfiglib
print(" ".join("{}='{}'".format(var, os.environ[var])
for var in kconfiglib.Kconfig(sys.argv[1]).env_vars))

View File

@ -0,0 +1,24 @@
# Evaluates an expression (e.g. "X86_64 || (X86_32 && X86_LOCAL_APIC)") in the
# context of a configuration. Note that this always yields a tristate value (n,
# m, or y).
#
# Usage:
#
# $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/eval_expr.py SCRIPT_ARG=<expr>
import sys
import kconfiglib
if len(sys.argv) < 3:
sys.exit("Pass the expression to evaluate with SCRIPT_ARG=<expression>")
kconf = kconfiglib.Kconfig(sys.argv[1])
expr = sys.argv[2]
# Enable modules so that m doesn't get demoted to n
kconf.modules.set_value(2)
print("the expression '{}' evaluates to {}"
.format(expr, kconf.eval_string(expr)))

View File

@ -0,0 +1,112 @@
# Prints all menu nodes that reference a given symbol any of their properties
# or property conditions, along with their parent menu nodes.
#
# Usage:
#
# $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/find_symbol.py SCRIPT_ARG=<name>
#
# Example output for SCRIPT_ARG=X86:
#
# Found 470 locations that reference X86:
#
# ========== Location 1 (init/Kconfig:1108) ==========
#
# config SGETMASK_SYSCALL
# bool
# prompt "sgetmask/ssetmask syscalls support" if EXPERT
# default PARISC || M68K || PPC || MIPS || X86 || SPARC || MICROBLAZE || SUPERH
# help
# sys_sgetmask and sys_ssetmask are obsolete system calls
# no longer supported in libc but still enabled by default in some
# architectures.
#
# If unsure, leave the default option here.
#
# ---------- Parent 1 (init/Kconfig:1077) ----------
#
# menuconfig EXPERT
# bool
# prompt "Configure standard kernel features (expert users)"
# select DEBUG_KERNEL
# help
# This option allows certain base kernel options and settings
# to be disabled or tweaked. This is for specialized
# environments which can tolerate a "non-standard" kernel.
# Only use this if you really know what you are doing.
#
# ---------- Parent 2 (init/Kconfig:39) ----------
#
# menu "General setup"
#
# ========== Location 2 (arch/Kconfig:29) ==========
#
# config OPROFILE_EVENT_MULTIPLEX
# bool
# prompt "OProfile multiplexing support (EXPERIMENTAL)"
# default "n"
# depends on OPROFILE && X86
# help
# The number of hardware counters is limited. The multiplexing
# feature enables OProfile to gather more events than counters
# are provided by the hardware. This is realized by switching
# between events at a user specified time interval.
#
# If unsure, say N.
#
# ---------- Parent 1 (arch/Kconfig:16) ----------
#
# config OPROFILE
# tristate
# prompt "OProfile system profiling"
# select RING_BUFFER
# select RING_BUFFER_ALLOW_SWAP
# depends on PROFILING && HAVE_OPROFILE
# help
# OProfile is a profiling system capable of profiling the
# whole system, include the kernel, kernel modules, libraries,
# and applications.
#
# If unsure, say N.
#
# ---------- Parent 2 (init/Kconfig:39) ----------
#
# menu "General setup"
#
# ... (tons more)
import sys
import kconfiglib
if len(sys.argv) < 3:
sys.exit('Pass symbol name (without "CONFIG_" prefix) with SCRIPT_ARG=<name>')
kconf = kconfiglib.Kconfig(sys.argv[1])
sym_name = sys.argv[2]
if sym_name not in kconf.syms:
print("No symbol {} exists in the configuration".format(sym_name))
sys.exit(0)
referencing = [node for node in kconf.node_iter()
if kconf.syms[sym_name] in node.referenced]
if not referencing:
print("No references to {} found".format(sym_name))
sys.exit(0)
print("Found {} locations that reference {}:\n"
.format(len(referencing), sym_name))
for i, node in enumerate(referencing, 1):
print("========== Location {} ({}:{}) ==========\n\n{}"
.format(i, node.filename, node.linenr, node))
# Print the parents of the menu node too
node = node.parent
parent_i = 1
while node is not kconf.top_node:
print("---------- Parent {} ({}:{}) ----------\n\n{}"
.format(parent_i, node.filename, node.linenr, node))
node = node.parent
parent_i += 1

View File

@ -0,0 +1,64 @@
# Does a case-insensitive search for a regular expression in the help texts of
# symbols and choices and the prompts of menus and comments. Prints the
# matching items together with their locations and the matching text.
#
# Usage:
#
# $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/help_grep.py SCRIPT_ARG=<regex>
#
# Shortened example output for SCRIPT_ARG=general:
#
# menu "General setup"
# location: init/Kconfig:39
#
# config SYSVIPC
# bool
# prompt "System V IPC"
# help
# ...
# exchange information. It is generally considered to be a good thing,
# ...
#
# location: init/Kconfig:233
#
# config BSD_PROCESS_ACCT
# bool
# prompt "BSD Process Accounting" if MULTIUSER
# help
# ...
# information. This is generally a good idea, so say Y.
#
# location: init/Kconfig:403
#
# ...
import re
import sys
from kconfiglib import Kconfig, Symbol, Choice, MENU, COMMENT
if len(sys.argv) < 3:
sys.exit("Pass the regex with SCRIPT_ARG=<regex>")
search = re.compile(sys.argv[2], re.IGNORECASE).search
for node in Kconfig(sys.argv[1]).node_iter():
match = False
if isinstance(node.item, (Symbol, Choice)) and \
node.help is not None and search(node.help):
print(node.item)
match = True
elif node.item == MENU and search(node.prompt[0]):
print('menu "{}"'.format(node.prompt[0]))
match = True
elif node.item == COMMENT and search(node.prompt[0]):
print('comment "{}"'.format(node.prompt[0]))
match = True
if match:
print("location: {}:{}\n".format(node.filename, node.linenr))

View File

@ -0,0 +1 @@
../kconfiglib.py

View File

@ -0,0 +1,156 @@
# Prints a list of symbols that are referenced in the Kconfig files of some
# architecture but not defined by the Kconfig files of any architecture.
#
# A Kconfig file might be shared between many architectures and legitimately
# reference undefined symbols for some of them, but if no architecture defines
# the symbol, it usually indicates a problem or potential cleanup.
#
# This script could be sped up a lot if needed. See the comment near the
# referencing_nodes() call.
#
# Run with the following command in the kernel root:
#
# $ python(3) Kconfiglib/examples/list_undefined.py
#
# Example output:
#
# Registering defined and undefined symbols for all arches
# Processing mips
# Processing ia64
# Processing metag
# ...
#
# Finding references to each undefined symbol
# Processing mips
# Processing ia64
# Processing metag
# ...
#
# The following globally undefined symbols were found, listed here
# together with the locations of the items that reference them.
# References might come from enclosing menus and ifs.
#
# ARM_ERRATA_753970: arch/arm/mach-mvebu/Kconfig:56, arch/arm/mach-mvebu/Kconfig:39
# SUNXI_CCU_MP: drivers/clk/sunxi-ng/Kconfig:14
# SUNXI_CCU_DIV: drivers/clk/sunxi-ng/Kconfig:14
# AC97: sound/ac97/Kconfig:6
# ...
import os
import subprocess
from kconfiglib import Kconfig
# Referenced inside the Kconfig files
os.environ["KERNELVERSION"] = str(
subprocess.check_output(("make", "kernelversion")).decode("utf-8").rstrip()
)
def all_arch_srcarch_pairs():
"""
Generates all valid (ARCH, SRCARCH) tuples for the kernel, corresponding to
different architectures. SRCARCH holds the arch/ subdirectory.
"""
for srcarch in os.listdir("arch"):
# Each subdirectory of arch/ containing a Kconfig file corresponds to
# an architecture
if os.path.exists(os.path.join("arch", srcarch, "Kconfig")):
yield (srcarch, srcarch)
# Some architectures define additional ARCH settings with ARCH != SRCARCH
# (search for "Additional ARCH settings for" in the top-level Makefile)
yield ("i386", "x86")
yield ("x86_64", "x86")
yield ("sparc32", "sparc")
yield ("sparc64", "sparc")
yield ("sh64", "sh")
yield ("um", "um")
def all_arch_srcarch_kconfigs():
"""
Generates Kconfig instances for all the architectures in the kernel
"""
os.environ["srctree"] = "."
os.environ["HOSTCC"] = "gcc"
os.environ["HOSTCXX"] = "g++"
os.environ["CC"] = "gcc"
os.environ["LD"] = "ld"
for arch, srcarch in all_arch_srcarch_pairs():
print(" Processing " + arch)
os.environ["ARCH"] = arch
os.environ["SRCARCH"] = srcarch
# um (User Mode Linux) uses a different base Kconfig file
yield Kconfig("Kconfig" if arch != "um" else "arch/x86/um/Kconfig",
warn=False)
print("Registering defined and undefined symbols for all arches")
# Sets holding the names of all defined and undefined symbols, for all
# architectures
defined = set()
undefined = set()
for kconf in all_arch_srcarch_kconfigs():
for name, sym in kconf.syms.items():
if sym.nodes:
# If the symbol has a menu node, it is defined
defined.add(name)
else:
# Undefined symbol. We skip some of the uninteresting ones.
# Due to how Kconfig works, integer literals show up as symbols
# (from e.g. 'default 1'). Skip those.
try:
int(name, 0)
continue
except ValueError:
# Interesting undefined symbol
undefined.add(name)
print("\nFinding references to each undefined symbol")
def referencing_nodes(kconf, name):
# Returns a list of all menu nodes that reference a symbol named 'name' in
# any of their properties or property conditions
res = []
for node in kconf.node_iter():
for ref in node.referenced:
if ref.name == name:
res.append(node)
return res
# Maps each globally undefined symbol to the menu nodes that reference it
undef_sym_refs = [(name, set()) for name in undefined - defined]
for kconf in all_arch_srcarch_kconfigs():
for name, refs in undef_sym_refs:
# This means that we search the entire configuration tree for each
# undefined symbol, which is terribly inefficient. We could speed
# things up by tweaking referencing_nodes() to compare each symbol to
# multiple symbols while walking the configuration tree.
for node in referencing_nodes(kconf, name):
refs.add("{}:{}".format(node.filename, node.linenr))
print("\nThe following globally undefined symbols were found, listed here\n"
"together with the locations of the items that reference them.\n"
"References might come from enclosing menus and ifs.\n")
for name, refs in undef_sym_refs:
print(" {}: {}".format(name, ", ".join(refs)))

View File

@ -0,0 +1,341 @@
#!/usr/bin/env python3
# Implements a simple configuration interface on top of Kconfiglib to
# demonstrate concepts for building a menuconfig-like. Emulates how the
# standard menuconfig prints menu entries.
#
# Always displays the entire Kconfig tree to keep things as simple as possible
# (all symbols, choices, menus, and comments).
#
# Usage:
#
# $ python(3) Kconfiglib/examples/menuconfig.py <Kconfig file>
#
# A sample Kconfig is available in Kconfiglib/examples/Kmenuconfig.
#
# Here's a notation guide. The notation matches the one used by menuconfig
# (scripts/kconfig/mconf):
#
# [ ] prompt - Bool
# < > prompt - Tristate
# {M} prompt - Tristate selected to m. Can only be set to m or y.
# -*- prompt - Bool/tristate selected to y, pinning it
# -M- prompt - Tristate selected to m that also has m visibility,
# pinning it to m
# (foo) prompt - String/int/hex symbol with value "foo"
# --> prompt - The selected symbol in a choice in y mode. This
# syntax is unique to this example.
#
# When modules are disabled, the .type attribute of TRISTATE symbols and
# choices automatically changes to BOOL. This trick is used by the C
# implementation as well, and gives the expected behavior without having to do
# anything extra here. The original type is available in .orig_type if needed.
#
# The Kconfiglib/examples/Kmenuconfig example uses named choices to be able to
# refer to choices by name. Named choices are supported in the C tools too, but
# I don't think I've ever seen them used in the wild.
#
# Sample session:
#
# $ python Kconfiglib/examples/menuconfig.py Kconfiglib/examples/Kmenuconfig
#
# ======== Example Kconfig configuration ========
#
# [*] Enable loadable module support (MODULES)
# Bool and tristate symbols
# [*] Bool symbol (BOOL)
# [ ] Dependent bool symbol (BOOL_DEP)
# < > Dependent tristate symbol (TRI_DEP)
# [ ] First prompt (TWO_MENU_NODES)
# < > Tristate symbol (TRI)
# [ ] Second prompt (TWO_MENU_NODES)
# *** These are selected by TRI_DEP ***
# < > Tristate selected by TRI_DEP (SELECTED_BY_TRI_DEP)
# < > Tristate implied by TRI_DEP (IMPLIED_BY_TRI_DEP)
# String, int, and hex symbols
# (foo) String symbol (STRING)
# (747) Int symbol (INT)
# (0xABC) Hex symbol (HEX)
# Various choices
# -*- Bool choice (BOOL_CHOICE)
# --> Bool choice sym 1 (BOOL_CHOICE_SYM_1)
# Bool choice sym 2 (BOOL_CHOICE_SYM_2)
# {M} Tristate choice (TRI_CHOICE)
# < > Tristate choice sym 1 (TRI_CHOICE_SYM_1)
# < > Tristate choice sym 2 (TRI_CHOICE_SYM_2)
# [ ] Optional bool choice (OPT_BOOL_CHOICE)
#
# Enter a symbol/choice name, "load_config", or "write_config" (or press CTRL+D to exit): BOOL
# Value for BOOL (available: n, y): n
#
# ======== Example Kconfig configuration ========
#
# [*] Enable loadable module support (MODULES)
# Bool and tristate symbols
# [ ] Bool symbol (BOOL)
# < > Tristate symbol (TRI)
# [ ] Second prompt (TWO_MENU_NODES)
# *** These are selected by TRI_DEP ***
# < > Tristate selected by TRI_DEP (SELECTED_BY_TRI_DEP)
# < > Tristate implied by TRI_DEP (IMPLIED_BY_TRI_DEP)
# String, int, and hex symbols
# (foo) String symbol (STRING)
# (747) Int symbol (INT)
# (0xABC) Hex symbol (HEX)
# Various choices
# -*- Bool choice (BOOL_CHOICE)
# --> Bool choice sym 1 (BOOL_CHOICE_SYM_1)
# Bool choice sym 2 (BOOL_CHOICE_SYM_2)
# {M} Tristate choice (TRI_CHOICE)
# < > Tristate choice sym 1 (TRI_CHOICE_SYM_1)
# < > Tristate choice sym 2 (TRI_CHOICE_SYM_2)
# [ ] Optional bool choice (OPT_BOOL_CHOICE)
#
# Enter a symbol/choice name, "load_config", or "write_config" (or press CTRL+D to exit): MODULES
# Value for MODULES (available: n, y): n
#
# ======== Example Kconfig configuration ========
#
# [ ] Enable loadable module support (MODULES)
# Bool and tristate symbols
# [ ] Bool symbol (BOOL)
# [ ] Tristate symbol (TRI)
# [ ] Second prompt (TWO_MENU_NODES)
# *** These are selected by TRI_DEP ***
# [ ] Tristate selected by TRI_DEP (SELECTED_BY_TRI_DEP)
# [ ] Tristate implied by TRI_DEP (IMPLIED_BY_TRI_DEP)
# String, int, and hex symbols
# (foo) String symbol (STRING)
# (747) Int symbol (INT)
# (0xABC) Hex symbol (HEX)
# Various choices
# -*- Bool choice (BOOL_CHOICE)
# --> Bool choice sym 1 (BOOL_CHOICE_SYM_1)
# Bool choice sym 2 (BOOL_CHOICE_SYM_2)
# -*- Tristate choice (TRI_CHOICE)
# --> Tristate choice sym 1 (TRI_CHOICE_SYM_1)
# Tristate choice sym 2 (TRI_CHOICE_SYM_2)
# [ ] Optional bool choice (OPT_BOOL_CHOICE)
#
# Enter a symbol/choice name, "load_config", or "write_config" (or press CTRL+D to exit): ^D
from __future__ import print_function
import readline
import sys
from kconfiglib import Kconfig, \
Symbol, MENU, COMMENT, \
BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN, \
expr_value, \
TRI_TO_STR
# Python 2/3 compatibility hack
if sys.version_info[0] < 3:
input = raw_input
def indent_print(s, indent):
print(indent*" " + s)
def value_str(sc):
"""
Returns the value part ("[*]", "<M>", "(foo)" etc.) of a menu entry.
sc: Symbol or Choice.
"""
if sc.type in (STRING, INT, HEX):
return "({})".format(sc.str_value)
# BOOL or TRISTATE
# The choice mode is an upper bound on the visibility of choice symbols, so
# we can check the choice symbols' own visibility to see if the choice is
# in y mode
if isinstance(sc, Symbol) and sc.choice and sc.visibility == 2:
# For choices in y mode, print '-->' next to the selected symbol
return "-->" if sc.choice.selection is sc else " "
tri_val_str = (" ", "M", "*")[sc.tri_value]
if len(sc.assignable) == 1:
# Pinned to a single value
return "-{}-".format(tri_val_str)
if sc.type == BOOL:
return "[{}]".format(tri_val_str)
if sc.type == TRISTATE:
if sc.assignable == (1, 2):
# m and y available
return "{" + tri_val_str + "}" # Gets a bit confusing with .format()
return "<{}>".format(tri_val_str)
def node_str(node):
"""
Returns the complete menu entry text for a menu node, or "" for invisible
menu nodes. Invisible menu nodes are those that lack a prompt or that do
not have a satisfied prompt condition.
Example return value: "[*] Bool symbol (BOOL)"
The symbol name is printed in parentheses to the right of the prompt. This
is so that symbols can easily be referred to in the configuration
interface.
"""
if not node.prompt:
return ""
# Even for menu nodes for symbols and choices, it's wrong to check
# Symbol.visibility / Choice.visibility here. The reason is that a symbol
# (and a choice, in theory) can be defined in multiple locations, giving it
# multiple menu nodes, which do not necessarily all have the same prompt
# visibility. Symbol.visibility / Choice.visibility is calculated as the OR
# of the visibility of all the prompts.
prompt, prompt_cond = node.prompt
if not expr_value(prompt_cond):
return ""
if node.item == MENU:
return " " + prompt
if node.item == COMMENT:
return " *** {} ***".format(prompt)
# Symbol or Choice
sc = node.item
if sc.type == UNKNOWN:
# Skip symbols defined without a type (these are obscure and generate
# a warning)
return ""
# {:3} sets the field width to three. Gives nice alignment for empty string
# values.
res = "{:3} {}".format(value_str(sc), prompt)
# Don't print the name for unnamed choices (the normal kind)
if sc.name is not None:
res += " ({})".format(sc.name)
return res
def print_menuconfig_nodes(node, indent):
"""
Prints a tree with all the menu entries rooted at 'node'. Child menu
entries are indented.
"""
while node:
string = node_str(node)
if string:
indent_print(string, indent)
if node.list:
print_menuconfig_nodes(node.list, indent + 8)
node = node.next
def print_menuconfig(kconf):
"""
Prints all menu entries for the configuration.
"""
# Print the expanded mainmenu text at the top. This is the same as
# kconf.top_node.prompt[0], but with variable references expanded.
print("\n======== {} ========\n".format(kconf.mainmenu_text))
print_menuconfig_nodes(kconf.top_node.list, 0)
print("")
def get_value_from_user(sc):
"""
Prompts the user for a value for the symbol or choice 'sc'. For
bool/tristate symbols and choices, provides a list of all the assignable
values.
"""
if not sc.visibility:
print(sc.name + " is not currently visible")
return False
prompt = "Value for {}".format(sc.name)
if sc.type in (BOOL, TRISTATE):
prompt += " (available: {})" \
.format(", ".join(TRI_TO_STR[val] for val in sc.assignable))
prompt += ": "
val = input(prompt)
# Automatically add a "0x" prefix for hex symbols, like the menuconfig
# interface does. This isn't done when loading .config files, hence why
# set_value() doesn't do it automatically.
if sc.type == HEX and not val.startswith(("0x", "0X")):
val = "0x" + val
# Let Kconfiglib itself print a warning here if the value is invalid. We
# could also disable warnings temporarily with 'kconf.warn = False' and
# print our own warning.
return sc.set_value(val)
if __name__ == "__main__":
if len(sys.argv) != 2:
sys.exit("usage: menuconfig.py <Kconfig file>")
# Load Kconfig configuration files
kconf = Kconfig(sys.argv[1])
# Print the initial configuration tree
print_menuconfig(kconf)
while True:
try:
cmd = input('Enter a symbol/choice name, "load_config", or '
'"write_config" (or press CTRL+D to exit): ').strip()
except EOFError:
print("")
break
if cmd == "load_config":
config_filename = input(".config file to load: ")
try:
# Returns a message telling which file got loaded
print(kconf.load_config(config_filename))
except EnvironmentError as e:
print(e, file=sys.stderr)
print_menuconfig(kconf)
continue
if cmd == "write_config":
config_filename = input("To this file: ")
try:
# Returns a message telling which file got saved
print(kconf.write_config(config_filename))
except EnvironmentError as e:
print(e, file=sys.stderr)
continue
# Assume 'cmd' is the name of a symbol or choice if it isn't one of the
# commands above, prompt the user for a value for it, and print the new
# configuration tree
if cmd in kconf.syms:
if get_value_from_user(kconf.syms[cmd]):
print_menuconfig(kconf)
continue
if cmd in kconf.named_choices:
if get_value_from_user(kconf.named_choices[cmd]):
print_menuconfig(kconf)
continue
print("No symbol/choice named '{}' in the configuration".format(cmd),
file=sys.stderr)

View File

@ -0,0 +1,121 @@
#!/usr/bin/env python3
# This script functions similarly to scripts/kconfig/merge_config.sh from the
# kernel tree, merging multiple configurations fragments to produce a complete
# .config, with unspecified values filled in as for alldefconfig.
#
# The generated .config respects symbol dependencies, and a warning is printed
# if any symbol gets a different value from the assigned value.
#
# For a real-world merging example based on this script, see
# https://github.com/zephyrproject-rtos/zephyr/blob/master/scripts/kconfig/kconfig.py.
#
# Here's a demo:
#
# Kconfig contents:
#
# config FOO
# bool "FOO"
#
# config BAR
# bool "BAR"
#
# config BAZ
# string "BAZ"
#
# config QAZ
# bool "QAZ" if n
#
#
# conf1 contents:
#
# CONFIG_FOO=y
#
#
# conf2 contents:
#
# CONFIG_BAR=y
#
#
# conf3 contents:
#
# # Assigned twice (would generate warning if 'warn_assign_override' was
# # True)
# # CONFIG_FOO is not set
#
# # Ops... this symbol doesn't exist
# CONFIG_OPS=y
#
# CONFIG_BAZ="baz string"
#
#
# conf4 contents:
#
# CONFIG_QAZ=y
#
#
# Running:
#
# $ python(3) merge_config.py Kconfig merged conf1 conf2 conf3 conf4
# Merged configuration 'conf1'
# Merged configuration 'conf2'
# conf3:5: warning: attempt to assign the value 'y' to the undefined symbol OPS
# Merged configuration 'conf3'
# Merged configuration 'conf4'
# Configuration saved to 'merged'
# warning: QAZ (defined at Kconfig:10) was assigned the value 'y' but got the value 'n' -- check dependencies
# $ cat merged
# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)
# # CONFIG_FOO is not set
# CONFIG_BAR=y
# CONFIG_BAZ="baz string"
from __future__ import print_function
import sys
from kconfiglib import Kconfig, BOOL, TRISTATE, TRI_TO_STR
if len(sys.argv) < 4:
sys.exit("usage: merge_config.py Kconfig merged_config config1 [config2 ...]")
kconf = Kconfig(sys.argv[1], suppress_traceback=True)
# Enable warnings for assignments to undefined symbols
kconf.warn_assign_undef = True
# (This script uses alldefconfig as the base. Other starting states could be
# set up here as well. The approach in examples/allnoconfig_simpler.py could
# provide an allnoconfig starting state for example.)
# Disable warnings generated for multiple assignments to the same symbol within
# a (set of) configuration files. Assigning a symbol multiple times might be
# done intentionally when merging configuration files.
kconf.warn_assign_override = False
kconf.warn_assign_redun = False
# Create a merged configuration by loading the fragments with replace=False.
# load_config() and write_config() returns a message to print.
for config in sys.argv[3:]:
print(kconf.load_config(config, replace=False))
# Write the merged configuration
print(kconf.write_config(sys.argv[2]))
# Print warnings for symbols whose actual value doesn't match the assigned
# value
for sym in kconf.defined_syms:
# Was the symbol assigned to?
if sym.user_value is not None:
# Tristate values are represented as 0, 1, 2. Having them as
# "n", "m", "y" is more convenient here, so convert.
if sym.type in (BOOL, TRISTATE):
user_value = TRI_TO_STR[sym.user_value]
else:
user_value = sym.user_value
if user_value != sym.str_value:
print("warning: {} was assigned the value '{}' but got the "
"value '{}' -- check dependencies".format(
sym.name_and_loc, user_value, sym.str_value),
file=sys.stderr)

View File

@ -0,0 +1,199 @@
# Prints menu entries as a tree with its value in the .config file. This can be
# handy e.g. for diffing between different .config files or versions of Kconfig files.
#
# Usage:
#
# $ make [ARCH=<arch>] scriptconfig SCRIPT=print_config_tree.py [SCRIPT_ARG=<.config>]
#
# If the variable WITH_HELP_DESC is modified to 'True', the help is added
# to the symbols.
#
# Here's a notation guide. The notation matches the one used by menuconfig
# (scripts/kconfig/mconf):
#
# [ ] prompt - Bool
# < > prompt - Tristate
# {M} prompt - Tristate selected to m. Can only be set to m or y.
# -*- prompt - Bool/tristate selected to y, pinning it
# -M- prompt - Tristate selected to m that also has m visibility,
# pinning it to m
# (foo) prompt - String/int/hex symbol with value "foo"
# --> prompt - The selected symbol in a choice in y mode. This
# syntax is unique to this example.
#
# When modules are disabled, the .type attribute of TRISTATE symbols and
# choices automatically changes to BOOL. This trick is used by the C
# implementation as well, and gives the expected behavior without having to do
# anything extra here. The original type is available in .orig_type if needed.
#
# Example output:
#
# $ make scriptconfig SCRIPT=Kconfiglib/examples/print_config_tree.py [SCRIPT_ARG=<.config file>]
#
# ======== Linux/x86 4.9.82 Kernel Configuration ========
#
# [*] 64-bit kernel (64BIT)
# General setup
# () Cross-compiler tool prefix (CROSS_COMPILE)
# [ ] Compile also drivers which will not load (COMPILE_TEST)
# () Local version - append to kernel release (LOCALVERSION)
# [*] Automatically append version information to the version string (LOCALVERSION_AUTO)
# -*- Kernel compression mode
# ...
#
# With the variable WITH_HELP_DESC modified to 'True':
#
# ======== Linux/x86 4.9.82 Kernel Configuration ========
#
# [*] 64-bit kernel - Say yes to build a 64-bit kernel - formerly known as x86_64 Say no to build a 32-bit kernel - formerly known as i386 (64BIT)
# General setup
# () Cross-compiler tool prefix - Same as running 'make CROSS_COMPILE=prefix-' but stored for default make runs in this kernel build directory. You don't need to set this unless you want the configured kernel build directory to select the cross-compiler automatically. (CROSS_COMPILE)
# [ ] Compile also drivers which will not load - Some drivers can be compiled on a different platform than they are intended to be run on. Despite they cannot be loaded there (or even when they load they cannot be used due to missing HW support), developers still, opposing to distributors, might want to build such drivers to compile-test them. If you are a developer and want to build everything available, say Y here. If you are a user/distributor, say N here to exclude useless drivers to be distributed. (COMPILE_TEST)
# ...
import sys
from kconfiglib import Kconfig, \
Symbol, MENU, COMMENT, \
BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN, \
expr_value
# Add help description to output
WITH_HELP_DESC = False
def indent_print(s, indent):
print(indent*" " + s)
def value_str(sc):
"""
Returns the value part ("[*]", "<M>", "(foo)" etc.) of a menu entry.
sc: Symbol or Choice.
"""
if sc.type in (STRING, INT, HEX):
return "({})".format(sc.str_value)
# BOOL or TRISTATE
# The choice mode is an upper bound on the visibility of choice symbols, so
# we can check the choice symbols' own visibility to see if the choice is
# in y mode
if isinstance(sc, Symbol) and sc.choice and sc.visibility == 2:
# For choices in y mode, print '-->' next to the selected symbol
return "-->" if sc.choice.selection is sc else " "
tri_val_str = (" ", "M", "*")[sc.tri_value]
if len(sc.assignable) == 1:
# Pinned to a single value
return "-{}-".format(tri_val_str)
if sc.type == BOOL:
return "[{}]".format(tri_val_str)
if sc.type == TRISTATE:
if sc.assignable == (1, 2):
# m and y available
return "{" + tri_val_str + "}" # Gets a bit confusing with .format()
return "<{}>".format(tri_val_str)
def node_str(node):
"""
Returns the complete menu entry text for a menu node, or "" for invisible
menu nodes. Invisible menu nodes are those that lack a prompt or that do
not have a satisfied prompt condition.
Example return value: "[*] Bool symbol (BOOL)"
The symbol name is printed in parentheses to the right of the prompt.
"""
if not node.prompt:
return ""
# Even for menu nodes for symbols and choices, it's wrong to check
# Symbol.visibility / Choice.visibility here. The reason is that a symbol
# (and a choice, in theory) can be defined in multiple locations, giving it
# multiple menu nodes, which do not necessarily all have the same prompt
# visibility. Symbol.visibility / Choice.visibility is calculated as the OR
# of the visibility of all the prompts.
prompt, prompt_cond = node.prompt
if not expr_value(prompt_cond):
return ""
if node.item == MENU:
return " " + prompt
if node.item == COMMENT:
return " *** {} ***".format(prompt)
# Symbol or Choice
sc = node.item
if sc.type == UNKNOWN:
# Skip symbols defined without a type (these are obscure and generate
# a warning)
return ""
# Add help text
if WITH_HELP_DESC:
prompt += ' - ' + str(node.help).replace('\n', ' ').replace('\r', '')
# {:3} sets the field width to three. Gives nice alignment for empty string
# values.
res = "{:3} {}".format(value_str(sc), prompt)
# Don't print the name for unnamed choices (the normal kind)
if sc.name is not None:
res += " ({})".format(sc.name)
return res
def print_menuconfig_nodes(node, indent):
"""
Prints a tree with all the menu entries rooted at 'node'. Child menu
entries are indented.
"""
while node:
string = node_str(node)
if string:
indent_print(string, indent)
if node.list:
print_menuconfig_nodes(node.list, indent + 8)
node = node.next
def print_menuconfig(kconf):
"""
Prints all menu entries for the configuration.
"""
# Print the expanded mainmenu text at the top. This is the same as
# kconf.top_node.prompt[0], but with variable references expanded.
print("\n======== {} ========\n".format(kconf.mainmenu_text))
print_menuconfig_nodes(kconf.top_node.list, 0)
print("")
if __name__ == "__main__":
# Load Kconfig configuration files
kconf = Kconfig(sys.argv[1])
# Set default .config file or load it from argv
if len(sys.argv) == 2:
config_filename = '.config'
else:
config_filename = sys.argv[2]
kconf.load_config(config_filename)
# Print the configuration tree
print_menuconfig(kconf)

View File

@ -0,0 +1,54 @@
# Loads a Kconfig and a .config and prints a symbol.
#
# Usage:
#
# $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/print_sym_info.py SCRIPT_ARG=<name>
#
# Example output for SCRIPT_ARG=MODULES:
#
# menuconfig MODULES
# bool
# prompt "Enable loadable module support"
# option modules
# help
# Kernel modules are small pieces of compiled code which can
# be inserted in the running kernel, rather than being
# permanently built into the kernel. You use the "modprobe"
# tool to add (and sometimes remove) them. If you say Y here,
# many parts of the kernel can be built as modules (by
# answering M instead of Y where indicated): this is most
# useful for infrequently used options which are not required
# for booting. For more information, see the man pages for
# modprobe, lsmod, modinfo, insmod and rmmod.
#
# If you say Y here, you will need to run "make
# modules_install" to put the modules under /lib/modules/
# where modprobe can find them (you may need to be root to do
# this).
#
# If unsure, say Y.
#
# value = n
# visibility = y
# currently assignable values: n, y
# defined at init/Kconfig:1674
import sys
from kconfiglib import Kconfig, TRI_TO_STR
if len(sys.argv) < 3:
sys.exit('Pass symbol name (without "CONFIG_" prefix) with SCRIPT_ARG=<name>')
kconf = Kconfig(sys.argv[1])
sym = kconf.syms[sys.argv[2]]
print(sym)
print("value = " + sym.str_value)
print("visibility = " + TRI_TO_STR[sym.visibility])
print("currently assignable values: " +
", ".join([TRI_TO_STR[v] for v in sym.assignable]))
for node in sym.nodes:
print("defined at {}:{}".format(node.filename, node.linenr))

View File

@ -0,0 +1,75 @@
# Prints the menu tree of the configuration. Dependencies between symbols can
# sometimes implicitly alter the menu structure (see kconfig-language.txt), and
# that's implemented too.
#
# Note: See the Kconfig.node_iter() function as well, which provides a simpler
# interface for walking the menu tree.
#
# Usage:
#
# $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py
#
# Example output:
#
# ...
# config HAVE_KERNEL_LZO
# config HAVE_KERNEL_LZ4
# choice
# config KERNEL_GZIP
# config KERNEL_BZIP2
# config KERNEL_LZMA
# config KERNEL_XZ
# config KERNEL_LZO
# config KERNEL_LZ4
# config DEFAULT_HOSTNAME
# config SWAP
# config SYSVIPC
# config SYSVIPC_SYSCTL
# config POSIX_MQUEUE
# config POSIX_MQUEUE_SYSCTL
# config CROSS_MEMORY_ATTACH
# config FHANDLE
# config USELIB
# config AUDIT
# config HAVE_ARCH_AUDITSYSCALL
# config AUDITSYSCALL
# config AUDIT_WATCH
# config AUDIT_TREE
# menu "IRQ subsystem"
# config MAY_HAVE_SPARSE_IRQ
# config GENERIC_IRQ_LEGACY
# config GENERIC_IRQ_PROBE
# ...
import sys
from kconfiglib import Kconfig, Symbol, Choice, MENU, COMMENT
def indent_print(s, indent):
print(indent*" " + s)
def print_items(node, indent):
while node:
if isinstance(node.item, Symbol):
indent_print("config " + node.item.name, indent)
elif isinstance(node.item, Choice):
indent_print("choice", indent)
elif node.item == MENU:
indent_print('menu "{}"'.format(node.prompt[0]), indent)
elif node.item == COMMENT:
indent_print('comment "{}"'.format(node.prompt[0]), indent)
if node.list:
print_items(node.list, indent + 2)
node = node.next
kconf = Kconfig(sys.argv[1])
print_items(kconf.top_node, 0)