Skip to content

Commit 17741f5

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

File tree

3 files changed

+253
-0
lines changed

3 files changed

+253
-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: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
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+
oldaskconfig(node)
54+
55+
if not conf_changed:
56+
break
57+
58+
print(kconf.write_config())
59+
60+
61+
def oldaskconfig(node):
62+
"""
63+
Prompts the user for a value if node.item is a visible symbol/choice
64+
"""
65+
# See main()
66+
global conf_changed
67+
68+
# Only symbols and choices can be configured
69+
if not isinstance(node.item, (Symbol, Choice)):
70+
return
71+
72+
# Skip symbols and choices that aren't visible
73+
if not node.item.visibility:
74+
return
75+
76+
# Skip symbols and choices that don't have a prompt (at this location)
77+
if not node.prompt:
78+
return
79+
80+
if isinstance(node.item, Symbol):
81+
sym = node.item
82+
83+
new = sym.user_value is None
84+
85+
# Skip symbols that can only have a single value, due to selects
86+
if len(sym.assignable) == 1:
87+
return
88+
89+
# Skip symbols in choices in y mode. We ask once for the entire choice
90+
# instead.
91+
if sym.choice and sym.choice.tri_value == 2:
92+
return
93+
94+
# Loop until the user enters a valid value or enters a blank string
95+
# (for the default value)
96+
while True:
97+
val = input("{} ({}) [{}]{} ".format(
98+
node.prompt[0], _name_and_loc_str(sym),
99+
_default_value_str(sym), _new_value_str(new)))
100+
101+
if val == "?":
102+
_print_help(node)
103+
continue
104+
105+
# Substitute a blank string with the default value the symbol
106+
# would get
107+
if not val:
108+
val = sym.str_value
109+
110+
# Automatically add a "0x" prefix for hex symbols, like the
111+
# menuconfig interface does. This isn't done when loading .config
112+
# files, hence why set_value() doesn't do it automatically.
113+
if sym.type == HEX and not val.startswith(("0x", "0X")):
114+
val = "0x" + val
115+
116+
old_str_val = sym.str_value
117+
118+
# Kconfiglib itself will print a warning here if the value
119+
# is invalid, so we don't need to bother
120+
if sym.set_value(val):
121+
# Valid value input. We're done with this node.
122+
123+
if sym.str_value != old_str_val:
124+
conf_changed = True
125+
126+
return
127+
128+
else:
129+
choice = node.item
130+
131+
# Skip choices that already have a visible user selection...
132+
if choice.user_selection and choice.user_selection.visibility == 2:
133+
# ...unless there are new visible symbols in the choice. (We know
134+
# they have y (2) visibility in that case, because m-visible
135+
# symbols get demoted to n-visibility in y-mode choices, and the
136+
# user-selected symbol had visibility y.)
137+
for sym in choice.syms:
138+
if sym is not choice.user_selection and sym.visibility and \
139+
sym.user_value is None:
140+
# New visible symbols in the choice
141+
break
142+
else:
143+
# No new visible symbols in the choice
144+
return
145+
146+
# Get a list of available selections. The mode of the choice limits
147+
# the visibility of the choice value symbols, so this will indirectly
148+
# skip choices in n and m mode.
149+
options = [sym for sym in choice.syms if sym.visibility == 2]
150+
151+
if not options:
152+
# No y-visible choice value symbols
153+
return
154+
155+
# Loop until the user enters a valid selection or a blank string (for
156+
# the default selection)
157+
while True:
158+
print("{} ({})".format(node.prompt[0], _name_and_loc_str(choice)))
159+
160+
for i, sym in enumerate(options, 1):
161+
print("{} {}. {} ({})".format(
162+
">" if sym is choice.selection else " ",
163+
i,
164+
# Assume people don't define choice symbols with multiple
165+
# prompts. That generates a warning anyway.
166+
sym.nodes[0].prompt[0],
167+
sym.name))
168+
169+
sel_index = input("choice[1-{}]: ".format(len(options)))
170+
171+
if sel_index == "?":
172+
_print_help(node)
173+
continue
174+
175+
# Pick the default selection if the string is blank
176+
if not sel_index:
177+
choice.selection.set_value(2)
178+
break
179+
180+
try:
181+
sel_index = int(sel_index)
182+
except ValueError:
183+
print("Bad index", file=sys.stderr)
184+
continue
185+
186+
if not 1 <= sel_index <= len(options):
187+
print("Bad index", file=sys.stderr)
188+
continue
189+
190+
# Valid selection
191+
192+
if options[sel_index - 1].tri_value != 2:
193+
conf_changed = True
194+
195+
options[sel_index - 1].set_value(2)
196+
break
197+
198+
# Give all of the non-selected visible choice symbols the user value n.
199+
# This makes it so that the choice is no longer considered new once we
200+
# do additional passes, if the reason that it was considered new was
201+
# that it had new visible choice symbols.
202+
#
203+
# Only giving visible choice symbols the user value n means we will
204+
# prompt for the choice again if later user selections make more new
205+
# choice symbols visible, which is correct.
206+
for sym in choice.syms:
207+
if sym is not choice.user_selection and sym.visibility:
208+
sym.set_value(0)
209+
210+
211+
def _name_and_loc_str(sc):
212+
# Helper for printing the name of the symbol/choice 'sc' along with the
213+
# location(s) in the Kconfig files where it is defined. Unnamed choices
214+
# return "choice" instead of the name.
215+
216+
return "{}, defined at {}".format(
217+
sc.name or "choice",
218+
", ".join("{}:{}".format(node.filename, node.linenr)
219+
for node in sc.nodes))
220+
221+
222+
def _print_help(node):
223+
print("\n" + (node.help or "No help text\n"))
224+
225+
226+
def _default_value_str(sym):
227+
# Returns the "m/M/y" string in e.g.
228+
#
229+
# TRISTATE_SYM prompt (TRISTATE_SYM, defined at Kconfig:9) [n/M/y]:
230+
#
231+
# For string/int/hex, returns the default value as-is.
232+
233+
if sym.type in (BOOL, TRISTATE):
234+
return "/".join(("NMY" if sym.tri_value == tri else "nmy")[tri]
235+
for tri in sym.assignable)
236+
237+
# string/int/hex
238+
return sym.str_value
239+
240+
241+
def _new_value_str(new):
242+
if new:
243+
return " (NEW)"
244+
return ""
245+
246+
247+
if __name__ == "__main__":
248+
_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)