Changeset 3e828ea in mainline for tools/config.py


Ignore:
Timestamp:
2019-09-23T12:49:29Z (6 years ago)
Author:
Jiri Svoboda <jiri@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
9be2358
Parents:
9259d20 (diff), 1a4ec93f (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
git-author:
Jiri Svoboda <jiri@…> (2019-09-22 12:49:07)
git-committer:
Jiri Svoboda <jiri@…> (2019-09-23 12:49:29)
Message:

Merge changes from master, especially Meson build

File:
1 edited

Legend:

Unmodified
Added
Removed
  • tools/config.py

    r9259d20 r3e828ea  
    4242import random
    4343
    44 RULES_FILE = sys.argv[1]
     44ARGPOS_RULES = 1
     45ARGPOS_PRESETS_DIR = 2
     46ARGPOS_CHOICE = 3
     47ARGPOS_PRESET = 4
     48ARGPOS_MASK_PLATFORM = 3
     49
     50RULES_FILE = sys.argv[ARGPOS_RULES]
    4551MAKEFILE = 'Makefile.config'
    4652MACROS = 'config.h'
    47 PRESETS_DIR = 'defaults'
     53PRESETS_DIR = sys.argv[ARGPOS_PRESETS_DIR]
     54
     55class 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
     84class 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)
    48197
    49198def read_config(fname, config):
     
    59208        inf.close()
    60209
    61 def 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 
    91 def 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 
    133210def parse_rules(fname, rules):
    134211        "Parse rules file"
     
    149226
    150227                        cond = res.group(1)
     228                        if cond:
     229                                cond = CondParser(cond).parse()
    151230                        varname = res.group(2)
    152231                        vartype = res.group(3)
     
    232311                varname, vartype, name, choices, cond = rule
    233312
    234                 if cond and (not check_condition(cond, config, rules)):
     313                if cond and not cond.evaluate(config):
    235314                        continue
    236315
     
    279358
    280359        # First check that this rule would make sense
    281         if cond:
    282                 if not check_condition(cond, config, rules):
    283                         return random_choices(config, rules, start_index + 1)
     360        if cond and not cond.evaluate(config):
     361                return random_choices(config, rules, start_index + 1)
    284362
    285363        # Remember previous choices for backtracking
     
    460538
    461539        try:
    462                 version = subprocess.Popen(['git', 'log', '-1', '--pretty=%h'], stdout = subprocess.PIPE).communicate()[0].decode().strip()
     540                version = subprocess.Popen(['git', '-C', os.path.dirname(RULES_FILE), 'log', '-1', '--pretty=%h'], stdout = subprocess.PIPE).communicate()[0].decode().strip()
    463541                sys.stderr.write("ok\n")
    464542        except:
     
    487565
    488566        for varname, vartype, name, choices, cond in rules:
    489                 if cond and (not check_condition(cond, config, rules)):
     567                if cond and not cond.evaluate(config):
    490568                        continue
    491569
     
    514592        outmk.write('TIMESTAMP_UNIX = %d\n' % timestamp_unix)
    515593        outmc.write('#define TIMESTAMP_UNIX %d\n' % timestamp_unix)
    516         defs += ' "-DTIMESTAMP_UNIX=%d"\n' % timestamp_unix
     594        defs += ' "-DTIMESTAMP_UNIX=%d"' % timestamp_unix
    517595
    518596        outmk.write('TIMESTAMP = %s\n' % timestamp)
    519597        outmc.write('#define TIMESTAMP %s\n' % timestamp)
    520         defs += ' "-DTIMESTAMP=%s"\n' % timestamp
    521 
    522         outmk.write(defs)
     598        defs += ' "-DTIMESTAMP=%s"' % timestamp
     599
     600        outmk.write('%s\n' % defs)
    523601
    524602        outmk.close()
     
    604682        parse_rules(RULES_FILE, rules)
    605683
     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
    606696        # Input configuration file can be specified on command line
    607697        # otherwise configuration from previous run is used.
    608         if len(sys.argv) >= 4:
    609                 profile = parse_profile_name(sys.argv[3])
     698        if preset is not None:
     699                profile = parse_profile_name(preset)
    610700                read_presets(profile, config)
    611701        elif os.path.exists(MAKEFILE):
     
    613703
    614704        # Default mode: check values and regenerate configuration files
    615         if (len(sys.argv) >= 3) and (sys.argv[2] == 'default'):
     705        if choice == 'default':
    616706                if (infer_verify_choices(config, rules)):
    617707                        preprocess_config(config, rules)
     
    621711        # Hands-off mode: check values and regenerate configuration files,
    622712        # but no interactive fallback
    623         if (len(sys.argv) >= 3) and (sys.argv[2] == 'hands-off'):
    624                 # We deliberately test sys.argv >= 4 because we do not want
     713        if choice == 'hands-off':
     714                # We deliberately test this because we do not want
    625715                # to read implicitly any possible previous run configuration
    626                 if len(sys.argv) < 4:
     716                if preset is None:
    627717                        sys.stderr.write("Configuration error: No presets specified\n")
    628718                        return 2
     
    637727
    638728        # Check mode: only check configuration
    639         if (len(sys.argv) >= 3) and (sys.argv[2] == 'check'):
     729        if choice == 'check':
    640730                if infer_verify_choices(config, rules):
    641731                        return 0
     
    643733
    644734        # Random mode
    645         if (len(sys.argv) == 3) and (sys.argv[2] == 'random'):
     735        if choice == 'random':
    646736                ok = random_choices(config, rules, 0)
    647737                if not ok:
     
    669759                        options = []
    670760                        opt2row = {}
    671                         cnt = 1
    672 
    673                         options.append("  --- Load preconfigured defaults ... ")
     761                        cnt = 0
     762
     763                        if not mask_platform:
     764                                cnt += 1
     765                                options.append("  --- Load preconfigured defaults ... ")
    674766
    675767                        for rule in rules:
    676768                                varname, vartype, name, choices, cond = rule
    677769
    678                                 if cond and (not check_condition(cond, config, rules)):
     770                                if cond and not cond.evaluate(config):
    679771                                        continue
     772
     773                                if mask_platform and (varname == "PLATFORM" or varname == "MACHINE" or varname == "COMPILER"):
     774                                        rule = varname, vartype, "(locked) " + name, choices, cond
    680775
    681776                                if varname == selname:
     
    725820                                        continue
    726821
    727                         if value == 0:
     822                        if value == 0 and not mask_platform:
    728823                                profile = choose_profile(PRESETS_DIR, MAKEFILE, screen, config)
    729824                                if profile != None:
     
    742837                        else:
    743838                                value = config[selname]
     839
     840                        if mask_platform and (selname == "PLATFORM" or selname == "MACHINE" or selname == "COMPILER"):
     841                                        continue
    744842
    745843                        if seltype == 'choice':
Note: See TracChangeset for help on using the changeset viewer.