From 7b4f72fa7a19b7c38312a6f6fd553ca45f04e9a6 Mon Sep 17 00:00:00 2001 From: Ulf Magnusson Date: Sat, 8 Dec 2018 00:41:01 +0100 Subject: Make {load,write}_config(filename=None) implement the standard behavior Make the previously obligatory 'filename' argument to load_config() and write_config() default to None, and have that implement the behavior you'd usually want: read/write either KCONFIG_CONFIG or ".config" if unset, and read the 'option defconfig_list' configuration file if KCONFIG_CONFIG/".config" doesn't exist. For load_config(), filename=None also allows the configuration file to be missing without raising an error. load_config() returns True if a local configuration file was loaded, which is useful to check in the menuconfig (if no local configuration file exists, we always want to prompt for saving the configuration when exiting). Also add a 'verbose' argument (default True) to load_config() and read_config() that makes them print which files were read/written in filename=None mode. Also generalize olddefconfig.py and oldconfig.py to not require there to already be a local configuration file. This was a bit silly for olddefconfig.py in particular. Remove the examples/defconfig.py script. It's a duplicate of olddefconfig.py. --- README.rst | 2 - alldefconfig.py | 2 +- allmodconfig.py | 2 +- allnoconfig.py | 2 +- allyesconfig.py | 2 +- examples/allnoconfig_walk.py | 2 +- examples/defconfig.py | 23 ----------- examples/defconfig_oldconfig.py | 10 ++--- genconfig.py | 2 +- kconfiglib.py | 87 +++++++++++++++++++++++++++++++++++++---- menuconfig.py | 35 ++++------------- oldconfig.py | 12 ++---- olddefconfig.py | 17 +++----- tests/reltest | 1 - 14 files changed, 107 insertions(+), 92 deletions(-) delete mode 100644 examples/defconfig.py diff --git a/README.rst b/README.rst index ef947be..11a85e9 100644 --- a/README.rst +++ b/README.rst @@ -492,8 +492,6 @@ Example scripts The `examples/ `_ directory contains some simple example scripts. Among these are the following ones. Make sure you run them with the latest version of Kconfiglib, as they might make use of newly added features. -- `defconfig.py `_ has the same effect as going into ``make menuconfig`` and immediately saving and exiting. - - `eval_expr.py `_ evaluates an expression in the context of a configuration. - `find_symbol.py `_ searches through expressions to find references to a symbol, also printing a "backtrace" with parents for each reference found. diff --git a/alldefconfig.py b/alldefconfig.py index 2c0b3fb..66759e0 100755 --- a/alldefconfig.py +++ b/alldefconfig.py @@ -18,7 +18,7 @@ import kconfiglib def main(): kconf = kconfiglib.standard_kconfig() kconfiglib.load_allconfig(kconf, "alldef.config") - kconf.write_config(kconfiglib.standard_config_filename()) + kconf.write_config() if __name__ == "__main__": main() diff --git a/allmodconfig.py b/allmodconfig.py index d1e64f6..25145f5 100755 --- a/allmodconfig.py +++ b/allmodconfig.py @@ -42,7 +42,7 @@ def main(): kconfiglib.load_allconfig(kconf, "allmod.config") - kconf.write_config(kconfiglib.standard_config_filename()) + kconf.write_config() if __name__ == "__main__": main() diff --git a/allnoconfig.py b/allnoconfig.py index 1ef6e27..f409cd4 100755 --- a/allnoconfig.py +++ b/allnoconfig.py @@ -36,7 +36,7 @@ def main(): kconfiglib.load_allconfig(kconf, "allno.config") - kconf.write_config(kconfiglib.standard_config_filename()) + kconf.write_config() if __name__ == "__main__": main() diff --git a/allyesconfig.py b/allyesconfig.py index 87ba9c6..09714ec 100755 --- a/allyesconfig.py +++ b/allyesconfig.py @@ -59,7 +59,7 @@ def main(): kconfiglib.load_allconfig(kconf, "allyes.config") - kconf.write_config(kconfiglib.standard_config_filename()) + kconf.write_config() if __name__ == "__main__": main() diff --git a/examples/allnoconfig_walk.py b/examples/allnoconfig_walk.py index 227effd..5abc80c 100644 --- a/examples/allnoconfig_walk.py +++ b/examples/allnoconfig_walk.py @@ -61,4 +61,4 @@ while True: if not changed: break -kconf.write_config(".config") +kconf.write_config() diff --git a/examples/defconfig.py b/examples/defconfig.py deleted file mode 100644 index c9cc7c6..0000000 --- a/examples/defconfig.py +++ /dev/null @@ -1,23 +0,0 @@ -# Works like entering "make menuconfig" and immediately saving and exiting -# -# Usage: -# -# $ make [ARCH=] scriptconfig SCRIPT=Kconfiglib/examples/allyesconfig.py - -import os -import sys - -import kconfiglib - - -kconf = kconfiglib.Kconfig(sys.argv[1]) - -if os.path.exists(".config"): - print("using existing .config") - kconf.load_config(".config") -elif kconf.defconfig_filename is not None: - print("using " + kconf.defconfig_filename) - kconf.load_config(kconf.defconfig_filename) - -kconf.write_config(".config") -print("configuration written to .config") diff --git a/examples/defconfig_oldconfig.py b/examples/defconfig_oldconfig.py index 0b24e1d..98f97a0 100644 --- a/examples/defconfig_oldconfig.py +++ b/examples/defconfig_oldconfig.py @@ -21,19 +21,19 @@ kconf = kconfiglib.Kconfig(sys.argv[1]) # Mirrors defconfig kconf.load_config("arch/x86/configs/x86_64_defconfig") -kconf.write_config(".config") +kconf.write_config() # Mirrors the first oldconfig -kconf.load_config(".config") +kconf.load_config() kconf.syms["ETHERNET"].set_value(0) -kconf.write_config(".config") +kconf.write_config() # Mirrors the second oldconfig -kconf.load_config(".config") +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 -kconf.write_config(".config") +kconf.write_config() diff --git a/genconfig.py b/genconfig.py index 5fd3cf2..fcb93b9 100755 --- a/genconfig.py +++ b/genconfig.py @@ -75,7 +75,7 @@ def main(): kconf = kconfiglib.Kconfig(args.kconfig_filename) - kconf.load_config(kconfiglib.standard_config_filename()) + kconf.load_config(verbose=False) kconf.write_autoconf(args.header_path) diff --git a/kconfiglib.py b/kconfiglib.py index b854186..a376d62 100644 --- a/kconfiglib.py +++ b/kconfiglib.py @@ -1021,7 +1021,7 @@ class Kconfig(object): return None - def load_config(self, filename, replace=True): + def load_config(self, filename=None, replace=True, verbose=True): """ Loads symbol values from a file in the .config format. Equivalent to calling Symbol.set_value() to set each of the values. @@ -1039,13 +1039,61 @@ class Kconfig(object): caught as OSError on Python 3. filename: - The file to load. Respects $srctree if set (see the class - documentation). + Path to load configuration from (a string). Respects $srctree if set + (see the class documentation). + + If 'filename' is None, the configuration file to load (if any) is + calculated automatically, giving the behavior you'd usually want: + + 1. If the KCONFIG_CONFIG environment variable is set, it gives the + path to the configuration file to load. Otherwise, ".config" is + used. See standard_config_filename(). + + 2. If the path from (1.) doesn't exist, the configuration file + given by kconf.defconfig_filename is loaded instead, which is + derived from the 'option defconfig_list' symbol. + + 3. If (1.) and (2.) fail to find a configuration file to load, no + configuration file is loaded, and symbols retain their current + values (e.g., their default values). This is not an error. + + See the return value as well. replace (default: True): - True if all existing user values should be cleared before loading the + If True, all existing user values will be cleared before loading the .config. Pass False to merge configurations. + + verbose (default: True): + If True and filename is None (automatically infer configuration + file), a message will be printed to stdout telling which file got + loaded (or that no file got loaded). This is meant to reduce + boilerplate in tools. + + Returns True if an existing configuration was loaded (that didn't come + from the 'option defconfig_list' symbol), and False otherwise. This is + mostly useful in conjunction with filename=None, as True will always be + returned otherwise. """ + loaded_existing = True + if filename is None: + filename = standard_config_filename() + if os.path.exists(filename): + if verbose: + print("Using existing configuration '{}' as base" + .format(filename)) + else: + filename = self.defconfig_filename + if filename is None: + if verbose: + print("Using default symbol values as base") + return False + + if verbose: + print("Using default configuration found in '{}' as " + "base".format(filename)) + + loaded_existing = False + # Disable the warning about assigning to symbols without prompts. This # is normal and expected within a .config file. self._warn_for_no_prompt = False @@ -1058,6 +1106,8 @@ class Kconfig(object): finally: self._warn_for_no_prompt = True + return loaded_existing + def _load_config(self, filename, replace): with self._open_config(filename) as f: if replace: @@ -1243,9 +1293,9 @@ class Kconfig(object): f.write("#define {}{} {}\n" .format(self.config_prefix, sym.name, val)) - def write_config(self, filename, + def write_config(self, filename=None, header="# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)\n", - save_old=True): + save_old=True, verbose=True): r""" Writes out symbol values in the .config format. The format matches the C implementation, including ordering. @@ -1258,8 +1308,12 @@ class Kconfig(object): See the 'Intro to symbol values' section in the module docstring to understand which symbols get written out. - filename: - Self-explanatory. + filename (default: None): + Filename to save configuration to (a string). + + If None, the filename in the the environment variable KCONFIG_CONFIG + is used if set, and ".config" otherwise. See + standard_config_filename(). header (default: "# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)\n"): Text that will be inserted verbatim at the beginning of the file. You @@ -1274,7 +1328,18 @@ class Kconfig(object): Errors are silently ignored if ..old cannot be written (e.g. due to being a directory). + + verbose (default: True): + If True and filename is None (automatically infer configuration + file), a message will be printed to stdout telling which file got + written. This is meant to reduce boilerplate in tools. """ + if filename is None: + filename = standard_config_filename() + be_verbose = verbose + else: + be_verbose = False + if save_old: _save_old(filename) @@ -1293,6 +1358,9 @@ class Kconfig(object): f.write("\n#\n# {}\n#\n".format(node.prompt[0])) + if be_verbose: + print("Configuration written to '{}'".format(filename)) + def write_min_config(self, filename, header="# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)\n"): """ @@ -5629,6 +5697,9 @@ def standard_config_filename(): """ Helper for tools. Returns the value of KCONFIG_CONFIG (which specifies the .config file to load/save) if it is set, and ".config" otherwise. + + Note: Calling load_config() with filename=None might give the behavior you + want, without having to use this function. """ return os.environ.get("KCONFIG_CONFIG", ".config") diff --git a/menuconfig.py b/menuconfig.py index 6c1d1ce..eebe3a0 100755 --- a/menuconfig.py +++ b/menuconfig.py @@ -608,39 +608,20 @@ def menuconfig(kconf): Kconfig instance to be configured """ global _kconf - global _config_filename global _show_all global _conf_changed _kconf = kconf - _config_filename = standard_config_filename() - - if os.path.exists(_config_filename): - _conf_changed = False - print("Using existing configuration '{}' as base" - .format(_config_filename)) - _kconf.load_config(_config_filename) - - else: - # Always prompt for save if the .config doesn't exist - _conf_changed = True - - if kconf.defconfig_filename is not None: - print("Using default configuration found in '{}' as base" - .format(kconf.defconfig_filename)) - _kconf.load_config(kconf.defconfig_filename) - - else: - print("Using default symbol values as base") - + # Always prompt for save if the configuration file doesn't exist + _conf_changed = not kconf.load_config() # Any visible items in the top menu? _show_all = False - if not _shown_nodes(_kconf.top_node): + if not _shown_nodes(kconf.top_node): # Nothing visible. Start in show-all mode and try again. _show_all = True - if not _shown_nodes(_kconf.top_node): + if not _shown_nodes(kconf.top_node): # Give up. The implementation relies on always having a selected # node. print("Empty configuration -- nothing to configure.\n" @@ -649,7 +630,7 @@ def menuconfig(kconf): # Disable warnings. They get mangled in curses mode, and we deal with # errors ourselves. - _kconf.disable_warnings() + kconf.disable_warnings() # Make curses use the locale settings specified in the environment locale.setlocale(locale.LC_ALL, "") @@ -821,7 +802,7 @@ def _menuconfig(stdscr): _conf_changed = False elif c in ("s", "S"): - if _save_dialog(_kconf.write_config, _config_filename, + if _save_dialog(_kconf.write_config, standard_config_filename(), "configuration"): _conf_changed = False @@ -869,11 +850,11 @@ def _quit_dialog(): return None if c == "y": - if _try_save(_kconf.write_config, _config_filename, + if _try_save(_kconf.write_config, standard_config_filename(), "configuration"): return "Configuration saved to '{}'" \ - .format(_config_filename) + .format(standard_config_filename()) elif c == "n": return "Configuration was not saved" diff --git a/oldconfig.py b/oldconfig.py index 956bc07..66ee6be 100755 --- a/oldconfig.py +++ b/oldconfig.py @@ -27,7 +27,7 @@ import os import sys from kconfiglib import Kconfig, Symbol, Choice, BOOL, TRISTATE, HEX, \ - standard_kconfig, standard_config_filename + standard_kconfig # Python 2/3 compatibility hack if sys.version_info[0] < 3: @@ -42,12 +42,8 @@ def _main(): # visible symbols. global conf_changed - config_filename = standard_config_filename() - if not os.path.exists(config_filename): - sys.exit("{}: '{}' not found".format(sys.argv[0], config_filename)) - kconf = standard_kconfig() - kconf.load_config(config_filename) + kconf.load_config() while True: conf_changed = False @@ -58,9 +54,7 @@ def _main(): if not conf_changed: break - kconf.write_config(config_filename) - - print("Updated configuration written to '{}'".format(config_filename)) + kconf.write_config() def oldconfig(node): diff --git a/olddefconfig.py b/olddefconfig.py index 5245735..2845740 100755 --- a/olddefconfig.py +++ b/olddefconfig.py @@ -3,10 +3,10 @@ # Copyright (c) 2018, Ulf Magnusson # SPDX-License-Identifier: ISC -# Works like 'make olddefconfig', updating an old .config file by filing in -# default values for all new symbols. This is the same as picking the default -# selection for all symbols in oldconfig, or entering the menuconfig interface -# and immediately saving. +# Works like 'make olddefconfig', updating an old .config file or creating a +# new one by filing in default values for all new symbols. This is the same as +# picking the default selection for all symbols in oldconfig, or entering the +# menuconfig interface and immediately saving. # # The default output filename is '.config'. A different filename can be passed # in the KCONFIG_CONFIG environment variable. @@ -18,14 +18,9 @@ import kconfiglib def main(): - config_filename = kconfiglib.standard_config_filename() - if not os.path.exists(config_filename): - sys.exit("{}: '{}' not found".format(sys.argv[0], config_filename)) - kconf = kconfiglib.standard_kconfig() - kconf.load_config(config_filename) - kconf.write_config(config_filename) - print("Updated configuration written to '{}'".format(config_filename)) + kconf.load_config() + kconf.write_config() if __name__ == "__main__": diff --git a/tests/reltest b/tests/reltest index 0916c7a..3e09e5f 100755 --- a/tests/reltest +++ b/tests/reltest @@ -32,7 +32,6 @@ for py in python2 python3; do # at least test_script Kconfiglib/examples/defconfig_oldconfig.py - test_script Kconfiglib/examples/defconfig.py test_script Kconfiglib/examples/eval_expr.py MODULES test_script Kconfiglib/examples/find_symbol.py X86 test_script Kconfiglib/examples/help_grep.py general -- cgit v1.2.3