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