1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
|
#!/usr/bin/env python
# Implements oldconfig-like functionality:
#
# 1. Load existing .config
# 2. Prompt the user for the value of all changeable symbols/choices
# that aren't already set in the .config
# 3. Write new .config
#
# Unlike 'make oldconfig', this script doesn't print menu titles and
# comments, but instead gives the Kconfig locations of all symbols and
# choices. Printing menu titles and comments as well 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).
#
# Inputting '?' on the prompt will display the help text of the item, if any.
# Hopefully no one will want to use that as a value.
#
# Sample session:
#
# OldconfigExample contents:
#
# config MODULES
# def_bool y
# option modules
#
# config BOOL_SYM
# bool "BOOL_SYM prompt"
# default y
#
# config TRISTATE_SYM
# tristate "TRISTATE_SYM prompt"
# default m
#
# config STRING_SYM
# string "STRING_SYM prompt"
# default "foo"
#
# config INT_SYM
# int "INT_SYM prompt"
#
# config HEX_SYM
# hex "HEX_SYM prompt"
#
# choice
# bool "A choice that defaults to CHOICE_B"
# default CHOICE_B
#
# config CHOICE_A
# bool "CHOICE_A's prompt"
#
# config CHOICE_B
# bool "CHOICE_B's prompt"
#
# config CHOICE_C
# bool "CHOICE_C's prompt"
#
# endchoice
#
#
# Running:
#
# $ touch .config # Run with empty .config
#
# $ python(3) oldconfig.py Kconfig
# BOOL_SYM prompt (BOOL_SYM, defined at Kconfig:5) [n/Y] foo
# Invalid tristate value
# BOOL_SYM prompt (BOOL_SYM, defined at Kconfig:5) [n/Y] n
# TRISTATE_SYM prompt (TRISTATE_SYM, defined at Kconfig:9) [n/M/y]
# STRING_SYM prompt (STRING_SYM, defined at Kconfig:13) [foo] bar
# INT_SYM prompt (INT_SYM, defined at Kconfig:17) [] 0x123
# warning: the value '0x123' is invalid for INT_SYM (defined at Kconfig:17), which has type int. Assignment ignored.
# INT_SYM prompt (INT_SYM, defined at Kconfig:17) [] 123
# HEX_SYM prompt (HEX_SYM, defined at Kconfig:20) [] 0x123
# A choice that default to B (defined at Kconfig:23)
# 1. CHOICE_A's prompt (CHOICE_A)
# > 2. CHOICE_B's prompt (CHOICE_B)
# 3. CHOICE_C's prompt (CHOICE_C)
# choice[1-3]: 5
# Bad index
# A choice that default to B (defined at Kconfig:23)
# 1. CHOICE_A's prompt (CHOICE_A)
# > 2. CHOICE_B's prompt (CHOICE_B)
# 3. CHOICE_C's prompt (CHOICE_C)
# choice[1-3]: 3
# Configuration written to .config
#
# $ cat .config
# # Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)
# CONFIG_MODULES=y
# # CONFIG_BOOL_SYM is not set
# CONFIG_TRISTATE_SYM=m
# CONFIG_STRING_SYM="bar"
# CONFIG_INT_SYM=123
# CONFIG_HEX_SYM=0x123
# # CONFIG_CHOICE_A is not set
# # CONFIG_CHOICE_B is not set
# CONFIG_CHOICE_C=y
#
# $ python oldconfig.py Kconfig # Everything's already up to date
# Configuration written to .config
from __future__ import print_function
from kconfiglib import Kconfig, Symbol, Choice, BOOL, TRISTATE, HEX, \
standard_kconfig, standard_config_filename
import os
import sys
# Python 2/3 compatibility hack
if sys.version_info[0] < 3:
input = raw_input
def eprint(*args):
print(*args, file=sys.stderr)
def print_help(node):
if node.help is not None:
print("\n" + node.help)
else:
print("\nNo help text\n")
def name_and_loc_str(sym):
"""
Helper for printing the symbol name along with the location(s) in the
Kconfig files where the symbol is defined
"""
return "{}, defined at {}".format(
sym.name,
", ".join("{}:{}".format(node.filename, node.linenr)
for node in sym.nodes))
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 do_oldconfig_for_node(node):
"""
Prompts the user for a value for the menu node item, where applicable in
oldconfig mode
"""
# See do_oldconfig()
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("{} (defined at {}:{})".format(
node.prompt[0], node.filename, node.linenr))
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:
eprint("Bad index")
continue
if not 1 <= sel_index <= len(options):
eprint("Bad index")
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)
# Entry point when run as an executable, split out so that setuptools'
# 'entry_points' can be used. It produces a handy oldconfig.exe launcher on
# Windows.
def main():
kconf = standard_kconfig()
config_filename = standard_config_filename()
if not os.path.exists(config_filename):
sys.exit("{}: '{}' does not exist"
.format(sys.argv[0], config_filename))
kconf.load_config(config_filename)
do_oldconfig(kconf)
kconf.write_config(config_filename)
print("Configuration saved to '{}'".format(config_filename))
def do_oldconfig(kconf):
# An earlier symbol in the Kconfig files might depend on a later symbol and
# become visible if its value changes. 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
while True:
conf_changed = False
do_oldconfig_rec(kconf.top_node)
if not conf_changed:
break
def do_oldconfig_rec(node):
while node:
do_oldconfig_for_node(node)
if node.list:
do_oldconfig_rec(node.list)
node = node.next
if __name__ == "__main__":
main()
|