diff options
| -rw-r--r-- | examples/Kmenuconfig | 102 | ||||
| -rw-r--r-- | examples/menuconfig.py | 186 | ||||
| -rw-r--r-- | kconfiglib.py | 12 |
3 files changed, 295 insertions, 5 deletions
diff --git a/examples/Kmenuconfig b/examples/Kmenuconfig new file mode 100644 index 0000000..f1cb67b --- /dev/null +++ b/examples/Kmenuconfig @@ -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 diff --git a/examples/menuconfig.py b/examples/menuconfig.py new file mode 100644 index 0000000..a77ae2d --- /dev/null +++ b/examples/menuconfig.py @@ -0,0 +1,186 @@ +# TODO: explain, screenshot + +from kconfiglib import Kconfig, \ + Symbol, Choice, MENU, COMMENT, \ + BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN, \ + expr_value, \ + TRI_TO_STR, STR_TO_TRI +import readline +import sys + +# 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): + if sc.type in (STRING, INT, HEX): + return "({})".format(sc.str_value) + + # BOOL or TRISTATE + + if isinstance(sc, Symbol) and sc.choice and sc.choice.tri_value == 2: + # For choices in y mode, print '-->' next to the selected symbol + if sc.choice.selection is sc: + return "-->" + return " " + + val_str = {0: " ", 1: "M", 2: "*"}[sc.tri_value] + + if len(sc.assignable) == 1: + return "-{}-".format(val_str) + + if sc.type == BOOL: + return "[{}]".format(val_str) + + if sc.type == TRISTATE: + if sc.assignable[0] == 1: + return "{" + val_str + "}" # Gets a bit confusing with .format() + return "<{}>".format(val_str) + +def node_str(node): + if not node.prompt: + return "" + + 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 + return "" + + # {:3} sets the field width to three. Gives nice alignment for empty string + # values. + return "{:3} {} ({})".format(value_str(sc), prompt, sc.name) + +def print_menuconfig_nodes(node, indent): + while node is not None: + string = node_str(node) + if string: + indent_print(string, indent) + + if node.list is not None: + print_menuconfig_nodes(node.list, indent + 8) + + node = node.next + +def print_menuconfig(kconf): + # 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): + 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_str = input(prompt).strip() + if sc.type in (BOOL, TRISTATE): + if val_str not in STR_TO_TRI: + print("'{}' is not a valid tristate value".format(val_str)) + return False + + # I was thinking of having set_value() accept "n", "m", "y" as well as + # a convenience for BOOL / TRISTATE symbols. Consistently using 0, 1, 2 + # makes the format clearer though. That's the best format for + # everything except readability (where it isn't horrible either). + val = STR_TO_TRI[val_str] + else: + val = val_str + + # 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.disable_warnings() / kconf.enable_warnings() 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: + kconf.load_config(config_filename) + except IOError as e: + # Print the (spammy) error from Kconfiglib itself + print(e.message + "\n") + else: + print("Configuration loaded from " + config_filename) + + print_menuconfig(kconf) + continue + + if cmd == "write_config": + config_filename = input("To this file: ") + + try: + kconf.write_config(config_filename) + except IOError as e: + print(e.message) + else: + print("Configuration written to " + config_filename) + + 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)) diff --git a/kconfiglib.py b/kconfiglib.py index d8f070b..f4672b7 100644 --- a/kconfiglib.py +++ b/kconfiglib.py @@ -906,7 +906,7 @@ class Kconfig(object): e = e2 raise IOError( - 'Could not open "{}" ({}: {}). Perhaps the $srctree ' + "Could not open '{}' ({}: {}). Perhaps the $srctree " "environment variable (which was {}) is set incorrectly. Note " "that the current value of $srctree is saved when the Kconfig " "instance is created (for consistency and to cleanly " @@ -2539,11 +2539,13 @@ class Symbol(object): and _is_base_n(value, 16) and int(value, 16) >= 0)): - warning = "the value '{}' is invalid for {}, which has type {}" \ - .format(value, self.name, TYPE_TO_STR[self.orig_type]) + # Display tristate values as n, m, y in the warning + warning = "the value {} is invalid for {}, which has type {}" \ + .format(TRI_TO_STR[value] if value in (0, 1, 2) else + "'{}'".format(value), + self.name, TYPE_TO_STR[self.orig_type]) - if self.orig_type in (BOOL, TRISTATE) and \ - value in ("n", "m", "y"): + if self.orig_type in (BOOL, TRISTATE) and value in ("n", "m", "y"): warning += ' (pass 0, 1, 2 for n, m, y, respectively)' self.kconfig._warn(warning) |
