|  | #!/usr/bin/env python3 | 
|  | # | 
|  | # Mini-Kconfig parser | 
|  | # | 
|  | # Copyright (c) 2015 Red Hat Inc. | 
|  | # | 
|  | # Authors: | 
|  | #  Paolo Bonzini <pbonzini@redhat.com> | 
|  | # | 
|  | # This work is licensed under the terms of the GNU GPL, version 2 | 
|  | # or, at your option, any later version.  See the COPYING file in | 
|  | # the top-level directory. | 
|  |  | 
|  | import os | 
|  | import sys | 
|  | import re | 
|  | import random | 
|  |  | 
|  | __all__ = [ 'KconfigDataError', 'KconfigParserError', | 
|  | 'KconfigData', 'KconfigParser' , | 
|  | 'defconfig', 'allyesconfig', 'allnoconfig', 'randconfig' ] | 
|  |  | 
|  | def debug_print(*args): | 
|  | #print('# ' + (' '.join(str(x) for x in args))) | 
|  | pass | 
|  |  | 
|  | # ------------------------------------------- | 
|  | # KconfigData implements the Kconfig semantics.  For now it can only | 
|  | # detect undefined symbols, i.e. symbols that were referenced in | 
|  | # assignments or dependencies but were not declared with "config FOO". | 
|  | # | 
|  | # Semantic actions are represented by methods called do_*.  The do_var | 
|  | # method return the semantic value of a variable (which right now is | 
|  | # just its name). | 
|  | # ------------------------------------------- | 
|  |  | 
|  | class KconfigDataError(Exception): | 
|  | def __init__(self, msg): | 
|  | self.msg = msg | 
|  |  | 
|  | def __str__(self): | 
|  | return self.msg | 
|  |  | 
|  | allyesconfig = lambda x: True | 
|  | allnoconfig = lambda x: False | 
|  | defconfig = lambda x: x | 
|  | randconfig = lambda x: random.randint(0, 1) == 1 | 
|  |  | 
|  | class KconfigData: | 
|  | class Expr: | 
|  | def __and__(self, rhs): | 
|  | return KconfigData.AND(self, rhs) | 
|  | def __or__(self, rhs): | 
|  | return KconfigData.OR(self, rhs) | 
|  | def __invert__(self): | 
|  | return KconfigData.NOT(self) | 
|  |  | 
|  | # Abstract methods | 
|  | def add_edges_to(self, var): | 
|  | pass | 
|  | def evaluate(self): | 
|  | assert False | 
|  |  | 
|  | class AND(Expr): | 
|  | def __init__(self, lhs, rhs): | 
|  | self.lhs = lhs | 
|  | self.rhs = rhs | 
|  | def __str__(self): | 
|  | return "(%s && %s)" % (self.lhs, self.rhs) | 
|  |  | 
|  | def add_edges_to(self, var): | 
|  | self.lhs.add_edges_to(var) | 
|  | self.rhs.add_edges_to(var) | 
|  | def evaluate(self): | 
|  | return self.lhs.evaluate() and self.rhs.evaluate() | 
|  |  | 
|  | class OR(Expr): | 
|  | def __init__(self, lhs, rhs): | 
|  | self.lhs = lhs | 
|  | self.rhs = rhs | 
|  | def __str__(self): | 
|  | return "(%s || %s)" % (self.lhs, self.rhs) | 
|  |  | 
|  | def add_edges_to(self, var): | 
|  | self.lhs.add_edges_to(var) | 
|  | self.rhs.add_edges_to(var) | 
|  | def evaluate(self): | 
|  | return self.lhs.evaluate() or self.rhs.evaluate() | 
|  |  | 
|  | class NOT(Expr): | 
|  | def __init__(self, lhs): | 
|  | self.lhs = lhs | 
|  | def __str__(self): | 
|  | return "!%s" % (self.lhs) | 
|  |  | 
|  | def add_edges_to(self, var): | 
|  | self.lhs.add_edges_to(var) | 
|  | def evaluate(self): | 
|  | return not self.lhs.evaluate() | 
|  |  | 
|  | class Var(Expr): | 
|  | def __init__(self, name): | 
|  | self.name = name | 
|  | self.value = None | 
|  | self.outgoing = set() | 
|  | self.clauses_for_var = list() | 
|  | def __str__(self): | 
|  | return self.name | 
|  |  | 
|  | def has_value(self): | 
|  | return not (self.value is None) | 
|  | def set_value(self, val, clause): | 
|  | self.clauses_for_var.append(clause) | 
|  | if self.has_value() and self.value != val: | 
|  | print("The following clauses were found for " + self.name) | 
|  | for i in self.clauses_for_var: | 
|  | print("    " + str(i), file=sys.stderr) | 
|  | raise KconfigDataError('contradiction between clauses when setting %s' % self) | 
|  | debug_print("=> %s is now %s" % (self.name, val)) | 
|  | self.value = val | 
|  |  | 
|  | # depth first search of the dependency graph | 
|  | def dfs(self, visited, f): | 
|  | if self in visited: | 
|  | return | 
|  | visited.add(self) | 
|  | for v in self.outgoing: | 
|  | v.dfs(visited, f) | 
|  | f(self) | 
|  |  | 
|  | def add_edges_to(self, var): | 
|  | self.outgoing.add(var) | 
|  | def evaluate(self): | 
|  | if not self.has_value(): | 
|  | raise KconfigDataError('cycle found including %s' % self) | 
|  | return self.value | 
|  |  | 
|  | class Clause: | 
|  | def __init__(self, dest): | 
|  | self.dest = dest | 
|  | def priority(self): | 
|  | return 0 | 
|  | def process(self): | 
|  | pass | 
|  |  | 
|  | class AssignmentClause(Clause): | 
|  | def __init__(self, dest, value): | 
|  | KconfigData.Clause.__init__(self, dest) | 
|  | self.value = value | 
|  | def __str__(self): | 
|  | return "CONFIG_%s=%s" % (self.dest, 'y' if self.value else 'n') | 
|  |  | 
|  | def process(self): | 
|  | self.dest.set_value(self.value, self) | 
|  |  | 
|  | class DefaultClause(Clause): | 
|  | def __init__(self, dest, value, cond=None): | 
|  | KconfigData.Clause.__init__(self, dest) | 
|  | self.value = value | 
|  | self.cond = cond | 
|  | if not (self.cond is None): | 
|  | self.cond.add_edges_to(self.dest) | 
|  | def __str__(self): | 
|  | value = 'y' if self.value else 'n' | 
|  | if self.cond is None: | 
|  | return "config %s default %s" % (self.dest, value) | 
|  | else: | 
|  | return "config %s default %s if %s" % (self.dest, value, self.cond) | 
|  |  | 
|  | def priority(self): | 
|  | # Defaults are processed just before leaving the variable | 
|  | return -1 | 
|  | def process(self): | 
|  | if not self.dest.has_value() and \ | 
|  | (self.cond is None or self.cond.evaluate()): | 
|  | self.dest.set_value(self.value, self) | 
|  |  | 
|  | class DependsOnClause(Clause): | 
|  | def __init__(self, dest, expr): | 
|  | KconfigData.Clause.__init__(self, dest) | 
|  | self.expr = expr | 
|  | self.expr.add_edges_to(self.dest) | 
|  | def __str__(self): | 
|  | return "config %s depends on %s" % (self.dest, self.expr) | 
|  |  | 
|  | def process(self): | 
|  | if not self.expr.evaluate(): | 
|  | self.dest.set_value(False, self) | 
|  |  | 
|  | class SelectClause(Clause): | 
|  | def __init__(self, dest, cond): | 
|  | KconfigData.Clause.__init__(self, dest) | 
|  | self.cond = cond | 
|  | self.cond.add_edges_to(self.dest) | 
|  | def __str__(self): | 
|  | return "select %s if %s" % (self.dest, self.cond) | 
|  |  | 
|  | def process(self): | 
|  | if self.cond.evaluate(): | 
|  | self.dest.set_value(True, self) | 
|  |  | 
|  | def __init__(self, value_mangler=defconfig): | 
|  | self.value_mangler = value_mangler | 
|  | self.previously_included = [] | 
|  | self.incl_info = None | 
|  | self.defined_vars = set() | 
|  | self.referenced_vars = dict() | 
|  | self.clauses = list() | 
|  |  | 
|  | # semantic analysis ------------- | 
|  |  | 
|  | def check_undefined(self): | 
|  | undef = False | 
|  | for i in self.referenced_vars: | 
|  | if not (i in self.defined_vars): | 
|  | print("undefined symbol %s" % (i), file=sys.stderr) | 
|  | undef = True | 
|  | return undef | 
|  |  | 
|  | def compute_config(self): | 
|  | if self.check_undefined(): | 
|  | raise KconfigDataError("there were undefined symbols") | 
|  | return None | 
|  |  | 
|  | debug_print("Input:") | 
|  | for clause in self.clauses: | 
|  | debug_print(clause) | 
|  |  | 
|  | debug_print("\nDependency graph:") | 
|  | for i in self.referenced_vars: | 
|  | debug_print(i, "->", [str(x) for x in self.referenced_vars[i].outgoing]) | 
|  |  | 
|  | # The reverse of the depth-first order is the topological sort | 
|  | dfo = dict() | 
|  | visited = set() | 
|  | debug_print("\n") | 
|  | def visit_fn(var): | 
|  | debug_print(var, "has DFS number", len(dfo)) | 
|  | dfo[var] = len(dfo) | 
|  |  | 
|  | for name, v in self.referenced_vars.items(): | 
|  | self.do_default(v, False) | 
|  | v.dfs(visited, visit_fn) | 
|  |  | 
|  | # Put higher DFS numbers and higher priorities first.  This | 
|  | # places the clauses in topological order and places defaults | 
|  | # after assignments and dependencies. | 
|  | self.clauses.sort(key=lambda x: (-dfo[x.dest], -x.priority())) | 
|  |  | 
|  | debug_print("\nSorted clauses:") | 
|  | for clause in self.clauses: | 
|  | debug_print(clause) | 
|  | clause.process() | 
|  |  | 
|  | debug_print("") | 
|  | values = dict() | 
|  | for name, v in self.referenced_vars.items(): | 
|  | debug_print("Evaluating", name) | 
|  | values[name] = v.evaluate() | 
|  |  | 
|  | return values | 
|  |  | 
|  | # semantic actions ------------- | 
|  |  | 
|  | def do_declaration(self, var): | 
|  | if (var in self.defined_vars): | 
|  | raise KconfigDataError('variable "' + var + '" defined twice') | 
|  |  | 
|  | self.defined_vars.add(var.name) | 
|  |  | 
|  | # var is a string with the variable's name. | 
|  | def do_var(self, var): | 
|  | if (var in self.referenced_vars): | 
|  | return self.referenced_vars[var] | 
|  |  | 
|  | var_obj = self.referenced_vars[var] = KconfigData.Var(var) | 
|  | return var_obj | 
|  |  | 
|  | def do_assignment(self, var, val): | 
|  | self.clauses.append(KconfigData.AssignmentClause(var, val)) | 
|  |  | 
|  | def do_default(self, var, val, cond=None): | 
|  | val = self.value_mangler(val) | 
|  | self.clauses.append(KconfigData.DefaultClause(var, val, cond)) | 
|  |  | 
|  | def do_depends_on(self, var, expr): | 
|  | self.clauses.append(KconfigData.DependsOnClause(var, expr)) | 
|  |  | 
|  | def do_select(self, var, symbol, cond=None): | 
|  | cond = (cond & var) if cond is not None else var | 
|  | self.clauses.append(KconfigData.SelectClause(symbol, cond)) | 
|  |  | 
|  | def do_imply(self, var, symbol, cond=None): | 
|  | # "config X imply Y [if COND]" is the same as | 
|  | # "config Y default y if X [&& COND]" | 
|  | cond = (cond & var) if cond is not None else var | 
|  | self.do_default(symbol, True, cond) | 
|  |  | 
|  | # ------------------------------------------- | 
|  | # KconfigParser implements a recursive descent parser for (simplified) | 
|  | # Kconfig syntax. | 
|  | # ------------------------------------------- | 
|  |  | 
|  | # tokens table | 
|  | TOKENS = {} | 
|  | TOK_NONE = -1 | 
|  | TOK_LPAREN = 0;   TOKENS[TOK_LPAREN] = '"("'; | 
|  | TOK_RPAREN = 1;   TOKENS[TOK_RPAREN] = '")"'; | 
|  | TOK_EQUAL = 2;    TOKENS[TOK_EQUAL] = '"="'; | 
|  | TOK_AND = 3;      TOKENS[TOK_AND] = '"&&"'; | 
|  | TOK_OR = 4;       TOKENS[TOK_OR] = '"||"'; | 
|  | TOK_NOT = 5;      TOKENS[TOK_NOT] = '"!"'; | 
|  | TOK_DEPENDS = 6;  TOKENS[TOK_DEPENDS] = '"depends"'; | 
|  | TOK_ON = 7;       TOKENS[TOK_ON] = '"on"'; | 
|  | TOK_SELECT = 8;   TOKENS[TOK_SELECT] = '"select"'; | 
|  | TOK_IMPLY = 9;    TOKENS[TOK_IMPLY] = '"imply"'; | 
|  | TOK_CONFIG = 10;  TOKENS[TOK_CONFIG] = '"config"'; | 
|  | TOK_DEFAULT = 11; TOKENS[TOK_DEFAULT] = '"default"'; | 
|  | TOK_Y = 12;       TOKENS[TOK_Y] = '"y"'; | 
|  | TOK_N = 13;       TOKENS[TOK_N] = '"n"'; | 
|  | TOK_SOURCE = 14;  TOKENS[TOK_SOURCE] = '"source"'; | 
|  | TOK_BOOL = 15;    TOKENS[TOK_BOOL] = '"bool"'; | 
|  | TOK_IF = 16;      TOKENS[TOK_IF] = '"if"'; | 
|  | TOK_ID = 17;      TOKENS[TOK_ID] = 'identifier'; | 
|  | TOK_EOF = 18;     TOKENS[TOK_EOF] = 'end of file'; | 
|  |  | 
|  | class KconfigParserError(Exception): | 
|  | def __init__(self, parser, msg, tok=None): | 
|  | self.loc = parser.location() | 
|  | tok = tok or parser.tok | 
|  | if tok != TOK_NONE: | 
|  | location = TOKENS.get(tok, None) or ('"%s"' % tok) | 
|  | msg = '%s before %s' % (msg, location) | 
|  | self.msg = msg | 
|  |  | 
|  | def __str__(self): | 
|  | return "%s: %s" % (self.loc, self.msg) | 
|  |  | 
|  | class KconfigParser: | 
|  |  | 
|  | @classmethod | 
|  | def parse(self, fp, mode=None): | 
|  | data = KconfigData(mode or KconfigParser.defconfig) | 
|  | parser = KconfigParser(data) | 
|  | parser.parse_file(fp) | 
|  | return data | 
|  |  | 
|  | def __init__(self, data): | 
|  | self.data = data | 
|  |  | 
|  | def parse_file(self, fp): | 
|  | self.abs_fname = os.path.abspath(fp.name) | 
|  | self.fname = fp.name | 
|  | self.data.previously_included.append(self.abs_fname) | 
|  | self.src = fp.read() | 
|  | if self.src == '' or self.src[-1] != '\n': | 
|  | self.src += '\n' | 
|  | self.cursor = 0 | 
|  | self.line = 1 | 
|  | self.line_pos = 0 | 
|  | self.get_token() | 
|  | self.parse_config() | 
|  |  | 
|  | def do_assignment(self, var, val): | 
|  | if not var.startswith("CONFIG_"): | 
|  | raise Error('assigned variable should start with CONFIG_') | 
|  | var = self.data.do_var(var[7:]) | 
|  | self.data.do_assignment(var, val) | 
|  |  | 
|  | # file management ----- | 
|  |  | 
|  | def error_path(self): | 
|  | inf = self.data.incl_info | 
|  | res = "" | 
|  | while inf: | 
|  | res = ("In file included from %s:%d:\n" % (inf['file'], | 
|  | inf['line'])) + res | 
|  | inf = inf['parent'] | 
|  | return res | 
|  |  | 
|  | def location(self): | 
|  | col = 1 | 
|  | for ch in self.src[self.line_pos:self.pos]: | 
|  | if ch == '\t': | 
|  | col += 8 - ((col - 1) % 8) | 
|  | else: | 
|  | col += 1 | 
|  | return '%s%s:%d:%d' %(self.error_path(), self.fname, self.line, col) | 
|  |  | 
|  | def do_include(self, include): | 
|  | incl_abs_fname = os.path.join(os.path.dirname(self.abs_fname), | 
|  | include) | 
|  | # catch inclusion cycle | 
|  | inf = self.data.incl_info | 
|  | while inf: | 
|  | if incl_abs_fname == os.path.abspath(inf['file']): | 
|  | raise KconfigParserError(self, "Inclusion loop for %s" | 
|  | % include) | 
|  | inf = inf['parent'] | 
|  |  | 
|  | # skip multiple include of the same file | 
|  | if incl_abs_fname in self.data.previously_included: | 
|  | return | 
|  | try: | 
|  | fp = open(incl_abs_fname, 'rt', encoding='utf-8') | 
|  | except IOError as e: | 
|  | raise KconfigParserError(self, | 
|  | '%s: %s' % (e.strerror, include)) | 
|  |  | 
|  | inf = self.data.incl_info | 
|  | self.data.incl_info = { 'file': self.fname, 'line': self.line, | 
|  | 'parent': inf } | 
|  | KconfigParser(self.data).parse_file(fp) | 
|  | self.data.incl_info = inf | 
|  |  | 
|  | # recursive descent parser ----- | 
|  |  | 
|  | # y_or_n: Y | N | 
|  | def parse_y_or_n(self): | 
|  | if self.tok == TOK_Y: | 
|  | self.get_token() | 
|  | return True | 
|  | if self.tok == TOK_N: | 
|  | self.get_token() | 
|  | return False | 
|  | raise KconfigParserError(self, 'Expected "y" or "n"') | 
|  |  | 
|  | # var: ID | 
|  | def parse_var(self): | 
|  | if self.tok == TOK_ID: | 
|  | val = self.val | 
|  | self.get_token() | 
|  | return self.data.do_var(val) | 
|  | else: | 
|  | raise KconfigParserError(self, 'Expected identifier') | 
|  |  | 
|  | # assignment_var: ID (starting with "CONFIG_") | 
|  | def parse_assignment_var(self): | 
|  | if self.tok == TOK_ID: | 
|  | val = self.val | 
|  | if not val.startswith("CONFIG_"): | 
|  | raise KconfigParserError(self, | 
|  | 'Expected identifier starting with "CONFIG_"', TOK_NONE) | 
|  | self.get_token() | 
|  | return self.data.do_var(val[7:]) | 
|  | else: | 
|  | raise KconfigParserError(self, 'Expected identifier') | 
|  |  | 
|  | # assignment: var EQUAL y_or_n | 
|  | def parse_assignment(self): | 
|  | var = self.parse_assignment_var() | 
|  | if self.tok != TOK_EQUAL: | 
|  | raise KconfigParserError(self, 'Expected "="') | 
|  | self.get_token() | 
|  | self.data.do_assignment(var, self.parse_y_or_n()) | 
|  |  | 
|  | # primary: NOT primary | 
|  | #       | LPAREN expr RPAREN | 
|  | #       | var | 
|  | def parse_primary(self): | 
|  | if self.tok == TOK_NOT: | 
|  | self.get_token() | 
|  | val = ~self.parse_primary() | 
|  | elif self.tok == TOK_LPAREN: | 
|  | self.get_token() | 
|  | val = self.parse_expr() | 
|  | if self.tok != TOK_RPAREN: | 
|  | raise KconfigParserError(self, 'Expected ")"') | 
|  | self.get_token() | 
|  | elif self.tok == TOK_ID: | 
|  | val = self.parse_var() | 
|  | else: | 
|  | raise KconfigParserError(self, 'Expected "!" or "(" or identifier') | 
|  | return val | 
|  |  | 
|  | # disj: primary (OR primary)* | 
|  | def parse_disj(self): | 
|  | lhs = self.parse_primary() | 
|  | while self.tok == TOK_OR: | 
|  | self.get_token() | 
|  | lhs = lhs | self.parse_primary() | 
|  | return lhs | 
|  |  | 
|  | # expr: disj (AND disj)* | 
|  | def parse_expr(self): | 
|  | lhs = self.parse_disj() | 
|  | while self.tok == TOK_AND: | 
|  | self.get_token() | 
|  | lhs = lhs & self.parse_disj() | 
|  | return lhs | 
|  |  | 
|  | # condition: IF expr | 
|  | #       | empty | 
|  | def parse_condition(self): | 
|  | if self.tok == TOK_IF: | 
|  | self.get_token() | 
|  | return self.parse_expr() | 
|  | else: | 
|  | return None | 
|  |  | 
|  | # property: DEFAULT y_or_n condition | 
|  | #       | DEPENDS ON expr | 
|  | #       | SELECT var condition | 
|  | #       | BOOL | 
|  | def parse_property(self, var): | 
|  | if self.tok == TOK_DEFAULT: | 
|  | self.get_token() | 
|  | val = self.parse_y_or_n() | 
|  | cond = self.parse_condition() | 
|  | self.data.do_default(var, val, cond) | 
|  | elif self.tok == TOK_DEPENDS: | 
|  | self.get_token() | 
|  | if self.tok != TOK_ON: | 
|  | raise KconfigParserError(self, 'Expected "on"') | 
|  | self.get_token() | 
|  | self.data.do_depends_on(var, self.parse_expr()) | 
|  | elif self.tok == TOK_SELECT: | 
|  | self.get_token() | 
|  | symbol = self.parse_var() | 
|  | cond = self.parse_condition() | 
|  | self.data.do_select(var, symbol, cond) | 
|  | elif self.tok == TOK_IMPLY: | 
|  | self.get_token() | 
|  | symbol = self.parse_var() | 
|  | cond = self.parse_condition() | 
|  | self.data.do_imply(var, symbol, cond) | 
|  | elif self.tok == TOK_BOOL: | 
|  | self.get_token() | 
|  | else: | 
|  | raise KconfigParserError(self, 'Error in recursive descent?') | 
|  |  | 
|  | # properties: properties property | 
|  | #       | /* empty */ | 
|  | def parse_properties(self, var): | 
|  | had_default = False | 
|  | while self.tok == TOK_DEFAULT or self.tok == TOK_DEPENDS or \ | 
|  | self.tok == TOK_SELECT or self.tok == TOK_BOOL or \ | 
|  | self.tok == TOK_IMPLY: | 
|  | self.parse_property(var) | 
|  |  | 
|  | # for nicer error message | 
|  | if self.tok != TOK_SOURCE and self.tok != TOK_CONFIG and \ | 
|  | self.tok != TOK_ID and self.tok != TOK_EOF: | 
|  | raise KconfigParserError(self, 'expected "source", "config", identifier, ' | 
|  | + '"default", "depends on", "imply" or "select"') | 
|  |  | 
|  | # declaration: config var properties | 
|  | def parse_declaration(self): | 
|  | if self.tok == TOK_CONFIG: | 
|  | self.get_token() | 
|  | var = self.parse_var() | 
|  | self.data.do_declaration(var) | 
|  | self.parse_properties(var) | 
|  | else: | 
|  | raise KconfigParserError(self, 'Error in recursive descent?') | 
|  |  | 
|  | # clause: SOURCE | 
|  | #       | declaration | 
|  | #       | assignment | 
|  | def parse_clause(self): | 
|  | if self.tok == TOK_SOURCE: | 
|  | val = self.val | 
|  | self.get_token() | 
|  | self.do_include(val) | 
|  | elif self.tok == TOK_CONFIG: | 
|  | self.parse_declaration() | 
|  | elif self.tok == TOK_ID: | 
|  | self.parse_assignment() | 
|  | else: | 
|  | raise KconfigParserError(self, 'expected "source", "config" or identifier') | 
|  |  | 
|  | # config: clause+ EOF | 
|  | def parse_config(self): | 
|  | while self.tok != TOK_EOF: | 
|  | self.parse_clause() | 
|  | return self.data | 
|  |  | 
|  | # scanner ----- | 
|  |  | 
|  | def get_token(self): | 
|  | while True: | 
|  | self.tok = self.src[self.cursor] | 
|  | self.pos = self.cursor | 
|  | self.cursor += 1 | 
|  |  | 
|  | self.val = None | 
|  | self.tok = self.scan_token() | 
|  | if self.tok is not None: | 
|  | return | 
|  |  | 
|  | def check_keyword(self, rest): | 
|  | if not self.src.startswith(rest, self.cursor): | 
|  | return False | 
|  | length = len(rest) | 
|  | if self.src[self.cursor + length].isalnum() or self.src[self.cursor + length] == '_': | 
|  | return False | 
|  | self.cursor += length | 
|  | return True | 
|  |  | 
|  | def scan_token(self): | 
|  | if self.tok == '#': | 
|  | self.cursor = self.src.find('\n', self.cursor) | 
|  | return None | 
|  | elif self.tok == '=': | 
|  | return TOK_EQUAL | 
|  | elif self.tok == '(': | 
|  | return TOK_LPAREN | 
|  | elif self.tok == ')': | 
|  | return TOK_RPAREN | 
|  | elif self.tok == '&' and self.src[self.pos+1] == '&': | 
|  | self.cursor += 1 | 
|  | return TOK_AND | 
|  | elif self.tok == '|' and self.src[self.pos+1] == '|': | 
|  | self.cursor += 1 | 
|  | return TOK_OR | 
|  | elif self.tok == '!': | 
|  | return TOK_NOT | 
|  | elif self.tok == 'd' and self.check_keyword("epends"): | 
|  | return TOK_DEPENDS | 
|  | elif self.tok == 'o' and self.check_keyword("n"): | 
|  | return TOK_ON | 
|  | elif self.tok == 's' and self.check_keyword("elect"): | 
|  | return TOK_SELECT | 
|  | elif self.tok == 'i' and self.check_keyword("mply"): | 
|  | return TOK_IMPLY | 
|  | elif self.tok == 'c' and self.check_keyword("onfig"): | 
|  | return TOK_CONFIG | 
|  | elif self.tok == 'd' and self.check_keyword("efault"): | 
|  | return TOK_DEFAULT | 
|  | elif self.tok == 'b' and self.check_keyword("ool"): | 
|  | return TOK_BOOL | 
|  | elif self.tok == 'i' and self.check_keyword("f"): | 
|  | return TOK_IF | 
|  | elif self.tok == 'y' and self.check_keyword(""): | 
|  | return TOK_Y | 
|  | elif self.tok == 'n' and self.check_keyword(""): | 
|  | return TOK_N | 
|  | elif (self.tok == 's' and self.check_keyword("ource")) or \ | 
|  | self.tok == 'i' and self.check_keyword("nclude"): | 
|  | # source FILENAME | 
|  | # include FILENAME | 
|  | while self.src[self.cursor].isspace(): | 
|  | self.cursor += 1 | 
|  | start = self.cursor | 
|  | self.cursor = self.src.find('\n', self.cursor) | 
|  | self.val = self.src[start:self.cursor] | 
|  | return TOK_SOURCE | 
|  | elif self.tok.isalnum(): | 
|  | # identifier | 
|  | while self.src[self.cursor].isalnum() or self.src[self.cursor] == '_': | 
|  | self.cursor += 1 | 
|  | self.val = self.src[self.pos:self.cursor] | 
|  | return TOK_ID | 
|  | elif self.tok == '\n': | 
|  | if self.cursor == len(self.src): | 
|  | return TOK_EOF | 
|  | self.line += 1 | 
|  | self.line_pos = self.cursor | 
|  | elif not self.tok.isspace(): | 
|  | raise KconfigParserError(self, 'invalid input') | 
|  |  | 
|  | return None | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | argv = sys.argv | 
|  | mode = defconfig | 
|  | if len(sys.argv) > 1: | 
|  | if argv[1] == '--defconfig': | 
|  | del argv[1] | 
|  | elif argv[1] == '--randconfig': | 
|  | random.seed() | 
|  | mode = randconfig | 
|  | del argv[1] | 
|  | elif argv[1] == '--allyesconfig': | 
|  | mode = allyesconfig | 
|  | del argv[1] | 
|  | elif argv[1] == '--allnoconfig': | 
|  | mode = allnoconfig | 
|  | del argv[1] | 
|  |  | 
|  | if len(argv) == 1: | 
|  | print ("%s: at least one argument is required" % argv[0], file=sys.stderr) | 
|  | sys.exit(1) | 
|  |  | 
|  | if argv[1].startswith('-'): | 
|  | print ("%s: invalid option %s" % (argv[0], argv[1]), file=sys.stderr) | 
|  | sys.exit(1) | 
|  |  | 
|  | data = KconfigData(mode) | 
|  | parser = KconfigParser(data) | 
|  | external_vars = set() | 
|  | for arg in argv[3:]: | 
|  | m = re.match(r'^(CONFIG_[A-Z0-9_]+)=([yn]?)$', arg) | 
|  | if m is not None: | 
|  | name, value = m.groups() | 
|  | parser.do_assignment(name, value == 'y') | 
|  | external_vars.add(name[7:]) | 
|  | else: | 
|  | fp = open(arg, 'rt', encoding='utf-8') | 
|  | parser.parse_file(fp) | 
|  | fp.close() | 
|  |  | 
|  | config = data.compute_config() | 
|  | for key in sorted(config.keys()): | 
|  | if key not in external_vars and config[key]: | 
|  | print ('CONFIG_%s=y' % key) | 
|  |  | 
|  | deps = open(argv[2], 'wt', encoding='utf-8') | 
|  | for fname in data.previously_included: | 
|  | print ('%s: %s' % (argv[1], fname), file=deps) | 
|  | deps.close() |