Changes in tools/config.py [3f1a481:a35b458] in mainline


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • tools/config.py

    r3f1a481 ra35b458  
    4242import random
    4343
    44 ARGPOS_RULES = 1
    45 ARGPOS_PRESETS_DIR = 2
    46 ARGPOS_CHOICE = 3
    47 ARGPOS_PRESET = 4
    48 ARGPOS_MASK_PLATFORM = 3
    49 
    50 RULES_FILE = sys.argv[ARGPOS_RULES]
     44RULES_FILE = sys.argv[1]
    5145MAKEFILE = 'Makefile.config'
    5246MACROS = 'config.h'
    53 PRESETS_DIR = sys.argv[ARGPOS_PRESETS_DIR]
    54 
    55 class BinaryOp:
    56         def __init__(self, operator, left, right):
    57                 assert operator in ('&', '|', '=', '!=')
    58 
    59                 self._operator = operator
    60                 self._left = left
    61                 self._right = right
    62 
    63         def evaluate(self, config):
    64                 if self._operator == '&':
    65                         return self._left.evaluate(config) and \
    66                             self._right.evaluate(config)
    67                 if self._operator == '|':
    68                         return self._left.evaluate(config) or \
    69                             self._right.evaluate(config)
    70 
    71                 # '=' or '!='
    72                 if not self._left in config:
    73                         config_val = ''
    74                 else:
    75                         config_val = config[self._left]
    76                         if config_val == '*':
    77                                 config_val = 'y'
    78 
    79                 if self._operator == '=':
    80                         return self._right == config_val
    81                 return self._right != config_val
    82 
    83 # Expression parser
    84 class CondParser:
    85         TOKEN_EOE = 0
    86         TOKEN_SPECIAL = 1
    87         TOKEN_STRING = 2
    88 
    89         def __init__(self, text):
    90                 self._text = text
    91 
    92         def parse(self):
    93                 self._position = -1
    94                 self._next_char()
    95                 self._next_token()
    96 
    97                 res = self._parse_expr()
    98                 if self._token_type != self.TOKEN_EOE:
    99                         self._error("Expected end of expression")
    100                 return res
    101 
    102         def _next_char(self):
    103                 self._position += 1
    104                 if self._position >= len(self._text):
    105                         self._char = None
    106                 else:
    107                         self._char = self._text[self._position]
    108                 self._is_special_char = self._char in \
    109                     ('&', '|', '=', '!', '(', ')')
    110 
    111         def _error(self, msg):
    112                 raise RuntimeError("Error parsing expression: %s:\n%s\n%s^" %
    113                     (msg, self._text, " " * self._token_position))
    114 
    115         def _next_token(self):
    116                 self._token_position = self._position
    117 
    118                 # End of expression
    119                 if self._char == None:
    120                         self._token = None
    121                         self._token_type = self.TOKEN_EOE
    122                         return
    123 
    124                 # '&', '|', '=', '!=', '(', ')'
    125                 if self._is_special_char:
    126                         self._token = self._char
    127                         self._next_char()
    128                         if self._token == '!':
    129                                 if self._char != '=':
    130                                         self._error("Expected '='")
    131                                 self._token += self._char
    132                                 self._next_char()
    133                         self._token_type = self.TOKEN_SPECIAL
    134                         return
    135 
    136                 # <var> or <val>
    137                 self._token = ''
    138                 self._token_type = self.TOKEN_STRING
    139                 while True:
    140                         self._token += self._char
    141                         self._next_char()
    142                         if self._is_special_char or self._char == None:
    143                                 break
    144 
    145         def _parse_expr(self):
    146                 """ <expr> ::= <or_expr> ('&' <or_expr>)* """
    147 
    148                 left = self._parse_or_expr()
    149                 while self._token == '&':
    150                         self._next_token()
    151                         left = BinaryOp('&', left, self._parse_or_expr())
    152                 return left
    153 
    154         def _parse_or_expr(self):
    155                 """ <or_expr> ::= <factor> ('|' <factor>)* """
    156 
    157                 left = self._parse_factor()
    158                 while self._token == '|':
    159                         self._next_token()
    160                         left = BinaryOp('|', left, self._parse_factor())
    161                 return left
    162 
    163         def _parse_factor(self):
    164                 """ <factor> ::= <var> <cond> | '(' <expr> ')' """
    165 
    166                 if self._token == '(':
    167                         self._next_token()
    168                         res = self._parse_expr()
    169                         if self._token != ')':
    170                                 self._error("Expected ')'")
    171                         self._next_token()
    172                         return res
    173 
    174                 if self._token_type == self.TOKEN_STRING:
    175                         var = self._token
    176                         self._next_token()
    177                         return self._parse_cond(var)
    178 
    179                 self._error("Expected '(' or <var>")
    180 
    181         def _parse_cond(self, var):
    182                 """ <cond> ::= '=' <val> | '!=' <val> """
    183 
    184                 if self._token not in ('=', '!='):
    185                         self._error("Expected '=' or '!='")
    186 
    187                 oper = self._token
    188                 self._next_token()
    189 
    190                 if self._token_type != self.TOKEN_STRING:
    191                         self._error("Expected <val>")
    192 
    193                 val = self._token
    194                 self._next_token()
    195 
    196                 return BinaryOp(oper, var, val)
     47PRESETS_DIR = 'defaults'
    19748
    19849def read_config(fname, config):
     
    20859        inf.close()
    20960
     61def check_condition(text, config, rules):
     62        "Check that the condition specified on input line is True (only CNF and DNF is supported)"
     63
     64        ctype = 'cnf'
     65
     66        if (')|' in text) or ('|(' in text):
     67                ctype = 'dnf'
     68
     69        if ctype == 'cnf':
     70                conds = text.split('&')
     71        else:
     72                conds = text.split('|')
     73
     74        for cond in conds:
     75                if cond.startswith('(') and cond.endswith(')'):
     76                        cond = cond[1:-1]
     77
     78                inside = check_inside(cond, config, ctype)
     79
     80                if (ctype == 'cnf') and (not inside):
     81                        return False
     82
     83                if (ctype == 'dnf') and inside:
     84                        return True
     85
     86        if ctype == 'cnf':
     87                return True
     88
     89        return False
     90
     91def check_inside(text, config, ctype):
     92        "Check for condition"
     93
     94        if ctype == 'cnf':
     95                conds = text.split('|')
     96        else:
     97                conds = text.split('&')
     98
     99        for cond in conds:
     100                res = re.match(r'^(.*?)(!?=)(.*)$', cond)
     101                if not res:
     102                        raise RuntimeError("Invalid condition: %s" % cond)
     103
     104                condname = res.group(1)
     105                oper = res.group(2)
     106                condval = res.group(3)
     107
     108                if not condname in config:
     109                        varval = ''
     110                else:
     111                        varval = config[condname]
     112                        if (varval == '*'):
     113                                varval = 'y'
     114
     115                if ctype == 'cnf':
     116                        if (oper == '=') and (condval == varval):
     117                                return True
     118
     119                        if (oper == '!=') and (condval != varval):
     120                                return True
     121                else:
     122                        if (oper == '=') and (condval != varval):
     123                                return False
     124
     125                        if (oper == '!=') and (condval == varval):
     126                                return False
     127
     128        if ctype == 'cnf':
     129                return False
     130
     131        return True
     132
    210133def parse_rules(fname, rules):
    211134        "Parse rules file"
     
    226149
    227150                        cond = res.group(1)
    228                         if cond:
    229                                 cond = CondParser(cond).parse()
    230151                        varname = res.group(2)
    231152                        vartype = res.group(3)
     
    311232                varname, vartype, name, choices, cond = rule
    312233
    313                 if cond and not cond.evaluate(config):
     234                if cond and (not check_condition(cond, config, rules)):
    314235                        continue
    315236
     
    358279
    359280        # First check that this rule would make sense
    360         if cond and not cond.evaluate(config):
    361                 return random_choices(config, rules, start_index + 1)
     281        if cond:
     282                if not check_condition(cond, config, rules):
     283                        return random_choices(config, rules, start_index + 1)
    362284
    363285        # Remember previous choices for backtracking
     
    538460
    539461        try:
    540                 version = subprocess.Popen(['git', '-C', os.path.dirname(RULES_FILE), 'log', '-1', '--pretty=%h'], stdout = subprocess.PIPE).communicate()[0].decode().strip()
     462                version = subprocess.Popen(['git', 'log', '-1', '--pretty=%h'], stdout = subprocess.PIPE).communicate()[0].decode().strip()
    541463                sys.stderr.write("ok\n")
    542464        except:
     
    565487
    566488        for varname, vartype, name, choices, cond in rules:
    567                 if cond and not cond.evaluate(config):
     489                if cond and (not check_condition(cond, config, rules)):
    568490                        continue
    569491
     
    592514        outmk.write('TIMESTAMP_UNIX = %d\n' % timestamp_unix)
    593515        outmc.write('#define TIMESTAMP_UNIX %d\n' % timestamp_unix)
    594         defs += ' "-DTIMESTAMP_UNIX=%d"' % timestamp_unix
     516        defs += ' "-DTIMESTAMP_UNIX=%d"\n' % timestamp_unix
    595517
    596518        outmk.write('TIMESTAMP = %s\n' % timestamp)
    597519        outmc.write('#define TIMESTAMP %s\n' % timestamp)
    598         defs += ' "-DTIMESTAMP=%s"' % timestamp
    599 
    600         outmk.write('%s\n' % defs)
     520        defs += ' "-DTIMESTAMP=%s"\n' % timestamp
     521
     522        outmk.write(defs)
    601523
    602524        outmk.close()
     
    682604        parse_rules(RULES_FILE, rules)
    683605
    684         if len(sys.argv) > ARGPOS_CHOICE:
    685                 choice = sys.argv[ARGPOS_CHOICE]
    686         else:
    687                 choice = None
    688 
    689         if len(sys.argv) > ARGPOS_PRESET:
    690                 preset = sys.argv[ARGPOS_PRESET]
    691         else:
    692                 preset = None
    693 
    694         mask_platform = (len(sys.argv) > ARGPOS_MASK_PLATFORM and sys.argv[ARGPOS_MASK_PLATFORM] == "--mask-platform")
    695 
    696606        # Input configuration file can be specified on command line
    697607        # otherwise configuration from previous run is used.
    698         if preset is not None:
    699                 profile = parse_profile_name(preset)
     608        if len(sys.argv) >= 4:
     609                profile = parse_profile_name(sys.argv[3])
    700610                read_presets(profile, config)
    701611        elif os.path.exists(MAKEFILE):
     
    703613
    704614        # Default mode: check values and regenerate configuration files
    705         if choice == 'default':
     615        if (len(sys.argv) >= 3) and (sys.argv[2] == 'default'):
    706616                if (infer_verify_choices(config, rules)):
    707617                        preprocess_config(config, rules)
     
    711621        # Hands-off mode: check values and regenerate configuration files,
    712622        # but no interactive fallback
    713         if choice == 'hands-off':
    714                 # We deliberately test this because we do not want
     623        if (len(sys.argv) >= 3) and (sys.argv[2] == 'hands-off'):
     624                # We deliberately test sys.argv >= 4 because we do not want
    715625                # to read implicitly any possible previous run configuration
    716                 if preset is None:
     626                if len(sys.argv) < 4:
    717627                        sys.stderr.write("Configuration error: No presets specified\n")
    718628                        return 2
     
    727637
    728638        # Check mode: only check configuration
    729         if choice == 'check':
     639        if (len(sys.argv) >= 3) and (sys.argv[2] == 'check'):
    730640                if infer_verify_choices(config, rules):
    731641                        return 0
     
    733643
    734644        # Random mode
    735         if choice == 'random':
     645        if (len(sys.argv) == 3) and (sys.argv[2] == 'random'):
    736646                ok = random_choices(config, rules, 0)
    737647                if not ok:
     
    759669                        options = []
    760670                        opt2row = {}
    761                         cnt = 0
    762 
    763                         if not mask_platform:
    764                                 cnt += 1
    765                                 options.append("  --- Load preconfigured defaults ... ")
     671                        cnt = 1
     672
     673                        options.append("  --- Load preconfigured defaults ... ")
    766674
    767675                        for rule in rules:
    768676                                varname, vartype, name, choices, cond = rule
    769677
    770                                 if cond and not cond.evaluate(config):
     678                                if cond and (not check_condition(cond, config, rules)):
    771679                                        continue
    772 
    773                                 if mask_platform and (varname == "PLATFORM" or varname == "MACHINE" or varname == "COMPILER"):
    774                                         rule = varname, vartype, "(locked) " + name, choices, cond
    775680
    776681                                if varname == selname:
     
    820725                                        continue
    821726
    822                         if value == 0 and not mask_platform:
     727                        if value == 0:
    823728                                profile = choose_profile(PRESETS_DIR, MAKEFILE, screen, config)
    824729                                if profile != None:
     
    837742                        else:
    838743                                value = config[selname]
    839 
    840                         if mask_platform and (selname == "PLATFORM" or selname == "MACHINE" or selname == "COMPILER"):
    841                                         continue
    842744
    843745                        if seltype == 'choice':
Note: See TracChangeset for help on using the changeset viewer.