Changes in tools/config.py [a35b458:1b20da0] in mainline


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • tools/config.py

    ra35b458 r1b20da0  
    4949def read_config(fname, config):
    5050        "Read saved values from last configuration run or a preset file"
    51 
     51       
    5252        inf = open(fname, 'r')
    53 
     53       
    5454        for line in inf:
    5555                res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
    5656                if res:
    5757                        config[res.group(1)] = res.group(2)
    58 
     58       
    5959        inf.close()
    6060
    6161def check_condition(text, config, rules):
    6262        "Check that the condition specified on input line is True (only CNF and DNF is supported)"
    63 
     63       
    6464        ctype = 'cnf'
    65 
     65       
    6666        if (')|' in text) or ('|(' in text):
    6767                ctype = 'dnf'
    68 
     68       
    6969        if ctype == 'cnf':
    7070                conds = text.split('&')
    7171        else:
    7272                conds = text.split('|')
    73 
     73       
    7474        for cond in conds:
    7575                if cond.startswith('(') and cond.endswith(')'):
    7676                        cond = cond[1:-1]
    77 
     77               
    7878                inside = check_inside(cond, config, ctype)
    79 
     79               
    8080                if (ctype == 'cnf') and (not inside):
    8181                        return False
    82 
     82               
    8383                if (ctype == 'dnf') and inside:
    8484                        return True
    85 
     85       
    8686        if ctype == 'cnf':
    8787                return True
    88 
     88       
    8989        return False
    9090
    9191def check_inside(text, config, ctype):
    9292        "Check for condition"
    93 
     93       
    9494        if ctype == 'cnf':
    9595                conds = text.split('|')
    9696        else:
    9797                conds = text.split('&')
    98 
     98       
    9999        for cond in conds:
    100100                res = re.match(r'^(.*?)(!?=)(.*)$', cond)
    101101                if not res:
    102102                        raise RuntimeError("Invalid condition: %s" % cond)
    103 
     103               
    104104                condname = res.group(1)
    105105                oper = res.group(2)
    106106                condval = res.group(3)
    107 
     107               
    108108                if not condname in config:
    109109                        varval = ''
     
    112112                        if (varval == '*'):
    113113                                varval = 'y'
    114 
     114               
    115115                if ctype == 'cnf':
    116116                        if (oper == '=') and (condval == varval):
    117117                                return True
    118 
     118               
    119119                        if (oper == '!=') and (condval != varval):
    120120                                return True
     
    122122                        if (oper == '=') and (condval != varval):
    123123                                return False
    124 
     124                       
    125125                        if (oper == '!=') and (condval == varval):
    126126                                return False
    127 
     127       
    128128        if ctype == 'cnf':
    129129                return False
    130 
     130       
    131131        return True
    132132
    133133def parse_rules(fname, rules):
    134134        "Parse rules file"
    135 
     135       
    136136        inf = open(fname, 'r')
    137 
     137       
    138138        name = ''
    139139        choices = []
    140 
     140       
    141141        for line in inf:
    142 
     142               
    143143                if line.startswith('!'):
    144144                        # Ask a question
    145145                        res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
    146 
     146                       
    147147                        if not res:
    148148                                raise RuntimeError("Weird line: %s" % line)
    149 
     149                       
    150150                        cond = res.group(1)
    151151                        varname = res.group(2)
    152152                        vartype = res.group(3)
    153 
     153                       
    154154                        rules.append((varname, vartype, name, choices, cond))
    155155                        name = ''
    156156                        choices = []
    157157                        continue
    158 
     158               
    159159                if line.startswith('@'):
    160160                        # Add new line into the 'choices' array
    161161                        res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
    162 
     162                       
    163163                        if not res:
    164164                                raise RuntimeError("Bad line: %s" % line)
    165 
     165                       
    166166                        choices.append((res.group(2), res.group(3)))
    167167                        continue
    168 
     168               
    169169                if line.startswith('%'):
    170170                        # Name of the option
    171171                        name = line[1:].strip()
    172172                        continue
    173 
     173               
    174174                if line.startswith('#') or (line == '\n'):
    175175                        # Comment or empty line
    176176                        continue
    177 
    178 
     177               
     178               
    179179                raise RuntimeError("Unknown syntax: %s" % line)
    180 
     180       
    181181        inf.close()
    182182
    183183def yes_no(default):
    184184        "Return '*' if yes, ' ' if no"
    185 
     185       
    186186        if default == 'y':
    187187                return '*'
    188 
     188       
    189189        return ' '
    190190
    191191def subchoice(screen, name, choices, default):
    192192        "Return choice of choices"
    193 
     193       
    194194        maxkey = 0
    195195        for key, val in choices:
     
    197197                if (length > maxkey):
    198198                        maxkey = length
    199 
     199       
    200200        options = []
    201201        position = None
     
    204204                if (default) and (key == default):
    205205                        position = cnt
    206 
     206               
    207207                options.append(" %-*s  %s " % (maxkey, key, val))
    208208                cnt += 1
    209 
     209       
    210210        (button, value) = xtui.choice_window(screen, name, 'Choose value', options, position)
    211 
     211       
    212212        if button == 'cancel':
    213213                return None
    214 
     214       
    215215        return choices[value][0]
    216216
     
    228228def infer_verify_choices(config, rules):
    229229        "Infer and verify configuration values."
    230 
     230       
    231231        for rule in rules:
    232232                varname, vartype, name, choices, cond = rule
    233 
     233               
    234234                if cond and (not check_condition(cond, config, rules)):
    235235                        continue
    236 
     236               
    237237                if not varname in config:
    238238                        value = None
    239239                else:
    240240                        value = config[varname]
    241 
     241               
    242242                if not validate_rule_value(rule, value):
    243243                        value = None
    244 
     244               
    245245                default = get_default_rule(rule)
    246 
     246               
    247247                #
    248248                # If we don't have a value but we do have
     
    252252                        value = default
    253253                        config[varname] = default
    254 
     254               
    255255                if not varname in config:
    256256                        return False
    257 
     257       
    258258        return True
    259259
     
    275275        if start_index >= len(rules):
    276276                return True
    277 
     277       
    278278        varname, vartype, name, choices, cond = rules[start_index]
    279279
     
    282282                if not check_condition(cond, config, rules):
    283283                        return random_choices(config, rules, start_index + 1)
    284 
     284       
    285285        # Remember previous choices for backtracking
    286286        yes_no = 0
    287287        choices_indexes = range(0, len(choices))
    288288        random.shuffle(choices_indexes)
    289 
     289       
    290290        # Remember current configuration value
    291291        old_value = None
     
    294294        except KeyError:
    295295                old_value = None
    296 
     296       
    297297        # For yes/no choices, we ran the loop at most 2 times, for select
    298298        # choices as many times as there are options.
     
    320320                else:
    321321                        raise RuntimeError("Unknown variable type: %s" % vartype)
    322 
     322       
    323323                config[varname] = value
    324 
     324               
    325325                ok = random_choices(config, rules, start_index + 1)
    326326                if ok:
    327327                        return True
    328 
     328               
    329329                try_counter = try_counter + 1
    330 
     330       
    331331        # Restore the old value and backtrack
    332332        # (need to delete to prevent "ghost" variables that do not exist under
     
    335335        if old_value is None:
    336336                del config[varname]
    337 
     337       
    338338        return random_choices(config, rules, start_index + 1)
    339 
     339       
    340340
    341341## Get default value from a rule.
    342342def get_default_rule(rule):
    343343        varname, vartype, name, choices, cond = rule
    344 
     344       
    345345        default = None
    346 
     346       
    347347        if vartype == 'choice':
    348348                # If there is just one option, use it
     
    359359        else:
    360360                raise RuntimeError("Unknown variable type: %s" % vartype)
    361 
     361       
    362362        return default
    363363
     
    371371def get_rule_option(rule, value):
    372372        varname, vartype, name, choices, cond = rule
    373 
     373       
    374374        option = None
    375 
     375       
    376376        if vartype == 'choice':
    377377                # If there is just one option, don't ask
     
    391391        else:
    392392                raise RuntimeError("Unknown variable type: %s" % vartype)
    393 
     393       
    394394        return option
    395395
     
    403403def validate_rule_value(rule, value):
    404404        varname, vartype, name, choices, cond = rule
    405 
     405       
    406406        if value == None:
    407407                return True
    408 
     408       
    409409        if vartype == 'choice':
    410410                if not value in [choice[0] for choice in choices]:
     
    424424        else:
    425425                raise RuntimeError("Unknown variable type: %s" % vartype)
    426 
     426       
    427427        return True
    428428
    429429def preprocess_config(config, rules):
    430430        "Preprocess configuration"
    431 
     431       
    432432        varname_mode = 'CONFIG_BFB_MODE'
    433433        varname_width = 'CONFIG_BFB_WIDTH'
    434434        varname_height = 'CONFIG_BFB_HEIGHT'
    435 
     435       
    436436        if varname_mode in config:
    437437                mode = config[varname_mode].partition('x')
    438 
     438               
    439439                config[varname_width] = mode[0]
    440440                rules.append((varname_width, 'choice', 'Default framebuffer width', None, None))
    441 
     441               
    442442                config[varname_height] = mode[2]
    443443                rules.append((varname_height, 'choice', 'Default framebuffer height', None, None))
     
    445445def create_output(mkname, mcname, config, rules):
    446446        "Create output configuration"
    447 
     447       
    448448        varname_strip = 'CONFIG_STRIP_REVISION_INFO'
    449449        strip_rev_info = (varname_strip in config) and (config[varname_strip] == 'y')
    450 
     450       
    451451        if strip_rev_info:
    452452                timestamp_unix = int(0)
     
    454454                # TODO: Use commit timestamp instead of build time.
    455455                timestamp_unix = int(time.time())
    456 
     456       
    457457        timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp_unix))
    458 
     458       
    459459        sys.stderr.write("Fetching current revision identifier ... ")
    460 
     460       
    461461        try:
    462462                version = subprocess.Popen(['git', 'log', '-1', '--pretty=%h'], stdout = subprocess.PIPE).communicate()[0].decode().strip()
     
    465465                version = None
    466466                sys.stderr.write("failed\n")
    467 
     467       
    468468        if (not strip_rev_info) and (version is not None):
    469469                revision = version
    470470        else:
    471471                revision = None
    472 
     472       
    473473        outmk = open(mkname, 'w')
    474474        outmc = open(mcname, 'w')
    475 
     475       
    476476        outmk.write('#########################################\n')
    477477        outmk.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
    478478        outmk.write('## Generated by: tools/config.py       ##\n')
    479479        outmk.write('#########################################\n\n')
    480 
     480       
    481481        outmc.write('/***************************************\n')
    482482        outmc.write(' * AUTO-GENERATED FILE, DO NOT EDIT!!! *\n')
    483483        outmc.write(' * Generated by: tools/config.py       *\n')
    484484        outmc.write(' ***************************************/\n\n')
    485 
     485       
    486486        defs = 'CONFIG_DEFS ='
    487 
     487       
    488488        for varname, vartype, name, choices, cond in rules:
    489489                if cond and (not check_condition(cond, config, rules)):
    490490                        continue
    491 
     491               
    492492                if not varname in config:
    493493                        value = ''
     
    496496                        if (value == '*'):
    497497                                value = 'y'
    498 
     498               
    499499                outmk.write('# %s\n%s = %s\n\n' % (name, varname, value))
    500 
     500               
    501501                if vartype in ["y", "n", "y/n", "n/y"]:
    502502                        if value == "y":
     
    506506                        outmc.write('/* %s */\n#define %s %s\n#define %s_%s\n\n' % (name, varname, value, varname, value))
    507507                        defs += ' -D%s=%s -D%s_%s' % (varname, value, varname, value)
    508 
     508       
    509509        if revision is not None:
    510510                outmk.write('REVISION = %s\n' % revision)
    511511                outmc.write('#define REVISION %s\n' % revision)
    512512                defs += ' "-DREVISION=%s"' % revision
    513 
     513       
    514514        outmk.write('TIMESTAMP_UNIX = %d\n' % timestamp_unix)
    515515        outmc.write('#define TIMESTAMP_UNIX %d\n' % timestamp_unix)
    516516        defs += ' "-DTIMESTAMP_UNIX=%d"\n' % timestamp_unix
    517 
     517       
    518518        outmk.write('TIMESTAMP = %s\n' % timestamp)
    519519        outmc.write('#define TIMESTAMP %s\n' % timestamp)
    520520        defs += ' "-DTIMESTAMP=%s"\n' % timestamp
    521 
     521       
    522522        outmk.write(defs)
    523 
     523       
    524524        outmk.close()
    525525        outmc.close()
     
    536536        opt2path = {}
    537537        cnt = 0
    538 
     538       
    539539        # Look for profiles
    540540        for name in sorted_dir(root):
    541541                path = os.path.join(root, name)
    542542                canon = os.path.join(path, fname)
    543 
     543               
    544544                if os.path.isdir(path) and os.path.exists(canon) and os.path.isfile(canon):
    545545                        subprofile = False
    546 
     546                       
    547547                        # Look for subprofiles
    548548                        for subname in sorted_dir(path):
    549549                                subpath = os.path.join(path, subname)
    550550                                subcanon = os.path.join(subpath, fname)
    551 
     551                               
    552552                                if os.path.isdir(subpath) and os.path.exists(subcanon) and os.path.isfile(subcanon):
    553553                                        subprofile = True
     
    555555                                        opt2path[cnt] = [name, subname]
    556556                                        cnt += 1
    557 
     557                       
    558558                        if not subprofile:
    559559                                options.append(name)
    560560                                opt2path[cnt] = [name]
    561561                                cnt += 1
    562 
     562       
    563563        (button, value) = xtui.choice_window(screen, 'Load preconfigured defaults', 'Choose configuration profile', options, None)
    564 
     564       
    565565        if button == 'cancel':
    566566                return None
    567 
     567       
    568568        return opt2path[value]
    569569
     
    576576        path = os.path.join(PRESETS_DIR, profile[0], MAKEFILE)
    577577        read_config(path, config)
    578 
     578       
    579579        if len(profile) > 1:
    580580                path = os.path.join(PRESETS_DIR, profile[0], profile[1], MAKEFILE)
     
    588588def parse_profile_name(profile_name):
    589589        profile = []
    590 
     590       
    591591        head, tail = os.path.split(profile_name)
    592592        if head != '':
    593593                profile.append(head)
    594 
     594       
    595595        profile.append(tail)
    596596        return profile
     
    600600        config = {}
    601601        rules = []
    602 
     602       
    603603        # Parse rules file
    604604        parse_rules(RULES_FILE, rules)
    605 
     605       
    606606        # Input configuration file can be specified on command line
    607607        # otherwise configuration from previous run is used.
     
    611611        elif os.path.exists(MAKEFILE):
    612612                read_config(MAKEFILE, config)
    613 
     613       
    614614        # Default mode: check values and regenerate configuration files
    615615        if (len(sys.argv) >= 3) and (sys.argv[2] == 'default'):
     
    618618                        create_output(MAKEFILE, MACROS, config, rules)
    619619                        return 0
    620 
     620       
    621621        # Hands-off mode: check values and regenerate configuration files,
    622622        # but no interactive fallback
     
    627627                        sys.stderr.write("Configuration error: No presets specified\n")
    628628                        return 2
    629 
     629               
    630630                if (infer_verify_choices(config, rules)):
    631631                        preprocess_config(config, rules)
    632632                        create_output(MAKEFILE, MACROS, config, rules)
    633633                        return 0
    634 
     634               
    635635                sys.stderr.write("Configuration error: The presets are ambiguous\n")
    636636                return 1
    637 
     637       
    638638        # Check mode: only check configuration
    639639        if (len(sys.argv) >= 3) and (sys.argv[2] == 'check'):
     
    641641                        return 0
    642642                return 1
    643 
     643       
    644644        # Random mode
    645645        if (len(sys.argv) == 3) and (sys.argv[2] == 'random'):
     
    653653                preprocess_config(config, rules)
    654654                create_output(MAKEFILE, MACROS, config, rules)
    655 
     655               
    656656                return 0
    657 
     657       
    658658        screen = xtui.screen_init()
    659659        try:
     
    661661                position = None
    662662                while True:
    663 
     663                       
    664664                        # Cancel out all values which have to be deduced
    665665                        for varname, vartype, name, choices, cond in rules:
    666666                                if (vartype == 'y') and (varname in config) and (config[varname] == '*'):
    667667                                        config[varname] = None
    668 
     668                       
    669669                        options = []
    670670                        opt2row = {}
    671671                        cnt = 1
    672 
     672                       
    673673                        options.append("  --- Load preconfigured defaults ... ")
    674 
     674                       
    675675                        for rule in rules:
    676676                                varname, vartype, name, choices, cond = rule
    677 
     677                               
    678678                                if cond and (not check_condition(cond, config, rules)):
    679679                                        continue
    680 
     680                               
    681681                                if varname == selname:
    682682                                        position = cnt
    683 
     683                               
    684684                                if not varname in config:
    685685                                        value = None
    686686                                else:
    687687                                        value = config[varname]
    688 
     688                               
    689689                                if not validate_rule_value(rule, value):
    690690                                        value = None
    691 
     691                               
    692692                                default = get_default_rule(rule)
    693 
     693                               
    694694                                #
    695695                                # If we don't have a value but we do have
     
    699699                                        value = default
    700700                                        config[varname] = default
    701 
     701                               
    702702                                option = get_rule_option(rule, value)
    703703                                if option != None:
     
    705705                                else:
    706706                                        continue
    707 
     707                               
    708708                                opt2row[cnt] = (varname, vartype, name, choices)
    709 
     709                               
    710710                                cnt += 1
    711 
     711                       
    712712                        if (position != None) and (position >= len(options)):
    713713                                position = None
    714 
     714                       
    715715                        (button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
    716 
     716                       
    717717                        if button == 'cancel':
    718718                                return 'Configuration canceled'
    719 
     719                       
    720720                        if button == 'done':
    721721                                if (infer_verify_choices(config, rules)):
     
    724724                                        xtui.error_dialog(screen, 'Error', 'Some options have still undefined values. These options are marked with the "?" sign.')
    725725                                        continue
    726 
     726                       
    727727                        if value == 0:
    728728                                profile = choose_profile(PRESETS_DIR, MAKEFILE, screen, config)
     
    731731                                position = 1
    732732                                continue
    733 
     733                       
    734734                        position = None
    735735                        if not value in opt2row:
    736736                                raise RuntimeError("Error selecting value: %s" % value)
    737 
     737                       
    738738                        (selname, seltype, name, choices) = opt2row[value]
    739 
     739                       
    740740                        if not selname in config:
    741741                                value = None
    742742                        else:
    743743                                value = config[selname]
    744 
     744                       
    745745                        if seltype == 'choice':
    746746                                config[selname] = subchoice(screen, name, choices, value)
     
    752752        finally:
    753753                xtui.screen_done(screen)
    754 
     754       
    755755        preprocess_config(config, rules)
    756756        create_output(MAKEFILE, MACROS, config, rules)
Note: See TracChangeset for help on using the changeset viewer.