Changes in tools/config.py [c01f8e6:28f4adb] in mainline


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • tools/config.py

    rc01f8e6 r28f4adb  
    33# Copyright (c) 2006 Ondrej Palkovsky
    44# Copyright (c) 2009 Martin Decky
    5 # Copyright (c) 2010 Jiri Svoboda
    65# All rights reserved.
    76#
     
    4140import xtui
    4241
    43 RULES_FILE = sys.argv[1]
     42INPUT = sys.argv[1]
    4443MAKEFILE = 'Makefile.config'
    4544MACROS = 'config.h'
    46 PRESETS_DIR = 'defaults'
    47 
    48 def read_config(fname, config):
    49         "Read saved values from last configuration run or a preset file"
     45PRECONF = 'defaults'
     46
     47def read_defaults(fname, defaults):
     48        "Read saved values from last configuration run"
    5049       
    5150        inf = open(fname, 'r')
     
    5352        for line in inf:
    5453                res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
    55                 if res:
    56                         config[res.group(1)] = res.group(2)
     54                if (res):
     55                        defaults[res.group(1)] = res.group(2)
    5756       
    5857        inf.close()
    5958
    60 def check_condition(text, config, rules):
     59def check_condition(text, defaults, ask_names):
    6160        "Check that the condition specified on input line is True (only CNF and DNF is supported)"
    6261       
    6362        ctype = 'cnf'
    6463       
    65         if (')|' in text) or ('|(' in text):
     64        if ((')|' in text) or ('|(' in text)):
    6665                ctype = 'dnf'
    6766       
    68         if ctype == 'cnf':
     67        if (ctype == 'cnf'):
    6968                conds = text.split('&')
    7069        else:
     
    7271       
    7372        for cond in conds:
    74                 if cond.startswith('(') and cond.endswith(')'):
     73                if (cond.startswith('(')) and (cond.endswith(')')):
    7574                        cond = cond[1:-1]
    7675               
    77                 inside = check_inside(cond, config, ctype)
     76                inside = check_inside(cond, defaults, ctype)
    7877               
    7978                if (ctype == 'cnf') and (not inside):
    8079                        return False
    8180               
    82                 if (ctype == 'dnf') and inside:
     81                if (ctype == 'dnf') and (inside):
    8382                        return True
    8483       
    85         if ctype == 'cnf':
     84        if (ctype == 'cnf'):
    8685                return True
    8786        return False
    8887
    89 def check_inside(text, config, ctype):
     88def check_inside(text, defaults, ctype):
    9089        "Check for condition"
    9190       
    92         if ctype == 'cnf':
     91        if (ctype == 'cnf'):
    9392                conds = text.split('|')
    9493        else:
     
    9796        for cond in conds:
    9897                res = re.match(r'^(.*?)(!?=)(.*)$', cond)
    99                 if not res:
     98                if (not res):
    10099                        raise RuntimeError("Invalid condition: %s" % cond)
    101100               
     
    104103                condval = res.group(3)
    105104               
    106                 if not condname in config:
     105                if (not condname in defaults):
    107106                        varval = ''
    108107                else:
    109                         varval = config[condname]
     108                        varval = defaults[condname]
    110109                        if (varval == '*'):
    111110                                varval = 'y'
    112111               
    113                 if ctype == 'cnf':
     112                if (ctype == 'cnf'):
    114113                        if (oper == '=') and (condval == varval):
    115114                                return True
     
    124123                                return False
    125124       
    126         if ctype == 'cnf':
     125        if (ctype == 'cnf'):
    127126                return False
    128127       
    129128        return True
    130129
    131 def parse_rules(fname, rules):
    132         "Parse rules file"
     130def parse_config(fname, ask_names):
     131        "Parse configuration file"
    133132       
    134133        inf = open(fname, 'r')
     
    139138        for line in inf:
    140139               
    141                 if line.startswith('!'):
     140                if (line.startswith('!')):
    142141                        # Ask a question
    143142                        res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
    144143                       
    145                         if not res:
     144                        if (not res):
    146145                                raise RuntimeError("Weird line: %s" % line)
    147146                       
     
    150149                        vartype = res.group(3)
    151150                       
    152                         rules.append((varname, vartype, name, choices, cond))
     151                        ask_names.append((varname, vartype, name, choices, cond))
    153152                        name = ''
    154153                        choices = []
    155154                        continue
    156155               
    157                 if line.startswith('@'):
     156                if (line.startswith('@')):
    158157                        # Add new line into the 'choices' array
    159158                        res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
     
    165164                        continue
    166165               
    167                 if line.startswith('%'):
     166                if (line.startswith('%')):
    168167                        # Name of the option
    169168                        name = line[1:].strip()
    170169                        continue
    171170               
    172                 if line.startswith('#') or (line == '\n'):
     171                if ((line.startswith('#')) or (line == '\n')):
    173172                        # Comment or empty line
    174173                        continue
     
    182181        "Return '*' if yes, ' ' if no"
    183182       
    184         if default == 'y':
     183        if (default == 'y'):
    185184                return '*'
    186185       
     
    200199        cnt = 0
    201200        for key, val in choices:
    202                 if (default) and (key == default):
     201                if ((default) and (key == default)):
    203202                        position = cnt
    204203               
     
    208207        (button, value) = xtui.choice_window(screen, name, 'Choose value', options, position)
    209208       
    210         if button == 'cancel':
     209        if (button == 'cancel'):
    211210                return None
    212211       
    213212        return choices[value][0]
    214213
    215 ## Infer and verify configuration values.
    216 #
    217 # Augment @a config with values that can be inferred, purge invalid ones
    218 # and verify that all variables have a value (previously specified or inferred).
    219 #
    220 # @param config Configuration to work on
    221 # @param rules  Rules
    222 #
    223 # @return True if configuration is complete and valid, False
    224 #         otherwise.
    225 #
    226 def infer_verify_choices(config, rules):
    227         "Infer and verify configuration values."
    228        
    229         for rule in rules:
    230                 varname, vartype, name, choices, cond = rule
    231                
    232                 if cond and (not check_condition(cond, config, rules)):
    233                         continue
    234                
    235                 if not varname in config:
    236                         value = None
    237                 else:
    238                         value = config[varname]
    239                
    240                 if not validate_rule_value(rule, value):
    241                         value = None
    242                
    243                 default = get_default_rule(rule)
    244 
    245                 #
    246                 # If we don't have a value but we do have
    247                 # a default, use it.
    248                 #
    249                 if value == None and default != None:
    250                         value = default
    251                         config[varname] = default
    252                
    253                 if not varname in config:
     214def check_choices(defaults, ask_names):
     215        "Check whether all accessible variables have a default"
     216       
     217        for varname, vartype, name, choices, cond in ask_names:
     218                if ((cond) and (not check_condition(cond, defaults, ask_names))):
     219                        continue
     220               
     221                if (not varname in defaults):
    254222                        return False
    255223       
    256224        return True
    257225
    258 ## Get default value from a rule.
    259 def get_default_rule(rule):
    260         varname, vartype, name, choices, cond = rule
    261        
    262         default = None
    263        
    264         if vartype == 'choice':
    265                 # If there is just one option, use it
    266                 if len(choices) == 1:
    267                         default = choices[0][0]
    268         elif vartype == 'y':
    269                 default = '*'
    270         elif vartype == 'n':
    271                 default = 'n'
    272         elif vartype == 'y/n':
    273                 default = 'y'
    274         elif vartype == 'n/y':
    275                 default = 'n'
    276         else:
    277                 raise RuntimeError("Unknown variable type: %s" % vartype)
    278        
    279         return default
    280 
    281 ## Get option from a rule.
    282 #
    283 # @param rule  Rule for a variable
    284 # @param value Current value of the variable
    285 #
    286 # @return Option (string) to ask or None which means not to ask.
    287 #
    288 def get_rule_option(rule, value):
    289         varname, vartype, name, choices, cond = rule
    290        
    291         option = None
    292        
    293         if vartype == 'choice':
    294                 # If there is just one option, don't ask
    295                 if len(choices) != 1:
    296                         if (value == None):
    297                                 option = "?     %s --> " % name
    298                         else:
    299                                 option = "      %s [%s] --> " % (name, value)
    300         elif vartype == 'y':
    301                 pass
    302         elif vartype == 'n':
    303                 pass
    304         elif vartype == 'y/n':
    305                 option = "  <%s> %s " % (yes_no(value), name)
    306         elif vartype == 'n/y':
    307                 option ="  <%s> %s " % (yes_no(value), name)
    308         else:
    309                 raise RuntimeError("Unknown variable type: %s" % vartype)
    310        
    311         return option
    312 
    313 ## Check if variable value is valid.
    314 #
    315 # @param rule  Rule for the variable
    316 # @param value Value of the variable
    317 #
    318 # @return True if valid, False if not valid.
    319 #
    320 def validate_rule_value(rule, value):
    321         varname, vartype, name, choices, cond = rule
    322        
    323         if value == None:
    324                 return True
    325        
    326         if vartype == 'choice':
    327                 if not value in [choice[0] for choice in choices]:
    328                         return False
    329         elif vartype == 'y':
    330                 if value != 'y':
    331                         return False
    332         elif vartype == 'n':
    333                 if value != 'n':
    334                         return False
    335         elif vartype == 'y/n':
    336                 if not value in ['y', 'n']:
    337                         return False
    338         elif vartype == 'n/y':
    339                 if not value in ['y', 'n']:
    340                         return False
    341         else:
    342                 raise RuntimeError("Unknown variable type: %s" % vartype)
    343        
    344         return True
    345 
    346 def create_output(mkname, mcname, config, rules):
     226def create_output(mkname, mcname, defaults, ask_names):
    347227        "Create output configuration"
    348228       
     
    358238                sys.stderr.write("failed\n")
    359239       
    360         if len(version) == 3:
     240        if (len(version) == 3):
    361241                revision = version[1]
    362                 if version[0] != 1:
     242                if (version[0] != 1):
    363243                        revision += 'M'
    364244                revision += ' (%s)' % version[2]
     
    379259        defs = 'CONFIG_DEFS ='
    380260       
    381         for varname, vartype, name, choices, cond in rules:
    382                 if cond and (not check_condition(cond, config, rules)):
    383                         continue
    384                
    385                 if not varname in config:
    386                         value = ''
     261        for varname, vartype, name, choices, cond in ask_names:
     262                if ((cond) and (not check_condition(cond, defaults, ask_names))):
     263                        continue
     264               
     265                if (not varname in defaults):
     266                        default = ''
    387267                else:
    388                         value = config[varname]
    389                         if (value == '*'):
    390                                 value = 'y'
    391                
    392                 outmk.write('# %s\n%s = %s\n\n' % (name, varname, value))
    393                
    394                 if vartype in ["y", "n", "y/n", "n/y"]:
    395                         if value == "y":
     268                        default = defaults[varname]
     269                        if (default == '*'):
     270                                default = 'y'
     271               
     272                outmk.write('# %s\n%s = %s\n\n' % (name, varname, default))
     273               
     274                if ((vartype == "y") or (vartype == "n") or (vartype == "y/n") or (vartype == "n/y")):
     275                        if (default == "y"):
    396276                                outmc.write('/* %s */\n#define %s\n\n' % (name, varname))
    397277                                defs += ' -D%s' % varname
    398278                else:
    399                         outmc.write('/* %s */\n#define %s %s\n#define %s_%s\n\n' % (name, varname, value, varname, value))
    400                         defs += ' -D%s=%s -D%s_%s' % (varname, value, varname, value)
    401        
    402         if revision is not None:
     279                        outmc.write('/* %s */\n#define %s %s\n#define %s_%s\n\n' % (name, varname, default, varname, default))
     280                        defs += ' -D%s=%s -D%s_%s' % (varname, default, varname, default)
     281       
     282        if (revision is not None):
    403283                outmk.write('REVISION = %s\n' % revision)
    404284                outmc.write('#define REVISION %s\n' % revision)
     
    419299        return list
    420300
    421 ## Ask user to choose a configuration profile.
    422 #
    423 def choose_profile(root, fname, screen, config):
     301def read_preconfigured(root, fname, screen, defaults):
    424302        options = []
    425303        opt2path = {}
     
    431309                canon = os.path.join(path, fname)
    432310               
    433                 if os.path.isdir(path) and os.path.exists(canon) and os.path.isfile(canon):
     311                if ((os.path.isdir(path)) and (os.path.exists(canon)) and (os.path.isfile(canon))):
    434312                        subprofile = False
    435313                       
     
    439317                                subcanon = os.path.join(subpath, fname)
    440318                               
    441                                 if os.path.isdir(subpath) and os.path.exists(subcanon) and os.path.isfile(subcanon):
     319                                if ((os.path.isdir(subpath)) and (os.path.exists(subcanon)) and (os.path.isfile(subcanon))):
    442320                                        subprofile = True
    443321                                        options.append("%s (%s)" % (name, subname))
    444                                         opt2path[cnt] = [name, subname]
     322                                        opt2path[cnt] = (canon, subcanon)
    445323                                        cnt += 1
    446324                       
    447                         if not subprofile:
     325                        if (not subprofile):
    448326                                options.append(name)
    449                                 opt2path[cnt] = [name]
     327                                opt2path[cnt] = (canon, None)
    450328                                cnt += 1
    451329       
    452330        (button, value) = xtui.choice_window(screen, 'Load preconfigured defaults', 'Choose configuration profile', options, None)
    453331       
    454         if button == 'cancel':
     332        if (button == 'cancel'):
    455333                return None
    456334       
    457         return opt2path[value]
    458 
    459 ## Read presets from a configuration profile.
    460 #
    461 # @param profile Profile to load from (a list of string components)
    462 # @param config  Output configuration
    463 #
    464 def read_presets(profile, config):
    465         path = os.path.join(PRESETS_DIR, profile[0], MAKEFILE)
    466         read_config(path, config)
    467        
    468         if len(profile) > 1:
    469                 path = os.path.join(PRESETS_DIR, profile[0], profile[1], MAKEFILE)
    470                 read_config(path, config)
    471 
    472 ## Parse profile name (relative OS path) into a list of components.
    473 #
    474 # @param profile_name Relative path (using OS separator)
    475 # @return             List of components
    476 #
    477 def parse_profile_name(profile_name):
    478         profile = []
    479        
    480         head, tail = os.path.split(profile_name)
    481         if head != '':
    482                 profile.append(head)
    483        
    484         profile.append(tail)
    485         return profile
     335        read_defaults(opt2path[value][0], defaults)
     336        if (opt2path[value][1] != None):
     337                read_defaults(opt2path[value][1], defaults)
    486338
    487339def main():
    488         profile = None
    489         config = {}
    490         rules = []
    491        
    492         # Parse rules file
    493         parse_rules(RULES_FILE, rules)
    494        
    495         # Input configuration file can be specified on command line
    496         # otherwise configuration from previous run is used.
    497         if len(sys.argv) >= 4:
    498                 profile = parse_profile_name(sys.argv[3])
    499                 read_presets(profile, config)
    500         elif os.path.exists(MAKEFILE):
    501                 read_config(MAKEFILE, config)
    502        
    503         # Default mode: check values and regenerate configuration files
    504         if (len(sys.argv) >= 3) and (sys.argv[2] == 'default'):
    505                 if (infer_verify_choices(config, rules)):
    506                         create_output(MAKEFILE, MACROS, config, rules)
     340        defaults = {}
     341        ask_names = []
     342       
     343        # Parse configuration file
     344        parse_config(INPUT, ask_names)
     345       
     346        # Read defaults from previous run
     347        if os.path.exists(MAKEFILE):
     348                read_defaults(MAKEFILE, defaults)
     349       
     350        # Default mode: only check defaults and regenerate configuration
     351        if ((len(sys.argv) >= 3) and (sys.argv[2] == 'default')):
     352                if (check_choices(defaults, ask_names)):
     353                        create_output(MAKEFILE, MACROS, defaults, ask_names)
    507354                        return 0
    508355       
    509         # Hands-off mode: check values and regenerate configuration files,
    510         # but no interactive fallback
    511         if (len(sys.argv) >= 3) and (sys.argv[2] == 'hands-off'):
    512                 # We deliberately test sys.argv >= 4 because we do not want
    513                 # to read implicitly any possible previous run configuration
    514                 if len(sys.argv) < 4:
    515                         sys.stderr.write("Configuration error: No presets specified\n")
    516                         return 2
    517                
    518                 if (infer_verify_choices(config, rules)):
    519                         create_output(MAKEFILE, MACROS, config, rules)
    520                         return 0
    521                
    522                 sys.stderr.write("Configuration error: The presets are ambiguous\n")
    523                 return 1
    524        
    525         # Check mode: only check configuration
    526         if (len(sys.argv) >= 3) and (sys.argv[2] == 'check'):
    527                 if infer_verify_choices(config, rules):
     356        # Check mode: only check defaults
     357        if ((len(sys.argv) >= 3) and (sys.argv[2] == 'check')):
     358                if (check_choices(defaults, ask_names)):
    528359                        return 0
    529360                return 1
     
    535366                while True:
    536367                       
    537                         # Cancel out all values which have to be deduced
    538                         for varname, vartype, name, choices, cond in rules:
    539                                 if (vartype == 'y') and (varname in config) and (config[varname] == '*'):
    540                                         config[varname] = None
     368                        # Cancel out all defaults which have to be deduced
     369                        for varname, vartype, name, choices, cond in ask_names:
     370                                if ((vartype == 'y') and (varname in defaults) and (defaults[varname] == '*')):
     371                                        defaults[varname] = None
    541372                       
    542373                        options = []
     
    546377                        options.append("  --- Load preconfigured defaults ... ")
    547378                       
    548                         for rule in rules:
    549                                 varname, vartype, name, choices, cond = rule
    550                                
    551                                 if cond and (not check_condition(cond, config, rules)):
     379                        for varname, vartype, name, choices, cond in ask_names:
     380                               
     381                                if ((cond) and (not check_condition(cond, defaults, ask_names))):
    552382                                        continue
    553383                               
    554                                 if varname == selname:
     384                                if (varname == selname):
    555385                                        position = cnt
    556386                               
    557                                 if not varname in config:
    558                                         value = None
     387                                if (not varname in defaults):
     388                                        default = None
    559389                                else:
    560                                         value = config[varname]
    561                                
    562                                 if not validate_rule_value(rule, value):
    563                                         value = None
    564                                
    565                                 default = get_default_rule(rule)
    566 
    567                                 #
    568                                 # If we don't have a value but we do have
    569                                 # a default, use it.
    570                                 #
    571                                 if value == None and default != None:
    572                                         value = default
    573                                         config[varname] = default
    574                                
    575                                 option = get_rule_option(rule, value)
    576                                 if option != None:
    577                                         options.append(option)
     390                                        default = defaults[varname]
     391                               
     392                                if (vartype == 'choice'):
     393                                        # Check if the default is an acceptable value
     394                                        if ((default) and (not default in [choice[0] for choice in choices])):
     395                                                default = None
     396                                                defaults.pop(varname)
     397                                       
     398                                        # If there is just one option, use it
     399                                        if (len(choices) == 1):
     400                                                defaults[varname] = choices[0][0]
     401                                                continue
     402                                       
     403                                        if (default == None):
     404                                                options.append("?     %s --> " % name)
     405                                        else:
     406                                                options.append("      %s [%s] --> " % (name, default))
     407                                elif (vartype == 'y'):
     408                                        defaults[varname] = '*'
     409                                        continue
     410                                elif (vartype == 'n'):
     411                                        defaults[varname] = 'n'
     412                                        continue
     413                                elif (vartype == 'y/n'):
     414                                        if (default == None):
     415                                                default = 'y'
     416                                                defaults[varname] = default
     417                                        options.append("  <%s> %s " % (yes_no(default), name))
     418                                elif (vartype == 'n/y'):
     419                                        if (default == None):
     420                                                default = 'n'
     421                                                defaults[varname] = default
     422                                        options.append("  <%s> %s " % (yes_no(default), name))
    578423                                else:
    579                                         continue
     424                                        raise RuntimeError("Unknown variable type: %s" % vartype)
    580425                               
    581426                                opt2row[cnt] = (varname, vartype, name, choices)
     
    588433                        (button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
    589434                       
    590                         if button == 'cancel':
     435                        if (button == 'cancel'):
    591436                                return 'Configuration canceled'
    592437                       
    593                         if button == 'done':
    594                                 if (infer_verify_choices(config, rules)):
     438                        if (button == 'done'):
     439                                if (check_choices(defaults, ask_names)):
    595440                                        break
    596441                                else:
     
    598443                                        continue
    599444                       
    600                         if value == 0:
    601                                 profile = choose_profile(PRESETS_DIR, MAKEFILE, screen, config)
    602                                 if profile != None:
    603                                         read_presets(profile, config)
     445                        if (value == 0):
     446                                read_preconfigured(PRECONF, MAKEFILE, screen, defaults)
    604447                                position = 1
    605448                                continue
    606449                       
    607450                        position = None
    608                         if not value in opt2row:
     451                        if (not value in opt2row):
    609452                                raise RuntimeError("Error selecting value: %s" % value)
    610453                       
    611454                        (selname, seltype, name, choices) = opt2row[value]
    612455                       
    613                         if not selname in config:
    614                                 value = None
     456                        if (not selname in defaults):
     457                                        default = None
    615458                        else:
    616                                 value = config[selname]
    617                        
    618                         if seltype == 'choice':
    619                                 config[selname] = subchoice(screen, name, choices, value)
    620                         elif (seltype == 'y/n') or (seltype == 'n/y'):
    621                                 if config[selname] == 'y':
    622                                         config[selname] = 'n'
     459                                default = defaults[selname]
     460                       
     461                        if (seltype == 'choice'):
     462                                defaults[selname] = subchoice(screen, name, choices, default)
     463                        elif ((seltype == 'y/n') or (seltype == 'n/y')):
     464                                if (defaults[selname] == 'y'):
     465                                        defaults[selname] = 'n'
    623466                                else:
    624                                         config[selname] = 'y'
     467                                        defaults[selname] = 'y'
    625468        finally:
    626469                xtui.screen_done(screen)
    627470       
    628         create_output(MAKEFILE, MACROS, config, rules)
     471        create_output(MAKEFILE, MACROS, defaults, ask_names)
    629472        return 0
    630473
Note: See TracChangeset for help on using the changeset viewer.