Skip to content

Commit

Permalink
Add oldaskconfig the default conf program
Browse files Browse the repository at this point in the history
  • Loading branch information
afbjorklund committed Sep 22, 2023
1 parent 061e71f commit 246deb1
Show file tree
Hide file tree
Showing 3 changed files with 254 additions and 0 deletions.
3 changes: 3 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ available in the C tools.

- `guiconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/guiconfig.py>`_

- `oldaskconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/oldaskconfig.py>`_

- `oldconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/oldconfig.py>`_

- `olddefconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/olddefconfig.py>`_
Expand Down Expand Up @@ -214,6 +216,7 @@ Getting started

5. To update an old ``.config`` file after the Kconfig files have changed (e.g.
to add new options), run ``oldconfig`` (prompts for values for new options)
or ``oldaskconfig`` (shows all options with any old value as the default)
or ``olddefconfig`` (gives new options their default value). Entering the
``menuconfig`` or ``guiconfig`` interface and saving the configuration will
also update it (the configuration interfaces always prompt for saving
Expand Down
249 changes: 249 additions & 0 deletions oldaskconfig.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
#!/usr/bin/env python3

# Copyright (c) 2018-2019, Ulf Magnusson
# SPDX-License-Identifier: ISC

"""
Implements oldaskconfig functionality.
1. Loads existing .config
2. Prompts for the value of all modifiable symbols/choices
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():
oldaskconfig(node)

if not conf_changed:
break

print(kconf.write_config())


def oldaskconfig(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

new = sym.user_value is None

# 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), _new_value_str(new)))

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


def _new_value_str(new):
if new:
return " (NEW)"
return ""


if __name__ == "__main__":
_main()
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"menuconfig",
"guiconfig",
"genconfig",
"oldaskconfig",
"oldconfig",
"olddefconfig",
"savedefconfig",
Expand All @@ -48,6 +49,7 @@
"menuconfig = menuconfig:_main",
"guiconfig = guiconfig:_main",
"genconfig = genconfig:main",
"oldaskconfig = oldaskconfig:_main",
"oldconfig = oldconfig:_main",
"olddefconfig = olddefconfig:main",
"savedefconfig = savedefconfig:main",
Expand Down

0 comments on commit 246deb1

Please sign in to comment.