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