Skip to content

Commit b5c2b0a

Browse files
committed
Add oldaskconfig the default conf program
1 parent 061e71f commit b5c2b0a

File tree

3 files changed

+254
-0
lines changed

3 files changed

+254
-0
lines changed

README.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ available in the C tools.
9595

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

98+
- `oldaskconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/oldaskconfig.py>`_
99+
98100
- `oldconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/oldconfig.py>`_
99101

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

215217
5. To update an old ``.config`` file after the Kconfig files have changed (e.g.
216218
to add new options), run ``oldconfig`` (prompts for values for new options)
219+
or ``oldaskconfig`` (shows all options with any old value as the default)
217220
or ``olddefconfig`` (gives new options their default value). Entering the
218221
``menuconfig`` or ``guiconfig`` interface and saving the configuration will
219222
also update it (the configuration interfaces always prompt for saving

oldaskconfig.py

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
#!/usr/bin/env python3
2+
3+
# Copyright (c) 2018-2019, Ulf Magnusson
4+
# SPDX-License-Identifier: ISC
5+
6+
"""
7+
Implements oldaskconfig functionality.
8+
9+
1. Loads existing .config
10+
2. Prompts for the value of all modifiable symbols/choices
11+
3. Writes an updated .config
12+
13+
The default input/output filename is '.config'. A different filename can be
14+
passed in the KCONFIG_CONFIG environment variable.
15+
16+
When overwriting a configuration file, the old version is saved to
17+
<filename>.old (e.g. .config.old).
18+
19+
Entering '?' displays the help text of the symbol/choice, if any.
20+
21+
Unlike 'make oldconfig', this script doesn't print menu titles and comments,
22+
but gives Kconfig definition locations. Printing menus and comments would be
23+
pretty easy to add: Look at the parents of each item, and print all menu
24+
prompts and comments unless they have already been printed (assuming you want
25+
to skip "irrelevant" menus).
26+
"""
27+
from __future__ import print_function
28+
29+
import sys
30+
31+
from kconfiglib import Symbol, Choice, BOOL, TRISTATE, HEX, standard_kconfig
32+
33+
34+
# Python 2/3 compatibility hack
35+
if sys.version_info[0] < 3:
36+
input = raw_input
37+
38+
39+
def _main():
40+
# Earlier symbols in Kconfig files might depend on later symbols and become
41+
# visible if their values change. This flag is set to True if the value of
42+
# any symbol changes, in which case we rerun the oldconfig to check for new
43+
# visible symbols.
44+
global conf_changed
45+
46+
kconf = standard_kconfig(__doc__)
47+
print(kconf.load_config())
48+
49+
while True:
50+
conf_changed = False
51+
52+
for node in kconf.node_iter():
53+
oldconfig(node)
54+
55+
if not conf_changed:
56+
break
57+
58+
print(kconf.write_config())
59+
60+
61+
def oldconfig(node):
62+
"""
63+
Prompts the user for a value if node.item is a visible symbol/choice with
64+
no user value.
65+
"""
66+
# See main()
67+
global conf_changed
68+
69+
# Only symbols and choices can be configured
70+
if not isinstance(node.item, (Symbol, Choice)):
71+
return
72+
73+
# Skip symbols and choices that aren't visible
74+
if not node.item.visibility:
75+
return
76+
77+
# Skip symbols and choices that don't have a prompt (at this location)
78+
if not node.prompt:
79+
return
80+
81+
if isinstance(node.item, Symbol):
82+
sym = node.item
83+
84+
new = sym.user_value is None
85+
86+
# Skip symbols that can only have a single value, due to selects
87+
if len(sym.assignable) == 1:
88+
return
89+
90+
# Skip symbols in choices in y mode. We ask once for the entire choice
91+
# instead.
92+
if sym.choice and sym.choice.tri_value == 2:
93+
return
94+
95+
# Loop until the user enters a valid value or enters a blank string
96+
# (for the default value)
97+
while True:
98+
val = input("{} ({}) [{}]{} ".format(
99+
node.prompt[0], _name_and_loc_str(sym),
100+
_default_value_str(sym), _new_value_str(new)))
101+
102+
if val == "?":
103+
_print_help(node)
104+
continue
105+
106+
# Substitute a blank string with the default value the symbol
107+
# would get
108+
if not val:
109+
val = sym.str_value
110+
111+
# Automatically add a "0x" prefix for hex symbols, like the
112+
# menuconfig interface does. This isn't done when loading .config
113+
# files, hence why set_value() doesn't do it automatically.
114+
if sym.type == HEX and not val.startswith(("0x", "0X")):
115+
val = "0x" + val
116+
117+
old_str_val = sym.str_value
118+
119+
# Kconfiglib itself will print a warning here if the value
120+
# is invalid, so we don't need to bother
121+
if sym.set_value(val):
122+
# Valid value input. We're done with this node.
123+
124+
if sym.str_value != old_str_val:
125+
conf_changed = True
126+
127+
return
128+
129+
else:
130+
choice = node.item
131+
132+
# Skip choices that already have a visible user selection...
133+
if choice.user_selection and choice.user_selection.visibility == 2:
134+
# ...unless there are new visible symbols in the choice. (We know
135+
# they have y (2) visibility in that case, because m-visible
136+
# symbols get demoted to n-visibility in y-mode choices, and the
137+
# user-selected symbol had visibility y.)
138+
for sym in choice.syms:
139+
if sym is not choice.user_selection and sym.visibility and \
140+
sym.user_value is None:
141+
# New visible symbols in the choice
142+
break
143+
else:
144+
# No new visible symbols in the choice
145+
return
146+
147+
# Get a list of available selections. The mode of the choice limits
148+
# the visibility of the choice value symbols, so this will indirectly
149+
# skip choices in n and m mode.
150+
options = [sym for sym in choice.syms if sym.visibility == 2]
151+
152+
if not options:
153+
# No y-visible choice value symbols
154+
return
155+
156+
# Loop until the user enters a valid selection or a blank string (for
157+
# the default selection)
158+
while True:
159+
print("{} ({})".format(node.prompt[0], _name_and_loc_str(choice)))
160+
161+
for i, sym in enumerate(options, 1):
162+
print("{} {}. {} ({})".format(
163+
">" if sym is choice.selection else " ",
164+
i,
165+
# Assume people don't define choice symbols with multiple
166+
# prompts. That generates a warning anyway.
167+
sym.nodes[0].prompt[0],
168+
sym.name))
169+
170+
sel_index = input("choice[1-{}]: ".format(len(options)))
171+
172+
if sel_index == "?":
173+
_print_help(node)
174+
continue
175+
176+
# Pick the default selection if the string is blank
177+
if not sel_index:
178+
choice.selection.set_value(2)
179+
break
180+
181+
try:
182+
sel_index = int(sel_index)
183+
except ValueError:
184+
print("Bad index", file=sys.stderr)
185+
continue
186+
187+
if not 1 <= sel_index <= len(options):
188+
print("Bad index", file=sys.stderr)
189+
continue
190+
191+
# Valid selection
192+
193+
if options[sel_index - 1].tri_value != 2:
194+
conf_changed = True
195+
196+
options[sel_index - 1].set_value(2)
197+
break
198+
199+
# Give all of the non-selected visible choice symbols the user value n.
200+
# This makes it so that the choice is no longer considered new once we
201+
# do additional passes, if the reason that it was considered new was
202+
# that it had new visible choice symbols.
203+
#
204+
# Only giving visible choice symbols the user value n means we will
205+
# prompt for the choice again if later user selections make more new
206+
# choice symbols visible, which is correct.
207+
for sym in choice.syms:
208+
if sym is not choice.user_selection and sym.visibility:
209+
sym.set_value(0)
210+
211+
212+
def _name_and_loc_str(sc):
213+
# Helper for printing the name of the symbol/choice 'sc' along with the
214+
# location(s) in the Kconfig files where it is defined. Unnamed choices
215+
# return "choice" instead of the name.
216+
217+
return "{}, defined at {}".format(
218+
sc.name or "choice",
219+
", ".join("{}:{}".format(node.filename, node.linenr)
220+
for node in sc.nodes))
221+
222+
223+
def _print_help(node):
224+
print("\n" + (node.help or "No help text\n"))
225+
226+
227+
def _default_value_str(sym):
228+
# Returns the "m/M/y" string in e.g.
229+
#
230+
# TRISTATE_SYM prompt (TRISTATE_SYM, defined at Kconfig:9) [n/M/y]:
231+
#
232+
# For string/int/hex, returns the default value as-is.
233+
234+
if sym.type in (BOOL, TRISTATE):
235+
return "/".join(("NMY" if sym.tri_value == tri else "nmy")[tri]
236+
for tri in sym.assignable)
237+
238+
# string/int/hex
239+
return sym.str_value
240+
241+
242+
def _new_value_str(new):
243+
if new:
244+
return " (NEW)"
245+
return ""
246+
247+
248+
if __name__ == "__main__":
249+
_main()

setup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"menuconfig",
3232
"guiconfig",
3333
"genconfig",
34+
"oldaskconfig",
3435
"oldconfig",
3536
"olddefconfig",
3637
"savedefconfig",
@@ -48,6 +49,7 @@
4849
"menuconfig = menuconfig:_main",
4950
"guiconfig = guiconfig:_main",
5051
"genconfig = genconfig:main",
52+
"oldaskconfig = oldaskconfig:_main",
5153
"oldconfig = oldconfig:_main",
5254
"olddefconfig = olddefconfig:main",
5355
"savedefconfig = savedefconfig:main",

0 commit comments

Comments
 (0)