mirror of
				https://github.com/riscv-software-src/opensbi
				synced 2025-11-04 14:00:31 +00:00 
			
		
		
		
	We adopt Kconfiglib v14.1.0 sources under scripts directory so that top-level OpenSBI makefile can directly use Kconfiglib scripts without expecting users to install a particular version of Kconfiglib on their build system. Signed-off-by: Anup Patel <apatel@ventanamicro.com> Tested-by: Andrew Jones <ajones@ventanamicro.com> Acked-by: Atish Patra <atishp@rivosinc.com> Tested-by: Atish Patra <atishp@rivosinc.com>
		
			
				
	
	
		
			247 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			247 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env python3
 | 
						|
 | 
						|
# Copyright (c) 2018-2019, Ulf Magnusson
 | 
						|
# SPDX-License-Identifier: ISC
 | 
						|
 | 
						|
"""
 | 
						|
Implements oldconfig functionality.
 | 
						|
 | 
						|
  1. Loads existing .config
 | 
						|
  2. Prompts for the value of all modifiable symbols/choices that
 | 
						|
     aren't already set in the .config
 | 
						|
  3. Writes an updated .config
 | 
						|
 | 
						|
The default input/output filename is '.config'. A different filename can be
 | 
						|
passed in the KCONFIG_CONFIG environment variable.
 | 
						|
 | 
						|
When overwriting a configuration file, the old version is saved to
 | 
						|
<filename>.old (e.g. .config.old).
 | 
						|
 | 
						|
Entering '?' displays the help text of the symbol/choice, if any.
 | 
						|
 | 
						|
Unlike 'make oldconfig', this script doesn't print menu titles and comments,
 | 
						|
but gives Kconfig definition locations. Printing menus and comments would be
 | 
						|
pretty easy to add: Look at the parents of each item, and print all menu
 | 
						|
prompts and comments unless they have already been printed (assuming you want
 | 
						|
to skip "irrelevant" menus).
 | 
						|
"""
 | 
						|
from __future__ import print_function
 | 
						|
 | 
						|
import sys
 | 
						|
 | 
						|
from kconfiglib import Symbol, Choice, BOOL, TRISTATE, HEX, standard_kconfig
 | 
						|
 | 
						|
 | 
						|
# Python 2/3 compatibility hack
 | 
						|
if sys.version_info[0] < 3:
 | 
						|
    input = raw_input
 | 
						|
 | 
						|
 | 
						|
def _main():
 | 
						|
    # Earlier symbols in Kconfig files might depend on later symbols and become
 | 
						|
    # visible if their values change. This flag is set to True if the value of
 | 
						|
    # any symbol changes, in which case we rerun the oldconfig to check for new
 | 
						|
    # visible symbols.
 | 
						|
    global conf_changed
 | 
						|
 | 
						|
    kconf = standard_kconfig(__doc__)
 | 
						|
    print(kconf.load_config())
 | 
						|
 | 
						|
    while True:
 | 
						|
        conf_changed = False
 | 
						|
 | 
						|
        for node in kconf.node_iter():
 | 
						|
            oldconfig(node)
 | 
						|
 | 
						|
        if not conf_changed:
 | 
						|
            break
 | 
						|
 | 
						|
    print(kconf.write_config())
 | 
						|
 | 
						|
 | 
						|
