cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

minikconf.py (23656B)


      1#!/usr/bin/env python3
      2#
      3# Mini-Kconfig parser
      4#
      5# Copyright (c) 2015 Red Hat Inc.
      6#
      7# Authors:
      8#  Paolo Bonzini <pbonzini@redhat.com>
      9#
     10# This work is licensed under the terms of the GNU GPL, version 2
     11# or, at your option, any later version.  See the COPYING file in
     12# the top-level directory.
     13
     14import os
     15import sys
     16import re
     17import random
     18
     19__all__ = [ 'KconfigDataError', 'KconfigParserError',
     20            'KconfigData', 'KconfigParser' ,
     21            'defconfig', 'allyesconfig', 'allnoconfig', 'randconfig' ]
     22
     23def debug_print(*args):
     24    #print('# ' + (' '.join(str(x) for x in args)))
     25    pass
     26
     27# -------------------------------------------
     28# KconfigData implements the Kconfig semantics.  For now it can only
     29# detect undefined symbols, i.e. symbols that were referenced in
     30# assignments or dependencies but were not declared with "config FOO".
     31#
     32# Semantic actions are represented by methods called do_*.  The do_var
     33# method return the semantic value of a variable (which right now is
     34# just its name).
     35# -------------------------------------------
     36
     37class KconfigDataError(Exception):
     38    def __init__(self, msg):
     39        self.msg = msg
     40
     41    def __str__(self):
     42        return self.msg
     43
     44allyesconfig = lambda x: True
     45allnoconfig = lambda x: False
     46defconfig = lambda x: x
     47randconfig = lambda x: random.randint(0, 1) == 1
     48
     49class KconfigData:
     50    class Expr:
     51        def __and__(self, rhs):
     52            return KconfigData.AND(self, rhs)
     53        def __or__(self, rhs):
     54            return KconfigData.OR(self, rhs)
     55        def __invert__(self):
     56            return KconfigData.NOT(self)
     57
     58        # Abstract methods
     59        def add_edges_to(self, var):
     60            pass
     61        def evaluate(self):
     62            assert False
     63
     64    class AND(Expr):
     65        def __init__(self, lhs, rhs):
     66            self.lhs = lhs
     67            self.rhs = rhs
     68        def __str__(self):
     69            return "(%s && %s)" % (self.lhs, self.rhs)
     70
     71        def add_edges_to(self, var):
     72            self.lhs.add_edges_to(var)
     73            self.rhs.add_edges_to(var)
     74        def evaluate(self):
     75            return self.lhs.evaluate() and self.rhs.evaluate()
     76
     77    class OR(Expr):
     78        def __init__(self, lhs, rhs):
     79            self.lhs = lhs
     80            self.rhs = rhs
     81        def __str__(self):
     82            return "(%s || %s)" % (self.lhs, self.rhs)
     83
     84        def add_edges_to(self, var):
     85            self.lhs.add_edges_to(var)
     86            self.rhs.add_edges_to(var)
     87        def evaluate(self):
     88            return self.lhs.evaluate() or self.rhs.evaluate()
     89
     90    class NOT(Expr):
     91        def __init__(self, lhs):
     92            self.lhs = lhs
     93        def __str__(self):
     94            return "!%s" % (self.lhs)
     95
     96        def add_edges_to(self, var):
     97            self.lhs.add_edges_to(var)
     98        def evaluate(self):
     99            return not self.lhs.evaluate()
    100
    101    class Var(Expr):
    102        def __init__(self, name):
    103            self.name = name
    104            self.value = None
    105            self.outgoing = set()
    106            self.clauses_for_var = list()
    107        def __str__(self):
    108            return self.name
    109
    110        def has_value(self):
    111            return not (self.value is None)
    112        def set_value(self, val, clause):
    113            self.clauses_for_var.append(clause)
    114            if self.has_value() and self.value != val:
    115                print("The following clauses were found for " + self.name)
    116                for i in self.clauses_for_var:
    117                    print("    " + str(i), file=sys.stderr)
    118                raise KconfigDataError('contradiction between clauses when setting %s' % self)
    119            debug_print("=> %s is now %s" % (self.name, val))
    120            self.value = val
    121
    122        # depth first search of the dependency graph
    123        def dfs(self, visited, f):
    124            if self in visited:
    125                return
    126            visited.add(self)
    127            for v in self.outgoing:
    128                v.dfs(visited, f)
    129            f(self)
    130
    131        def add_edges_to(self, var):
    132            self.outgoing.add(var)
    133        def evaluate(self):
    134            if not self.has_value():
    135                raise KconfigDataError('cycle found including %s' % self)
    136            return self.value
    137
    138    class Clause:
    139        def __init__(self, dest):
    140            self.dest = dest
    141        def priority(self):
    142            return 0
    143        def process(self):
    144            pass
    145
    146    class AssignmentClause(Clause):
    147        def __init__(self, dest, value):
    148            KconfigData.Clause.__init__(self, dest)
    149            self.value = value
    150        def __str__(self):
    151            return "CONFIG_%s=%s" % (self.dest, 'y' if self.value else 'n')
    152
    153        def process(self):
    154            self.dest.set_value(self.value, self)
    155
    156    class DefaultClause(Clause):
    157        def __init__(self, dest, value, cond=None):
    158            KconfigData.Clause.__init__(self, dest)
    159            self.value = value
    160            self.cond = cond
    161            if not (self.cond is None):
    162                self.cond.add_edges_to(self.dest)
    163        def __str__(self):
    164            value = 'y' if self.value else 'n'
    165            if self.cond is None:
    166                return "config %s default %s" % (self.dest, value)
    167            else:
    168                return "config %s default %s if %s" % (self.dest, value, self.cond)
    169
    170        def priority(self):
    171            # Defaults are processed just before leaving the variable
    172            return -1
    173        def process(self):
    174            if not self.dest.has_value() and \
    175                    (self.cond is None or self.cond.evaluate()):
    176                self.dest.set_value(self.value, self)
    177
    178    class DependsOnClause(Clause):
    179        def __init__(self, dest, expr):
    180            KconfigData.Clause.__init__(self, dest)
    181            self.expr = expr
    182            self.expr.add_edges_to(self.dest)
    183        def __str__(self):
    184            return "config %s depends on %s" % (self.dest, self.expr)
    185
    186        def process(self):
    187            if not self.expr.evaluate():
    188                self.dest.set_value(False, self)
    189
    190    class SelectClause(Clause):
    191        def __init__(self, dest, cond):
    192            KconfigData.Clause.__init__(self, dest)
    193            self.cond = cond
    194            self.cond.add_edges_to(self.dest)
    195        def __str__(self):
    196            return "select %s if %s" % (self.dest, self.cond)
    197
    198        def process(self):
    199            if self.cond.evaluate():
    200                self.dest.set_value(True, self)
    201
    202    def __init__(self, value_mangler=defconfig):
    203        self.value_mangler = value_mangler
    204        self.previously_included = []
    205        self.incl_info = None
    206        self.defined_vars = set()
    207        self.referenced_vars = dict()
    208        self.clauses = list()
    209
    210    # semantic analysis -------------
    211
    212    def check_undefined(self):
    213        undef = False
    214        for i in self.referenced_vars:
    215            if not (i in self.defined_vars):
    216                print("undefined symbol %s" % (i), file=sys.stderr)
    217                undef = True
    218        return undef
    219
    220    def compute_config(self):
    221        if self.check_undefined():
    222            raise KconfigDataError("there were undefined symbols")
    223            return None
    224
    225        debug_print("Input:")
    226        for clause in self.clauses:
    227            debug_print(clause)
    228
    229        debug_print("\nDependency graph:")
    230        for i in self.referenced_vars:
    231            debug_print(i, "->", [str(x) for x in self.referenced_vars[i].outgoing])
    232
    233        # The reverse of the depth-first order is the topological sort
    234        dfo = dict()
    235        visited = set()
    236        debug_print("\n")
    237        def visit_fn(var):
    238            debug_print(var, "has DFS number", len(dfo))
    239            dfo[var] = len(dfo)
    240
    241        for name, v in self.referenced_vars.items():
    242            self.do_default(v, False)
    243            v.dfs(visited, visit_fn)
    244
    245        # Put higher DFS numbers and higher priorities first.  This
    246        # places the clauses in topological order and places defaults
    247        # after assignments and dependencies.
    248        self.clauses.sort(key=lambda x: (-dfo[x.dest], -x.priority()))
    249
    250        debug_print("\nSorted clauses:")
    251        for clause in self.clauses:
    252            debug_print(clause)
    253            clause.process()
    254
    255        debug_print("")
    256        values = dict()
    257        for name, v in self.referenced_vars.items():
    258            debug_print("Evaluating", name)
    259            values[name] = v.evaluate()
    260
    261        return values
    262
    263    # semantic actions -------------
    264
    265    def do_declaration(self, var):
    266        if (var in self.defined_vars):
    267            raise KconfigDataError('variable "' + var + '" defined twice')
    268
    269        self.defined_vars.add(var.name)
    270
    271    # var is a string with the variable's name.
    272    def do_var(self, var):
    273        if (var in self.referenced_vars):
    274            return self.referenced_vars[var]
    275
    276        var_obj = self.referenced_vars[var] = KconfigData.Var(var)
    277        return var_obj
    278
    279    def do_assignment(self, var, val):
    280        self.clauses.append(KconfigData.AssignmentClause(var, val))
    281
    282    def do_default(self, var, val, cond=None):
    283        val = self.value_mangler(val)
    284        self.clauses.append(KconfigData.DefaultClause(var, val, cond))
    285
    286    def do_depends_on(self, var, expr):
    287        self.clauses.append(KconfigData.DependsOnClause(var, expr))
    288
    289    def do_select(self, var, symbol, cond=None):
    290        cond = (cond & var) if cond is not None else var
    291        self.clauses.append(KconfigData.SelectClause(symbol, cond))
    292
    293    def do_imply(self, var, symbol, cond=None):
    294        # "config X imply Y [if COND]" is the same as
    295        # "config Y default y if X [&& COND]"
    296        cond = (cond & var) if cond is not None else var
    297        self.do_default(symbol, True, cond)
    298
    299# -------------------------------------------
    300# KconfigParser implements a recursive descent parser for (simplified)
    301# Kconfig syntax.
    302# -------------------------------------------
    303
    304# tokens table
    305TOKENS = {}
    306TOK_NONE = -1
    307TOK_LPAREN = 0;   TOKENS[TOK_LPAREN] = '"("';
    308TOK_RPAREN = 1;   TOKENS[TOK_RPAREN] = '")"';
    309TOK_EQUAL = 2;    TOKENS[TOK_EQUAL] = '"="';
    310TOK_AND = 3;      TOKENS[TOK_AND] = '"&&"';
    311TOK_OR = 4;       TOKENS[TOK_OR] = '"||"';
    312TOK_NOT = 5;      TOKENS[TOK_NOT] = '"!"';
    313TOK_DEPENDS = 6;  TOKENS[TOK_DEPENDS] = '"depends"';
    314TOK_ON = 7;       TOKENS[TOK_ON] = '"on"';
    315TOK_SELECT = 8;   TOKENS[TOK_SELECT] = '"select"';
    316TOK_IMPLY = 9;    TOKENS[TOK_IMPLY] = '"imply"';
    317TOK_CONFIG = 10;  TOKENS[TOK_CONFIG] = '"config"';
    318TOK_DEFAULT = 11; TOKENS[TOK_DEFAULT] = '"default"';
    319TOK_Y = 12;       TOKENS[TOK_Y] = '"y"';
    320TOK_N = 13;       TOKENS[TOK_N] = '"n"';
    321TOK_SOURCE = 14;  TOKENS[TOK_SOURCE] = '"source"';
    322TOK_BOOL = 15;    TOKENS[TOK_BOOL] = '"bool"';
    323TOK_IF = 16;      TOKENS[TOK_IF] = '"if"';
    324TOK_ID = 17;      TOKENS[TOK_ID] = 'identifier';
    325TOK_EOF = 18;     TOKENS[TOK_EOF] = 'end of file';
    326
    327class KconfigParserError(Exception):
    328    def __init__(self, parser, msg, tok=None):
    329        self.loc = parser.location()
    330        tok = tok or parser.tok
    331        if tok != TOK_NONE:
    332            location = TOKENS.get(tok, None) or ('"%s"' % tok)
    333            msg = '%s before %s' % (msg, location)
    334        self.msg = msg
    335
    336    def __str__(self):
    337        return "%s: %s" % (self.loc, self.msg)
    338
    339class KconfigParser:
    340
    341    @classmethod
    342    def parse(self, fp, mode=None):
    343        data = KconfigData(mode or KconfigParser.defconfig)
    344        parser = KconfigParser(data)
    345        parser.parse_file(fp)
    346        return data
    347
    348    def __init__(self, data):
    349        self.data = data
    350
    351    def parse_file(self, fp):
    352        self.abs_fname = os.path.abspath(fp.name)
    353        self.fname = fp.name
    354        self.data.previously_included.append(self.abs_fname)
    355        self.src = fp.read()
    356        if self.src == '' or self.src[-1] != '\n':
    357            self.src += '\n'
    358        self.cursor = 0
    359        self.line = 1
    360        self.line_pos = 0
    361        self.get_token()
    362        self.parse_config()
    363
    364    def do_assignment(self, var, val):
    365        if not var.startswith("CONFIG_"):
    366            raise Error('assigned variable should start with CONFIG_')
    367        var = self.data.do_var(var[7:])
    368        self.data.do_assignment(var, val)
    369
    370    # file management -----
    371
    372    def error_path(self):
    373        inf = self.data.incl_info
    374        res = ""
    375        while inf:
    376            res = ("In file included from %s:%d:\n" % (inf['file'],
    377                                                       inf['line'])) + res
    378            inf = inf['parent']
    379        return res
    380
    381    def location(self):
    382        col = 1
    383        for ch in self.src[self.line_pos:self.pos]:
    384            if ch == '\t':
    385                col += 8 - ((col - 1) % 8)
    386            else:
    387                col += 1
    388        return '%s%s:%d:%d' %(self.error_path(), self.fname, self.line, col)
    389
    390    def do_include(self, include):
    391        incl_abs_fname = os.path.join(os.path.dirname(self.abs_fname),
    392                                      include)
    393        # catch inclusion cycle
    394        inf = self.data.incl_info
    395        while inf:
    396            if incl_abs_fname == os.path.abspath(inf['file']):
    397                raise KconfigParserError(self, "Inclusion loop for %s"
    398                                    % include)
    399            inf = inf['parent']
    400
    401        # skip multiple include of the same file
    402        if incl_abs_fname in self.data.previously_included:
    403            return
    404        try:
    405            fp = open(incl_abs_fname, 'rt', encoding='utf-8')
    406        except IOError as e:
    407            raise KconfigParserError(self,
    408                                '%s: %s' % (e.strerror, include))
    409
    410        inf = self.data.incl_info
    411        self.data.incl_info = { 'file': self.fname, 'line': self.line,
    412                'parent': inf }
    413        KconfigParser(self.data).parse_file(fp)
    414        self.data.incl_info = inf
    415
    416    # recursive descent parser -----
    417
    418    # y_or_n: Y | N
    419    def parse_y_or_n(self):
    420        if self.tok == TOK_Y:
    421            self.get_token()
    422            return True
    423        if self.tok == TOK_N:
    424            self.get_token()
    425            return False
    426        raise KconfigParserError(self, 'Expected "y" or "n"')
    427
    428    # var: ID
    429    def parse_var(self):
    430        if self.tok == TOK_ID:
    431            val = self.val
    432            self.get_token()
    433            return self.data.do_var(val)
    434        else:
    435            raise KconfigParserError(self, 'Expected identifier')
    436
    437    # assignment_var: ID (starting with "CONFIG_")
    438    def parse_assignment_var(self):
    439        if self.tok == TOK_ID:
    440            val = self.val
    441            if not val.startswith("CONFIG_"):
    442                raise KconfigParserError(self,
    443                           'Expected identifier starting with "CONFIG_"', TOK_NONE)
    444            self.get_token()
    445            return self.data.do_var(val[7:])
    446        else:
    447            raise KconfigParserError(self, 'Expected identifier')
    448
    449    # assignment: var EQUAL y_or_n
    450    def parse_assignment(self):
    451        var = self.parse_assignment_var()
    452        if self.tok != TOK_EQUAL:
    453            raise KconfigParserError(self, 'Expected "="')
    454        self.get_token()
    455        self.data.do_assignment(var, self.parse_y_or_n())
    456
    457    # primary: NOT primary
    458    #       | LPAREN expr RPAREN
    459    #       | var
    460    def parse_primary(self):
    461        if self.tok == TOK_NOT:
    462            self.get_token()
    463            val = ~self.parse_primary()
    464        elif self.tok == TOK_LPAREN:
    465            self.get_token()
    466            val = self.parse_expr()
    467            if self.tok != TOK_RPAREN:
    468                raise KconfigParserError(self, 'Expected ")"')
    469            self.get_token()
    470        elif self.tok == TOK_ID:
    471            val = self.parse_var()
    472        else:
    473            raise KconfigParserError(self, 'Expected "!" or "(" or identifier')
    474        return val
    475
    476    # disj: primary (OR primary)*
    477    def parse_disj(self):
    478        lhs = self.parse_primary()
    479        while self.tok == TOK_OR:
    480            self.get_token()
    481            lhs = lhs | self.parse_primary()
    482        return lhs
    483
    484    # expr: disj (AND disj)*
    485    def parse_expr(self):
    486        lhs = self.parse_disj()
    487        while self.tok == TOK_AND:
    488            self.get_token()
    489            lhs = lhs & self.parse_disj()
    490        return lhs
    491
    492    # condition: IF expr
    493    #       | empty
    494    def parse_condition(self):
    495        if self.tok == TOK_IF:
    496            self.get_token()
    497            return self.parse_expr()
    498        else:
    499            return None
    500
    501    # property: DEFAULT y_or_n condition
    502    #       | DEPENDS ON expr
    503    #       | SELECT var condition
    504    #       | BOOL
    505    def parse_property(self, var):
    506        if self.tok == TOK_DEFAULT:
    507            self.get_token()
    508            val = self.parse_y_or_n()
    509            cond = self.parse_condition()
    510            self.data.do_default(var, val, cond)
    511        elif self.tok == TOK_DEPENDS:
    512            self.get_token()
    513            if self.tok != TOK_ON:
    514                raise KconfigParserError(self, 'Expected "on"')
    515            self.get_token()
    516            self.data.do_depends_on(var, self.parse_expr())
    517        elif self.tok == TOK_SELECT:
    518            self.get_token()
    519            symbol = self.parse_var()
    520            cond = self.parse_condition()
    521            self.data.do_select(var, symbol, cond)
    522        elif self.tok == TOK_IMPLY:
    523            self.get_token()
    524            symbol = self.parse_var()
    525            cond = self.parse_condition()
    526            self.data.do_imply(var, symbol, cond)
    527        elif self.tok == TOK_BOOL:
    528            self.get_token()
    529        else:
    530            raise KconfigParserError(self, 'Error in recursive descent?')
    531
    532    # properties: properties property
    533    #       | /* empty */
    534    def parse_properties(self, var):
    535        had_default = False
    536        while self.tok == TOK_DEFAULT or self.tok == TOK_DEPENDS or \
    537              self.tok == TOK_SELECT or self.tok == TOK_BOOL or \
    538              self.tok == TOK_IMPLY:
    539            self.parse_property(var)
    540
    541        # for nicer error message
    542        if self.tok != TOK_SOURCE and self.tok != TOK_CONFIG and \
    543           self.tok != TOK_ID and self.tok != TOK_EOF:
    544            raise KconfigParserError(self, 'expected "source", "config", identifier, '
    545                    + '"default", "depends on", "imply" or "select"')
    546
    547    # declaration: config var properties
    548    def parse_declaration(self):
    549        if self.tok == TOK_CONFIG:
    550            self.get_token()
    551            var = self.parse_var()
    552            self.data.do_declaration(var)
    553            self.parse_properties(var)
    554        else:
    555            raise KconfigParserError(self, 'Error in recursive descent?')
    556
    557    # clause: SOURCE
    558    #       | declaration
    559    #       | assignment
    560    def parse_clause(self):
    561        if self.tok == TOK_SOURCE:
    562            val = self.val
    563            self.get_token()
    564            self.do_include(val)
    565        elif self.tok == TOK_CONFIG:
    566            self.parse_declaration()
    567        elif self.tok == TOK_ID:
    568            self.parse_assignment()
    569        else:
    570            raise KconfigParserError(self, 'expected "source", "config" or identifier')
    571
    572    # config: clause+ EOF
    573    def parse_config(self):
    574        while self.tok != TOK_EOF:
    575            self.parse_clause()
    576        return self.data
    577
    578    # scanner -----
    579
    580    def get_token(self):
    581        while True:
    582            self.tok = self.src[self.cursor]
    583            self.pos = self.cursor
    584            self.cursor += 1
    585
    586            self.val = None
    587            self.tok = self.scan_token()
    588            if self.tok is not None:
    589                return
    590
    591    def check_keyword(self, rest):
    592        if not self.src.startswith(rest, self.cursor):
    593            return False
    594        length = len(rest)
    595        if self.src[self.cursor + length].isalnum() or self.src[self.cursor + length] == '_':
    596            return False
    597        self.cursor += length
    598        return True
    599
    600    def scan_token(self):
    601        if self.tok == '#':
    602            self.cursor = self.src.find('\n', self.cursor)
    603            return None
    604        elif self.tok == '=':
    605            return TOK_EQUAL
    606        elif self.tok == '(':
    607            return TOK_LPAREN
    608        elif self.tok == ')':
    609            return TOK_RPAREN
    610        elif self.tok == '&' and self.src[self.pos+1] == '&':
    611            self.cursor += 1
    612            return TOK_AND
    613        elif self.tok == '|' and self.src[self.pos+1] == '|':
    614            self.cursor += 1
    615            return TOK_OR
    616        elif self.tok == '!':
    617            return TOK_NOT
    618        elif self.tok == 'd' and self.check_keyword("epends"):
    619            return TOK_DEPENDS
    620        elif self.tok == 'o' and self.check_keyword("n"):
    621            return TOK_ON
    622        elif self.tok == 's' and self.check_keyword("elect"):
    623            return TOK_SELECT
    624        elif self.tok == 'i' and self.check_keyword("mply"):
    625            return TOK_IMPLY
    626        elif self.tok == 'c' and self.check_keyword("onfig"):
    627            return TOK_CONFIG
    628        elif self.tok == 'd' and self.check_keyword("efault"):
    629            return TOK_DEFAULT
    630        elif self.tok == 'b' and self.check_keyword("ool"):
    631            return TOK_BOOL
    632        elif self.tok == 'i' and self.check_keyword("f"):
    633            return TOK_IF
    634        elif self.tok == 'y' and self.check_keyword(""):
    635            return TOK_Y
    636        elif self.tok == 'n' and self.check_keyword(""):
    637            return TOK_N
    638        elif (self.tok == 's' and self.check_keyword("ource")) or \
    639              self.tok == 'i' and self.check_keyword("nclude"):
    640            # source FILENAME
    641            # include FILENAME
    642            while self.src[self.cursor].isspace():
    643                self.cursor += 1
    644            start = self.cursor
    645            self.cursor = self.src.find('\n', self.cursor)
    646            self.val = self.src[start:self.cursor]
    647            return TOK_SOURCE
    648        elif self.tok.isalnum():
    649            # identifier
    650            while self.src[self.cursor].isalnum() or self.src[self.cursor] == '_':
    651                self.cursor += 1
    652            self.val = self.src[self.pos:self.cursor]
    653            return TOK_ID
    654        elif self.tok == '\n':
    655            if self.cursor == len(self.src):
    656                return TOK_EOF
    657            self.line += 1
    658            self.line_pos = self.cursor
    659        elif not self.tok.isspace():
    660            raise KconfigParserError(self, 'invalid input')
    661
    662        return None
    663
    664if __name__ == '__main__':
    665    argv = sys.argv
    666    mode = defconfig
    667    if len(sys.argv) > 1:
    668        if argv[1] == '--defconfig':
    669            del argv[1]
    670        elif argv[1] == '--randconfig':
    671            random.seed()
    672            mode = randconfig
    673            del argv[1]
    674        elif argv[1] == '--allyesconfig':
    675            mode = allyesconfig
    676            del argv[1]
    677        elif argv[1] == '--allnoconfig':
    678            mode = allnoconfig
    679            del argv[1]
    680
    681    if len(argv) == 1:
    682        print ("%s: at least one argument is required" % argv[0], file=sys.stderr)
    683        sys.exit(1)
    684
    685    if argv[1].startswith('-'):
    686        print ("%s: invalid option %s" % (argv[0], argv[1]), file=sys.stderr)
    687        sys.exit(1)
    688
    689    data = KconfigData(mode)
    690    parser = KconfigParser(data)
    691    external_vars = set()
    692    for arg in argv[3:]:
    693        m = re.match(r'^(CONFIG_[A-Z0-9_]+)=([yn]?)$', arg)
    694        if m is not None:
    695            name, value = m.groups()
    696            parser.do_assignment(name, value == 'y')
    697            external_vars.add(name[7:])
    698        else:
    699            fp = open(arg, 'rt', encoding='utf-8')
    700            parser.parse_file(fp)
    701            fp.close()
    702
    703    config = data.compute_config()
    704    for key in sorted(config.keys()):
    705        if key not in external_vars and config[key]:
    706            print ('CONFIG_%s=y' % key)
    707
    708    deps = open(argv[2], 'wt', encoding='utf-8')
    709    for fname in data.previously_included:
    710        print ('%s: %s' % (argv[1], fname), file=deps)
    711    deps.close()