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


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • tools/config.py

    r28f4adb re3c3172  
    33# Copyright (c) 2006 Ondrej Palkovsky
    44# Copyright (c) 2009 Martin Decky
     5# Copyright (c) 2010 Jiri Svoboda
    56# All rights reserved.
    67#
     
    4041import xtui
    4142
    42 INPUT = sys.argv[1]
     43RULES_FILE = sys.argv[1]
    4344MAKEFILE = 'Makefile.config'
    4445MACROS = 'config.h'
    45 PRECONF = 'defaults'
    46 
    47 def read_defaults(fname, defaults):
    48         "Read saved values from last configuration run"
     46PRESETS_DIR = 'defaults'
     47
     48def read_config(fname, config):
     49        "Read saved values from last configuration run or a preset file"
    4950       
    5051        inf = open(fname, 'r')
     
    5253        for line in inf:
    5354                res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
    54                 if (res):
    55                         defaults[res.group(1)] = res.group(2)
     55                if res:
     56                        config[res.group(1)] = res.group(2)
    5657       
    5758        inf.close()
    5859
    59 def check_condition(text, defaults, ask_names):
     60def check_condition(text, config, rules):
    6061        "Check that the condition specified on input line is True (only CNF and DNF is supported)"
    6162       
    6263        ctype = 'cnf'
    6364       
    64         if ((')|' in text) or ('|(' in text)):
     65        if (')|' in text) or ('|(' in text):
    6566                ctype = 'dnf'
    6667       
    67         if (ctype == 'cnf'):
     68        if ctype == 'cnf':
    6869                conds = text.split('&')
    6970        else:
     
    7172       
    7273        for cond in conds:
    73                 if (cond.startswith('(')) and (cond.endswith(')')):
     74                if cond.startswith('(') and cond.endswith(')'):
    7475                        cond = cond[1:-1]
    7576               
    76                 inside = check_inside(cond, defaults, ctype)
     77                inside = check_inside(cond, config, ctype)
    7778               
    7879                if (ctype == 'cnf') and (not inside):
    7980                        return False
    8081               
    81                 if (ctype == 'dnf') and (inside):
     82                if (ctype == 'dnf') and inside:
    8283                        return True
    8384       
    84         if (ctype == 'cnf'):
     85        if ctype == 'cnf':
    8586                return True
    8687        return False
    8788
    88 def check_inside(text, defaults, ctype):
     89def check_inside(text, config, ctype):
    8990        "Check for condition"
    9091       
    91         if (ctype == 'cnf'):
     92        if ctype == 'cnf':
    9293                conds = text.split('|')
    9394        else:
     
    9697        for cond in conds:
    9798                res = re.match(r'^(.*?)(!?=)(.*)$', cond)
    98                 if (not res):
     99                if not res:
    99100                        raise RuntimeError("Invalid condition: %s" % cond)
    100101               
     
    103104                condval = res.group(3)
    104105               
    105                 if (not condname in defaults):
     106                if not condname in config:
    106107                        varval = ''
    107108                else:
    108                         varval = defaults[condname]
     109                        varval = config[condname]
    109110                        if (varval == '*'):
    110111                                varval = 'y'
    111112               
    112                 if (ctype == 'cnf'):
     113                if ctype == 'cnf':
    113114                        if (oper == '=') and (condval == varval):
    114115                                return True
     
    123124                                return False
    124125       
    125         if (ctype == 'cnf'):
     126        if ctype == 'cnf':
    126127                return False
    127128       
    128129        return True
    129130
    130 def parse_config(fname, ask_names):
    131         "Parse configuration file"
     131def parse_rules(fname, rules):
     132        "Parse rules file"
    132133       
    133134        inf = open(fname, 'r')
     
    138139        for line in inf:
    139140               
    140                 if (line.startswith('!')):
     141                if line.startswith('!'):
    141142                        # Ask a question
    142143                        res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
    143144                       
    144                         if (not res):
     145                        if not res:
    145146                                raise RuntimeError("Weird line: %s" % line)
    146147                       
     
    149150                        vartype = res.group(3)
    150151                       
    151                         ask_names.append((varname, vartype, name, choices, cond))
     152                        rules.append((varname, vartype, name, choices, cond))
    152153                        name = ''
    153154                        choices = []
    154155                        continue
    155156               
    156                 if (line.startswith('@')):
     157                if line.startswith('@'):
    157158                        # Add new line into the 'choices' array
    158159                        res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
     
    164165                        continue
    165166               
    166                 if (line.startswith('%')):
     167                if line.startswith('%'):
    167168                        # Name of the option
    168169                        name = line[1:].strip()
    169170                        continue
    170171               
    171                 if ((line.startswith('#')) or (line == '\n')):
     172                if line.startswith('#') or (line == '\n'):
    172173                        # Comment or empty line
    173174                        continue
     
    181182        "Return '*' if yes, ' ' if no"
    182183       
    183         if (default == 'y'):
     184        if default == 'y':
    184185                return '*'
    185186       
     
    199200        cnt = 0
    200201        for key, val in choices:
    201                 if ((default) and (key == default)):
     202                if (default) and (key == default):
    202203                        position = cnt
    203204               
     
    207208        (button, value) = xtui.choice_window(screen, name, 'Choose value', options, position)
    208209       
    209         if (button == 'cancel'):
     210        if button == 'cancel':
    210211                return None
    211212       
    212213        return choices[value][0]
    213214
    214 def 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))):
     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#
     226def 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)):
    219233                        continue
    220234               
    221                 if (not varname in defaults):
     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                if default != None:
     245                        config[varname] = default
     246               
     247                if not varname in config:
    222248                        return False
    223249       
    224250        return True
    225251
    226 def create_output(mkname, mcname, defaults, ask_names):
     252## Get default value from a rule.
     253def get_default_rule(rule):
     254        varname, vartype, name, choices, cond = rule
     255       
     256        default = None
     257       
     258        if vartype == 'choice':
     259                # If there is just one option, use it
     260                if len(choices) == 1:
     261                        default = choices[0][0]
     262        elif vartype == 'y':
     263                default = '*'
     264        elif vartype == 'n':
     265                default = 'n'
     266        elif vartype == 'y/n':
     267                default = 'y'
     268        elif vartype == 'n/y':
     269                default = 'n'
     270        else:
     271                raise RuntimeError("Unknown variable type: %s" % vartype)
     272       
     273        return default
     274
     275## Get option from a rule.
     276#
     277# @param rule  Rule for a variable
     278# @param value Current value of the variable
     279#
     280# @return Option (string) to ask or None which means not to ask.
     281#
     282def get_rule_option(rule, value):
     283        varname, vartype, name, choices, cond = rule
     284       
     285        option = None
     286       
     287        if vartype == 'choice':
     288                # If there is just one option, don't ask
     289                if len(choices) != 1:
     290                        if (value == None):
     291                                option = "?     %s --> " % name
     292                        else:
     293                                option = "      %s [%s] --> " % (name, value)
     294        elif vartype == 'y':
     295                pass
     296        elif vartype == 'n':
     297                pass
     298        elif vartype == 'y/n':
     299                option = "  <%s> %s " % (yes_no(value), name)
     300        elif vartype == 'n/y':
     301                option ="  <%s> %s " % (yes_no(value), name)
     302        else:
     303                raise RuntimeError("Unknown variable type: %s" % vartype)
     304       
     305        return option
     306
     307## Check if variable value is valid.
     308#
     309# @param rule  Rule for the variable
     310# @param value Value of the variable
     311#
     312# @return True if valid, False if not valid.
     313#
     314def validate_rule_value(rule, value):
     315        varname, vartype, name, choices, cond = rule
     316       
     317        if value == None:
     318                return True
     319       
     320        if vartype == 'choice':
     321                if not value in [choice[0] for choice in choices]:
     322                        return False
     323        elif vartype == 'y':
     324                if value != 'y':
     325                        return False
     326        elif vartype == 'n':
     327                if value != 'n':
     328                        return False
     329        elif vartype == 'y/n':
     330                if not value in ['y', 'n']:
     331                        return False
     332        elif vartype == 'n/y':
     333                if not value in ['y', 'n']:
     334                        return False
     335        else:
     336                raise RuntimeError("Unknown variable type: %s" % vartype)
     337       
     338        return True
     339
     340def create_output(mkname, mcname, config, rules):
    227341        "Create output configuration"
    228342       
     
    238352                sys.stderr.write("failed\n")
    239353       
    240         if (len(version) == 3):
     354        if len(version) == 3:
    241355                revision = version[1]
    242                 if (version[0] != 1):
     356                if version[0] != 1:
    243357                        revision += 'M'
    244358                revision += ' (%s)' % version[2]
     
    259373        defs = 'CONFIG_DEFS ='
    260374       
    261         for varname, vartype, name, choices, cond in ask_names:
    262                 if ((cond) and (not check_condition(cond, defaults, ask_names))):
     375        for varname, vartype, name, choices, cond in rules:
     376                if cond and (not check_condition(cond, config, rules)):
    263377                        continue
    264378               
    265                 if (not varname in defaults):
    266                         default = ''
     379                if not varname in config:
     380                        value = ''
    267381                else:
    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"):
     382                        value = config[varname]
     383                        if (value == '*'):
     384                                value = 'y'
     385               
     386                outmk.write('# %s\n%s = %s\n\n' % (name, varname, value))
     387               
     388                if vartype in ["y", "n", "y/n", "n/y"]:
     389                        if value == "y":
    276390                                outmc.write('/* %s */\n#define %s\n\n' % (name, varname))
    277391                                defs += ' -D%s' % varname
    278392                else:
    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):
     393                        outmc.write('/* %s */\n#define %s %s\n#define %s_%s\n\n' % (name, varname, value, varname, value))
     394                        defs += ' -D%s=%s -D%s_%s' % (varname, value, varname, value)
     395       
     396        if revision is not None:
    283397                outmk.write('REVISION = %s\n' % revision)
    284398                outmc.write('#define REVISION %s\n' % revision)
     
    299413        return list
    300414
    301 def read_preconfigured(root, fname, screen, defaults):
     415## Ask user to choose a configuration profile.
     416#
     417def choose_profile(root, fname, screen, config):
    302418        options = []
    303419        opt2path = {}
     
    309425                canon = os.path.join(path, fname)
    310426               
    311                 if ((os.path.isdir(path)) and (os.path.exists(canon)) and (os.path.isfile(canon))):
     427                if os.path.isdir(path) and os.path.exists(canon) and os.path.isfile(canon):
    312428                        subprofile = False
    313429                       
     
    317433                                subcanon = os.path.join(subpath, fname)
    318434                               
    319                                 if ((os.path.isdir(subpath)) and (os.path.exists(subcanon)) and (os.path.isfile(subcanon))):
     435                                if os.path.isdir(subpath) and os.path.exists(subcanon) and os.path.isfile(subcanon):
    320436                                        subprofile = True
    321437                                        options.append("%s (%s)" % (name, subname))
    322                                         opt2path[cnt] = (canon, subcanon)
     438                                        opt2path[cnt] = [name, subname]
    323439                                        cnt += 1
    324440                       
    325                         if (not subprofile):
     441                        if not subprofile:
    326442                                options.append(name)
    327                                 opt2path[cnt] = (canon, None)
     443                                opt2path[cnt] = [name]
    328444                                cnt += 1
    329445       
    330446        (button, value) = xtui.choice_window(screen, 'Load preconfigured defaults', 'Choose configuration profile', options, None)
    331447       
    332         if (button == 'cancel'):
     448        if button == 'cancel':
    333449                return None
    334450       
    335         read_defaults(opt2path[value][0], defaults)
    336         if (opt2path[value][1] != None):
    337                 read_defaults(opt2path[value][1], defaults)
     451        return opt2path[value]
     452
     453## Read presets from a configuration profile.
     454#
     455# @param profile Profile to load from (a list of string components)
     456# @param config  Output configuration
     457#
     458def read_presets(profile, config):
     459        path = os.path.join(PRESETS_DIR, profile[0], MAKEFILE)
     460        read_config(path, config)
     461       
     462        if len(profile) > 1:
     463                path = os.path.join(PRESETS_DIR, profile[0], profile[1], MAKEFILE)
     464                read_config(path, config)
     465
     466## Parse profile name (relative OS path) into a list of components.
     467#
     468# @param profile_name Relative path (using OS separator)
     469# @return             List of components
     470#
     471def parse_profile_name(profile_name):
     472        profile = []
     473       
     474        head, tail = os.path.split(profile_name)
     475        if head != '':
     476                profile.append(head)
     477       
     478        profile.append(tail)
     479        return profile
    338480
    339481def main():
    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)
     482        profile = None
     483        config = {}
     484        rules = []
     485       
     486        # Parse rules file
     487        parse_rules(RULES_FILE, rules)
     488       
     489        # Input configuration file can be specified on command line
     490        # otherwise configuration from previous run is used.
     491        if len(sys.argv) >= 4:
     492                profile = parse_profile_name(sys.argv[3])
     493                read_presets(profile, config)
     494        elif os.path.exists(MAKEFILE):
     495                read_config(MAKEFILE, config)
     496       
     497        # Default mode: check values and regenerate configuration files
     498        if (len(sys.argv) >= 3) and (sys.argv[2] == 'default'):
     499                if (infer_verify_choices(config, rules)):
     500                        create_output(MAKEFILE, MACROS, config, rules)
    354501                        return 0
    355502       
    356         # Check mode: only check defaults
    357         if ((len(sys.argv) >= 3) and (sys.argv[2] == 'check')):
    358                 if (check_choices(defaults, ask_names)):
     503        # Hands-off mode: check values and regenerate configuration files,
     504        # but no interactive fallback
     505        if (len(sys.argv) >= 3) and (sys.argv[2] == 'hands-off'):
     506                # We deliberately test sys.argv >= 4 because we do not want
     507                # to read implicitly any possible previous run configuration
     508                if len(sys.argv) < 4:
     509                        sys.stderr.write("Configuration error: No presets specified\n")
     510                        return 2
     511               
     512                if (infer_verify_choices(config, rules)):
     513                        create_output(MAKEFILE, MACROS, config, rules)
     514                        return 0
     515               
     516                sys.stderr.write("Configuration error: The presets are ambiguous\n")
     517                return 1
     518       
     519        # Check mode: only check configuration
     520        if (len(sys.argv) >= 3) and (sys.argv[2] == 'check'):
     521                if infer_verify_choices(config, rules):
    359522                        return 0
    360523                return 1
     
    366529                while True:
    367530                       
    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
     531                        # Cancel out all values which have to be deduced
     532                        for varname, vartype, name, choices, cond in rules:
     533                                if (vartype == 'y') and (varname in config) and (config[varname] == '*'):
     534                                        config[varname] = None
    372535                       
    373536                        options = []
     
    377540                        options.append("  --- Load preconfigured defaults ... ")
    378541                       
    379                         for varname, vartype, name, choices, cond in ask_names:
    380                                
    381                                 if ((cond) and (not check_condition(cond, defaults, ask_names))):
     542                        for rule in rules:
     543                                varname, vartype, name, choices, cond = rule
     544                               
     545                                if cond and (not check_condition(cond, config, rules)):
    382546                                        continue
    383547                               
    384                                 if (varname == selname):
     548                                if varname == selname:
    385549                                        position = cnt
    386550                               
    387                                 if (not varname in defaults):
    388                                         default = None
     551                                if not varname in config:
     552                                        value = None
    389553                                else:
    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] = '*'
     554                                        value = config[varname]
     555                               
     556                                if not validate_rule_value(rule, value):
     557                                        value = None
     558                               
     559                                default = get_default_rule(rule)
     560                                if default != None:
     561                                        if value == None:
     562                                                value = default
     563                                        config[varname] = value
     564                               
     565                                option = get_rule_option(rule, value)
     566                                if option != None:
     567                                        options.append(option)
     568                                else:
    409569                                        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))
    423                                 else:
    424                                         raise RuntimeError("Unknown variable type: %s" % vartype)
    425570                               
    426571                                opt2row[cnt] = (varname, vartype, name, choices)
     
    433578                        (button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
    434579                       
    435                         if (button == 'cancel'):
     580                        if button == 'cancel':
    436581                                return 'Configuration canceled'
    437582                       
    438                         if (button == 'done'):
    439                                 if (check_choices(defaults, ask_names)):
     583                        if button == 'done':
     584                                if (infer_verify_choices(config, rules)):
    440585                                        break
    441586                                else:
     
    443588                                        continue
    444589                       
    445                         if (value == 0):
    446                                 read_preconfigured(PRECONF, MAKEFILE, screen, defaults)
     590                        if value == 0:
     591                                profile = choose_profile(PRESETS_DIR, MAKEFILE, screen, config)
     592                                if profile != None:
     593                                        read_presets(profile, config)
    447594                                position = 1
    448595                                continue
    449596                       
    450597                        position = None
    451                         if (not value in opt2row):
     598                        if not value in opt2row:
    452599                                raise RuntimeError("Error selecting value: %s" % value)
    453600                       
    454601                        (selname, seltype, name, choices) = opt2row[value]
    455602                       
    456                         if (not selname in defaults):
    457                                         default = None
     603                        if not selname in config:
     604                                value = None
    458605                        else:
    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'
     606                                value = config[selname]
     607                       
     608                        if seltype == 'choice':
     609                                config[selname] = subchoice(screen, name, choices, value)
     610                        elif (seltype == 'y/n') or (seltype == 'n/y'):
     611                                if config[selname] == 'y':
     612                                        config[selname] = 'n'
    466613                                else:
    467                                         defaults[selname] = 'y'
     614                                        config[selname] = 'y'
    468615        finally:
    469616                xtui.screen_done(screen)
    470617       
    471         create_output(MAKEFILE, MACROS, defaults, ask_names)
     618        create_output(MAKEFILE, MACROS, config, rules)
    472619        return 0
    473620
Note: See TracChangeset for help on using the changeset viewer.