def oldconfig(node):
 | 
						|
    """
 | 
						|
    Prompts the user for a value if node.item is a visible symbol/choice with
 | 
						|
    no user value.
 | 
						|
    """
 | 
						|
    # See main()
 | 
						|
    global conf_changed
 | 
						|
 | 
						|
    # Only symbols and choices can be configured
 | 
						|
    if not isinstance(node.item, (Symbol, Choice)):
 | 
						|
        return
 | 
						|
 | 
						|
    # Skip symbols and choices that aren't visible
 | 
						|
    if not node.item.visibility:
 | 
						|
        return
 | 
						|
 | 
						|
    # Skip symbols and choices that don't have a prompt (at this location)
 | 
						|
    if not node.prompt:
 | 
						|
        return
 | 
						|
 | 
						|
    if isinstance(node.item, Symbol):
 | 
						|
        sym = node.item
 | 
						|
 | 
						|
        # Skip symbols that already have a user value
 | 
						|
        if sym.user_value is not None:
 | 
						|
            return
 | 
						|
 | 
						|
        # Skip symbols that can only have a single value, due to selects
 | 
						|
        if len(sym.assignable) == 1:
 | 
						|
            return
 | 
						|
 | 
						|
        # Skip symbols in choices in y mode. We ask once for the entire choice
 | 
						|
        # instead.
 | 
						|
        if sym.choice and sym.choice.tri_value == 2:
 | 
						|
            return
 | 
						|
 | 
						|
        # Loop until the user enters a valid value or enters a blank string
 | 
						|
        # (for the default value)
 | 
						|
        while True:
 | 
						|
            val = input("{} ({}) [{}] ".format(
 | 
						|
                node.prompt[0], _name_and_loc_str(sym),
 | 
						|
                _default_value_str(sym)))
 | 
						|
 | 
						|
            if val == "?":
 | 
						|
                _print_help(node)
 | 
						|
                continue
 | 
						|
 | 
						|
            # Substitute a blank string with the default value the symbol
 | 
						|
            # would get
 | 
						|
            if not val:
 | 
						|
                val = sym.str_value
 | 
						|
 | 
						|
            # 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 sym.type == HEX and not val.startswith(("0x", "0X")):
 | 
						|
                val = "0x" + val
 | 
						|
 | 
						|
            old_str_val = sym.str_value
 | 
						|
 | 
						|
            # Kconfiglib itself will print a warning here if the value
 | 
						|
            # is invalid, so we don't need to bother
 | 
						|
            if sym.set_value(val):
 | 
						|
                # Valid value input. We're done with this node.
 | 
						|
 | 
						|
                if sym.str_value != old_str_val:
 | 
						|
                    conf_changed = True
 | 
						|
 | 
						|
                return
 | 
						|
 | 
						|
    else:
 | 
						|
        choice = node.item
 | 
						|
 | 
						|
        # Skip choices that already have a visible user selection...
 | 
						|
        if choice.user_selection and choice.user_selection.visibility == 2:
 | 
						|
            # ...unless there are new visible symbols in the choice. (We know
 | 
						|
            # they have y (2) visibility in that case, because m-visible
 | 
						|
            # symbols get demoted to n-visibility in y-mode choices, and the
 | 
						|
            # user-selected symbol had visibility y.)
 | 
						|
            for sym in choice.syms:
 | 
						|
                if sym is not choice.user_selection and sym.visibility and \
 | 
						|
                   sym.user_value is None:
 | 
						|
                    # New visible symbols in the choice
 | 
						|
                    break
 | 
						|
            else:
 | 
						|
                # No new visible symbols in the choice
 | 
						|
                return
 | 
						|
 | 
						|
        # Get a list of available selections. The mode of the choice limits
 | 
						|
        # the visibility of the choice value symbols, so this will indirectly
 | 
						|
        # skip choices in n and m mode.
 | 
						|
        options = [sym for sym in choice.syms if sym.visibility == 2]
 | 
						|
 | 
						|
        if not options:
 | 
						|
            # No y-visible choice value symbols
 | 
						|
            return
 | 
						|
 | 
						|
        # Loop until the user enters a valid selection or a blank string (for
 | 
						|
        # the default selection)
 | 
						|
        while True:
 | 
						|
            print("{} ({})".format(node.prompt[0], _name_and_loc_str(choice)))
 | 
						|
 | 
						|
            for i, sym in enumerate(options, 1):
 | 
						|
                print("{} {}. {} ({})".format(
 | 
						|
                    ">" if sym is choice.selection else " ",
 | 
						|
                    i,
 | 
						|
                    # Assume people don't define choice symbols with multiple
 | 
						|
                    # prompts. That generates a warning anyway.
 | 
						|
                    sym.nodes[0].prompt[0],
 | 
						|
                    sym.name))
 | 
						|
 | 
						|
            sel_index = input("choice[1-{}]: ".format(len(options)))
 | 
						|
 | 
						|
            if sel_index == "?":
 | 
						|
                _print_help(node)
 | 
						|
                continue
 | 
						|
 | 
						|
            # Pick the default selection if the string is blank
 | 
						|
            if not sel_index:
 | 
						|
                choice.selection.set_value(2)
 | 
						|
                break
 | 
						|
 | 
						|
            try:
 | 
						|
                sel_index = int(sel_index)
 | 
						|
            except ValueError:
 | 
						|
                print("Bad index", file=sys.stderr)
 | 
						|
                continue
 | 
						|
 | 
						|
            if not 1 <= sel_index <= len(options):
 | 
						|
                print("Bad index", file=sys.stderr)
 | 
						|
                continue
 | 
						|
 | 
						|
            # Valid selection
 | 
						|
 | 
						|
            if options[sel_index - 1].tri_value != 2:
 | 
						|
                conf_changed = True
 | 
						|
 | 
						|
            options[sel_index - 1].set_value(2)
 | 
						|
            break
 | 
						|
 | 
						|
        # Give all of the non-selected visible choice symbols the user value n.
 | 
						|
        # This makes it so that the choice is no longer considered new once we
 | 
						|
        # do additional passes, if the reason that it was considered new was
 | 
						|
        # that it had new visible choice symbols.
 | 
						|
        #
 | 
						|
        # Only giving visible choice symbols the user value n means we will
 | 
						|
        # prompt for the choice again if later user selections make more new
 | 
						|
        # choice symbols visible, which is correct.
 | 
						|
        for sym in choice.syms:
 | 
						|
            if sym is not choice.user_selection and sym.visibility:
 | 
						|
                sym.set_value(0)
 | 
						|
 | 
						|
 | 
						|
def _name_and_loc_str(sc):
 | 
						|
    # Helper for printing the name of the symbol/choice 'sc' along with the
 | 
						|
    # location(s) in the Kconfig files where it is defined. Unnamed choices
 | 
						|
    # return "choice" instead of the name.
 | 
						|
 | 
						|
    return "{}, defined at {}".format(
 | 
						|
        sc.name or "choice",
 | 
						|
        ", ".join("{}:{}".format(node.filename, node.linenr)
 | 
						|
                  for node in sc.nodes))
 | 
						|
 | 
						|
 | 
						|
def _print_help(node):
 | 
						|
    print("\n" + (node.help or "No help text\n"))
 | 
						|
 | 
						|
 | 
						|
def _default_value_str(sym):
 | 
						|
    # Returns the "m/M/y" string in e.g.
 | 
						|
    #
 | 
						|
    #   TRISTATE_SYM prompt (TRISTATE_SYM, defined at Kconfig:9) [n/M/y]:
 | 
						|
    #
 | 
						|
    # For string/int/hex, returns the default value as-is.
 | 
						|
 | 
						|
    if sym.type in (BOOL, TRISTATE):
 | 
						|
        return "/".join(("NMY" if sym.tri_value == tri else "nmy")[tri]
 | 
						|
                        for tri in sym.assignable)
 | 
						|
 | 
						|
    # string/int/hex
 | 
						|
    return sym.str_value
 | 
						|
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    _main()
 |